fixed claude login
This commit is contained in:
parent
5e2b31385f
commit
ac1e5c332f
|
|
@ -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
|
- 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
|
- 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
|
- 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)
|
- 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
|
- 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
|
- Tabbed Logs: Separate Tabs für Alle, Gateway, RVS, Proxy, Server — mit Zähler pro Tab
|
||||||
|
|
|
||||||
|
|
@ -119,9 +119,19 @@
|
||||||
<div id="login-done" style="display:none;margin-top:6px;color:#34C759;font-size:12px">Login abgeschlossen!</div>
|
<div id="login-done" style="display:none;margin-top:6px;color:#34C759;font-size:12px">Login abgeschlossen!</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="proxy-creds-box" style="margin-top:6px;display:none">
|
||||||
|
<div style="background:#0A1A1A;border:1px solid #0096FF33;border-radius:6px;padding:8px 10px">
|
||||||
|
<div style="font-size:11px;color:#0096FF;margin-bottom:4px;font-weight:bold">Credentials einfuegen</div>
|
||||||
|
<div style="font-size:10px;color:#8888AA;margin-bottom:6px">Auf einem Rechner mit Claude Login: <code style="background:#1E1E2E;padding:1px 4px;border-radius:3px">cat ~/.config/claude/.credentials.json</code><br>Inhalt hier einfuegen:</div>
|
||||||
|
<textarea id="creds-input" rows="4" style="width:100%;background:#080810;border:1px solid #333;border-radius:4px;padding:6px;color:#E0E0F0;font-family:monospace;font-size:11px;resize:vertical" placeholder='{"claudeAiOauth":{"accessToken":"...","refreshToken":"...","expiresAt":"..."}}'></textarea>
|
||||||
|
<button class="btn" onclick="submitCredentials()" style="margin-top:4px;padding:4px 12px;font-size:11px">Speichern</button>
|
||||||
|
<div id="creds-status" style="font-size:11px;margin-top:4px"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button class="btn secondary" id="btn-proxy-test" onclick="testProxyBtn()" style="margin-top:6px">Proxy testen</button>
|
<button class="btn secondary" id="btn-proxy-test" onclick="testProxyBtn()" style="margin-top:6px">Proxy testen</button>
|
||||||
<button class="btn secondary" onclick="checkProxyAuth()" style="margin-top:6px">Auth pruefen</button>
|
<button class="btn secondary" onclick="checkProxyAuth()" style="margin-top:6px">Auth pruefen</button>
|
||||||
<button class="btn secondary" id="btn-proxy-login" onclick="startProxyLogin()" style="margin-top:6px">Login starten</button>
|
<button class="btn secondary" id="btn-proxy-login" onclick="startProxyLogin()" style="margin-top:6px">Login starten</button>
|
||||||
|
<button class="btn secondary" onclick="toggleCredsBox()" style="margin-top:6px">Credentials einfuegen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -324,10 +334,14 @@
|
||||||
document.getElementById('login-done').style.display = 'block';
|
document.getElementById('login-done').style.display = 'block';
|
||||||
document.getElementById('btn-proxy-login').disabled = false;
|
document.getElementById('btn-proxy-login').disabled = false;
|
||||||
document.getElementById('btn-proxy-login').textContent = 'Erneut einloggen';
|
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') {
|
} else if (msg.status === 'error') {
|
||||||
const el = document.getElementById('login-output');
|
const el = document.getElementById('login-output');
|
||||||
el.textContent += 'FEHLER: ' + msg.error + '\n';
|
el.textContent += 'FEHLER: ' + msg.error + '\n';
|
||||||
document.getElementById('btn-proxy-login').disabled = false;
|
document.getElementById('btn-proxy-login').disabled = false;
|
||||||
|
const cs = document.getElementById('creds-status');
|
||||||
|
cs.textContent = msg.error; cs.style.color = '#FF6B6B';
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -376,6 +390,24 @@
|
||||||
send({ action: 'proxy_login' });
|
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() {
|
function loadDockerLogs() {
|
||||||
if (!DOCKER_TABS.includes(activeTab)) return;
|
if (!DOCKER_TABS.includes(activeTab)) return;
|
||||||
send({ action: 'docker_logs', tab: activeTab, tail: 200 });
|
send({ action: 'docker_logs', tab: activeTab, tail: 200 });
|
||||||
|
|
|
||||||
|
|
@ -475,6 +475,14 @@ function dockerExecStreaming(containerName, cmd, onData, onEnd) {
|
||||||
createReq.end();
|
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() {
|
function startProxyLogin() {
|
||||||
if (loginProcess) {
|
if (loginProcess) {
|
||||||
log("warn", "proxy", "Login laeuft bereits");
|
log("warn", "proxy", "Login laeuft bereits");
|
||||||
|
|
@ -485,26 +493,20 @@ function startProxyLogin() {
|
||||||
log("info", "proxy", "Starte Claude Login im Proxy-Container...");
|
log("info", "proxy", "Starte Claude Login im Proxy-Container...");
|
||||||
broadcast({ type: "login_status", status: "starting" });
|
broadcast({ type: "login_status", status: "starting" });
|
||||||
|
|
||||||
// Volume ist :ro — wir muessen es rw remounten oder direkt im Container schreiben
|
// TERM=dumb und NO_COLOR=1 deaktivieren die TUI und Farben
|
||||||
// Da das Volume ro ist, schreiben wir die Credentials in einen temp-Pfad und kopieren sie danach
|
dockerExecStreaming("aria-proxy", "TERM=dumb NO_COLOR=1 CI=1 claude login 2>&1",
|
||||||
// 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",
|
|
||||||
(chunk) => {
|
(chunk) => {
|
||||||
// Jeder Chunk wird live an die UI gesendet
|
const clean = stripAnsi(chunk).trim();
|
||||||
log("info", "proxy", `[login] ${chunk.trim()}`);
|
if (!clean) return;
|
||||||
|
|
||||||
|
log("info", "proxy", `[login] ${clean}`);
|
||||||
|
|
||||||
// URLs erkennen und hervorheben
|
// URLs erkennen und hervorheben
|
||||||
const urlMatch = chunk.match(/https?:\/\/[^\s\]]+/);
|
const urlMatch = clean.match(/https?:\/\/[^\s\]]+/);
|
||||||
if (urlMatch) {
|
if (urlMatch) {
|
||||||
broadcast({ type: "login_url", url: urlMatch[0], raw: chunk.trim() });
|
broadcast({ type: "login_url", url: urlMatch[0], raw: clean });
|
||||||
} else {
|
} else {
|
||||||
broadcast({ type: "login_output", text: chunk.trim() });
|
broadcast({ type: "login_output", text: clean });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err) => {
|
(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() {
|
async function checkProxyAuth() {
|
||||||
try {
|
try {
|
||||||
log("info", "proxy", "Pruefe Auth-Dateien im Proxy-Container...");
|
log("info", "proxy", "Pruefe Auth-Dateien im Proxy-Container...");
|
||||||
|
|
@ -709,6 +738,8 @@ wss.on("connection", (ws) => {
|
||||||
checkProxyAuth();
|
checkProxyAuth();
|
||||||
} else if (msg.action === "proxy_login") {
|
} else if (msg.action === "proxy_login") {
|
||||||
startProxyLogin();
|
startProxyLogin();
|
||||||
|
} else if (msg.action === "write_credentials") {
|
||||||
|
writeProxyCredentials(msg.credentials);
|
||||||
} else if (msg.action === "docker_logs") {
|
} else if (msg.action === "docker_logs") {
|
||||||
handleDockerLogs(ws, msg.tab, msg.tail);
|
handleDockerLogs(ws, msg.tab, msg.tail);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue