import { existsSync, readFileSync, readdirSync, realpathSync, statSync, } from "node:fs"; import path from "node:path"; const COMPLETE_RUN_FILES = [ "manifest.json", "status.json", "capture-jobs.jsonl", "scan-results.json", "processing-report.json", ] as const; export interface NativeScannerRunDirectoryOptions { outputRoot: string; requestedRunDir?: string; activeRunDir?: string; } export function resolveNativeScannerRunDirectory({ outputRoot, requestedRunDir, activeRunDir, }: NativeScannerRunDirectoryOptions) { const root = path.resolve(outputRoot); const requested = requestedRunDir?.trim(); if (requested) return resolveContainedDirectory(root, requested); const active = activeRunDir?.trim(); if (active) return resolveContainedDirectory(root, active); return newestCompleteNativeScannerRun(root); } export function newestCompleteNativeScannerRun(outputRoot: string) { const root = path.resolve(outputRoot); if (!isDirectory(root)) return ""; try { const candidates = readdirSync(root, { withFileTypes: true }) .filter((entry) => entry.isDirectory()) .map((entry) => completeRunCandidate(root, path.join(root, entry.name))) .filter((entry): entry is CompleteRunCandidate => Boolean(entry)) .sort((left, right) => right.createdAt - left.createdAt || right.name.localeCompare(left.name)); return candidates[0]?.runDir ?? ""; } catch { return ""; } } interface CompleteRunCandidate { runDir: string; name: string; createdAt: number; } function completeRunCandidate(root: string, candidate: string): CompleteRunCandidate | null { const runDir = resolveContainedDirectory(root, candidate); if (!runDir || COMPLETE_RUN_FILES.some((file) => !existsSync(path.join(runDir, file)))) return null; try { const statusPayload = JSON.parse(readFileSync(path.join(runDir, "status.json"), "utf8")); const status = statusPayload?.scanner ?? statusPayload; const target = Number(status?.target ?? 0); const captured = Number(status?.captured ?? 0); if (status?.running !== false || status?.status !== "done" || target < 1 || captured < target) return null; const results = JSON.parse(readFileSync(path.join(runDir, "scan-results.json"), "utf8")); if (!Array.isArray(results) || results.length < captured) return null; const manifest = JSON.parse(readFileSync(path.join(runDir, "manifest.json"), "utf8")); const manifestCreatedAt = Date.parse(String(manifest?.createdAt ?? "")); return { runDir, name: path.basename(runDir), createdAt: Number.isFinite(manifestCreatedAt) ? manifestCreatedAt : statSync(runDir).mtimeMs, }; } catch { return null; } } function resolveContainedDirectory(root: string, candidate: string) { try { const resolvedRoot = realpathSync(root); const resolvedCandidate = realpathSync(path.resolve(candidate)); if (!isPathInside(resolvedRoot, resolvedCandidate) || !isDirectory(resolvedCandidate)) return ""; return resolvedCandidate; } catch { return ""; } } function isDirectory(candidate: string) { try { return statSync(candidate).isDirectory(); } catch { return false; } } function isPathInside(root: string, candidate: string) { const relative = path.relative(root, candidate); return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative)); }