From 145c64e67f705eb1e67f30af4e33d0c09fb8137d Mon Sep 17 00:00:00 2001 From: duffyduck Date: Thu, 24 Sep 2026 18:29:37 +0200 Subject: [PATCH] feat(diagnostic): RVS-Raum-Monitor + Server/GPU-Icon fuer die Compute-Flotte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raum-Monitor (haette uns die Nacht erspart): der RVS-Log kuerzt Tokens auf 8 Zeichen -> zwei verschiedene Tokens mit gleichem Prefix ergeben zwei Raeume, die im Log IDENTISCH aussehen (Prefix-Kollision). Jetzt zeigt die Diagnostic (Satelliten-Tab, Panel 'RVS-Raeume 🚪') alle Raeume mit token8 + laengerem Fingerprint (sha256) + Token-Laenge + Client-Zahl, ★ = eigener Raum. >1 Raum = Warnung 'Token-Mismatch'. Keine vollen Tokens (nur Fingerprint). - rvs: rooms_query -> direkt rooms_info antworten (wie update_check); Typen in ALLOWED_TYPES; sha256-Fingerprint via crypto. - diagnostic: rooms_query durchreichen, rooms_info an Browser; Panel + Tabelle. Dazu: Compute-Flotte-Ueberschrift bekommt statt 🖥️ ein Inline-SVG (Server mit GPU-Karte, currentColor -> theme-aware). Co-Authored-By: Claude Opus 4.8 --- diagnostic/index.html | 54 ++++++++++++++++++++++++++++++++++++++++++- diagnostic/server.js | 7 ++++++ rvs/server.js | 27 ++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/diagnostic/index.html b/diagnostic/index.html index 903f7e0..962f4ab 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -1451,6 +1451,23 @@
+
+

RVS-Räume 🚪

+ +
+
+

+ Alle Räume auf dem RVS. Jeder Token bildet einen eigenen Raum — nur Clients + im GLEICHEN Raum sehen sich. Der RVS-Log kürzt Tokens auf 8 Zeichen, dadurch + können zwei verschiedene Tokens gleich aussehen (Prefix-Kollision). Der + Fingerprint + die Länge hier machen das + eindeutig: mehrere Räume = Token-Mismatch irgendwo. ★ = dein Raum + (Diagnostic). +

+
+
+
(Aktualisieren klicken)
+

Satelliten 🛰️

@@ -1484,7 +1501,19 @@
-

Compute-Flotte 🖥️

+

Compute-Flotte + +

@@ -2155,6 +2184,7 @@ return; } if (msg.type === 'host_update') { hosts = msg.hosts || []; renderHosts(); return; } + if (msg.type === 'rooms_info') { rooms = (msg.payload && msg.payload.rooms) || []; renderRooms(); return; } if (msg.type === 'sat_creds_list_result') { const p = msg.payload || msg; const sat = p.satellite || ''; @@ -4442,6 +4472,7 @@ } else if (tab === 'triggers') { loadTriggers(); } else if (tab === 'satellites') { + requestRooms(); requestSatellites(); requestHosts(); requestWorkers(); @@ -4575,6 +4606,27 @@ function requestSatCreds(id) { send({ action: 'sat_creds_list', satellite: id }); } + let rooms = []; // [{token8, fp, len, clients, you}] + function requestRooms() { send({ action: 'rooms_query' }); } + function renderRooms() { + const box = document.getElementById('rooms-list'); + if (!box) return; + if (!rooms.length) { box.innerHTML = '(keine Räume — RVS leer?)'; return; } + const warn = rooms.length > 1 + ? '
⚠ ' + rooms.length + ' Räume — Clients in verschiedenen Räumen sehen sich NICHT. Meist ein abweichender/abgeschnittener RVS_TOKEN.
' + : '
✓ Genau 1 Raum — alle Clients zusammen.
'; + box.innerHTML = warn + '' + + '' + + rooms.map(r => { + const you = r.you ? ' ★ du' : ''; + const c = rooms.length > 1 ? (r.you ? '#3FFF3F' : '#FFB020') : '#E0E0F0'; + return '' + + '' + + '' + + '' + + ''; + }).join('') + '
Raum (token8)FingerprintLängeClients
' + escapeHtml(r.token8) + '…' + you + '' + escapeHtml(r.fp) + '' + r.len + '' + r.clients + '
'; + } let hosts = []; // [{hostId, name, os, caps, control, online}] function requestHosts() { send({ action: 'host_list' }); } function renderHosts() { diff --git a/diagnostic/server.js b/diagnostic/server.js index e353878..fe95de6 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -1095,6 +1095,9 @@ function connectRVS(forcePlain) { } else if (msg.type === "host_ping") { const p = msg.payload || {}; if (p.hostId && hosts.has(p.hostId)) hosts.get(p.hostId).last_seen = Date.now(); + } else if (msg.type === "rooms_info") { + // Antwort des RVS auf rooms_query → an den Browser (Raum-Diagnose). + broadcast({ type: "rooms_info", payload: msg.payload || {} }); } else if (msg.type === "sat_devices") { // Antwort eines Satelliten auf sat_discover → Geraeteliste an Browser. const p = msg.payload || {}; @@ -2789,6 +2792,10 @@ wss.on("connection", (ws) => { } else if (msg.action === "host_list") { // Browser will die aktuelle Host-Agenten-Liste. ws.send(JSON.stringify({ type: "host_update", hosts: hostList() })); + } else if (msg.action === "rooms_query") { + // Browser will die RVS-Raum-Diagnose — via persistente rvsWs anfragen, + // die Antwort (rooms_info) kommt auf derselben Verbindung zurueck. + sendToRVS_raw({ type: "rooms_query", payload: {}, timestamp: Date.now() }); } else if (msg.action === "worker_list") { // Browser will die aktuelle Compute-Flotte. ws.send(JSON.stringify({ type: "worker_update", workers: workerList() })); diff --git a/rvs/server.js b/rvs/server.js index 5d51904..c97a8c9 100644 --- a/rvs/server.js +++ b/rvs/server.js @@ -88,6 +88,10 @@ const ALLOWED_TYPES = new Set([ // host_hello/host_ping und fuehrt host_command aus (exec/read/write/info/ // screenshot) -> host_result. ARIA steuert so Rechner auch hinter NAT. "host_hello", "host_ping", "host_command", "host_result", + // Raum-Diagnose: Diagnostic fragt die aktuellen RVS-Raeume ab (rooms_query), + // RVS antwortet direkt mit rooms_info (Fingerprint + Laenge + Client-Zahl je + // Raum). Deckt Token-Prefix-Kollisionen auf (2 Raeume, gleicher 8-Zeichen-Log). + "rooms_query", "rooms_info", // Compute-Flotte (AI-Boxen): Worker (f5tts/whisper/voxtral/llm-adapter) melden // sich per worker_hello an und pingen per worker_ping; der Diagnostic-Server // aggregiert das und broadcastet worker_update/worker_list an die Browser-UI. @@ -349,6 +353,29 @@ function registerClient(ws, token) { return; } + // Raum-Diagnose: direkt an den anfragenden Client antworten (nicht relay'en). + // Zeigt ALLE Raeume mit token8 (wie im Log), einem laengeren Fingerprint und + // der Token-Laenge — so werden Prefix-Kollisionen (2 Raeume, gleicher 8-Zeichen- + // Log, aber verschiedene volle Tokens) sofort sichtbar. KEINE vollen Tokens. + if (msg.type === "rooms_query") { + const crypto = require("crypto"); + const out = []; + for (const [tok, room] of rooms) { + let live = 0; + for (const c of room.clients) if (c.readyState === 1) live++; + out.push({ + token8: tok.slice(0, 8), + fp: crypto.createHash("sha256").update(tok).digest("hex").slice(0, 12), + len: tok.length, + clients: live, + you: tok === ws._token, + }); + } + out.sort((a, b) => b.clients - a.clients); + ws.send(JSON.stringify({ type: "rooms_info", payload: { rooms: out }, timestamp: Date.now() })); + return; + } + // Update-Download: APK als Base64 ueber WebSocket senden if (msg.type === "update_download") { const apkInfo = getLatestAPK();