added setting and permissions
This commit is contained in:
parent
08256c6113
commit
cd9d8cda1f
|
|
@ -87,11 +87,49 @@
|
|||
.modal-close:hover { color:#FF3B30; }
|
||||
.modal-body { flex:1; padding:4px; min-height:400px; }
|
||||
.modal-footer { padding:6px 14px; border-top:1px solid #1E1E2E; font-size:11px; color:#8888AA; }
|
||||
|
||||
/* Haupt-Navigation */
|
||||
.main-nav { display:flex; gap:0; margin-bottom:16px; border-bottom:2px solid #1E1E2E; }
|
||||
.main-nav-btn { background:none; border:none; color:#8888AA; font-family:inherit; font-size:14px;
|
||||
padding:10px 20px; cursor:pointer; border-bottom:2px solid transparent; margin-bottom:-2px;
|
||||
transition: color 0.2s, border-color 0.2s; }
|
||||
.main-nav-btn:hover { color:#E0E0F0; }
|
||||
.main-nav-btn.active { color:#0096FF; border-bottom-color:#0096FF; }
|
||||
.main-tab { display:none; }
|
||||
.main-tab.visible { display:block; }
|
||||
|
||||
/* Settings */
|
||||
.settings-section { margin-bottom:20px; }
|
||||
.settings-section h2 { margin-bottom:12px; }
|
||||
.perm-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:8px; }
|
||||
.perm-item { display:flex; align-items:center; justify-content:space-between; background:#12122A;
|
||||
border:1px solid #1E1E2E; border-radius:6px; padding:8px 12px; }
|
||||
.perm-item .perm-info { flex:1; min-width:0; }
|
||||
.perm-item .perm-name { font-size:13px; color:#E0E0F0; font-weight:bold; }
|
||||
.perm-item .perm-desc { font-size:10px; color:#8888AA; margin-top:2px; }
|
||||
.toggle { position:relative; width:40px; height:22px; flex-shrink:0; margin-left:8px; }
|
||||
.toggle input { opacity:0; width:0; height:0; }
|
||||
.toggle .slider { position:absolute; cursor:pointer; top:0; left:0; right:0; bottom:0;
|
||||
background:#333; border-radius:22px; transition:0.3s; }
|
||||
.toggle .slider:before { content:""; position:absolute; height:16px; width:16px; left:3px; bottom:3px;
|
||||
background:#888; border-radius:50%; transition:0.3s; }
|
||||
.toggle input:checked + .slider { background:#0096FF; }
|
||||
.toggle input:checked + .slider:before { transform:translateX(18px); background:#fff; }
|
||||
.toggle input:disabled + .slider { opacity:0.4; cursor:not-allowed; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ARIA Diagnostic</h1>
|
||||
|
||||
<!-- Haupt-Navigation -->
|
||||
<div class="main-nav">
|
||||
<button class="main-nav-btn active" onclick="switchMainTab('main')">Main</button>
|
||||
<button class="main-nav-btn" onclick="switchMainTab('settings')">Einstellungen</button>
|
||||
</div>
|
||||
|
||||
<!-- ══════ TAB: Main ══════ -->
|
||||
<div id="tab-main" class="main-tab visible">
|
||||
|
||||
<!-- Verbindungsstatus -->
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
|
|
@ -266,6 +304,55 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /tab-main -->
|
||||
|
||||
<!-- ══════ TAB: Einstellungen ══════ -->
|
||||
<div id="tab-settings" class="main-tab">
|
||||
|
||||
<!-- Tool-Berechtigungen -->
|
||||
<div class="settings-section">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
|
||||
<h2>Tool-Berechtigungen</h2>
|
||||
<div style="display:flex;gap:6px;">
|
||||
<button class="btn secondary" onclick="loadPermissions()" style="padding:4px 12px;font-size:11px;">Laden</button>
|
||||
<button class="btn" onclick="savePermissions()" id="btn-save-perms" style="padding:4px 12px;font-size:11px;" disabled>Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size:11px;color:#8888AA;margin-bottom:12px;">
|
||||
Hier kannst du festlegen, welche Tools ARIA benutzen darf. Aenderungen werden in der OpenClaw-Konfiguration gespeichert.
|
||||
</div>
|
||||
<div id="perms-status" style="font-size:11px;margin-bottom:8px;display:none;"></div>
|
||||
<div id="perms-grid" class="perm-grid">
|
||||
<!-- Wird dynamisch gefuellt -->
|
||||
<div style="color:#555570;padding:12px;">Klicke "Laden" um die aktuellen Berechtigungen abzurufen</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Model-Einstellungen -->
|
||||
<div class="settings-section">
|
||||
<h2>Model</h2>
|
||||
<div class="card" style="max-width:500px;">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
|
||||
<span style="font-size:12px;color:#8888AA;white-space:nowrap;">Aktives Model:</span>
|
||||
<input type="text" id="setting-model" placeholder="z.B. proxy/claude-sonnet-4" style="flex:1;background:#1E1E2E;border:1px solid #333;border-radius:4px;padding:6px 8px;color:#E0E0F0;font-family:inherit;font-size:12px;">
|
||||
<button class="btn secondary" onclick="loadModel()" style="padding:4px 8px;font-size:10px;">Laden</button>
|
||||
<button class="btn" onclick="saveModel()" style="padding:4px 8px;font-size:10px;">Setzen</button>
|
||||
</div>
|
||||
<div id="model-status" style="font-size:10px;color:#8888AA;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OpenClaw Config Info -->
|
||||
<div class="settings-section">
|
||||
<h2>OpenClaw Config</h2>
|
||||
<div class="card">
|
||||
<button class="btn secondary" onclick="loadOpenClawConfig()" style="padding:4px 12px;font-size:11px;margin-bottom:8px;">Config laden</button>
|
||||
<pre id="openclaw-config" style="font-size:10px;color:#8888AA;white-space:pre-wrap;max-height:300px;overflow-y:auto;margin:0;">(Noch nicht geladen)</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /tab-settings -->
|
||||
|
||||
<!-- Terminal Modal -->
|
||||
<div class="modal-overlay" id="term-modal">
|
||||
<div class="modal-box">
|
||||
|
|
@ -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 = '<div style="color:#8888AA;padding:12px;">Lade Berechtigungen...</div>';
|
||||
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 += `<div style="grid-column:1/-1;font-size:11px;color:#0096FF;margin-top:8px;text-transform:uppercase;letter-spacing:1px;">${escapeHtml(cat)}</div>`;
|
||||
for (const tool of tools) {
|
||||
const checked = permState[tool.id] ? 'checked' : '';
|
||||
html += `<div class="perm-item">`
|
||||
+ `<div class="perm-info"><div class="perm-name">${escapeHtml(tool.name)}</div><div class="perm-desc">${escapeHtml(tool.desc)}</div></div>`
|
||||
+ `<label class="toggle"><input type="checkbox" ${checked} onchange="togglePerm('${tool.id}', this.checked)"><span class="slider"></span></label>`
|
||||
+ `</div>`;
|
||||
}
|
||||
}
|
||||
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();
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -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", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue