diff --git a/diagnostic/index.html b/diagnostic/index.html
index a40ba44..9d89d55 100644
--- a/diagnostic/index.html
+++ b/diagnostic/index.html
@@ -584,7 +584,7 @@
if (msg.ok) {
// Nachricht merken und nach Reload wieder anzeigen
permsSavedMsg = (msg.info || 'Berechtigungen gespeichert!')
- + '
Aenderungen werden erst bei einer neuen Session wirksam.'
+ + '
Session muss neu gestartet werden damit Aenderungen wirksam werden.'
+ ' ';
s.style.color = '#34C759';
s.innerHTML = permsSavedMsg;
@@ -595,6 +595,21 @@
}
return;
}
+ if (msg.type === 'session_restarted') {
+ const s = document.getElementById('perms-status');
+ s.style.display = 'block';
+ if (msg.status === 'restarting') {
+ s.style.color = '#FFD60A';
+ s.textContent = 'aria-core wird neu gestartet...';
+ } else if (msg.status === 'ok') {
+ s.style.color = '#34C759';
+ s.textContent = msg.info || 'Session neu gestartet!';
+ } else {
+ s.style.color = '#FF6B6B';
+ s.textContent = 'Restart fehlgeschlagen: ' + (msg.error || '?');
+ }
+ return;
+ }
if (msg.type === 'model_info') {
const el = document.getElementById('setting-model');
const st = document.getElementById('model-status');
@@ -1306,12 +1321,11 @@
}
function restartAriaSession() {
- if (!confirm('Neue Session starten? Die aktuelle Session bleibt erhalten aber wird inaktiv.')) return;
- const ts = Date.now().toString(36);
- send({ action: 'create_session', sessionName: `aria-${ts}` });
+ if (!confirm('Aktive Session neu starten? Der Kontext bleibt erhalten, Permissions werden neu geladen.')) return;
+ send({ action: 'restart_session' });
const s = document.getElementById('perms-status');
- s.style.color = '#34C759';
- s.textContent = 'Neue Session wird gestartet...';
+ s.style.color = '#FFD60A';
+ s.textContent = 'Session wird neu gestartet...';
}
// ── Einstellungen: Model ────────────────────────────────
diff --git a/diagnostic/server.js b/diagnostic/server.js
index e447a36..a892a59 100644
--- a/diagnostic/server.js
+++ b/diagnostic/server.js
@@ -983,6 +983,8 @@ wss.on("connection", (ws) => {
ws.send(JSON.stringify({ type: "active_session", sessionKey: activeSessionKey }));
} else if (msg.action === "create_session") {
handleCreateSession(ws, msg.sessionName);
+ } else if (msg.action === "restart_session") {
+ handleRestartSession(ws);
} else if (msg.action === "list_brain") {
handleListBrain(ws);
} else if (msg.action === "read_brain_file") {
@@ -1314,6 +1316,56 @@ async function handleCreateSession(clientWs, sessionName) {
}
}
+// ── Session neu starten (Container Restart) ────────────
+async function handleRestartSession(clientWs) {
+ try {
+ log("info", "server", "Starte aria-core Container neu (Session Restart)...");
+ clientWs.send(JSON.stringify({ type: "session_restarted", status: "restarting" }));
+
+ // Container neu starten via Docker API
+ const http = require("http");
+ await new Promise((resolve, reject) => {
+ const req = http.request({
+ socketPath: "/var/run/docker.sock",
+ path: "/containers/aria-core/restart?t=5",
+ method: "POST",
+ }, (res) => {
+ let body = "";
+ res.on("data", d => body += d);
+ res.on("end", () => {
+ if (res.statusCode === 204 || res.statusCode === 200) resolve();
+ else reject(new Error(`Restart fehlgeschlagen: HTTP ${res.statusCode} ${body}`));
+ });
+ });
+ req.on("error", reject);
+ req.end();
+ });
+
+ log("info", "server", "aria-core Container neu gestartet");
+ // Warten bis Gateway wieder erreichbar (max 30s)
+ for (let i = 0; i < 15; i++) {
+ await new Promise(r => setTimeout(r, 2000));
+ try {
+ await dockerExec("aria-core", "echo ok");
+ log("info", "server", "aria-core ist wieder erreichbar");
+ clientWs.send(JSON.stringify({
+ type: "session_restarted", status: "ok",
+ info: "aria-core neu gestartet — Permissions werden bei der naechsten Nachricht geladen",
+ }));
+ // Aktive Session beibehalten
+ for (const c of browserClients) {
+ c.send(JSON.stringify({ type: "active_session", sessionKey: activeSessionKey }));
+ }
+ return;
+ } catch {}
+ }
+ clientWs.send(JSON.stringify({ type: "session_restarted", status: "timeout", error: "aria-core antwortet nicht nach 30s" }));
+ } catch (err) {
+ log("error", "server", `Session Restart fehlgeschlagen: ${err.message}`);
+ clientWs.send(JSON.stringify({ type: "session_restarted", status: "error", error: err.message }));
+ }
+}
+
// ── Brain Viewer ────────────────────────────────────────
async function handleListBrain(clientWs) {
@@ -1401,6 +1453,8 @@ const OPENCLAW_SETTINGS_PATHS = [
"/home/node/.openclaw/settings.json",
"/home/node/.openclaw/agents/main/agent/settings.json",
"/home/node/.openclaw/config.json",
+ "/home/node/.claude/settings.json", // Claude Code User-Level
+ "/home/node/.openclaw/workspace/.claude/settings.json", // Claude Code Projekt-Level
];
async function findSettingsFile() {
@@ -1479,41 +1533,67 @@ async function handleSavePermissions(clientWs, allowedTools) {
try {
log("info", "server", `Speichere ${allowedTools.length} Tool-Berechtigungen in alle Settings-Pfade`);
- // Bestehende Settings lesen und mergen
- // Schreiben in ALLE moeglichen Pfade damit OpenClaw es sicher findet
+ // In ALLE moeglichen Pfade schreiben, in BEIDEN Formaten:
+ // 1. allowedTools: ["Bash", "Read", ...] — OpenClaw Format
+ // 2. permissions.allow: ["Bash(*)", "Read(*)", ...] — Claude Code Format
const script = [
'const fs=require("fs");const path=require("path");',
`const paths=${JSON.stringify(OPENCLAW_SETTINGS_PATHS)};`,
`const tools=${JSON.stringify(allowedTools)};`,
+ // Claude Code Format: Tool-Namen mit (*) Glob-Pattern
+ 'const ccAllow=tools.map(t=>t+"(*)");',
'for(const f of paths){',
'let s={};try{s=JSON.parse(fs.readFileSync(f,"utf8"));}catch(e){}',
+ // OpenClaw Format
's.allowedTools=tools;',
+ // Claude Code Format
+ 'if(!s.permissions)s.permissions={};',
+ 's.permissions.allow=ccAllow;',
+ 's.permissions.deny=[];',
'const dir=path.dirname(f);',
'try{fs.mkdirSync(dir,{recursive:true});}catch(e){}',
'fs.writeFileSync(f,JSON.stringify(s,null,2));',
'}',
- // Verify: ersten Pfad zuruecklesen
- `const v=JSON.parse(fs.readFileSync(paths[0],"utf8"));`,
- 'process.stdout.write(JSON.stringify(v.allowedTools||[]));',
+ // Verify: alle Pfade zuruecklesen
+ 'const result={};',
+ 'for(const f of paths){',
+ 'try{const d=JSON.parse(fs.readFileSync(f,"utf8"));',
+ 'result[f]={allowedTools:d.allowedTools||[],permsAllow:d.permissions&&d.permissions.allow||[]};}',
+ 'catch(e){result[f]={error:e.message};}',
+ '}',
+ 'process.stdout.write(JSON.stringify(result));',
].join("");
const b64 = Buffer.from(script).toString("base64");
const verifyRaw = await dockerExec("aria-core", `echo ${b64} | base64 -d | node`);
- // Verify: gespeicherte Daten mit gewuenschten vergleichen
+ // Verify: gespeicherte Daten pruefen
+ let verifyResult = {};
let verified = false;
- let savedTools = [];
+ let verifyDetails = [];
try {
- savedTools = JSON.parse(verifyRaw.trim());
- verified = Array.isArray(savedTools)
- && savedTools.length === allowedTools.length
- && allowedTools.every(t => savedTools.includes(t));
- } catch {}
+ verifyResult = JSON.parse(verifyRaw.trim());
+ verified = true;
+ for (const [fpath, data] of Object.entries(verifyResult)) {
+ if (data.error) {
+ verifyDetails.push(`${fpath}: FEHLER ${data.error}`);
+ verified = false;
+ } else {
+ const ok = data.allowedTools.length === allowedTools.length
+ && allowedTools.every(t => data.allowedTools.includes(t));
+ const ccOk = data.permsAllow.length === allowedTools.length;
+ verifyDetails.push(`${fpath}: allowedTools=${ok?"OK":"FEHLER"} permissions.allow=${ccOk?"OK":"FEHLER"} (${data.allowedTools.length}/${data.permsAllow.length} Tools)`);
+ if (!ok) verified = false;
+ }
+ }
+ } catch (e) {
+ verifyDetails.push(`Parse-Fehler: ${e.message}`);
+ }
+ log("info", "server", `Verify:\n${verifyDetails.join("\n")}`);
if (!verified) {
- log("error", "server", `Verify fehlgeschlagen! Erwartet: ${JSON.stringify(allowedTools)}, Gelesen: ${verifyRaw}`);
clientWs.send(JSON.stringify({
type: "permissions_saved", ok: false,
- error: `Verify fehlgeschlagen — Datei wurde nicht korrekt geschrieben. Erwartet ${allowedTools.length} Tools, gelesen: ${savedTools.length}`,
+ error: `Verify fehlgeschlagen:\n${verifyDetails.join("\n")}`,
}));
return;
}
diff --git a/docker-compose.yml b/docker-compose.yml
index ca04f85..c98e63c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -47,6 +47,7 @@ services:
- ./aria-data/config/BOOTSTRAP.md:/home/node/.openclaw/workspace/BOOTSTRAP.md
- ./aria-data/config/BOOTSTRAP.md:/home/node/.openclaw/workspace/CLAUDE.md
- ./aria-data/config/openclaw.env:/home/node/.openclaw/workspace/.env
+ - claude-config:/home/node/.claude # Claude Code Settings (Permissions)
- ./aria-data/ssh:/home/node/.ssh # SSH Keys fuer VM-Zugriff
- /tmp/.X11-unix:/tmp/.X11-unix
- /var/run/docker.sock:/var/run/docker.sock # VM von innen verwalten
@@ -100,6 +101,7 @@ services:
volumes:
openclaw-config: # Persistiert ~/.openclaw (Model, Auth, Sessions)
+ claude-config: # Persistiert ~/.claude (Permissions, Settings)
networks:
aria-net: