diff --git a/diagnostic/index.html b/diagnostic/index.html index 06518b1..b161fbb 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -863,17 +863,19 @@ let html = ''; html += '' + '' - + '' - + '' + + '' + '' + ''; for (const s of data.sessions) { - const date = s.modified ? new Date(s.modified * 1000).toLocaleString('de-DE') : '?'; + const date = s.modified ? new Date(s.modified * 1000).toLocaleString('de-DE', {day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}) : '?'; const key = escapeHtml(s.sessionKey || s.path.split('/').pop()); + const orphanBadge = s.orphan ? ' verwaist' : ''; + const modelBadge = s.model ? `
${escapeHtml(s.model)}
` : ''; + const keyColor = s.orphan ? '#555570' : '#E0E0F0'; html += `` - + `` + + `` + `` - + `` + `` + `` + ''; diff --git a/diagnostic/server.js b/diagnostic/server.js index 2aaf689..1eadca4 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -1106,60 +1106,94 @@ function checkDesktopAvailable(clientWs) { // ── Session Viewer ────────────────────────────────────── +const SESSIONS_DIR = "/home/node/.openclaw/agents/main/sessions"; + async function handleListSessions(clientWs) { try { log("info", "server", "Lade Sessions aus aria-core..."); - // OpenClaw/Claude Code speichert Sessions in projects//sessions/ + + // sessions.json als Index lesen + Datei-Details holen const raw = await dockerExec("aria-core", ` - echo '=== SESSIONS ===' && - find /home/node/.openclaw/projects -name '*.jsonl' -type f 2>/dev/null | sort && - echo '=== DETAILS ===' && - for f in $(find /home/node/.openclaw/projects -name '*.jsonl' -type f 2>/dev/null | sort); do + cat ${SESSIONS_DIR}/sessions.json 2>/dev/null || echo '{}' && + echo '===FILE_DETAILS===' && + for f in ${SESSIONS_DIR}/*.jsonl; do + [ -f "$f" ] || continue + name=$(basename "$f") lines=$(wc -l < "$f" 2>/dev/null || echo 0) size=$(du -h "$f" 2>/dev/null | cut -f1) modified=$(stat -c '%Y' "$f" 2>/dev/null || echo 0) - # Erste Zeile fuer Session-Info - first=$(head -1 "$f" 2>/dev/null | head -c 500) - echo "FILE:$f|LINES:$lines|SIZE:$size|MODIFIED:$modified|FIRST:$first" + echo "FILE:$name|LINES:$lines|SIZE:$size|MODIFIED:$modified" done `.trim()); - // Parse Output - const sessions = []; - const lines = raw.split("\n"); - for (const line of lines) { - if (!line.startsWith("FILE:")) continue; - const parts = {}; - for (const seg of line.split("|")) { - const idx = seg.indexOf(":"); - if (idx > 0) parts[seg.slice(0, idx)] = seg.slice(idx + 1); - } - if (!parts.FILE) continue; + const parts = raw.split("===FILE_DETAILS==="); + let sessionsIndex = {}; + try { sessionsIndex = JSON.parse(parts[0].trim()); } catch {} - // Session-Key aus Pfad oder erster Zeile extrahieren - let sessionKey = ""; - try { - const firstObj = JSON.parse(parts.FIRST || "{}"); - sessionKey = firstObj.sessionKey || firstObj.key || ""; - } catch {} - if (!sessionKey) { - // Dateiname als Fallback - sessionKey = parts.FILE.split("/").pop().replace(".jsonl", ""); + // Datei-Details parsen + const fileDetails = {}; + if (parts[1]) { + for (const line of parts[1].trim().split("\n")) { + if (!line.startsWith("FILE:")) continue; + const segs = {}; + for (const seg of line.split("|")) { + const idx = seg.indexOf(":"); + if (idx > 0) segs[seg.slice(0, idx)] = seg.slice(idx + 1); + } + if (segs.FILE) fileDetails[segs.FILE] = segs; } + } + + // Sessions zusammenbauen: Index + Datei-Info kombinieren + const sessions = []; + + // Aus sessions.json die Session-Keys und IDs + const indexEntries = Array.isArray(sessionsIndex) ? sessionsIndex + : Array.isArray(sessionsIndex.sessions) ? sessionsIndex.sessions + : Object.entries(sessionsIndex).map(([k, v]) => ({ key: k, ...(typeof v === "object" ? v : { id: v }) })); + + for (const entry of indexEntries) { + const id = entry.id || entry.sessionId || ""; + const rawKey = entry.key || entry.sessionKey || entry.name || id; + // "agent:main:aria-diagnostic" → "aria-diagnostic" + const key = rawKey.replace(/^agent:main:/, ""); + const filename = `${id}.jsonl`; + const details = fileDetails[filename] || {}; + // updatedAt aus sessions.json (ms) ist genauer als stat + const updatedAt = entry.updatedAt || 0; + const model = entry.model || ""; sessions.push({ - path: parts.FILE, - sessionKey, - lines: parseInt(parts.LINES) || 0, - size: parts.SIZE || "?", - modified: parseInt(parts.MODIFIED) || 0, + path: `${SESSIONS_DIR}/${filename}`, + sessionKey: key, + sessionId: id, + lines: parseInt(details.LINES) || 0, + size: details.SIZE || "?", + modified: updatedAt ? Math.floor(updatedAt / 1000) : (parseInt(details.MODIFIED) || 0), + model, + }); + + // Aus fileDetails entfernen (fuer Waisen-Check) + delete fileDetails[filename]; + } + + // Dateien die nicht im Index stehen (Waisen / Reset-Files) + for (const [filename, details] of Object.entries(fileDetails)) { + const id = filename.replace(".jsonl", ""); + sessions.push({ + path: `${SESSIONS_DIR}/${filename}`, + sessionKey: id.slice(0, 12) + "...", + sessionId: id, + lines: parseInt(details.LINES) || 0, + size: details.SIZE || "?", + modified: parseInt(details.MODIFIED) || 0, + orphan: true, }); } - // Nach Aenderungsdatum sortieren (neueste zuerst) sessions.sort((a, b) => b.modified - a.modified); - clientWs.send(JSON.stringify({ type: "sessions_list", sessions, raw: sessions.length === 0 ? raw : undefined })); + clientWs.send(JSON.stringify({ type: "sessions_list", sessions })); log("info", "server", `${sessions.length} Session(s) gefunden`); } catch (err) { log("error", "server", `Sessions laden fehlgeschlagen: ${err.message}`); @@ -1190,7 +1224,7 @@ async function handleReadSession(clientWs, sessionPath) { } async function handleDeleteSession(clientWs, sessionPath) { - if (!sessionPath || sessionPath.includes("..") || !sessionPath.startsWith("/home/node/.openclaw/")) { + if (!sessionPath || sessionPath.includes("..") || !sessionPath.startsWith(SESSIONS_DIR)) { clientWs.send(JSON.stringify({ type: "session_deleted", ok: false, error: "Ungueltiger Pfad" })); return; }
SessionZeilenGroesseMsgsZuletzt
${key}` + + `
${key}${orphanBadge}
${modelBadge}
${s.lines}${escapeHtml(s.size)}${date}