Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e05c66baa | |||
| 4082a6bf2a | |||
| 3485642b3e | |||
| 1240ae3829 | |||
| 2dd4d38dce | |||
| 7f862ce1f4 | |||
| 528fe97b59 |
@@ -79,8 +79,8 @@ android {
|
||||
applicationId "com.ariacockpit"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 10103
|
||||
versionName "0.1.1.3"
|
||||
versionCode 10106
|
||||
versionName "0.1.1.6"
|
||||
// Fallback fuer Libraries mit Product Flavors
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aria-cockpit",
|
||||
"version": "0.1.1.3",
|
||||
"version": "0.1.1.6",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
ToastAndroid,
|
||||
AppState,
|
||||
NativeModules,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import RNFS from 'react-native-fs';
|
||||
@@ -1249,9 +1250,29 @@ const ChatScreen: React.FC = () => {
|
||||
? '\u270D\uFE0F ARIA schreibt...'
|
||||
: '\uD83D\uDCAD ARIA denkt...'}
|
||||
</Text>
|
||||
<TouchableOpacity style={styles.thinkingCancel} onPress={cancelRequest}>
|
||||
<Text style={styles.thinkingCancelText}>Abbrechen</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={{flexDirection: 'row', gap: 6}}>
|
||||
<TouchableOpacity style={[styles.thinkingCancel, {borderColor: '#FF9500'}]} onPress={() => rvs.send('doctor_fix' as any, {})}>
|
||||
<Text style={[styles.thinkingCancelText, {color: '#FF9500'}]}>{'🔧'}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.thinkingCancel, {borderColor: '#FF3B30'}]}
|
||||
onPress={() => {
|
||||
Alert.alert(
|
||||
'ARIA hart neu starten?',
|
||||
'Container-Restart (~15s). Laufende Anfragen gehen verloren.',
|
||||
[
|
||||
{ text: 'Abbrechen', style: 'cancel' },
|
||||
{ text: 'Neu starten', style: 'destructive', onPress: () => rvs.send('aria_restart' as any, {}) },
|
||||
],
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.thinkingCancelText, {color: '#FF3B30'}]}>{'🚨'}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.thinkingCancel} onPress={cancelRequest}>
|
||||
<Text style={styles.thinkingCancelText}>Abbrechen</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1288,6 +1288,74 @@ const SettingsScreen: React.FC = () => {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* === ARIA Reparatur === */}
|
||||
<Text style={[styles.sectionTitle, {marginTop: 16}]}>Reparatur</Text>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.toggleHint}>
|
||||
Wenn ARIA gar nicht mehr antwortet oder auf jede Anfrage mit
|
||||
"Antwort ohne Text" zurueckkommt — meistens ein steckengebliebener
|
||||
Run im aria-core. Dieser Button fuehrt {'“'}openclaw doctor --fix{'”'}
|
||||
aus und macht ARIA wieder ansprechbar.
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={[styles.clearButton, {marginTop: 8, backgroundColor: 'rgba(255,149,0,0.15)'}]}
|
||||
onPress={() => {
|
||||
rvs.send('doctor_fix' as any, {});
|
||||
ToastAndroid.show('Reparatur-Befehl gesendet — Antwort kommt gleich', ToastAndroid.SHORT);
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.clearButtonText, {color: '#FF9500'}]}>{'🔧 ARIA reparieren'}</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.toggleHint, {marginTop: 12}]}>
|
||||
Wenn auch Reparieren nicht hilft — Container hart neu starten.
|
||||
ARIA ist dann ~15 Sekunden weg und kommt mit frischem State zurueck.
|
||||
Laufende Anfragen gehen verloren.
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={[styles.clearButton, {marginTop: 8, backgroundColor: 'rgba(255,59,48,0.15)'}]}
|
||||
onPress={() => {
|
||||
Alert.alert(
|
||||
'ARIA hart neu starten?',
|
||||
'Container-Restart (~15s). Laufende Anfragen gehen verloren.',
|
||||
[
|
||||
{ text: 'Abbrechen', style: 'cancel' },
|
||||
{ text: 'Neu starten', style: 'destructive', onPress: () => {
|
||||
rvs.send('aria_restart' as any, {});
|
||||
ToastAndroid.show('Container-Restart angestossen…', ToastAndroid.LONG);
|
||||
}},
|
||||
],
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.clearButtonText, {color: '#FF3B30'}]}>{'🚨 ARIA hart neu starten'}</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={[styles.toggleHint, {marginTop: 12}]}>
|
||||
Konversation komplett zuruecksetzen — alle bisherigen Nachrichten
|
||||
aus ARIA's Session loeschen + Container neu. Anders als der harte
|
||||
Restart wird hier auch ARIA's Erinnerung an die laufende
|
||||
Konversation gewipt. Geschieht automatisch alle 140 Nachrichten
|
||||
(Bridge-Setting COMPACT_AFTER_MESSAGES).
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={[styles.clearButton, {marginTop: 8, backgroundColor: 'rgba(255,149,0,0.15)'}]}
|
||||
onPress={() => {
|
||||
Alert.alert(
|
||||
'Konversation komprimieren?',
|
||||
'Alle Nachrichten in ARIAs aktueller Session werden geloescht und der Container neu gestartet. ARIA vergisst den bisherigen Gespraechsverlauf.',
|
||||
[
|
||||
{ text: 'Abbrechen', style: 'cancel' },
|
||||
{ text: 'Komprimieren', style: 'destructive', onPress: () => {
|
||||
rvs.send('aria_session_reset' as any, {});
|
||||
ToastAndroid.show('Session wird zurueckgesetzt…', ToastAndroid.LONG);
|
||||
}},
|
||||
],
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.clearButtonText, {color: '#FF9500'}]}>{'🧹 Konversation komprimieren'}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
</>)}
|
||||
|
||||
{/* === Logs === */}
|
||||
|
||||
@@ -549,6 +549,12 @@ class ARIABridge:
|
||||
# Beeinflusst das Timeout fuer stt_request — bei "loading" warten wir laenger,
|
||||
# weil das Modell beim ersten Request noch ~1-2 Min runtergeladen werden kann.
|
||||
self._remote_stt_ready: bool = False
|
||||
# User-Message-Counter fuer Auto-Compact. Bei zu langer Konversation
|
||||
# sprengt die argv-Liste beim Claude-Subprocess-Spawn (E2BIG). Bei
|
||||
# COMPACT_AFTER erreicht → Sessions reset + Container restart.
|
||||
# Counter ueberlebt Bridge-Restart nicht (frischer Zaehler beim Start ok).
|
||||
self._user_message_count: int = 0
|
||||
self._compact_after = int(os.getenv("COMPACT_AFTER_MESSAGES", "140"))
|
||||
# Pending Files: wenn die App ein Bild + Text gleichzeitig schickt, kommen
|
||||
# zwei separate RVS-Events ('file' und 'chat') — wir buffern die Files
|
||||
# kurz und mergen sie mit dem nachfolgenden Chat-Text zu einer einzigen
|
||||
@@ -1170,12 +1176,53 @@ class ARIABridge:
|
||||
await self.send_to_core(text, source="app-file+chat")
|
||||
return True
|
||||
|
||||
async def _trigger_session_reset(self) -> None:
|
||||
"""Sessions loeschen + Container restart via Diagnostic HTTP-API."""
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
"http://localhost:3001/api/aria-session-reset",
|
||||
data=b"{}",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
def _do_reset():
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=45) as resp:
|
||||
return resp.status
|
||||
except Exception as e:
|
||||
return f"err:{e}"
|
||||
result = await asyncio.get_event_loop().run_in_executor(None, _do_reset)
|
||||
logger.info("[core] Session-Reset Result: %s", result)
|
||||
except Exception as e:
|
||||
logger.warning("[core] Session-Reset Trigger fehlgeschlagen: %s", e)
|
||||
|
||||
async def send_to_core(self, text: str, source: str = "bridge") -> None:
|
||||
"""Sendet Text an aria-core (OpenClaw chat.send Protokoll)."""
|
||||
if self.ws_core is None:
|
||||
logger.error("[core] Nicht verbunden — Nachricht verworfen: '%s'", text[:60])
|
||||
return
|
||||
|
||||
# Auto-Compact: bei zu vielen User-Messages laeuft argv beim Subprocess-
|
||||
# Spawn ueber (E2BIG). Vor send pruefen, ggf. Sessions resetten.
|
||||
if source.startswith("app") and self._compact_after > 0:
|
||||
self._user_message_count += 1
|
||||
if self._user_message_count >= self._compact_after:
|
||||
logger.warning("[core] Auto-Compact: %d Messages erreicht — Session-Reset",
|
||||
self._user_message_count)
|
||||
self._user_message_count = 0
|
||||
# Reset triggern via Diagnostic (asynchron, blockiert send nicht)
|
||||
asyncio.create_task(self._trigger_session_reset())
|
||||
# User informieren — der naechste Request kommt erst nach Restart durch
|
||||
await self._send_to_rvs({
|
||||
"type": "chat",
|
||||
"payload": {
|
||||
"text": f"[Compact] Konversation war lang ({self._compact_after} Nachrichten) — Session wurde geleert, ARIA startet frisch. Deine letzte Nachricht bitte gleich nochmal senden.",
|
||||
"sender": "aria",
|
||||
},
|
||||
"timestamp": int(asyncio.get_event_loop().time() * 1000),
|
||||
})
|
||||
return
|
||||
|
||||
# Aktive Session vom Diagnostic holen
|
||||
self._fetch_active_session()
|
||||
|
||||
@@ -1580,6 +1627,76 @@ class ARIABridge:
|
||||
except Exception as e:
|
||||
logger.warning("[rvs] file_saved konnte nicht an App gesendet werden: %s", e)
|
||||
|
||||
elif msg_type == "aria_session_reset":
|
||||
# Manueller Compact-Trigger: Sessions weg + Restart
|
||||
logger.warning("[rvs] aria_session_reset Request von App")
|
||||
self._user_message_count = 0
|
||||
asyncio.create_task(self._trigger_session_reset())
|
||||
return
|
||||
|
||||
elif msg_type == "aria_restart":
|
||||
# App-Button "ARIA hart neu starten" → docker restart aria-core
|
||||
# via Diagnostic (der hat den Docker-Socket gemountet).
|
||||
logger.warning("[rvs] aria_restart Request von App — harter Container-Restart")
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
"http://localhost:3001/api/aria-restart",
|
||||
data=b"{}",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
def _do_restart():
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=45) as resp:
|
||||
return resp.status, resp.read().decode("utf-8", errors="ignore")
|
||||
except Exception as e:
|
||||
return None, str(e)
|
||||
status, body = await asyncio.get_event_loop().run_in_executor(None, _do_restart)
|
||||
logger.info("[rvs] aria_restart Result: status=%s", status)
|
||||
# Note: bei erfolgreichem Restart ist die RVS-Verbindung sehr
|
||||
# wahrscheinlich kurz weg (aria-bridge ist im service:aria-Network).
|
||||
# Die Antwort kommt evtl. nicht mehr durch — egal.
|
||||
except Exception as e:
|
||||
logger.warning("[rvs] aria_restart Weiterleitung fehlgeschlagen: %s", e)
|
||||
return
|
||||
|
||||
elif msg_type == "doctor_fix":
|
||||
# App-Button "ARIA reparieren" → openclaw doctor --fix anstossen.
|
||||
# Bridge erreicht aria-core nicht via docker (kein docker-socket
|
||||
# gemountet), aber der Diagnostic-Server hat den Socket. HTTP-Call
|
||||
# an http://localhost:3001/api/doctor-fix.
|
||||
logger.info("[rvs] doctor_fix Request von App — leite an Diagnostic weiter")
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
"http://localhost:3001/api/doctor-fix",
|
||||
data=b"{}",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
# Blocking call ist OK weil openclaw doctor schnell durchlaeuft.
|
||||
# In Executor laufen lassen damit der asyncio-Loop nicht blockt.
|
||||
def _do_fix():
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return resp.status, resp.read().decode("utf-8", errors="ignore")
|
||||
except Exception as e:
|
||||
return None, str(e)
|
||||
status, body = await asyncio.get_event_loop().run_in_executor(None, _do_fix)
|
||||
ok = status == 200
|
||||
logger.info("[rvs] doctor_fix Result: status=%s ok=%s", status, ok)
|
||||
await self._send_to_rvs({
|
||||
"type": "chat",
|
||||
"payload": {
|
||||
"text": "[Reparatur] ARIA wurde durchgecheckt — sollte wieder antworten." if ok
|
||||
else f"[Reparatur] Fehlgeschlagen: {body[:200]}",
|
||||
"sender": "aria",
|
||||
},
|
||||
"timestamp": int(asyncio.get_event_loop().time() * 1000),
|
||||
})
|
||||
except Exception as e:
|
||||
logger.warning("[rvs] doctor_fix Weiterleitung fehlgeschlagen: %s", e)
|
||||
return
|
||||
|
||||
elif msg_type == "file_request":
|
||||
# App fordert eine Datei an (Re-Download nach Cache-Leerung)
|
||||
server_path = payload.get("serverPath", "")
|
||||
|
||||
+47
-1
@@ -288,7 +288,12 @@
|
||||
<div class="chat-box" id="chat-box"></div>
|
||||
<div id="thinking-indicator" style="display:none;padding:6px 10px;font-size:12px;color:#FFD60A;background:#1E1E2E;border-radius:0 0 6px 6px;margin-top:-8px;margin-bottom:8px;align-items:center;justify-content:space-between;">
|
||||
<span><span style="animation:pulse 1s infinite;">💭</span> <span id="thinking-text">ARIA denkt...</span></span>
|
||||
<button class="btn secondary" onclick="cancelRequest()" style="padding:2px 10px;font-size:11px;color:#FF3B30;border-color:#FF3B30;">Abbrechen</button>
|
||||
<div style="display:flex;gap:6px;">
|
||||
<button class="btn secondary" onclick="doctorFix()" style="padding:2px 10px;font-size:11px;color:#FF9500;border-color:#FF9500;" title="ARIA reparieren — openclaw doctor --fix">🔧 Reparieren</button>
|
||||
<button class="btn secondary" onclick="ariaSessionReset()" style="padding:2px 10px;font-size:11px;color:#FF9500;border-color:#FF9500;" title="Konversation komprimieren — Sessions weg + Restart">🧹 Compact</button>
|
||||
<button class="btn secondary" onclick="ariaRestart()" style="padding:2px 10px;font-size:11px;color:#FF3B30;border-color:#FF3B30;" title="Container hart neu starten (~15s)">🚨 Hart neu</button>
|
||||
<button class="btn secondary" onclick="cancelRequest()" style="padding:2px 10px;font-size:11px;color:#FF3B30;border-color:#FF3B30;">Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="diag-pending-attachments" style="display:none;padding:6px 10px;background:#1E1E2E;border-radius:6px 6px 0 0;margin-bottom:-4px;display:flex;gap:6px;flex-wrap:wrap;align-items:center;">
|
||||
</div>
|
||||
@@ -1858,6 +1863,47 @@
|
||||
renderDiagPending();
|
||||
}
|
||||
|
||||
// ── Reparieren — openclaw doctor --fix ──────
|
||||
function doctorFix() {
|
||||
fetch('/api/doctor-fix', { method: 'POST' })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.ok) {
|
||||
addLog('info', 'server', 'Reparatur ausgefuehrt: ' + (data.output || 'OK').slice(0, 200));
|
||||
} else {
|
||||
addLog('error', 'server', 'Reparatur fehlgeschlagen: ' + (data.error || ''));
|
||||
}
|
||||
})
|
||||
.catch(err => addLog('error', 'server', 'Reparatur Request fehlgeschlagen: ' + err.message));
|
||||
}
|
||||
|
||||
// ── Hard-Restart — docker restart aria-core ──────
|
||||
function ariaRestart() {
|
||||
if (!confirm('ARIA wird hart neu gestartet (Container-Restart, ~15s).\n\nLaufende Anfragen gehen verloren. Sicher?')) return;
|
||||
fetch('/api/aria-restart', { method: 'POST' })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.ok) {
|
||||
addLog('info', 'server', 'ARIA neu gestartet — wartet auf Reconnect');
|
||||
} else {
|
||||
addLog('error', 'server', 'Restart fehlgeschlagen: ' + (data.error || ''));
|
||||
}
|
||||
})
|
||||
.catch(err => addLog('error', 'server', 'Restart Request fehlgeschlagen: ' + err.message));
|
||||
}
|
||||
|
||||
// ── Compact / Session-Reset ──────
|
||||
function ariaSessionReset() {
|
||||
if (!confirm('Konversation komprimieren: alle Nachrichten in ARIAs aktueller Session werden geloescht und der Container neu gestartet. ARIA vergisst den bisherigen Gespraechsverlauf. Sicher?')) return;
|
||||
fetch('/api/aria-session-reset', { method: 'POST' })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.ok) addLog('info', 'server', 'Session geleert, ARIA neu gestartet');
|
||||
else addLog('error', 'server', 'Reset fehlgeschlagen: ' + (data.error || ''));
|
||||
})
|
||||
.catch(err => addLog('error', 'server', 'Reset Request fehlgeschlagen: ' + err.message));
|
||||
}
|
||||
|
||||
// ── Abbrechen ──────────────────────────────
|
||||
function cancelRequest() {
|
||||
send({ action: 'cancel_request' });
|
||||
|
||||
@@ -1342,6 +1342,97 @@ const server = http.createServer((req, res) => {
|
||||
dockerExec("aria-core", "openclaw doctor --fix 2>/dev/null || true").catch(() => {});
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: true }));
|
||||
} else if (req.url === "/api/doctor-fix" && req.method === "POST") {
|
||||
// Manueller "ARIA reparieren"-Button — stuck OpenClaw-Runs aufloesen.
|
||||
log("info", "server", "HTTP /api/doctor-fix — manueller Reparatur-Trigger");
|
||||
dockerExec("aria-core", "openclaw doctor --fix 2>&1")
|
||||
.then(out => {
|
||||
const summary = (out || "").split("\n").filter(l => l.trim()).slice(-3).join(" | ");
|
||||
broadcast({ type: "watchdog", status: "fixed", message: `Reparatur ausgefuehrt: ${summary || "OK"}` });
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: true, output: out }));
|
||||
})
|
||||
.catch(err => {
|
||||
broadcast({ type: "watchdog", status: "error", message: `Reparatur fehlgeschlagen: ${err.message}` });
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: false, error: err.message }));
|
||||
});
|
||||
return;
|
||||
} else if (req.url === "/api/aria-session-reset" && req.method === "POST") {
|
||||
// Sessions weg + Container neu — fuer Compact-After-N-Messages.
|
||||
// E2BIG bei zu langen Sessions: argv beim Subprocess-spawn ueberschritten.
|
||||
log("warn", "server", "HTTP /api/aria-session-reset — Sessions loeschen + Restart");
|
||||
broadcast({ type: "watchdog", status: "fixing", message: "Sessions werden geleert — ARIA bekommt frischen Start" });
|
||||
dockerExec("aria-core", "rm -f /home/node/.openclaw/agents/main/sessions/*.jsonl /home/node/.openclaw/agents/main/sessions/*.lock 2>&1 && echo '{}' > /home/node/.openclaw/agents/main/sessions/sessions.json")
|
||||
.then(() => {
|
||||
// Restart via Docker-API (gleicher Pfad wie /api/aria-restart)
|
||||
const restartReq = http.request({
|
||||
socketPath: "/var/run/docker.sock",
|
||||
path: "/containers/aria-core/restart?t=10",
|
||||
method: "POST",
|
||||
headers: { "Content-Length": 0 },
|
||||
timeout: 30000,
|
||||
}, (dRes) => {
|
||||
if (dRes.statusCode === 204) {
|
||||
log("info", "server", "aria-session-reset OK");
|
||||
broadcast({ type: "watchdog", status: "fixed", message: "Sessions geleert, ARIA neu gestartet" });
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: true }));
|
||||
} else {
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: false, error: `Docker-API ${dRes.statusCode}` }));
|
||||
}
|
||||
});
|
||||
restartReq.on("error", (err) => {
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: false, error: err.message }));
|
||||
});
|
||||
restartReq.end();
|
||||
})
|
||||
.catch((err) => {
|
||||
log("error", "server", `aria-session-reset Cleanup fehlgeschlagen: ${err.message}`);
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: false, error: err.message }));
|
||||
});
|
||||
return;
|
||||
} else if (req.url === "/api/aria-restart" && req.method === "POST") {
|
||||
// Harter Restart — fuer Faelle wo doctor --fix nicht reicht (alive aber
|
||||
// haengender Run). Geht ueber Docker-API (Socket), kein CLI noetig.
|
||||
// POST /containers/aria-core/restart?t=10 → SIGTERM, dann nach 10s SIGKILL.
|
||||
log("warn", "server", "HTTP /api/aria-restart — harter Container-Restart");
|
||||
broadcast({ type: "watchdog", status: "fixing", message: "ARIA wird hart neu gestartet (~15s)" });
|
||||
const restartReq = http.request({
|
||||
socketPath: "/var/run/docker.sock",
|
||||
path: "/containers/aria-core/restart?t=10",
|
||||
method: "POST",
|
||||
headers: { "Content-Length": 0 },
|
||||
timeout: 30000,
|
||||
}, (dRes) => {
|
||||
let body = "";
|
||||
dRes.on("data", (c) => body += c);
|
||||
dRes.on("end", () => {
|
||||
// Docker-API: 204 = OK, 404 = container nicht da, 500 = anderer Fehler
|
||||
if (dRes.statusCode === 204) {
|
||||
log("info", "server", "aria-restart OK (Docker-API)");
|
||||
broadcast({ type: "watchdog", status: "fixed", message: "ARIA wurde neu gestartet — sollte gleich antworten" });
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: true }));
|
||||
} else {
|
||||
log("error", "server", `aria-restart Docker-API ${dRes.statusCode}: ${body.slice(0, 200)}`);
|
||||
broadcast({ type: "watchdog", status: "error", message: `Restart fehlgeschlagen: HTTP ${dRes.statusCode}` });
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: false, error: `Docker-API ${dRes.statusCode}: ${body}` }));
|
||||
}
|
||||
});
|
||||
});
|
||||
restartReq.on("error", (err) => {
|
||||
log("error", "server", `aria-restart Socket-Fehler: ${err.message}`);
|
||||
broadcast({ type: "watchdog", status: "error", message: `Restart fehlgeschlagen: ${err.message}` });
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: false, error: err.message }));
|
||||
});
|
||||
restartReq.end();
|
||||
return;
|
||||
} else if (req.url.startsWith("/shared/")) {
|
||||
// Dateien aus Shared Volume ausliefern (Bilder, Uploads)
|
||||
const filePath = decodeURIComponent(req.url);
|
||||
|
||||
@@ -87,6 +87,7 @@ services:
|
||||
- RVS_TLS=${RVS_TLS:-true}
|
||||
- RVS_TLS_FALLBACK=${RVS_TLS_FALLBACK:-true}
|
||||
- RVS_TOKEN=${RVS_TOKEN:-}
|
||||
- COMPACT_AFTER_MESSAGES=${COMPACT_AFTER_MESSAGES:-140}
|
||||
restart: unless-stopped
|
||||
|
||||
# ─── Diagnostic (Selbstcheck-UI und Einstellungen) ────
|
||||
|
||||
@@ -19,6 +19,9 @@ const ALLOWED_TYPES = new Set([
|
||||
"agent_activity", "cancel_request",
|
||||
"audio_pcm",
|
||||
"file_from_aria",
|
||||
"doctor_fix",
|
||||
"aria_restart",
|
||||
"aria_session_reset",
|
||||
"xtts_delete_voice",
|
||||
"voice_preload", "voice_ready",
|
||||
"stt_request", "stt_response",
|
||||
|
||||
Reference in New Issue
Block a user