feat(diagnostic): Host-Agenten in der Satelliten-Flotte anzeigen (Phase E)
Diagnostic-Server trackt host_hello/host_ping (hosts-Map), broadcastet host_update und beantwortet die host_list-Action. UI: neues Panel "Host-Agenten" im Satelliten-Tab — zeigt Name/OS/Caps/online + ob CONTROL_ENABLED. Reine Sichtbarkeit; Steuerung laeuft ueber ARIA. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1467,6 +1467,22 @@
|
||||
<div id="sat-list" style="font-size:12px;color:#8888AA;">(Lade...)</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin:16px 0 8px;">
|
||||
<h2 style="margin:0;">Host-Agenten 💻</h2>
|
||||
<button class="btn secondary" onclick="requestHosts()" style="padding:4px 10px;font-size:11px;">Aktualisieren</button>
|
||||
</div>
|
||||
<div class="card" style="margin-bottom:8px;">
|
||||
<p style="color:#8888AA;font-size:12px;margin:0;">
|
||||
Agenten, die DIREKT auf einem Rechner laufen (Binary + <code>.env</code>) und
|
||||
sich ausgehend mit RVS verbinden — ARIA steuert den Rechner damit auch
|
||||
hinter NAT/Firewall. ARIA nutzt sie ueber <code>host_exec</code> & Co.;
|
||||
Setup siehe <code>host-agent/README</code>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div id="host-list" style="font-size:12px;color:#8888AA;">(Lade...)</div>
|
||||
</div>
|
||||
|
||||
<div id="compute-fleet" style="display:flex;justify-content:space-between;align-items:center;margin:16px 0 8px;scroll-margin-top:70px;">
|
||||
<h2 style="margin:0;">Compute-Flotte 🖥️</h2>
|
||||
<button class="btn secondary" onclick="requestWorkers()" style="padding:4px 10px;font-size:11px;">Aktualisieren</button>
|
||||
@@ -2138,6 +2154,7 @@
|
||||
satellites.forEach(s => { if (s.online !== false) requestSatCreds(s.id); });
|
||||
return;
|
||||
}
|
||||
if (msg.type === 'host_update') { hosts = msg.hosts || []; renderHosts(); return; }
|
||||
if (msg.type === 'sat_creds_list_result') {
|
||||
const p = msg.payload || msg;
|
||||
const sat = p.satellite || '';
|
||||
@@ -4426,6 +4443,7 @@
|
||||
loadTriggers();
|
||||
} else if (tab === 'satellites') {
|
||||
requestSatellites();
|
||||
requestHosts();
|
||||
requestWorkers();
|
||||
}
|
||||
}
|
||||
@@ -4557,6 +4575,29 @@
|
||||
function requestSatCreds(id) {
|
||||
send({ action: 'sat_creds_list', satellite: id });
|
||||
}
|
||||
let hosts = []; // [{hostId, name, os, caps, control, online}]
|
||||
function requestHosts() { send({ action: 'host_list' }); }
|
||||
function renderHosts() {
|
||||
const box = document.getElementById('host-list');
|
||||
if (!box) return;
|
||||
if (!hosts.length) {
|
||||
box.innerHTML = '<span style="color:#8888AA;">Kein Host-Agent verbunden. ' +
|
||||
'Baue die Binary (<code>host-agent/build.sh</code>), leg eine <code>.env</code> ' +
|
||||
'daneben und starte sie auf dem Ziel-Rechner.</span>';
|
||||
return;
|
||||
}
|
||||
box.innerHTML = hosts.map(h => {
|
||||
const dot = h.online ? '#34C759' : '#FF3B30';
|
||||
const caps = (h.caps || []).join(', ') || '—';
|
||||
const ctrl = h.control ? '<span style="color:#0096FF;">steuerbar</span>' : '<span style="color:#FF6E6E;">CONTROL_ENABLED=false</span>';
|
||||
return '<div style="border:1px solid #1E1E2E;border-radius:8px;padding:10px;margin-bottom:8px;">' +
|
||||
'<div><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:' + dot + ';margin-right:6px;"></span>' +
|
||||
'<strong style="color:#E0E0F0;">💻 ' + escapeHtml(h.name || h.hostId) + '</strong> ' +
|
||||
'<span style="color:#666;font-size:11px;">id=' + escapeHtml(h.hostId) + '</span></div>' +
|
||||
'<div style="color:#8888AA;font-size:11px;margin-top:4px;">' + escapeHtml(h.os || '') + ' · kann: ' + escapeHtml(caps) + ' · ' + ctrl + '</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
function scanSatellite(id) {
|
||||
satScanning = id;
|
||||
renderSatellites();
|
||||
|
||||
@@ -550,6 +550,21 @@ function broadcastSatellites() {
|
||||
broadcast({ type: "sat_update", satellites: satelliteList() });
|
||||
}
|
||||
|
||||
// ── Host-Agenten: Direktzugriff auf einen Rechner ────────────────
|
||||
const hosts = new Map(); // hostId → {hostId, name, os, caps, control, last_seen}
|
||||
|
||||
function hostList() {
|
||||
const now = Date.now();
|
||||
return Array.from(hosts.values()).map(h => ({
|
||||
hostId: h.hostId, name: h.name, os: h.os, caps: h.caps, control: h.control,
|
||||
online: (now - (h.last_seen || 0)) < 300000,
|
||||
}));
|
||||
}
|
||||
|
||||
function broadcastHosts() {
|
||||
broadcast({ type: "host_update", hosts: hostList() });
|
||||
}
|
||||
|
||||
// ── Compute-Fleet: GPU-Worker (voxtral/whisper/f5tts/llm) ──────────
|
||||
// Worker melden sich per worker_hello + halten sich per worker_ping (busy) frisch.
|
||||
const workers = new Map(); // instanceId → {instanceId, service, node, gpus, model, busy, last_seen}
|
||||
@@ -1065,6 +1080,21 @@ function connectRVS(forcePlain) {
|
||||
});
|
||||
broadcastSatellites();
|
||||
}
|
||||
} else if (msg.type === "host_hello") {
|
||||
// Ein Host-Agent (Direktzugriff auf einen Rechner) meldet sich.
|
||||
const p = msg.payload || {};
|
||||
if (p.hostId) {
|
||||
const wasKnown = hosts.has(p.hostId);
|
||||
hosts.set(p.hostId, {
|
||||
hostId: p.hostId, name: p.name || p.hostId, os: p.os || "",
|
||||
caps: p.caps || [], control: !!p.control, last_seen: Date.now(),
|
||||
});
|
||||
if (!wasKnown) broadcastHosts();
|
||||
else hosts.get(p.hostId).last_seen = Date.now();
|
||||
}
|
||||
} 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 === "sat_devices") {
|
||||
// Antwort eines Satelliten auf sat_discover → Geraeteliste an Browser.
|
||||
const p = msg.payload || {};
|
||||
@@ -2721,6 +2751,8 @@ wss.on("connection", (ws) => {
|
||||
if (currentDiskStatus) ws.send(JSON.stringify(currentDiskStatus));
|
||||
// Aktuell bekannte Satelliten mitgeben (RVS replayt sat_hello nicht).
|
||||
ws.send(JSON.stringify({ type: "sat_update", satellites: satelliteList() }));
|
||||
// Aktuell bekannte Host-Agenten mitgeben.
|
||||
ws.send(JSON.stringify({ type: "host_update", hosts: hostList() }));
|
||||
// Aktuell bekannte Compute-Worker mitgeben (RVS replayt worker_hello nicht).
|
||||
ws.send(JSON.stringify({ type: "worker_update", workers: workerList() }));
|
||||
|
||||
@@ -2754,6 +2786,9 @@ wss.on("connection", (ws) => {
|
||||
} else if (msg.action === "sat_list") {
|
||||
// Browser will die aktuelle Satelliten-Liste.
|
||||
ws.send(JSON.stringify({ type: "sat_update", satellites: satelliteList() }));
|
||||
} 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 === "worker_list") {
|
||||
// Browser will die aktuelle Compute-Flotte.
|
||||
ws.send(JSON.stringify({ type: "worker_update", workers: workerList() }));
|
||||
|
||||
Reference in New Issue
Block a user