@@ -489,6 +576,27 @@
}
if (msg.type === 'brain_list') { renderBrainList(msg); return; }
if (msg.type === 'brain_content') { renderBrainContent(msg); return; }
+ // Settings
+ if (msg.type === 'permissions_list') { renderPermissions(msg); return; }
+ if (msg.type === 'permissions_saved') {
+ const s = document.getElementById('perms-status');
+ s.style.display = 'block';
+ if (msg.ok) { s.style.color = '#34C759'; s.textContent = 'Berechtigungen gespeichert!'; loadPermissions(); }
+ else { s.style.color = '#FF6B6B'; s.textContent = 'Fehler: ' + (msg.error || '?'); }
+ return;
+ }
+ if (msg.type === 'model_info') {
+ const el = document.getElementById('setting-model');
+ const st = document.getElementById('model-status');
+ if (msg.model) el.value = msg.model;
+ st.textContent = msg.info || '';
+ st.style.color = msg.error ? '#FF6B6B' : '#34C759';
+ return;
+ }
+ if (msg.type === 'openclaw_config') {
+ document.getElementById('openclaw-config').textContent = msg.config || msg.error || '(leer)';
+ return;
+ }
if (msg.type === 'response') { return; }
};
}
@@ -1059,6 +1167,130 @@
document.getElementById('brain-content').style.display = 'none';
}
+ // ── Haupt-Tab Navigation ──────────────────────────────────
+
+ function switchMainTab(tab) {
+ document.querySelectorAll('.main-tab').forEach(el => el.classList.remove('visible'));
+ document.querySelectorAll('.main-nav-btn').forEach(b => b.classList.remove('active'));
+ const target = document.getElementById('tab-' + tab);
+ if (target) target.classList.add('visible');
+ // Button aktivieren
+ document.querySelectorAll('.main-nav-btn').forEach(b => {
+ if (b.textContent.trim().toLowerCase().includes(tab === 'main' ? 'main' : 'einstellung')) b.classList.add('active');
+ });
+ }
+
+ // ── Einstellungen: Tool-Berechtigungen ──────────────────
+
+ const TOOL_DEFS = [
+ { id: 'Bash', name: 'Bash', desc: 'Shell-Befehle ausfuehren', cat: 'System' },
+ { id: 'Read', name: 'Read', desc: 'Dateien lesen', cat: 'Dateien' },
+ { id: 'Write', name: 'Write', desc: 'Dateien schreiben/erstellen', cat: 'Dateien' },
+ { id: 'Edit', name: 'Edit', desc: 'Dateien bearbeiten (Diff)', cat: 'Dateien' },
+ { id: 'Grep', name: 'Grep', desc: 'In Dateien suchen', cat: 'Dateien' },
+ { id: 'Glob', name: 'Glob', desc: 'Dateien nach Muster finden', cat: 'Dateien' },
+ { id: 'WebFetch', name: 'WebFetch', desc: 'Webseiten abrufen (HTTP)', cat: 'Netzwerk' },
+ { id: 'WebSearch', name: 'WebSearch', desc: 'Web-Suche durchfuehren', cat: 'Netzwerk' },
+ { id: 'NotebookEdit', name: 'NotebookEdit', desc: 'Jupyter Notebooks bearbeiten', cat: 'Dateien' },
+ { id: 'TodoWrite', name: 'TodoWrite', desc: 'Aufgaben/Todos verwalten', cat: 'Organisation' },
+ { id: 'Agent', name: 'Agent', desc: 'Sub-Agenten starten', cat: 'System' },
+ { id: 'AskUserQuestion', name: 'AskUserQuestion', desc: 'Rueckfragen stellen', cat: 'Kommunikation' },
+ ];
+
+ let permState = {}; // { toolId: true/false }
+ let permsDirty = false;
+
+ function loadPermissions() {
+ const grid = document.getElementById('perms-grid');
+ grid.innerHTML = '
Lade Berechtigungen...
';
+ send({ action: 'list_permissions' });
+ }
+
+ function renderPermissions(data) {
+ const grid = document.getElementById('perms-grid');
+ const status = document.getElementById('perms-status');
+
+ if (data.error) {
+ status.style.display = 'block';
+ status.style.color = '#FF6B6B';
+ status.textContent = 'Fehler: ' + data.error;
+ } else if (data.info) {
+ status.style.display = 'block';
+ status.style.color = '#8888AA';
+ status.textContent = data.info;
+ } else {
+ status.style.display = 'none';
+ }
+
+ // Merge: Server-Daten als Basis, unbekannte Tools default an
+ permState = {};
+ const serverPerms = data.permissions || {};
+ for (const tool of TOOL_DEFS) {
+ // Wenn Server explizit sagt: check allowedTools
+ if (data.allowedTools && data.allowedTools.length > 0) {
+ permState[tool.id] = data.allowedTools.includes(tool.id);
+ } else if (serverPerms[tool.id] !== undefined) {
+ permState[tool.id] = !!serverPerms[tool.id];
+ } else {
+ permState[tool.id] = true; // Default: an
+ }
+ }
+
+ // Gruppieren nach Kategorie
+ const cats = {};
+ for (const tool of TOOL_DEFS) {
+ if (!cats[tool.cat]) cats[tool.cat] = [];
+ cats[tool.cat].push(tool);
+ }
+
+ let html = '';
+ for (const [cat, tools] of Object.entries(cats)) {
+ html += `
${escapeHtml(cat)}
`;
+ for (const tool of tools) {
+ const checked = permState[tool.id] ? 'checked' : '';
+ html += `
`
+ + `
${escapeHtml(tool.name)}
${escapeHtml(tool.desc)}
`
+ + `
`
+ + `
`;
+ }
+ }
+ grid.innerHTML = html;
+ permsDirty = false;
+ document.getElementById('btn-save-perms').disabled = true;
+ }
+
+ function togglePerm(toolId, enabled) {
+ permState[toolId] = enabled;
+ permsDirty = true;
+ document.getElementById('btn-save-perms').disabled = false;
+ }
+
+ function savePermissions() {
+ if (!permsDirty) return;
+ const allowedTools = Object.entries(permState).filter(([_, v]) => v).map(([k]) => k);
+ send({ action: 'save_permissions', allowedTools });
+ document.getElementById('btn-save-perms').disabled = true;
+ }
+
+ // ── Einstellungen: Model ────────────────────────────────
+
+ function loadModel() {
+ send({ action: 'get_model' });
+ }
+
+ function saveModel() {
+ const model = document.getElementById('setting-model').value.trim();
+ if (!model) return;
+ send({ action: 'set_model', model });
+ }
+
+ // ── Einstellungen: OpenClaw Config ──────────────────────
+
+ function loadOpenClawConfig() {
+ document.getElementById('openclaw-config').textContent = 'Lade...';
+ send({ action: 'get_openclaw_config' });
+ }
+
connectWS();
diff --git a/diagnostic/server.js b/diagnostic/server.js
index 2d55ba7..a8a11b0 100644
--- a/diagnostic/server.js
+++ b/diagnostic/server.js
@@ -987,6 +987,17 @@ wss.on("connection", (ws) => {
handleListBrain(ws);
} else if (msg.action === "read_brain_file") {
handleReadBrainFile(ws, msg.filename);
+ // ── Einstellungen ──
+ } else if (msg.action === "list_permissions") {
+ handleListPermissions(ws);
+ } else if (msg.action === "save_permissions") {
+ handleSavePermissions(ws, msg.allowedTools);
+ } else if (msg.action === "get_model") {
+ handleGetModel(ws);
+ } else if (msg.action === "set_model") {
+ handleSetModel(ws, msg.model);
+ } else if (msg.action === "get_openclaw_config") {
+ handleGetOpenClawConfig(ws);
}
} catch {}
});
@@ -1384,6 +1395,166 @@ async function handleReadBrainFile(clientWs, filename) {
}
}
+// ── Einstellungen: Tool-Berechtigungen ──────────────────
+
+const OPENCLAW_SETTINGS_PATHS = [
+ "/home/node/.openclaw/settings.json",
+ "/home/node/.openclaw/agents/main/agent/settings.json",
+ "/home/node/.openclaw/config.json",
+];
+
+async function findSettingsFile() {
+ // Pruefen welche Settings-Dateien existieren
+ try {
+ const raw = await dockerExec("aria-core", `
+ for f in ${OPENCLAW_SETTINGS_PATHS.join(" ")}; do
+ [ -f "$f" ] && echo "FOUND:$f"
+ done
+ `.trim());
+ for (const line of raw.split("\n")) {
+ if (line.startsWith("FOUND:")) return line.slice(6);
+ }
+ } catch {}
+ // Default: erster Pfad (wird erstellt)
+ return OPENCLAW_SETTINGS_PATHS[0];
+}
+
+async function handleListPermissions(clientWs) {
+ try {
+ log("info", "server", "Lade Tool-Berechtigungen...");
+
+ const settingsPath = await findSettingsFile();
+ let settings = {};
+ let info = "";
+
+ try {
+ const raw = await dockerExec("aria-core", `cat '${settingsPath}' 2>/dev/null || echo '{}'`);
+ settings = JSON.parse(raw.trim() || "{}");
+ info = `Geladen aus: ${settingsPath}`;
+ } catch (e) {
+ info = `Settings-Datei nicht lesbar (${settingsPath}) — Default-Berechtigungen`;
+ }
+
+ // OpenClaw/Claude Code Format: allowedTools ist ein Array von Tool-Namen
+ // Wenn leer/nicht vorhanden: alle Tools erlaubt
+ const allowedTools = settings.allowedTools || [];
+ const permissions = settings.permissions || {};
+
+ clientWs.send(JSON.stringify({
+ type: "permissions_list",
+ allowedTools,
+ permissions,
+ settingsPath,
+ info,
+ }));
+ log("info", "server", `Berechtigungen geladen (${allowedTools.length} Tools explizit erlaubt)`);
+ } catch (err) {
+ log("error", "server", `Berechtigungen laden fehlgeschlagen: ${err.message}`);
+ clientWs.send(JSON.stringify({ type: "permissions_list", error: err.message, allowedTools: [], permissions: {} }));
+ }
+}
+
+async function handleSavePermissions(clientWs, allowedTools) {
+ if (!Array.isArray(allowedTools)) {
+ clientWs.send(JSON.stringify({ type: "permissions_saved", ok: false, error: "Ungueltige Daten" }));
+ return;
+ }
+ try {
+ const settingsPath = await findSettingsFile();
+ log("info", "server", `Speichere ${allowedTools.length} Tool-Berechtigungen in ${settingsPath}`);
+
+ // Bestehende Settings lesen und mergen
+ const script = [
+ 'const fs=require("fs");',
+ `const f="${settingsPath}";`,
+ `const tools=${JSON.stringify(allowedTools)};`,
+ 'let s={};try{s=JSON.parse(fs.readFileSync(f,"utf8"));}catch(e){}',
+ 's.allowedTools=tools;',
+ `const dir=f.substring(0,f.lastIndexOf("/"));`,
+ 'try{fs.mkdirSync(dir,{recursive:true});}catch(e){}',
+ 'fs.writeFileSync(f,JSON.stringify(s,null,2));',
+ ].join("");
+ const b64 = Buffer.from(script).toString("base64");
+ await dockerExec("aria-core", `echo ${b64} | base64 -d | node`);
+
+ clientWs.send(JSON.stringify({ type: "permissions_saved", ok: true }));
+ log("info", "server", "Berechtigungen gespeichert");
+ } catch (err) {
+ log("error", "server", `Berechtigungen speichern fehlgeschlagen: ${err.message}`);
+ clientWs.send(JSON.stringify({ type: "permissions_saved", ok: false, error: err.message }));
+ }
+}
+
+// ── Einstellungen: Model ────────────────────────────────
+
+async function handleGetModel(clientWs) {
+ try {
+ const raw = await dockerExec("aria-core", `echo $DEFAULT_MODEL`);
+ clientWs.send(JSON.stringify({ type: "model_info", model: raw.trim(), info: "Aktuelles Model (ENV)" }));
+ } catch (err) {
+ clientWs.send(JSON.stringify({ type: "model_info", error: err.message }));
+ }
+}
+
+async function handleSetModel(clientWs, model) {
+ if (!model || typeof model !== "string") {
+ clientWs.send(JSON.stringify({ type: "model_info", error: "Kein Model angegeben" }));
+ return;
+ }
+ try {
+ // Model in Settings speichern (OpenClaw liest das)
+ const settingsPath = await findSettingsFile();
+ const script = [
+ 'const fs=require("fs");',
+ `const f="${settingsPath}";`,
+ 'let s={};try{s=JSON.parse(fs.readFileSync(f,"utf8"));}catch(e){}',
+ `s.model=${JSON.stringify(model)};`,
+ `const dir=f.substring(0,f.lastIndexOf("/"));`,
+ 'try{fs.mkdirSync(dir,{recursive:true});}catch(e){}',
+ 'fs.writeFileSync(f,JSON.stringify(s,null,2));',
+ ].join("");
+ const b64 = Buffer.from(script).toString("base64");
+ await dockerExec("aria-core", `echo ${b64} | base64 -d | node`);
+
+ clientWs.send(JSON.stringify({ type: "model_info", model, info: `Model auf "${model}" gesetzt (Neustart noetig)` }));
+ log("info", "server", `Model gesetzt: ${model}`);
+ } catch (err) {
+ clientWs.send(JSON.stringify({ type: "model_info", error: err.message }));
+ }
+}
+
+// ── Einstellungen: OpenClaw Config ──────────────────────
+
+async function handleGetOpenClawConfig(clientWs) {
+ try {
+ const raw = await dockerExec("aria-core", `
+ echo '=== Umgebungsvariablen ==='
+ echo "DEFAULT_MODEL=$DEFAULT_MODEL"
+ echo "RATE_LIMIT_PER_USER=$RATE_LIMIT_PER_USER"
+ echo "OPENCLAW_GATEWAY_TOKEN=$(echo $OPENCLAW_GATEWAY_TOKEN | head -c 8)..."
+ echo "OPENCLAW_GATEWAY_BIND=$OPENCLAW_GATEWAY_BIND"
+ echo ""
+ echo '=== Settings-Dateien ==='
+ for f in ${OPENCLAW_SETTINGS_PATHS.join(" ")}; do
+ if [ -f "$f" ]; then
+ echo "--- $f ---"
+ cat "$f"
+ echo ""
+ fi
+ done
+ echo ""
+ echo '=== Agent-Verzeichnis ==='
+ ls -la /home/node/.openclaw/agents/main/agent/ 2>&1
+ echo ""
+ echo '=== Workspace ==='
+ ls -la /home/node/.openclaw/workspace/ 2>&1
+ `.trim());
+ clientWs.send(JSON.stringify({ type: "openclaw_config", config: raw }));
+ } catch (err) {
+ clientWs.send(JSON.stringify({ type: "openclaw_config", error: err.message }));
+ }
+}
+
// ── Start ───────────────────────────────────────────────
server.listen(HTTP_PORT, "0.0.0.0", () => {