diff --git a/diagnostic/index.html b/diagnostic/index.html index 555446b..0f5c3d4 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -891,6 +891,18 @@ else alert('Loeschen fehlgeschlagen: ' + (msg.error || '?')); return; } + if (msg.type === 'session_export') { + if (!msg.ok) { alert('Export fehlgeschlagen: ' + (msg.error || '?')); return; } + const blob = new Blob([msg.markdown], { type: 'text/markdown;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = msg.filename; + document.body.appendChild(a); + a.click(); + setTimeout(() => { URL.revokeObjectURL(url); a.remove(); }, 100); + return; + } if (msg.type === 'active_session') { updateActiveSessionBar(msg.sessionKey); loadSessions(); // Tabelle neu rendern @@ -1679,7 +1691,8 @@ + `${date}` + `` + (isActive ? '' : ``) - + `` + + `` + + `` + ``; } html += ''; @@ -1743,6 +1756,10 @@ send({ action: 'delete_session', sessionPath: path }); } + function exportSession(path, sessionKey) { + send({ action: 'export_session', sessionPath: path, sessionKey }); + } + function activateSession(sessionKey) { send({ action: 'set_active_session', sessionKey }); } diff --git a/diagnostic/server.js b/diagnostic/server.js index cfc1fb7..222f785 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -1274,6 +1274,8 @@ wss.on("connection", (ws) => { handleListSessions(ws); } else if (msg.action === "read_session") { handleReadSession(ws, msg.sessionPath); + } else if (msg.action === "export_session") { + handleExportSession(ws, msg.sessionPath, msg.sessionKey); } else if (msg.action === "delete_session") { handleDeleteSession(ws, msg.sessionPath); } else if (msg.action === "set_active_session") { @@ -1656,6 +1658,68 @@ async function handleReadSession(clientWs, sessionPath) { } } +async function handleExportSession(clientWs, sessionPath, sessionKey) { + if (!sessionPath || sessionPath.includes("..") || !sessionPath.startsWith(SESSIONS_DIR)) { + clientWs.send(JSON.stringify({ type: "session_export", ok: false, error: "Ungueltiger Pfad" })); + return; + } + try { + const safePath = sessionPath.replace(/'/g, ""); + const raw = await dockerExec("aria-core", `cat '${safePath}'`); + const lines = raw.split("\n").filter(l => l.trim()); + + const blocks = []; + for (const line of lines) { + let obj; + try { obj = JSON.parse(line); } catch { continue; } + if (obj.type !== "message" || !obj.message) continue; + const role = obj.message.role; + if (role !== "user" && role !== "assistant") continue; + + let text = ""; + const content = obj.message.content; + if (typeof content === "string") text = content; + else if (Array.isArray(content)) text = content.filter(c => c.type === "text").map(c => c.text || "").join("\n"); + if (!text) continue; + + if (role === "user") { + text = text.replace(/^Sender \(untrusted metadata\):[\s\S]*?```[\s\S]*?```\s*\n*/m, "").trim(); + text = text.replace(/^\[.*?\]\s*/, "").trim(); + } else { + text = text.replace(/^\[\[reply_to_\w+\]\]\s*/g, "").trim(); + } + if (!text) continue; + + const ts = obj.message.timestamp || obj.timestamp || 0; + const when = ts ? new Date(ts).toISOString().replace("T", " ").slice(0, 19) : ""; + const heading = role === "user" ? "## 🧑 User" : "## 🤖 ARIA"; + blocks.push(`${heading}${when ? ` — ${when}` : ""}\n\n${text}`); + } + + const exportedAt = new Date().toISOString().replace("T", " ").slice(0, 19); + const title = sessionKey || sessionPath.split("/").pop().replace(".jsonl", ""); + const markdown = [ + `# Session: ${title}`, + ``, + `Exportiert: ${exportedAt} `, + `Quelle: ${sessionPath}`, + ``, + `---`, + ``, + blocks.join("\n\n---\n\n"), + ``, + ].join("\n"); + + const safeKey = (sessionKey || "session").replace(/[^a-zA-Z0-9_-]/g, "_"); + const filename = `${exportedAt.slice(0, 10)}_${safeKey}.md`; + clientWs.send(JSON.stringify({ type: "session_export", ok: true, filename, markdown })); + log("info", "server", `Session exportiert: ${filename} (${blocks.length} Nachrichten)`); + } catch (err) { + log("error", "server", `Session-Export fehlgeschlagen: ${err.message}`); + clientWs.send(JSON.stringify({ type: "session_export", ok: false, error: err.message })); + } +} + async function handleDeleteSession(clientWs, sessionPath) { if (!sessionPath || sessionPath.includes("..") || !sessionPath.startsWith(SESSIONS_DIR)) { clientWs.send(JSON.stringify({ type: "session_deleted", ok: false, error: "Ungueltiger Pfad" })); diff --git a/issue.md b/issue.md index 1c0ad39..4702870 100644 --- a/issue.md +++ b/issue.md @@ -30,6 +30,7 @@ - [x] Paste-Support fuer Bilder in Diagnostic Chat - [x] Markdown-Bereinigung fuer TTS (fett, kursiv, code, links, etc.) - [x] SSH Volume read-write fuer Proxy (kein -F Workaround mehr) +- [x] Diagnostic: Sessions als Markdown exportieren (Download-Button) ## Offen