#!/usr/bin/env node // arkivklar.mjs // D7 steg 18: orkestrator for /okr:arkivklar. Leser en OKF-bundle og skriver et // VURDERINGSGRUNNLAG mot bevaringsforskrifta -- aldri en avgjoerelse. // // Arbeidsdelingen foelger D6-beslutning 1: lib/arkivklar.mjs eier traversering, // klassifisering og rendring; dette skriptet eier disk-siden (hvor rota ligger, // hvor rapporten havner) og CLI-kontrakten. // // STDOUT ER DEFAULT, fil er opt-in. Kommandoen (/okr:arkivklar) deklarerer // verken Write eller Edit, og en verify-kjoering mot tests/fixtures/ skal ikke // kunne skitne til fixturen. Oppgis en rapport-fil, skrives den ATOMISK // (temp i samme katalog + renameSync) og ALDRI inn i bundlen selv. // // INGEN SLETTEMODELL FINNES HER, og ingen skal innfoeres. Retensjonsbehovet er // dekket av /okr:oppsett arkiver (arkivering av syklus) og /okr:export (uttrekk). // Kassasjon krever Nasjonalarkivets hjemmel etter arkivlova § 13 og er ikke en // operasjon dette verktoeyet kan ha. // // Exit: 0 = rapport produsert · 1 = uventet feil (inkl. confinement-brudd) // 2 = bruksfeil (manglende/ukjent sti) import { writeFileSync, renameSync, existsSync, statSync, realpathSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { traverserBundle, rapport } from '../lib/arkivklar.mjs'; // Atomisk: temp-fil i SAMME katalog, deretter renameSync over maalet. // Speiler write-org-profile.mjs:34-40 / innboks-write.mjs. function writeAtomic(target, data) { const dir = path.dirname(target); const tmp = path.join(dir, `${path.basename(target)}.${process.pid}.tmp`); writeFileSync(tmp, data); renameSync(tmp, target); } export function byggRapport(bundleRoot, opts) { return rapport(traverserBundle(bundleRoot), opts); } // --- CLI --- const isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]; if (isMain) { const [bundleRoot, rapportFil] = process.argv.slice(2); if (!bundleRoot) { process.stderr.write('Bruk: node arkivklar.mjs [rapport-fil]\n'); process.exit(2); } if (!existsSync(bundleRoot) || !statSync(bundleRoot).isDirectory()) { process.stderr.write(`Finnes ikke (eller er ikke en katalog): ${bundleRoot}\n`); process.exit(2); } try { const ut = byggRapport(bundleRoot); if (rapportFil) { // Haandhever loeftet i headeren: rapporten skal ALDRI havne i bundlen den // vurderer. En rapport i treet ville blitt klassifisert av neste kjoering // (selv-forurensning), og et loefte som bare staar i prosa er ikke et // loefte -- samme standard som tool-lista i commands/arkivklar.md. // // BEGGE sider maa realpath-es, ellers er sammenligningen verdiloes: paa // macOS er /var en symlenke til /private/var, saa en realpath-et rot mot // en bare resolvet maal-sti sammenligner to skrivemaater av samme sted og // slipper alt gjennom. Maalet finnes ikke ennaa, saa det er FORELDRE- // katalogen som realpath-es -- samme grep som writeConfined i // lib/innboks-write.mjs. const maalDir = realpathSync(path.dirname(path.resolve(rapportFil))); const maal = path.join(maalDir, path.basename(rapportFil)); const rot = realpathSync(path.resolve(bundleRoot)); if (maal === rot || maal.startsWith(rot + path.sep)) { process.stderr.write(`arkivklar: rapporten kan ikke skrives inne i bundlen som vurderes: ${rapportFil}\n`); process.exit(1); } writeAtomic(maal, ut); process.stdout.write(`Arkivklar: vurderingsgrunnlag skrevet til ${rapportFil}\n`); } else { process.stdout.write(ut); } process.exit(0); } catch (e) { process.stderr.write(`arkivklar: ${e.message}\n`); process.exit(1); } }