added claude login for credentials creation if credentials not exist
This commit is contained in:
parent
c711899e4d
commit
5e2b31385f
|
|
@ -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
|
||||||
- Docker Exec API: Generische `dockerExec()` Funktion für Befehle in laufenden Containern (via Docker Socket)
|
- Claude Login via UI: "Login starten" Button führt `claude login` im Proxy-Container aus, streamt Output live (URL + Code), klickbarer Link zum Autorisieren
|
||||||
|
- 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
|
||||||
- Autoscroll-Pause: Automatisch wenn hochgescrollt, "Nach unten" Button zum Fortsetzen
|
- Autoscroll-Pause: Automatisch wenn hochgescrollt, "Nach unten" Button zum Fortsetzen
|
||||||
|
|
@ -37,6 +38,7 @@ Alle Änderungen am Projekt. Format: [Keep a Changelog](https://keepachangelog.c
|
||||||
- `OPENCLAW_GATEWAY_TOKEN` statt `AUTH_TOKEN` — korrekter Env-Var-Name
|
- `OPENCLAW_GATEWAY_TOKEN` statt `AUTH_TOKEN` — korrekter Env-Var-Name
|
||||||
- `ARIA_AUTH_TOKEN` an Bridge und Diagnostic durchgereicht
|
- `ARIA_AUTH_TOKEN` an Bridge und Diagnostic durchgereicht
|
||||||
- Port 3001 auf aria-Service gemappt (für Diagnostic Web-UI)
|
- Port 3001 auf aria-Service gemappt (für Diagnostic Web-UI)
|
||||||
|
- Proxy Claude-Config Volume `:ro` → `:rw` — Login via Diagnostic-UI braucht Schreibzugriff
|
||||||
|
|
||||||
### Behoben
|
### Behoben
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,20 @@
|
||||||
<div style="font-size:10px;color:#555570;margin-top:4px" id="proxy-models-hint"></div>
|
<div style="font-size:10px;color:#555570;margin-top:4px" id="proxy-models-hint"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="proxy-auth" style="margin-top:6px;display:none;background:#080810;border:1px solid #1E1E2E;border-radius:4px;padding:6px 8px;font-size:10px;line-height:1.5;max-height:120px;overflow-y:auto;white-space:pre-wrap;color:#8888AA"></div>
|
<div id="proxy-auth" style="margin-top:6px;display:none;background:#080810;border:1px solid #1E1E2E;border-radius:4px;padding:6px 8px;font-size:10px;line-height:1.5;max-height:120px;overflow-y:auto;white-space:pre-wrap;color:#8888AA"></div>
|
||||||
|
<div id="proxy-login-box" style="margin-top:6px;display:none">
|
||||||
|
<div style="background:#1A1A0A;border:1px solid #FFD60A33;border-radius:6px;padding:8px 10px">
|
||||||
|
<div style="font-size:11px;color:#FFD60A;margin-bottom:6px;font-weight:bold">Claude Login</div>
|
||||||
|
<div id="login-output" style="font-size:11px;line-height:1.6;color:#AAB;max-height:100px;overflow-y:auto;white-space:pre-wrap"></div>
|
||||||
|
<div id="login-url-box" style="display:none;margin-top:6px">
|
||||||
|
<div style="font-size:11px;color:#8888AA;margin-bottom:4px">Link oeffnen und autorisieren:</div>
|
||||||
|
<a id="login-url-link" href="#" target="_blank" style="color:#0096FF;font-size:12px;word-break:break-all"></a>
|
||||||
|
</div>
|
||||||
|
<div id="login-done" style="display:none;margin-top:6px;color:#34C759;font-size:12px">Login abgeschlossen!</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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -291,6 +303,34 @@
|
||||||
el.textContent = msg.error ? `Fehler: ${msg.error}` : msg.info;
|
el.textContent = msg.error ? `Fehler: ${msg.error}` : msg.info;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (msg.type === 'login_output') {
|
||||||
|
const el = document.getElementById('login-output');
|
||||||
|
el.textContent += msg.text + '\n';
|
||||||
|
el.scrollTop = el.scrollHeight;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (msg.type === 'login_url') {
|
||||||
|
const el = document.getElementById('login-output');
|
||||||
|
el.textContent += msg.raw + '\n';
|
||||||
|
el.scrollTop = el.scrollHeight;
|
||||||
|
document.getElementById('login-url-box').style.display = 'block';
|
||||||
|
const link = document.getElementById('login-url-link');
|
||||||
|
link.href = msg.url;
|
||||||
|
link.textContent = msg.url;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (msg.type === 'login_status') {
|
||||||
|
if (msg.status === 'done') {
|
||||||
|
document.getElementById('login-done').style.display = 'block';
|
||||||
|
document.getElementById('btn-proxy-login').disabled = false;
|
||||||
|
document.getElementById('btn-proxy-login').textContent = 'Erneut einloggen';
|
||||||
|
} else if (msg.status === 'error') {
|
||||||
|
const el = document.getElementById('login-output');
|
||||||
|
el.textContent += 'FEHLER: ' + msg.error + '\n';
|
||||||
|
document.getElementById('btn-proxy-login').disabled = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (msg.type === 'docker_logs') {
|
if (msg.type === 'docker_logs') {
|
||||||
showDockerLogs(msg);
|
showDockerLogs(msg);
|
||||||
return;
|
return;
|
||||||
|
|
@ -327,6 +367,15 @@
|
||||||
send({ action: 'check_proxy_auth' });
|
send({ action: 'check_proxy_auth' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startProxyLogin() {
|
||||||
|
document.getElementById('proxy-login-box').style.display = 'block';
|
||||||
|
document.getElementById('login-output').textContent = 'Starte Login...\n';
|
||||||
|
document.getElementById('login-url-box').style.display = 'none';
|
||||||
|
document.getElementById('login-done').style.display = 'none';
|
||||||
|
document.getElementById('btn-proxy-login').disabled = true;
|
||||||
|
send({ action: 'proxy_login' });
|
||||||
|
}
|
||||||
|
|
||||||
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 });
|
||||||
|
|
|
||||||
|
|
@ -425,6 +425,103 @@ async function testProxy(prompt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Claude Login im Proxy-Container ─────────────────────
|
||||||
|
|
||||||
|
let loginProcess = null;
|
||||||
|
|
||||||
|
function dockerExecStreaming(containerName, cmd, onData, onEnd) {
|
||||||
|
const createBody = JSON.stringify({
|
||||||
|
AttachStdout: true, AttachStderr: true, AttachStdin: false, Tty: true,
|
||||||
|
Cmd: Array.isArray(cmd) ? cmd : ["sh", "-c", cmd],
|
||||||
|
});
|
||||||
|
|
||||||
|
const createReq = http.request({
|
||||||
|
socketPath: "/var/run/docker.sock",
|
||||||
|
path: `/containers/${containerName}/exec`,
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(createBody) },
|
||||||
|
}, (res) => {
|
||||||
|
let data = "";
|
||||||
|
res.on("data", (c) => data += c);
|
||||||
|
res.on("end", () => {
|
||||||
|
if (res.statusCode !== 201) {
|
||||||
|
onEnd(new Error(`Exec create: HTTP ${res.statusCode}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const execId = JSON.parse(data).Id;
|
||||||
|
|
||||||
|
const startBody = JSON.stringify({ Detach: false, Tty: true });
|
||||||
|
const startReq = http.request({
|
||||||
|
socketPath: "/var/run/docker.sock",
|
||||||
|
path: `/exec/${execId}/start`,
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(startBody) },
|
||||||
|
}, (sRes) => {
|
||||||
|
// Tty=true: kein Multiplexing, direkt Text
|
||||||
|
sRes.on("data", (chunk) => {
|
||||||
|
const text = chunk.toString("utf-8");
|
||||||
|
if (text.trim()) onData(text);
|
||||||
|
});
|
||||||
|
sRes.on("end", () => onEnd(null));
|
||||||
|
sRes.on("error", (err) => onEnd(err));
|
||||||
|
});
|
||||||
|
startReq.on("error", (err) => onEnd(err));
|
||||||
|
startReq.write(startBody);
|
||||||
|
startReq.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
createReq.on("error", (err) => onEnd(err));
|
||||||
|
createReq.write(createBody);
|
||||||
|
createReq.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startProxyLogin() {
|
||||||
|
if (loginProcess) {
|
||||||
|
log("warn", "proxy", "Login laeuft bereits");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loginProcess = true;
|
||||||
|
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",
|
||||||
|
(chunk) => {
|
||||||
|
// Jeder Chunk wird live an die UI gesendet
|
||||||
|
log("info", "proxy", `[login] ${chunk.trim()}`);
|
||||||
|
|
||||||
|
// URLs erkennen und hervorheben
|
||||||
|
const urlMatch = chunk.match(/https?:\/\/[^\s\]]+/);
|
||||||
|
if (urlMatch) {
|
||||||
|
broadcast({ type: "login_url", url: urlMatch[0], raw: chunk.trim() });
|
||||||
|
} else {
|
||||||
|
broadcast({ type: "login_output", text: chunk.trim() });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
loginProcess = null;
|
||||||
|
if (err) {
|
||||||
|
log("error", "proxy", `Login fehlgeschlagen: ${err.message}`);
|
||||||
|
broadcast({ type: "login_status", status: "error", error: err.message });
|
||||||
|
} else {
|
||||||
|
log("info", "proxy", "Login-Prozess beendet");
|
||||||
|
broadcast({ type: "login_status", status: "done" });
|
||||||
|
// Auth nochmal pruefen
|
||||||
|
setTimeout(() => checkProxyAuth(), 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
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...");
|
||||||
|
|
@ -610,6 +707,8 @@ wss.on("connection", (ws) => {
|
||||||
testProxy(msg.text);
|
testProxy(msg.text);
|
||||||
} else if (msg.action === "check_proxy_auth") {
|
} else if (msg.action === "check_proxy_auth") {
|
||||||
checkProxyAuth();
|
checkProxyAuth();
|
||||||
|
} else if (msg.action === "proxy_login") {
|
||||||
|
startProxyLogin();
|
||||||
} else if (msg.action === "docker_logs") {
|
} else if (msg.action === "docker_logs") {
|
||||||
handleDockerLogs(ws, msg.tab, msg.tail);
|
handleDockerLogs(ws, msg.tab, msg.tail);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ services:
|
||||||
sed -i 's/if (model\.includes/if ((model||\"claude-sonnet-4\").includes/g' $$DIST/adapter/cli-to-openai.js &&
|
sed -i 's/if (model\.includes/if ((model||\"claude-sonnet-4\").includes/g' $$DIST/adapter/cli-to-openai.js &&
|
||||||
claude-max-api"
|
claude-max-api"
|
||||||
volumes:
|
volumes:
|
||||||
- ~/.config/claude:/root/.config/claude:ro # Claude CLI Auth
|
- ~/.config/claude:/root/.config/claude # Claude CLI Auth (rw fuer Login via Diagnostic)
|
||||||
environment:
|
environment:
|
||||||
- HOST=0.0.0.0
|
- HOST=0.0.0.0
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue