diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c37dda..d028c4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ Alle Änderungen am Projekt. Format: [Keep a Changelog](https://keepachangelog.c - Status-Karten: OpenClaw Gateway, RVS, Claude Proxy — jeweils mit Dot-Indicator - Claude Proxy Test: Prüft Erreichbarkeit (`/v1/models`) und sendet Test-Prompt an Claude — zeigt verfügbare Modelle als Tags + `DEFAULT_MODEL` Hinweis für docker-compose.yml - Auth-Check: "Auth prüfen" Button zeigt Inhalt von `/root/.config/claude/` im Proxy-Container (Dateien + `.credentials.json`) — per Docker Exec API -- Claude Login via UI: "Login starten" Button führt `claude login` im Proxy-Container aus, streamt Output live (URL + Code), klickbarer Link zum Autorisieren +- Claude Login via UI: "Login starten" Button führt `claude login` (mit `TERM=dumb NO_COLOR=1 CI=1`) im Proxy-Container aus, streamt Output live, ANSI-Codes werden gestrippt +- Credentials manuell einfügen: "Credentials einfügen" Button — JSON von einem eingeloggten Rechner (`cat ~/.config/claude/.credentials.json`) kopieren und direkt in den Container schreiben - Docker Exec API: Generische `dockerExec()` + Streaming-Variante `dockerExecStreaming()` für Befehle in laufenden Containern (via Docker Socket) - Chat-Test: Nachrichten direkt über Gateway oder via RVS senden - Tabbed Logs: Separate Tabs für Alle, Gateway, RVS, Proxy, Server — mit Zähler pro Tab diff --git a/diagnostic/index.html b/diagnostic/index.html index 1bad9e0..bf663bc 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -119,9 +119,19 @@ + + @@ -324,10 +334,14 @@ document.getElementById('login-done').style.display = 'block'; document.getElementById('btn-proxy-login').disabled = false; document.getElementById('btn-proxy-login').textContent = 'Erneut einloggen'; + const cs = document.getElementById('creds-status'); + cs.textContent = 'Erfolgreich!'; cs.style.color = '#34C759'; } else if (msg.status === 'error') { const el = document.getElementById('login-output'); el.textContent += 'FEHLER: ' + msg.error + '\n'; document.getElementById('btn-proxy-login').disabled = false; + const cs = document.getElementById('creds-status'); + cs.textContent = msg.error; cs.style.color = '#FF6B6B'; } return; } @@ -376,6 +390,24 @@ send({ action: 'proxy_login' }); } + function toggleCredsBox() { + const box = document.getElementById('proxy-creds-box'); + box.style.display = box.style.display === 'none' ? 'block' : 'none'; + } + + function submitCredentials() { + const input = document.getElementById('creds-input').value.trim(); + const status = document.getElementById('creds-status'); + if (!input) { status.textContent = 'Bitte JSON einfuegen'; status.style.color = '#FF6B6B'; return; } + try { + JSON.parse(input); + } catch (e) { + status.textContent = 'Ungueltig: Kein gueltiges JSON'; status.style.color = '#FF6B6B'; return; + } + status.textContent = 'Speichere...'; status.style.color = '#FFD60A'; + send({ action: 'write_credentials', credentials: input }); + } + function loadDockerLogs() { if (!DOCKER_TABS.includes(activeTab)) return; send({ action: 'docker_logs', tab: activeTab, tail: 200 }); diff --git a/diagnostic/server.js b/diagnostic/server.js index fe06b7f..dd546fb 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -475,6 +475,14 @@ function dockerExecStreaming(containerName, cmd, onData, onEnd) { createReq.end(); } +function stripAnsi(str) { + // ANSI escape sequences entfernen (Farben, Cursor, etc.) + return str.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, "") + .replace(/\x1b\][^\x07]*\x07/g, "") + .replace(/\x1b[><=][0-9]*/g, "") + .replace(/[\x00-\x08\x0e-\x1f]/g, ""); +} + function startProxyLogin() { if (loginProcess) { log("warn", "proxy", "Login laeuft bereits"); @@ -485,26 +493,20 @@ function startProxyLogin() { log("info", "proxy", "Starte Claude Login im Proxy-Container..."); broadcast({ type: "login_status", status: "starting" }); - // Volume ist :ro — wir muessen es rw remounten oder direkt im Container schreiben - // Da das Volume ro ist, schreiben wir die Credentials in einen temp-Pfad und kopieren sie danach - // Einfacher: claude login schreibt nach /root/.config/claude/ — das ist das gemountete Volume - // Problem: Volume ist :ro! Login kann nicht schreiben. - // Loesung: Login OHNE Volume ausfuehren, dann Credentials rauskopieren - // Alternative: Wir machen es anders — Login direkt, Output streamen - - // claude login: --no-open verhindert Browser-Oeffnung (Container hat keinen) - // Fallback auf verschiedene CLI-Versionen - dockerExecStreaming("aria-proxy", "claude login 2>&1 || echo 'Login-Befehl fehlgeschlagen. claude --version:' && claude --version 2>&1", + // TERM=dumb und NO_COLOR=1 deaktivieren die TUI und Farben + dockerExecStreaming("aria-proxy", "TERM=dumb NO_COLOR=1 CI=1 claude login 2>&1", (chunk) => { - // Jeder Chunk wird live an die UI gesendet - log("info", "proxy", `[login] ${chunk.trim()}`); + const clean = stripAnsi(chunk).trim(); + if (!clean) return; + + log("info", "proxy", `[login] ${clean}`); // URLs erkennen und hervorheben - const urlMatch = chunk.match(/https?:\/\/[^\s\]]+/); + const urlMatch = clean.match(/https?:\/\/[^\s\]]+/); if (urlMatch) { - broadcast({ type: "login_url", url: urlMatch[0], raw: chunk.trim() }); + broadcast({ type: "login_url", url: urlMatch[0], raw: clean }); } else { - broadcast({ type: "login_output", text: chunk.trim() }); + broadcast({ type: "login_output", text: clean }); } }, (err) => { @@ -522,6 +524,33 @@ function startProxyLogin() { ); } +// Credentials manuell einfuegen (von einem Rechner wo Claude eingeloggt ist) +async function writeProxyCredentials(credentialsJson) { + try { + // Validieren + const parsed = JSON.parse(credentialsJson); + if (!parsed.claudeAiOauth && !parsed.oauth) { + throw new Error("Ungueltig: Kein OAuth-Objekt gefunden. Erwartet 'claudeAiOauth' oder 'oauth' Key."); + } + + log("info", "proxy", "Schreibe Credentials in Proxy-Container..."); + + // Escaped fuer Shell — Einfache Anfuehrungszeichen im JSON escapen + const escaped = credentialsJson.replace(/'/g, "'\\''"); + await dockerExec("aria-proxy", `mkdir -p /root/.config/claude && echo '${escaped}' > /root/.config/claude/.credentials.json`); + + log("info", "proxy", "Credentials geschrieben!"); + broadcast({ type: "login_status", status: "done" }); + broadcast({ type: "login_output", text: "Credentials erfolgreich geschrieben! Proxy muss neu gestartet werden." }); + + // Auth pruefen + setTimeout(() => checkProxyAuth(), 500); + } catch (err) { + log("error", "proxy", `Credentials schreiben fehlgeschlagen: ${err.message}`); + broadcast({ type: "login_status", status: "error", error: err.message }); + } +} + async function checkProxyAuth() { try { log("info", "proxy", "Pruefe Auth-Dateien im Proxy-Container..."); @@ -709,6 +738,8 @@ wss.on("connection", (ws) => { checkProxyAuth(); } else if (msg.action === "proxy_login") { startProxyLogin(); + } else if (msg.action === "write_credentials") { + writeProxyCredentials(msg.credentials); } else if (msg.action === "docker_logs") { handleDockerLogs(ws, msg.tab, msg.tail); }