// Optional convenience: list/read .alog files from a local directory the roastetta skill // (or the operator) already populated, without this webapp owning the Cloudflare/login // problem itself. Path-traversal guarded: only a bare basename ending in .alog, resolved // and asserted to stay inside ALOG_DIR. import * as fs from "node:fs/promises"; import * as path from "node:path"; import * as os from "node:os"; import { parseAlog } from "./alog.js"; function resolveAlogDir() { if (process.env.ALOG_DIR) return process.env.ALOG_DIR; return path.join(os.homedir(), "Roastetta"); } async function firstExistingDir(candidates) { for (const dir of candidates) { try { const stat = await fs.stat(dir); if (stat.isDirectory()) return dir; } catch { /* try next */ } } return null; } export async function listAlogLibrary() { const dir = await firstExistingDir([resolveAlogDir(), "/Users/shane/dev/hope_roaster/ref/roasts"]); if (!dir) return []; const entries = await fs.readdir(dir, { withFileTypes: true }); const files = []; for (const entry of entries) { if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".alog")) continue; const full = path.join(dir, entry.name); const stat = await fs.stat(full); files.push({ filename: entry.name, sizeBytes: stat.size, mtime: stat.mtime.toISOString() }); } return files.sort((a, b) => b.mtime.localeCompare(a.mtime)); } export async function readAlogFromLibrary(filenameParam) { const base = path.basename(String(filenameParam ?? "")); if (!base || !base.toLowerCase().endsWith(".alog")) { const err = new Error("Filename must end in .alog"); err.code = "bad_filename"; throw err; } const dir = await firstExistingDir([resolveAlogDir(), "/Users/shane/dev/hope_roaster/ref/roasts"]); if (!dir) { const err = new Error("No .alog library directory found."); err.code = "not_found"; throw err; } const full = path.resolve(dir, base); if (!full.startsWith(path.resolve(dir) + path.sep)) { const err = new Error("Invalid filename."); err.code = "bad_filename"; throw err; } let content; try { content = await fs.readFile(full, "utf8"); } catch { const err = new Error(`${base} not found in the .alog library.`); err.code = "not_found"; throw err; } return parseAlog(content, base); }