@sema-agent/core
Version:
Stateless, task-oriented AI agent core
124 lines (123 loc) • 5.26 kB
JavaScript
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { chargeUsageRecord, readUsageRecord, EMPTY_USAGE_WINDOW_RECORD, } from "../../core/usage-window-store.js";
import { atomicWriteFile, ensureDir, sanitizeScope } from "./fs-atomic.js";
import { assertAdoptionBootGate } from "./adoption/marker.js";
import { guardCorruptSink } from "./corrupt-read-sink.js";
export class FileUsageWindowStore {
dir;
onCorruptRead;
constructor(root, opts) {
assertAdoptionBootGate(root, "FileUsageWindowStore");
this.dir = join(root, "usage-windows");
ensureDir(this.dir);
this.onCorruptRead = guardCorruptSink(opts?.onCorruptRead);
}
pathFor(key) {
return join(this.dir, `${sanitizeScope(key)}.json`);
}
loadRecord(key) {
const path = this.pathFor(key);
try {
return judgeRecord(path);
}
catch (err) {
if (!(err instanceof LedgerFault))
throw err;
this.onCorruptRead?.({ kind: "file", path, reason: err.message });
throw corrupt(key, err.message);
}
}
async charge(key, tokens, at, windows, costMicroUsd) {
const next = chargeUsageRecord(this.loadRecord(key), tokens, at, windows, costMicroUsd);
atomicWriteFile(join(this.dir, "tmp"), this.pathFor(key), JSON.stringify(next));
}
async read(key, windows, now) {
return readUsageRecord(this.loadRecord(key), windows, now);
}
}
class LedgerFault extends Error {
}
function judgeRecord(path) {
let raw;
try {
raw = readFileSync(path, "utf8");
}
catch (err) {
if (err.code === "ENOENT")
return EMPTY_USAGE_WINDOW_RECORD;
throw new LedgerFault(`read failed (${err.code ?? err.name})`);
}
let parsed;
try {
parsed = JSON.parse(raw);
}
catch {
throw new LedgerFault("unparseable JSON (torn/zero-byte write?)");
}
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
throw new LedgerFault("not a JSON object");
const rec = parsed;
return { slots: validateSlots(rec.slots), buckets: validateBuckets(rec.buckets) };
}
function corrupt(key, reason) {
const e = new Error(`usage-window ledger for key ${JSON.stringify(key)} is unreadable (${reason}). Refused rather than treated as empty: an unreadable ceiling would grant the key an unlimited, silent allowance. Repair it: fix the file, or delete it (deleting it = no usage recorded for the key). The file's location is reported to the store's corrupt-read disclosure (onCorruptRead), not in this message.`);
e.code = "usage_window.store_corrupt";
return e;
}
function validateSlots(value) {
if (value === undefined)
return [];
if (!Array.isArray(value))
throw new LedgerFault("`slots` is not an array");
return value.map((s) => {
if (s === null || typeof s !== "object")
throw new LedgerFault("`slots` entry is not an object");
const { at, tokens, costMicroUsd, costUnknown } = s;
if (typeof at !== "number" || !Number.isFinite(at) || typeof tokens !== "number" || !Number.isFinite(tokens)) {
throw new LedgerFault("`slots` entry has a non-numeric `at`/`tokens`");
}
if (costMicroUsd !== undefined && (typeof costMicroUsd !== "number" || !Number.isFinite(costMicroUsd))) {
throw new LedgerFault("`slots` entry has a non-numeric `costMicroUsd`");
}
if (costUnknown !== undefined && costUnknown !== true)
throw new LedgerFault("`slots` entry has a `costUnknown` that is not `true`");
return {
at,
tokens,
...(costMicroUsd === undefined ? {} : { costMicroUsd }),
...(costUnknown === true ? { costUnknown: true } : {}),
};
});
}
function validateBuckets(value) {
if (value === undefined)
return [];
if (!Array.isArray(value))
throw new LedgerFault("`buckets` is not an array");
return value.map((b) => {
if (b === null || typeof b !== "object")
throw new LedgerFault("`buckets` entry is not an object");
const { windowMs, openedAt, tokens, costMicroUsd, costUnknown } = b;
if (typeof windowMs !== "number" ||
!Number.isFinite(windowMs) ||
typeof openedAt !== "number" ||
!Number.isFinite(openedAt) ||
typeof tokens !== "number" ||
!Number.isFinite(tokens)) {
throw new LedgerFault("`buckets` entry has a non-numeric `windowMs`/`openedAt`/`tokens`");
}
if (costMicroUsd !== undefined && (typeof costMicroUsd !== "number" || !Number.isFinite(costMicroUsd))) {
throw new LedgerFault("`buckets` entry has a non-numeric `costMicroUsd`");
}
if (costUnknown !== undefined && costUnknown !== true)
throw new LedgerFault("`buckets` entry has a `costUnknown` that is not `true`");
return {
windowMs,
openedAt,
tokens,
...(costMicroUsd === undefined ? {} : { costMicroUsd }),
...(costUnknown === true ? { costUnknown: true } : {}),
};
});
}