75 lines
2.3 KiB
JavaScript
Executable File
75 lines
2.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* Exportiert ein OpenClaw Session-JSONL (auch .reset.*) als Markdown.
|
|
*
|
|
* Nutzung:
|
|
* node export-jsonl-to-md.js <input.jsonl> [output.md]
|
|
*
|
|
* Oder direkt aus dem aria-core Container:
|
|
* docker exec aria-core cat /home/node/.openclaw/agents/main/sessions/<ID>.jsonl.reset.<TS> \
|
|
* | node export-jsonl-to-md.js - > output.md
|
|
*/
|
|
|
|
const fs = require("fs");
|
|
|
|
const inputArg = process.argv[2];
|
|
const outputArg = process.argv[3];
|
|
|
|
if (!inputArg) {
|
|
console.error("Usage: export-jsonl-to-md.js <input.jsonl|-> [output.md]");
|
|
process.exit(1);
|
|
}
|
|
|
|
const raw = inputArg === "-" ? fs.readFileSync(0, "utf-8") : fs.readFileSync(inputArg, "utf-8");
|
|
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 = inputArg === "-" ? "Session" : inputArg.split("/").pop().replace(/\.jsonl.*/, "");
|
|
const md = [
|
|
`# Session: ${title}`,
|
|
``,
|
|
`Exportiert: ${exportedAt} `,
|
|
`Quelle: ${inputArg === "-" ? "stdin" : inputArg}`,
|
|
`Nachrichten: ${blocks.length}`,
|
|
``,
|
|
`---`,
|
|
``,
|
|
blocks.join("\n\n---\n\n"),
|
|
``,
|
|
].join("\n");
|
|
|
|
if (outputArg) {
|
|
fs.writeFileSync(outputArg, md);
|
|
console.error(`OK: ${blocks.length} Nachrichten → ${outputArg}`);
|
|
} else {
|
|
process.stdout.write(md);
|
|
}
|