Intelligence
Artifacts
Browse the repository, read documents, and manage the governance folders. Source, runtime, and infrastructure are read-only.
Repository
repositories/aaf-holdings/hq01/lib/missions/store.ts
4.7 KB
import fs from "node:fs";
import path from "node:path";
import { MISSIONS_ROOT } from "./config";
import type { Mission, MissionEvent, MissionReport } from "./types";
/**
* Filesystem store for the Mission Registry — the source of truth.
*
* Layout:
* /srv/aaf/missions/MISSION-000001/
* mission.json the mission record
* history/log.jsonl append-only event history
* reports/ execution reports stored under the mission
* artifacts/ durable assets owned by the mission
*/
const ID_RE = /^MISSION-\d{6}$/;
export function isValidMissionId(id: string): boolean {
return ID_RE.test(id);
}
export function missionDir(id: string): string {
return path.join(MISSIONS_ROOT, id);
}
export function missionJsonPath(id: string): string {
return path.join(missionDir(id), "mission.json");
}
export function historyDir(id: string): string {
return path.join(missionDir(id), "history");
}
export function historyLogPath(id: string): string {
return path.join(historyDir(id), "log.jsonl");
}
export function reportsDir(id: string): string {
return path.join(missionDir(id), "reports");
}
export function artifactsDir(id: string): string {
return path.join(missionDir(id), "artifacts");
}
/** Create the full directory skeleton for a mission. */
export function ensureMissionDirs(id: string): void {
fs.mkdirSync(historyDir(id), { recursive: true });
fs.mkdirSync(reportsDir(id), { recursive: true });
fs.mkdirSync(artifactsDir(id), { recursive: true });
}
function writeJsonAtomic(file: string, data: unknown): void {
fs.mkdirSync(path.dirname(file), { recursive: true });
const tmp = `${file}.tmp`;
fs.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
fs.renameSync(tmp, file);
}
export function writeMission(mission: Mission): void {
writeJsonAtomic(missionJsonPath(mission.id), mission);
}
export function readMission(id: string): Mission | null {
if (!isValidMissionId(id)) return null;
try {
return JSON.parse(fs.readFileSync(missionJsonPath(id), "utf8")) as Mission;
} catch {
return null;
}
}
export function listMissionIds(): string[] {
let entries: fs.Dirent[];
try {
entries = fs.readdirSync(MISSIONS_ROOT, { withFileTypes: true });
} catch {
return [];
}
return entries
.filter((e) => e.isDirectory() && isValidMissionId(e.name))
.map((e) => e.name)
.filter((id) => fs.existsSync(missionJsonPath(id)));
}
/** Next sequential, human-readable mission id (MISSION-000001, …). */
export function nextMissionId(): string {
let max = 0;
for (const id of listMissionIds()) {
const n = Number(id.slice("MISSION-".length));
if (Number.isFinite(n) && n > max) max = n;
}
return `MISSION-${String(max + 1).padStart(6, "0")}`;
}
/** Append an event to a mission's append-only history log. */
export function appendEvent(id: string, event: MissionEvent): void {
fs.mkdirSync(historyDir(id), { recursive: true });
fs.appendFileSync(historyLogPath(id), JSON.stringify(event) + "\n", "utf8");
}
export function readHistory(id: string): MissionEvent[] {
let raw: string;
try {
raw = fs.readFileSync(historyLogPath(id), "utf8");
} catch {
return [];
}
return raw
.split("\n")
.filter(Boolean)
.map((line) => {
try {
return JSON.parse(line) as MissionEvent;
} catch {
return null;
}
})
.filter((e): e is MissionEvent => Boolean(e));
}
export function listReports(id: string): MissionReport[] {
let names: string[];
try {
names = fs.readdirSync(reportsDir(id));
} catch {
return [];
}
const out: MissionReport[] = [];
for (const name of names) {
if (!name.endsWith(".md")) continue;
const full = path.join(reportsDir(id), name);
let stat: fs.Stats;
try {
stat = fs.statSync(full);
} catch {
continue;
}
const stem = name.replace(/\.md$/, "");
out.push({
id: stem,
file: name,
session_id: stem || null,
stored_at: stat.mtime.toISOString(),
size: stat.size,
});
}
return out.sort((a, b) => b.stored_at.localeCompare(a.stored_at));
}
/** True if a report with this id has already been stored (idempotency guard). */
export function reportStored(id: string, reportId: string): boolean {
return fs.existsSync(path.join(reportsDir(id), `${reportId}.md`));
}
/** Store a report file under the mission. */
export function storeReport(id: string, reportId: string, content: string): void {
fs.mkdirSync(reportsDir(id), { recursive: true });
writeJsonAtomicText(path.join(reportsDir(id), `${reportId}.md`), content);
}
function writeJsonAtomicText(file: string, content: string): void {
const tmp = `${file}.tmp`;
fs.writeFileSync(tmp, content, "utf8");
fs.renameSync(tmp, file);
}
root · /srv/aaf