// model-usage's entrypoint — the usage context store's consumer (novox/hq ADR 0054). mesh-controller is // a CLI and cannot consume events, so the store that keeps the latest usage reading is a MODULE: it // subscribes to `*.usage.*` and upserts each row. Like the audit-logger, the on(...) IS the whole // handshake — the node's runtime launches this once the broker is bound (ADR 0198), and every usage // event any producer emits lands here as well as on the audit trail. // // **No event is lost** (novox/hq issue 276). A row the store did not take is thrown, so the bus offers // the event again; the first failure spools it on disk, its last delivery is taken from the spool, and // the spool is replayed into the store once it answers again (spool.ts). The upsert keeps the reading // observed latest, so a redelivery or a late replay never writes an older one over a newer. // // The schema is not brought up here: the prepare step does it before this version starts // (prepare/index.ts; ADR 0135), as for the module catalogue. import { on } from "@novox/mesh-sdk/events"; import { UsageStore } from "./store.js"; import { usageSpoolPath, writerFor } from "./consume.js"; import { replayEvery, Spool, takeOrSpool } from "./spool.js"; const say = (line: string) => console.error(`[model-usage] ${line}`); const store = UsageStore.fromEnv(); const spool = await Spool.open(usageSpoolPath()); const write = writerFor(store, say); replayEvery(spool, write, 30_000, say); await on("*.usage.*", (event) => takeOrSpool(event, write, spool, say)); console.log( "[model-usage] recording model usage to its store" + (spool.size > 0 ? `; ${spool.size} spooled event(s) wait in ${spool.dir}` : ""), );