diff --git a/diagnostic/index.html b/diagnostic/index.html
index c567151..a09d2c0 100644
--- a/diagnostic/index.html
+++ b/diagnostic/index.html
@@ -127,6 +127,18 @@
+
+
+
+ ⚠️
+
+
+ docker system prune -a --volumes -f
+
+
+
+
+
ARIA Diagnostic
@@ -753,6 +765,11 @@
return;
}
+ if (msg.type === 'disk_status') {
+ updateDiskBanner(msg);
+ return;
+ }
+
if (msg.type === 'mode' && msg.payload) {
// Bridge hat den Modus geaendert (evtl. von anderer App/Diagnostic) β UI syncen
const mode = (msg.payload.mode || '').toLowerCase();
@@ -2155,6 +2172,55 @@
const ttsToggleEl = document.getElementById('tts-debug-toggle');
if (ttsToggleEl) ttsToggleEl.checked = showTtsDebug;
+ // Disk-Space Banner aktualisieren (wird vom Server via disk_status gepusht)
+ function updateDiskBanner(status) {
+ const banner = document.getElementById('disk-banner');
+ const icon = document.getElementById('disk-banner-icon');
+ const text = document.getElementById('disk-banner-text');
+ if (!banner) return;
+ if (!status || status.level === 'ok') {
+ banner.style.display = 'none';
+ return;
+ }
+ const gb = (n) => (n / 1024 / 1024 / 1024).toFixed(1);
+ const pct = status.percent;
+ const used = gb(status.usedBytes);
+ const total = gb(status.totalBytes);
+ const avail = gb(status.availBytes);
+ let bg, col, msg;
+ if (status.level === 'critical') {
+ bg = '#5C1A1A'; col = '#FF6B6B'; icon.innerHTML = '🚨'; // π¨
+ msg = `KRITISCH: Platte ${pct}% voll (${used}GB von ${total}GB, nur noch ${avail}GB frei). aria-core kann bald nicht mehr schreiben β sofort aufraeumen!`;
+ } else if (status.level === 'warn') {
+ bg = '#5C3A1A'; col = '#FFAA55'; icon.innerHTML = '⚠️'; // β οΈ
+ msg = `Warnung: Platte ${pct}% voll (${avail}GB frei). Bald aufraeumen.`;
+ } else {
+ bg = '#4A3A1A'; col = '#FFD60A'; icon.innerHTML = 'ℹ️'; // βΉοΈ
+ msg = `Hinweis: Platte ${pct}% voll (${avail}GB frei).`;
+ }
+ banner.style.background = bg;
+ banner.style.color = col;
+ banner.style.borderBottom = `2px solid ${col}`;
+ text.textContent = msg;
+ banner.style.display = 'block';
+ }
+
+ function copyDiskCmd() {
+ const cmd = document.getElementById('disk-banner-cmd').textContent.trim();
+ navigator.clipboard.writeText(cmd).then(() => {
+ const btn = event.target;
+ const old = btn.textContent;
+ btn.textContent = 'Kopiert!';
+ setTimeout(() => { btn.textContent = old; }, 1500);
+ }).catch(() => {
+ // Fallback: selektieren
+ const range = document.createRange();
+ range.selectNode(document.getElementById('disk-banner-cmd'));
+ window.getSelection().removeAllRanges();
+ window.getSelection().addRange(range);
+ });
+ }
+
connectWS();
diff --git a/diagnostic/server.js b/diagnostic/server.js
index 3738144..56f6287 100644
--- a/diagnostic/server.js
+++ b/diagnostic/server.js
@@ -1148,6 +1148,53 @@ function updateAgentActivity() {
watchdogWarned = false;
}
+// ββ Disk-Space Monitor βββββββββββββββββββββββββββββββ
+// Prueft regelmaessig die Host-Disk (via gemountetem /shared) und
+// broadcastet bei kritischen Schwellwerten ein disk_status Event.
+let lastDiskStatus = null;
+let currentDiskStatus = null; // Vollstaendig fuer neu verbundene Clients
+function checkDiskSpace() {
+ const { exec } = require("child_process");
+ exec("df -B1 /shared", (err, stdout) => {
+ if (err) return;
+ const lines = stdout.trim().split("\n");
+ if (lines.length < 2) return;
+ const cols = lines[1].split(/\s+/);
+ // Filesystem Size Used Avail Use% MountedOn
+ const total = parseInt(cols[1], 10);
+ const used = parseInt(cols[2], 10);
+ const avail = parseInt(cols[3], 10);
+ if (!total) return;
+ const pct = Math.round((used / total) * 100);
+ let level = "ok";
+ if (pct >= 95) level = "critical";
+ else if (pct >= 85) level = "warn";
+ else if (pct >= 70) level = "info";
+ const status = {
+ type: "disk_status",
+ level,
+ percent: pct,
+ usedBytes: used,
+ totalBytes: total,
+ availBytes: avail,
+ };
+ currentDiskStatus = status;
+ // Nur broadcasten wenn sich was geaendert hat (oder alle 60s Refresh)
+ const key = `${level}-${pct}`;
+ if (lastDiskStatus !== key) {
+ lastDiskStatus = key;
+ broadcast(status);
+ if (level !== "ok") {
+ log(level === "critical" ? "error" : "warn", "server",
+ `Disk ${pct}% belegt (${(used/1024/1024/1024).toFixed(1)}GB von ${(total/1024/1024/1024).toFixed(1)}GB)`);
+ }
+ }
+ });
+}
+// Beim Start + alle 30s
+setTimeout(checkDiskSpace, 2000);
+setInterval(checkDiskSpace, 30000);
+
// Watchdog prΓΌft alle 30s ob ARIA nach einer gesendeten Nachricht reagiert
setInterval(async () => {
if (pendingMessageTime === 0) return; // Keine Nachricht gesendet
@@ -1281,6 +1328,8 @@ wss.on("connection", (ws) => {
browserClients.add(ws);
// Initialen State + letzte Logs senden
ws.send(JSON.stringify({ type: "init", state, logs: logs.slice(-100) }));
+ // Letzten Disk-Status mitgeben damit der Client sofort weiss wie's um Platz steht
+ if (currentDiskStatus) ws.send(JSON.stringify(currentDiskStatus));
ws.on("message", (raw) => {
try {