fixed session viewer and brain
This commit is contained in:
@@ -863,17 +863,19 @@
|
|||||||
let html = '<table style="width:100%;border-collapse:collapse;">';
|
let html = '<table style="width:100%;border-collapse:collapse;">';
|
||||||
html += '<tr style="color:#8888AA;font-size:10px;text-align:left;border-bottom:1px solid #1E1E2E;">'
|
html += '<tr style="color:#8888AA;font-size:10px;text-align:left;border-bottom:1px solid #1E1E2E;">'
|
||||||
+ '<th style="padding:4px 6px;">Session</th>'
|
+ '<th style="padding:4px 6px;">Session</th>'
|
||||||
+ '<th style="padding:4px 6px;">Zeilen</th>'
|
+ '<th style="padding:4px 6px;">Msgs</th>'
|
||||||
+ '<th style="padding:4px 6px;">Groesse</th>'
|
|
||||||
+ '<th style="padding:4px 6px;">Zuletzt</th>'
|
+ '<th style="padding:4px 6px;">Zuletzt</th>'
|
||||||
+ '<th style="padding:4px 6px;"></th></tr>';
|
+ '<th style="padding:4px 6px;"></th></tr>';
|
||||||
for (const s of data.sessions) {
|
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 key = escapeHtml(s.sessionKey || s.path.split('/').pop());
|
||||||
|
const orphanBadge = s.orphan ? ' <span style="background:#FF3B30;color:#fff;font-size:9px;padding:1px 4px;border-radius:3px;">verwaist</span>' : '';
|
||||||
|
const modelBadge = s.model ? `<div style="font-size:9px;color:#555570;">${escapeHtml(s.model)}</div>` : '';
|
||||||
|
const keyColor = s.orphan ? '#555570' : '#E0E0F0';
|
||||||
html += `<tr style="border-bottom:1px solid #0D0D1A;cursor:pointer;" onmouseover="this.style.background='#1E1E2E'" onmouseout="this.style.background=''">`
|
html += `<tr style="border-bottom:1px solid #0D0D1A;cursor:pointer;" onmouseover="this.style.background='#1E1E2E'" onmouseout="this.style.background=''">`
|
||||||
+ `<td style="padding:4px 6px;color:#E0E0F0;" onclick="viewSession('${escapeHtml(s.path)}')">${key}</td>`
|
+ `<td style="padding:4px 6px;" onclick="viewSession('${escapeHtml(s.path)}')">`
|
||||||
|
+ `<div style="color:${keyColor};">${key}${orphanBadge}</div>${modelBadge}</td>`
|
||||||
+ `<td style="padding:4px 6px;color:#8888AA;">${s.lines}</td>`
|
+ `<td style="padding:4px 6px;color:#8888AA;">${s.lines}</td>`
|
||||||
+ `<td style="padding:4px 6px;color:#8888AA;">${escapeHtml(s.size)}</td>`
|
|
||||||
+ `<td style="padding:4px 6px;color:#8888AA;font-size:10px;">${date}</td>`
|
+ `<td style="padding:4px 6px;color:#8888AA;font-size:10px;">${date}</td>`
|
||||||
+ `<td style="padding:4px 6px;"><button class="btn secondary" onclick="event.stopPropagation();deleteSession('${escapeHtml(s.path)}')" style="padding:2px 6px;font-size:10px;color:#FF6B6B;">X</button></td>`
|
+ `<td style="padding:4px 6px;"><button class="btn secondary" onclick="event.stopPropagation();deleteSession('${escapeHtml(s.path)}')" style="padding:2px 6px;font-size:10px;color:#FF6B6B;">X</button></td>`
|
||||||
+ '</tr>';
|
+ '</tr>';
|
||||||
|
|||||||
+70
-36
@@ -1106,60 +1106,94 @@ function checkDesktopAvailable(clientWs) {
|
|||||||
|
|
||||||
// ── Session Viewer ──────────────────────────────────────
|
// ── Session Viewer ──────────────────────────────────────
|
||||||
|
|
||||||
|
const SESSIONS_DIR = "/home/node/.openclaw/agents/main/sessions";
|
||||||
|
|
||||||
async function handleListSessions(clientWs) {
|
async function handleListSessions(clientWs) {
|
||||||
try {
|
try {
|
||||||
log("info", "server", "Lade Sessions aus aria-core...");
|
log("info", "server", "Lade Sessions aus aria-core...");
|
||||||
// OpenClaw/Claude Code speichert Sessions in projects/<hash>/sessions/
|
|
||||||
|
// sessions.json als Index lesen + Datei-Details holen
|
||||||
const raw = await dockerExec("aria-core", `
|
const raw = await dockerExec("aria-core", `
|
||||||
echo '=== SESSIONS ===' &&
|
cat ${SESSIONS_DIR}/sessions.json 2>/dev/null || echo '{}' &&
|
||||||
find /home/node/.openclaw/projects -name '*.jsonl' -type f 2>/dev/null | sort &&
|
echo '===FILE_DETAILS===' &&
|
||||||
echo '=== DETAILS ===' &&
|
for f in ${SESSIONS_DIR}/*.jsonl; do
|
||||||
for f in $(find /home/node/.openclaw/projects -name '*.jsonl' -type f 2>/dev/null | sort); do
|
[ -f "$f" ] || continue
|
||||||
|
name=$(basename "$f")
|
||||||
lines=$(wc -l < "$f" 2>/dev/null || echo 0)
|
lines=$(wc -l < "$f" 2>/dev/null || echo 0)
|
||||||
size=$(du -h "$f" 2>/dev/null | cut -f1)
|
size=$(du -h "$f" 2>/dev/null | cut -f1)
|
||||||
modified=$(stat -c '%Y' "$f" 2>/dev/null || echo 0)
|
modified=$(stat -c '%Y' "$f" 2>/dev/null || echo 0)
|
||||||
# Erste Zeile fuer Session-Info
|
echo "FILE:$name|LINES:$lines|SIZE:$size|MODIFIED:$modified"
|
||||||
first=$(head -1 "$f" 2>/dev/null | head -c 500)
|
|
||||||
echo "FILE:$f|LINES:$lines|SIZE:$size|MODIFIED:$modified|FIRST:$first"
|
|
||||||
done
|
done
|
||||||
`.trim());
|
`.trim());
|
||||||
|
|
||||||
// Parse Output
|
const parts = raw.split("===FILE_DETAILS===");
|
||||||
const sessions = [];
|
let sessionsIndex = {};
|
||||||
const lines = raw.split("\n");
|
try { sessionsIndex = JSON.parse(parts[0].trim()); } catch {}
|
||||||
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;
|
|
||||||
|
|
||||||
// Session-Key aus Pfad oder erster Zeile extrahieren
|
// Datei-Details parsen
|
||||||
let sessionKey = "";
|
const fileDetails = {};
|
||||||
try {
|
if (parts[1]) {
|
||||||
const firstObj = JSON.parse(parts.FIRST || "{}");
|
for (const line of parts[1].trim().split("\n")) {
|
||||||
sessionKey = firstObj.sessionKey || firstObj.key || "";
|
if (!line.startsWith("FILE:")) continue;
|
||||||
} catch {}
|
const segs = {};
|
||||||
if (!sessionKey) {
|
for (const seg of line.split("|")) {
|
||||||
// Dateiname als Fallback
|
const idx = seg.indexOf(":");
|
||||||
sessionKey = parts.FILE.split("/").pop().replace(".jsonl", "");
|
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({
|
sessions.push({
|
||||||
path: parts.FILE,
|
path: `${SESSIONS_DIR}/${filename}`,
|
||||||
sessionKey,
|
sessionKey: key,
|
||||||
lines: parseInt(parts.LINES) || 0,
|
sessionId: id,
|
||||||
size: parts.SIZE || "?",
|
lines: parseInt(details.LINES) || 0,
|
||||||
modified: parseInt(parts.MODIFIED) || 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);
|
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`);
|
log("info", "server", `${sessions.length} Session(s) gefunden`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log("error", "server", `Sessions laden fehlgeschlagen: ${err.message}`);
|
log("error", "server", `Sessions laden fehlgeschlagen: ${err.message}`);
|
||||||
@@ -1190,7 +1224,7 @@ async function handleReadSession(clientWs, sessionPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleDeleteSession(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" }));
|
clientWs.send(JSON.stringify({ type: "session_deleted", ok: false, error: "Ungueltiger Pfad" }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user