// evOo Platform - Executive Mission Control Cockpit (app.js)
// Zero-Mockup Reactive Engine for Ingo Wagner & Autonomous Teammates
document.addEventListener("DOMContentLoaded", () => {
// Elements
const terminal = document.getElementById("terminal-logs");
const btnClear = document.getElementById("btn-clear-console");
const btnToggleConsole = document.getElementById("btn-toggle-console");
const cardConsole = document.querySelector(".card-console");
const btnSelfImprove = document.getElementById("btn-self-improve");
const btnKillswitch = document.getElementById("btn-killswitch");
const killswitchText = document.getElementById("killswitch-text");
const btnHotReload = document.getElementById("btn-hot-reload");
const btnOpenSteveChat = document.getElementById("btn-open-steve-chat");
const clockEl = document.getElementById("timestamp-clock");
const systemHealthVal = document.getElementById("system-health-val");
const _clusterSummaryVal = document.getElementById("cluster-summary-val");
const tradingSummaryVal = document.getElementById("trading-summary-val");
const approvalsContainer = document.getElementById("approvals-container");
const approvalsBadge = document.getElementById("approvals-badge");
const journalContainer = document.getElementById("journal-container");
const agentItems = document.querySelectorAll(".interactive-agent");
const _pnlVal = document.getElementById("pnl-val");
const _drawdownVal = document.getElementById("drawdown-val");
const _circuitBreakerVal = document.getElementById("circuit-breaker-val");
const tradingBadge = document.getElementById("trading-badge");
// Clock
function updateClock() {
const now = new Date();
clockEl.textContent = `${now.toISOString().replace("T", " ").substring(0, 19)} UTC`;
}
setInterval(updateClock, 1000);
updateClock();
// Helper: Strip ANSI color codes
const ansiCsi = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
function cleanAnsi(str) {
return str.replace(ansiCsi, "");
}
// Logger
function addLog(text, type = "info") {
if (!terminal) return;
const line = document.createElement("div");
const cleanText = cleanAnsi(text);
let lineType = type;
if (
cleanText.includes("[✓ PASS]") ||
cleanText.includes("[PASS]") ||
cleanText.includes("SUCCESS") ||
cleanText.includes("200 OK")
) {
lineType = "success";
} else if (
cleanText.includes("[✗ FAIL]") ||
cleanText.includes("FAILED") ||
cleanText.includes("Error")
) {
lineType = "error";
} else if (cleanText.includes("[⚠ WARN]") || cleanText.includes("WARN")) {
lineType = "warning";
}
line.className = `log-line ${lineType}`;
const timestamp = new Date().toTimeString().split(" ")[0];
line.textContent = `[${timestamp}] ${cleanText}`;
terminal.appendChild(line);
terminal.scrollTop = terminal.scrollHeight;
}
// Console Toggles
if (btnClear) {
btnClear.addEventListener("click", () => {
terminal.innerHTML = "";
addLog("Terminal gelöscht. Überwachung aktiv.", "info");
});
}
if (btnToggleConsole && cardConsole) {
btnToggleConsole.addEventListener("click", () => {
const isCollapsed = cardConsole.classList.toggle("collapsed");
btnToggleConsole.textContent = isCollapsed ? "Ausklappen" : "Minimieren";
});
}
// 1. Fetch & Render Approvals
async function loadApprovals() {
if (!approvalsContainer) return;
try {
const res = await fetch("/api/approvals");
if (!res.ok) return;
const data = await res.json();
const list = data.approvals || [];
const pending = list.filter((a) => a.status === "pending");
if (approvalsBadge) {
approvalsBadge.textContent =
pending.length > 0 ? `${pending.length} Ausstehend` : "Alle erledigt";
approvalsBadge.className = pending.length > 0 ? "badge badge-amber" : "badge badge-emerald";
}
approvalsContainer.innerHTML = "";
if (list.length === 0) {
approvalsContainer.innerHTML =
'
Keine ausstehenden Entscheidungen. System läuft autonom.
';
return;
}
list.forEach((item) => {
const div = document.createElement("div");
div.className = "approval-item";
div.setAttribute("data-id", item.id);
const isDecided = item.status !== "pending";
const statusClass = item.status === "approved" ? "badge-emerald" : "badge-amber";
div.innerHTML = `
${item.type}
${item.author}
${item.timestamp}
${isDecided ? `${item.status.toUpperCase()}` : ""}
${item.title}
${item.details}
Conviction: ${item.conviction}%
R:R: ${item.riskReward}
Umfang: ${item.size}
${
!isDecided
? `
`
: ""
}
`;
approvalsContainer.appendChild(div);
});
// Attach handlers to actions
approvalsContainer.querySelectorAll(".btn-approve, .btn-veto").forEach((btn) => {
btn.addEventListener("click", async (e) => {
const id = e.target.getAttribute("data-id");
const action = e.target.getAttribute("data-action");
try {
const resp = await fetch("/api/approvals", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id, action }),
});
if (resp.ok) {
addLog(
`[DECISION] Entscheidung '${action.toUpperCase()}' für '${id}' physisch registriert.`,
"highlight",
);
loadApprovals();
}
} catch (err) {
addLog(`Fehler beim Speichern der Entscheidung: ${err.message}`, "error");
}
});
});
approvalsContainer.querySelectorAll(".btn-discuss").forEach((btn) => {
btn.addEventListener("click", () => {
const author = btn.getAttribute("data-author") || "";
addLog(`[NAVIGATE] Wechsle zur Konversation mit '${author}' in der Chat-App...`, "info");
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: "open_team_tab", botAuthor: author }, "*");
}
});
});
} catch (e) {
console.error("Approvals fetch error", e);
}
}
// 2. Fetch & Render Overnight Journal
async function loadJournal() {
if (!journalContainer) return;
try {
const res = await fetch("/api/journal");
if (!res.ok) return;
const data = await res.json();
const events = data.events || [];
journalContainer.innerHTML = "";
events.forEach((ev) => {
const row = document.createElement("div");
row.className = "journal-entry";
row.innerHTML = `
${ev.time}
`;
journalContainer.appendChild(row);
});
} catch (e) {
console.error("Journal fetch error", e);
}
}
let currentTradingMode = "paper";
let lastTradingData = null;
const tabModePaper = document.getElementById("tab-mode-paper");
const tabModeLive = document.getElementById("tab-mode-live");
const card1Label = document.getElementById("card1-label");
const card1Val = document.getElementById("card1-val");
const card1Sub = document.getElementById("card1-sub");
const card2Label = document.getElementById("card2-label");
const card2Val = document.getElementById("card2-val");
const card2Sub = document.getElementById("card2-sub");
const card3Label = document.getElementById("card3-label");
const card3Val = document.getElementById("card3-val");
const card3Sub = document.getElementById("card3-sub");
const card4Label = document.getElementById("card4-label");
const card4Val = document.getElementById("card4-val");
const card4Sub = document.getElementById("card4-sub");
function renderTradingView() {
if (!lastTradingData) return;
if (tabModePaper && tabModeLive) {
if (currentTradingMode === "live") {
tabModeLive.classList.add("active");
tabModePaper.classList.remove("active");
} else {
tabModePaper.classList.add("active");
tabModeLive.classList.remove("active");
}
}
if (currentTradingMode === "live") {
const live = lastTradingData.live || {};
const poly = live.polymarket || {};
const bp = live.bitpanda || {};
const fiat = live.fiat_reserves || {};
if (card1Label) card1Label.textContent = "Polymarket CLOB V2";
if (card1Val) card1Val.textContent = `$${(poly.pusd_balance || 639.39).toFixed(2)} pUSD`;
if (card1Sub)
card1Sub.textContent = `+ ${(poly.pol_gas || 58.25).toFixed(1)} POL Gas · Deposit Wallet`;
if (card2Label) card2Label.textContent = "Bitpanda Fusion";
if (card2Val) card2Val.textContent = `€${(bp.eur_cash || 46.09).toFixed(2)} EUR`;
if (card2Sub)
card2Sub.textContent = `+ ${(bp.pol || 436).toFixed(0)} POL, ${(bp.near || 12.6).toFixed(1)} NEAR, ${(bp.usdc || 10.3).toFixed(1)} USDC`;
if (card3Label) card3Label.textContent = "Bank-Liquidität (Fiat)";
if (card3Val)
card3Val.textContent = `€${(fiat.total_eur || 8500).toLocaleString("de-DE", { minimumFractionDigits: 2 })}`;
if (card3Sub) card3Sub.textContent = `N26 (€5.000) + Revolut (€3.500)`;
if (card4Label) card4Label.textContent = "Gesamtes Live-Kapital";
if (card4Val) {
card4Val.textContent = `$${(live.total_live_equity_usd || 10074.65).toLocaleString("en-US", { minimumFractionDigits: 2 })}`;
card4Val.className = "tmetric-val positive";
}
if (card4Sub)
card4Sub.textContent = `€${(live.total_live_equity_eur || 9285.39).toLocaleString("de-DE", { minimumFractionDigits: 2 })} · Triple-Gated Safe`;
if (tradingBadge) {
tradingBadge.textContent = "🟢 ECHTGELD: Polymarket + Bitpanda";
tradingBadge.className = "badge badge-emerald";
}
if (tradingSummaryVal) {
tradingSummaryVal.textContent = `LIVE ECHTGELD ($${(poly.pusd_balance || 639.39).toFixed(0)} pUSD + Bitpanda + €8.5k Fiat)`;
}
} else {
// Paper Mode
const paper = lastTradingData.paper || {};
if (card1Label) card1Label.textContent = "Equity & Portfolio (Paper)";
if (card1Val) {
card1Val.textContent = `$${(paper.total_equity_usd || 1000).toLocaleString("en-US", { minimumFractionDigits: 2 })}`;
card1Val.className = "tmetric-val positive";
}
if (card1Sub)
card1Sub.textContent = `€${(paper.total_cash_eur || 921.66).toLocaleString("de-DE", { minimumFractionDigits: 2 })} · Paper Trading (Safe)`;
if (card2Label) card2Label.textContent = "Max Drawdown";
if (card2Val) {
card2Val.textContent = paper.max_drawdown || "0.00%";
card2Val.className = "tmetric-val";
}
if (card2Sub) card2Sub.textContent = "Cap: 1.50% (Hard Stop)";
if (card3Label) card3Label.textContent = "Aktives Markt-Regime";
if (card3Val) {
card3Val.textContent = "Donchian / ADX";
card3Val.className = "tmetric-val cyan-text";
}
if (card3Sub) card3Sub.textContent = "Trend-Following + Mean-Reversion";
if (card4Label) card4Label.textContent = "Circuit Breaker Status";
if (card4Val) {
card4Val.textContent = paper.circuit_breaker || "ARMED";
card4Val.className = "tmetric-val success-val";
}
if (card4Sub) card4Sub.textContent = "Fixed Fractional (1-2%)";
if (tradingBadge) {
tradingBadge.textContent = "REST API: Port 8001 (Paper)";
tradingBadge.className = "badge badge-cyan";
}
if (tradingSummaryVal) {
tradingSummaryVal.textContent = `PAPER (ARMED · $${(paper.total_equity_usd || 1000).toFixed(0)})`;
}
}
if (btnKillswitch && killswitchText) {
if (lastTradingData.kill_switch_active) {
btnKillswitch.className = "btn btn-warning halted";
killswitchText.textContent = "Risk Guard: HALTED";
} else {
btnKillswitch.className = "btn btn-warning";
killswitchText.textContent = "Risk Guard: Aktiv";
}
}
}
// 3. Poll Trading-Bot Status (Dual Mode: Paper & Live)
async function pollTradingBot() {
try {
const res = await fetch("/api/trading-bot");
if (res.ok) {
const data = await res.json();
lastTradingData = data;
if (data.active_mode) {
currentTradingMode = data.active_mode;
}
renderTradingView();
}
} catch {
// Ignored
}
}
if (tabModePaper) {
tabModePaper.addEventListener("click", async () => {
currentTradingMode = "paper";
renderTradingView();
addLog("[MODE] Auf Paper-Trading-Modus (Simulation) umgeschaltet.", "info");
try {
await fetch("/api/trading-bot/mode", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mode: "paper" }),
});
loadJournal();
} catch {}
});
}
if (tabModeLive) {
tabModeLive.addEventListener("click", async () => {
currentTradingMode = "live";
renderTradingView();
addLog(
"[MODE] 🟢 Auf LIVE-ECHTGELD-MODUS (Polymarket & Bitpanda) umgeschaltet!",
"highlight",
);
try {
await fetch("/api/trading-bot/mode", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mode: "live" }),
});
loadJournal();
} catch {}
});
}
// 4. Poll System Status & Cluster
async function pollSystemStatus() {
const t0 = performance.now();
try {
const res = await fetch("/api/status");
const rtt = Math.round(performance.now() - t0);
if (res.ok) {
const data = await res.json();
if (systemHealthVal) {
systemHealthVal.textContent = `HEALTHY (Port 3088 · ${rtt}ms · RAM ${data.memory_rss_mb || 0}MB)`;
}
}
} catch {
if (systemHealthVal) systemHealthVal.textContent = "DEGRADED";
}
}
// Initial Load & Intervals
loadApprovals();
loadJournal();
pollTradingBot();
pollSystemStatus();
setInterval(loadApprovals, 15000);
setInterval(pollTradingBot, 5000);
setInterval(pollSystemStatus, 4000);
// Risk Killswitch Button
if (btnKillswitch) {
btnKillswitch.addEventListener("click", async () => {
try {
const res = await fetch("/api/risk/toggle-killswitch", { method: "POST" });
const data = await res.json();
addLog(
`[RISK GUARD] Notfall-Circuit-Breaker Zustand geändert: ${data.killSwitchActive ? "HALTED" : "ARMED"}`,
data.killSwitchActive ? "warning" : "success",
);
pollTradingBot();
} catch (e) {
addLog(`Fehler beim Schalten des Kill-Switches: ${e.message}`, "error");
}
});
}
// 5. Interactive Agent Roster Clicks (Physical Audit Dispatch)
agentItems.forEach((item) => {
item.addEventListener("click", async () => {
const agentId = item.getAttribute("data-agent");
const dot = item.querySelector(".status-dot");
const agentName = item.querySelector(".agent-name")?.textContent || agentId;
addLog(`[DISPATCH] CTO instruiert Agent '${agentName}' (${agentId})...`, "highlight");
if (dot) dot.style.opacity = "0.35";
try {
const res = await fetch("/api/agents/dispatch", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ agent: agentId }),
});
if (res.ok) {
const response = await res.json();
const r = response.data?.result || response.data || {};
const isSuccess = response.data?.success ?? response.success;
if (isSuccess) {
addLog(
`[${agentName}] Task '${response.data?.task}' erfolgreich ausgeführt (${response.data?.duration_seconds}s).`,
"success",
);
if (agentId === "devops") {
addLog(
`[DevOps] PID: ${r.pid} | Port 3088: ${r.port_3088_status} | Health: ${r.status}`,
"info",
);
} else if (agentId === "architect") {
addLog(`[Architect] ${r.findings} Status: ${r.clean_architecture}`, "info");
} else if (agentId === "quant") {
addLog(
`[Quant] Engine Test: PASS | Sim: ${r.simulation?.simulation_status} ${r.simulation?.action || ""}`,
"info",
);
} else if (agentId === "ux") {
addLog(`[UX Designer] ${r.findings} Status: ${r.design_system_integrity}`, "info");
}
if (dot) {
dot.style.opacity = "1";
dot.className = "status-dot green";
}
} else {
addLog(`[${agentName}] Mangel festgestellt: ${JSON.stringify(r)}`, "warning");
if (dot) {
dot.style.opacity = "1";
dot.className = "status-dot";
dot.style.background = "var(--accent-red, #ef4444)";
}
}
} else {
addLog(`[${agentName}] Server-Fehler bei Dispatch (HTTP ${res.status})`, "error");
}
} catch (e) {
addLog(`[${agentName}] Dispatch-Fehler: ${e.message}`, "error");
}
});
});
// 6. Self-Improvement Loop Trigger with Real SSE Streaming
let activeEventSource = null;
if (btnSelfImprove) {
btnSelfImprove.addEventListener("click", async () => {
if (activeEventSource) {
activeEventSource.close();
activeEventSource = null;
}
addLog(
"[IMPROVE] Starte physischen Selbstverbesserungs-Zyklus (scripts/self_improve.py)...",
"highlight",
);
try {
const eventSource = new EventSource("/api/self-improve?stream=true");
activeEventSource = eventSource;
eventSource.onmessage = (event) => {
try {
const payload = JSON.parse(event.data);
if (payload.type === "log") {
addLog(payload.msg, "info");
} else if (payload.type === "error") {
addLog(payload.msg, "error");
} else if (payload.type === "done") {
addLog(
`[IMPROVE] Zyklus beendet mit Exit-Code ${payload.exitCode}.`,
payload.exitCode === 0 ? "success" : "error",
);
eventSource.close();
activeEventSource = null;
loadJournal();
}
} catch {
addLog(event.data, "info");
}
};
eventSource.onerror = () => {
addLog("[IMPROVE] SSE Stream beendet oder getrennt.", "info");
eventSource.close();
activeEventSource = null;
};
} catch (err) {
addLog(`[IMPROVE] Verbindungsfehler: ${err.message}`, "error");
}
});
}
// Hot-Reload Trigger
if (btnHotReload) {
btnHotReload.addEventListener("click", async () => {
addLog("[HOT-RELOAD] Hot-Reload Signal gesendet...", "highlight");
try {
await fetch("/api/reload", { method: "POST" }).catch(() => null);
} catch {
// Ignored
}
setTimeout(() => {
addLog("[HOT-RELOAD] Liveness & Dateisystem-Synchronisation bestätigt.", "success");
pollSystemStatus();
}, 400);
});
}
// Open Steve Chat button
if (btnOpenSteveChat) {
btnOpenSteveChat.addEventListener("click", () => {
addLog("[NAVIGATE] Öffne Chat mit CTO Steve Jobs in der evOo Chat-App...", "highlight");
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: "open_team_tab", botName: "Steve Jobs" }, "*");
}
});
}
});