// Daily cost tracker. Resets at UTC midnight by keying the file by date. // Hard ceilings are the policy surface — the bot refuses to call the // cloud once the budget is exhausted for the day. import { readFile, writeFile, mkdir } from "node:fs/promises"; import { join } from "node:path"; import type { CostState } from "./types.ts"; const COST_DIR = "/home/profit/lakehouse/data/_bot"; export const DAILY_CALLS_BUDGET = 20; export const DAILY_TOKENS_BUDGET = 8000 * DAILY_CALLS_BUDGET; // 160k tokens/day ceiling function todayUtc(): string { return new Date().toISOString().slice(0, 10); } function costFile(date: string): string { return join(COST_DIR, `cost-${date}.json`); } export async function readCost(): Promise { const date = todayUtc(); try { const raw = await readFile(costFile(date), "utf8"); return JSON.parse(raw) as CostState; } catch { return { date, calls: 0, tokens: 0 }; } } export async function recordCost(calls: number, tokens: number): Promise { await mkdir(COST_DIR, { recursive: true }); const current = await readCost(); const updated: CostState = { date: current.date, calls: current.calls + calls, tokens: current.tokens + tokens, }; await writeFile(costFile(updated.date), JSON.stringify(updated, null, 2)); return updated; } export function budgetCheck(c: CostState): { ok: boolean; reason: string } { if (c.calls >= DAILY_CALLS_BUDGET) { return { ok: false, reason: `daily call budget exhausted (${c.calls}/${DAILY_CALLS_BUDGET})` }; } if (c.tokens >= DAILY_TOKENS_BUDGET) { return { ok: false, reason: `daily token budget exhausted (${c.tokens}/${DAILY_TOKENS_BUDGET})` }; } return { ok: true, reason: `${c.calls}/${DAILY_CALLS_BUDGET} calls, ${c.tokens}/${DAILY_TOKENS_BUDGET} tokens used today` }; }