fix windows and write credentials
This commit is contained in:
parent
3a82f9bab0
commit
9783de85f5
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -12,10 +12,12 @@ Alle Änderungen am Projekt. Format: [Keep a Changelog](https://keepachangelog.c
|
||||||
- Neuer Container `aria-diagnostic` mit Web-UI auf Port 3001
|
- Neuer Container `aria-diagnostic` mit Web-UI auf Port 3001
|
||||||
- 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 durchsucht alle bekannten Credential-Pfade im Proxy-Container (`/root/.config/claude/`, `/root/.claude/`, `/root/.claude/auth/`) rekursiv — zeigt gefundene Dateien und deren Inhalt
|
||||||
- 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
|
- Claude Login via UI: "Login starten" Button öffnet interaktives Terminal (xterm.js) in einem Modal-Overlay — führt `claude login` im Proxy-Container aus, volle TUI-Unterstützung (kein ANSI-Stripping mehr nötig)
|
||||||
- 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
|
- xterm.js Terminal: Bidirektionaler Stream über Docker Exec API mit `Tty: true` + HTTP Upgrade auf Raw-TCP-Socket — echtes interaktives Terminal im Browser
|
||||||
- Docker Exec API: Generische `dockerExec()` + Streaming-Variante `dockerExecStreaming()` für Befehle in laufenden Containern (via Docker Socket)
|
- UTF-8 Fix: Eingehende Daten werden als `Uint8Array` an xterm.write() übergeben (statt `atob()` → Latin-1 String, der Multi-Byte UTF-8 zerstört), ausgehende Daten über `TextEncoder` UTF-8-safe kodiert
|
||||||
|
- Credentials manuell einfügen: "Credentials einfügen" Button — JSON von einem eingeloggten Rechner kopieren und direkt in den Container schreiben (schreibt in beide mögliche Pfade: `.config/claude/` und `.claude/`)
|
||||||
|
- Docker Exec API: Generische `dockerExec()` (nicht-interaktiv, multiplexed stream) + `attachTerminal()` (interaktiv, Tty, raw TCP socket) 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
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,20 @@
|
||||||
.input-row { display: flex; gap: 6px; }
|
.input-row { display: flex; gap: 6px; }
|
||||||
.input-row input { flex: 1; background: #1E1E2E; border: 1px solid #333; border-radius: 6px;
|
.input-row input { flex: 1; background: #1E1E2E; border: 1px solid #333; border-radius: 6px;
|
||||||
padding: 8px 12px; color: #E0E0F0; font-family: inherit; font-size: 13px; }
|
padding: 8px 12px; color: #E0E0F0; font-family: inherit; font-size: 13px; }
|
||||||
|
|
||||||
|
/* Terminal Modal */
|
||||||
|
.modal-overlay { display:none; position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.85);
|
||||||
|
z-index:1000; justify-content:center; align-items:center; }
|
||||||
|
.modal-overlay.open { display:flex; }
|
||||||
|
.modal-box { background:#0D0D1A; border:1px solid #FFD60A44; border-radius:10px; width:90vw; max-width:860px;
|
||||||
|
max-height:90vh; display:flex; flex-direction:column; overflow:hidden; }
|
||||||
|
.modal-header { display:flex; justify-content:space-between; align-items:center; padding:10px 14px;
|
||||||
|
border-bottom:1px solid #1E1E2E; }
|
||||||
|
.modal-header h3 { font-size:14px; color:#FFD60A; margin:0; }
|
||||||
|
.modal-close { background:none; border:none; color:#888; font-size:20px; cursor:pointer; padding:0 6px; }
|
||||||
|
.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; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -108,14 +122,8 @@
|
||||||
<div id="proxy-models-list" style="font-size:12px;line-height:1.6"></div>
|
<div id="proxy-models-list" style="font-size:12px;line-height:1.6"></div>
|
||||||
<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:220px;overflow-y:auto;white-space:pre-wrap;color:#8888AA"></div>
|
||||||
<div id="proxy-term-box" style="margin-top:6px;display:none">
|
<!-- Terminal Modal wird weiter unten definiert -->
|
||||||
<div style="background:#000;border:1px solid #FFD60A33;border-radius:6px;padding:4px;position:relative">
|
|
||||||
<div style="font-size:11px;color:#FFD60A;padding:4px 6px;font-weight:bold">Claude Login Terminal</div>
|
|
||||||
<div id="terminal-container" style="height:320px"></div>
|
|
||||||
<div id="term-status" style="font-size:10px;color:#8888AA;padding:4px 6px"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="proxy-creds-box" style="margin-top:6px;display:none">
|
<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="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:11px;color:#0096FF;margin-bottom:4px;font-weight:bold">Credentials einfuegen</div>
|
||||||
|
|
@ -176,6 +184,18 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Terminal Modal -->
|
||||||
|
<div class="modal-overlay" id="term-modal">
|
||||||
|
<div class="modal-box">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>Claude Login Terminal</h3>
|
||||||
|
<button class="modal-close" onclick="closeTermModal()">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="terminal-container"></div>
|
||||||
|
<div class="modal-footer" id="term-status"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const chatBox = document.getElementById('chat-box');
|
const chatBox = document.getElementById('chat-box');
|
||||||
const pauseHint = document.getElementById('pause-hint');
|
const pauseHint = document.getElementById('pause-hint');
|
||||||
|
|
@ -317,16 +337,17 @@
|
||||||
}
|
}
|
||||||
if (msg.type === 'term_data') {
|
if (msg.type === 'term_data') {
|
||||||
if (term) {
|
if (term) {
|
||||||
const bytes = atob(msg.data);
|
// base64 → Uint8Array (UTF-8 safe)
|
||||||
|
const raw = atob(msg.data);
|
||||||
|
const bytes = new Uint8Array(raw.length);
|
||||||
|
for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
|
||||||
term.write(bytes);
|
term.write(bytes);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.type === 'term_exit') {
|
if (msg.type === 'term_exit') {
|
||||||
if (term) term.writeln('\r\n\x1b[33m--- Session beendet ---\x1b[0m');
|
if (term) term.writeln('\r\n\x1b[33m--- Session beendet ---\x1b[0m');
|
||||||
document.getElementById('term-status').textContent = 'Session beendet';
|
document.getElementById('term-status').textContent = 'Session beendet — Fenster schliessen oder erneut einloggen';
|
||||||
document.getElementById('btn-proxy-login').disabled = false;
|
|
||||||
document.getElementById('btn-proxy-login').textContent = 'Erneut einloggen';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.type === 'term_error') {
|
if (msg.type === 'term_error') {
|
||||||
|
|
@ -381,13 +402,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let term = null;
|
let term = null;
|
||||||
|
let termFitAddon = null;
|
||||||
|
|
||||||
function startProxyLogin() {
|
function startProxyLogin() {
|
||||||
document.getElementById('proxy-term-box').style.display = 'block';
|
document.getElementById('term-modal').classList.add('open');
|
||||||
document.getElementById('btn-proxy-login').disabled = true;
|
document.getElementById('btn-proxy-login').disabled = true;
|
||||||
document.getElementById('term-status').textContent = 'Starte Terminal...';
|
document.getElementById('term-status').textContent = 'Starte Terminal...';
|
||||||
|
|
||||||
// xterm.js laden falls noch nicht geladen
|
|
||||||
if (typeof Terminal === 'undefined') {
|
if (typeof Terminal === 'undefined') {
|
||||||
const s = document.createElement('script');
|
const s = document.createElement('script');
|
||||||
s.src = 'https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.min.js';
|
s.src = 'https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.min.js';
|
||||||
|
|
@ -403,38 +424,50 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeTermModal() {
|
||||||
|
document.getElementById('term-modal').classList.remove('open');
|
||||||
|
document.getElementById('btn-proxy-login').disabled = false;
|
||||||
|
document.getElementById('btn-proxy-login').textContent = 'Login starten';
|
||||||
|
// Terminal aufräumen
|
||||||
|
if (term) { term.dispose(); term = null; }
|
||||||
|
}
|
||||||
|
|
||||||
function initTerminal() {
|
function initTerminal() {
|
||||||
const container = document.getElementById('terminal-container');
|
const container = document.getElementById('terminal-container');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
term = new Terminal({
|
term = new Terminal({
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
fontSize: 13,
|
fontSize: 14,
|
||||||
fontFamily: "'Courier New', monospace",
|
fontFamily: "'DejaVu Sans Mono', 'Courier New', monospace",
|
||||||
theme: { background: '#000000', foreground: '#E0E0F0' },
|
theme: { background: '#000000', foreground: '#E0E0F0' },
|
||||||
cols: 80,
|
|
||||||
rows: 20,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.FitAddon) {
|
if (window.FitAddon) {
|
||||||
const fitAddon = new FitAddon.FitAddon();
|
termFitAddon = new FitAddon.FitAddon();
|
||||||
term.loadAddon(fitAddon);
|
term.loadAddon(termFitAddon);
|
||||||
term.open(container);
|
term.open(container);
|
||||||
fitAddon.fit();
|
termFitAddon.fit();
|
||||||
} else {
|
} else {
|
||||||
term.open(container);
|
term.open(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tastatureingabe → Server
|
// Tastatureingabe → Server (UTF-8 safe)
|
||||||
term.onData((data) => {
|
term.onData((data) => {
|
||||||
const b64 = btoa(unescape(encodeURIComponent(data)));
|
const encoder = new TextEncoder();
|
||||||
send({ action: 'term_input', data: b64 });
|
const bytes = encoder.encode(data);
|
||||||
|
let b64 = '';
|
||||||
|
for (let i = 0; i < bytes.length; i++) b64 += String.fromCharCode(bytes[i]);
|
||||||
|
send({ action: 'term_input', data: btoa(b64) });
|
||||||
});
|
});
|
||||||
|
|
||||||
term.writeln('\x1b[33mVerbinde mit Proxy-Container...\x1b[0m');
|
term.writeln('\x1b[33mVerbinde mit Proxy-Container...\x1b[0m');
|
||||||
send({ action: 'proxy_login' });
|
send({ action: 'proxy_login' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resize bei Fensteraenderung
|
||||||
|
window.addEventListener('resize', () => { if (termFitAddon && term) termFitAddon.fit(); });
|
||||||
|
|
||||||
function toggleCredsBox() {
|
function toggleCredsBox() {
|
||||||
const box = document.getElementById('proxy-creds-box');
|
const box = document.getElementById('proxy-creds-box');
|
||||||
box.style.display = box.style.display === 'none' ? 'block' : 'none';
|
box.style.display = box.style.display === 'none' ? 'block' : 'none';
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ async function testProxy(prompt) {
|
||||||
|
|
||||||
// Schritt 1b: Auth-Dateien im Proxy-Container pruefen
|
// Schritt 1b: Auth-Dateien im Proxy-Container pruefen
|
||||||
try {
|
try {
|
||||||
const authInfo = await dockerExec("aria-proxy", "ls -la /root/.config/claude/ 2>&1 && echo '---' && cat /root/.config/claude/.credentials.json 2>/dev/null | head -c 500 || echo '(keine .credentials.json)'");
|
const authInfo = await dockerExec("aria-proxy", "echo '--- /root/.config/claude/ ---' && ls -la /root/.config/claude/ 2>&1 && echo '--- /root/.claude/ ---' && ls -la /root/.claude/ 2>&1 && echo '--- Credential-Dateien ---' && find /root/.config/claude /root/.claude -name '*.json' -o -name '*credential*' -o -name '*auth*' -o -name '*token*' 2>/dev/null | head -20");
|
||||||
log("info", "proxy", `Auth-Dateien im Container:\n${authInfo}`);
|
log("info", "proxy", `Auth-Dateien im Container:\n${authInfo}`);
|
||||||
broadcast({ type: "proxy_auth", info: authInfo });
|
broadcast({ type: "proxy_auth", info: authInfo });
|
||||||
} catch (authErr) {
|
} catch (authErr) {
|
||||||
|
|
@ -550,7 +550,8 @@ async function writeProxyCredentials(credentialsJson) {
|
||||||
|
|
||||||
// Escaped fuer Shell — Einfache Anfuehrungszeichen im JSON escapen
|
// Escaped fuer Shell — Einfache Anfuehrungszeichen im JSON escapen
|
||||||
const escaped = credentialsJson.replace(/'/g, "'\\''");
|
const escaped = credentialsJson.replace(/'/g, "'\\''");
|
||||||
await dockerExec("aria-proxy", `mkdir -p /root/.config/claude && echo '${escaped}' > /root/.config/claude/.credentials.json`);
|
// In beide moegliche Speicherorte schreiben
|
||||||
|
await dockerExec("aria-proxy", `mkdir -p /root/.config/claude && echo '${escaped}' > /root/.config/claude/.credentials.json && mkdir -p /root/.claude && echo '${escaped}' > /root/.claude/credentials.json`);
|
||||||
|
|
||||||
log("info", "proxy", "Credentials geschrieben!");
|
log("info", "proxy", "Credentials geschrieben!");
|
||||||
broadcast({ type: "login_status", status: "done" });
|
broadcast({ type: "login_status", status: "done" });
|
||||||
|
|
@ -567,7 +568,29 @@ async function writeProxyCredentials(credentialsJson) {
|
||||||
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...");
|
||||||
const authInfo = await dockerExec("aria-proxy", "echo '=== /root/.config/claude/ ===' && ls -la /root/.config/claude/ 2>&1 && echo '' && echo '=== .credentials.json ===' && cat /root/.config/claude/.credentials.json 2>/dev/null || echo '(nicht vorhanden)'");
|
// Breit suchen: Claude Code speichert Credentials je nach Version an verschiedenen Orten
|
||||||
|
const authInfo = await dockerExec("aria-proxy", `
|
||||||
|
echo '=== /root/.config/claude/ ===' &&
|
||||||
|
ls -la /root/.config/claude/ 2>&1 &&
|
||||||
|
echo '' &&
|
||||||
|
echo '=== /root/.claude/ ===' &&
|
||||||
|
ls -la /root/.claude/ 2>&1 &&
|
||||||
|
echo '' &&
|
||||||
|
echo '=== /root/.claude/auth/ ===' &&
|
||||||
|
ls -la /root/.claude/auth/ 2>&1 &&
|
||||||
|
echo '' &&
|
||||||
|
echo '=== Credentials-Dateien (rekursiv) ===' &&
|
||||||
|
find /root/.config/claude /root/.claude -name '*.json' -o -name '*credential*' -o -name '*auth*' -o -name '*token*' -o -name '*oauth*' -o -name '*session*' 2>/dev/null | head -20 &&
|
||||||
|
echo '' &&
|
||||||
|
echo '=== .credentials.json ===' &&
|
||||||
|
cat /root/.config/claude/.credentials.json 2>/dev/null || echo '(nicht in .config/claude/)' &&
|
||||||
|
echo '' &&
|
||||||
|
echo '=== /root/.claude/credentials.json ===' &&
|
||||||
|
cat /root/.claude/credentials.json 2>/dev/null || echo '(nicht in .claude/)' &&
|
||||||
|
echo '' &&
|
||||||
|
echo '=== /root/.claude/auth/*.json ===' &&
|
||||||
|
cat /root/.claude/auth/*.json 2>/dev/null || echo '(keine auth/*.json)'
|
||||||
|
`.trim());
|
||||||
log("info", "proxy", `Auth-Dateien:\n${authInfo}`);
|
log("info", "proxy", `Auth-Dateien:\n${authInfo}`);
|
||||||
broadcast({ type: "proxy_auth", info: authInfo });
|
broadcast({ type: "proxy_auth", info: authInfo });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue