import { run } from "../db"; import { randomId } from "../crypto"; type UsageKind = "cache_bytes_stored" | "cache_bytes_freed"; /** * Audit trail of Actions cache writes and frees. Billing does not read it: self-hosted runner seconds are * metered at job completion (src/server/metering/ci.ts) and cache storage is a gauge over action_caches. * Single seam for usage metering of Actions cache storage. Events land in `runner_usage_events` until the * shared metering API exists; swapping this function body is the only change needed to forward them there. */ async function recordRunnerUsage(repoId: string, kind: UsageKind, quantity: number, subjectId: string | null) { if (!Number.isFinite(quantity) || quantity <= 0) return; try { await run( `INSERT INTO runner_usage_events (id, owner_id, repo_id, kind, quantity, subject_id, created_at) SELECT ?, owner_id, id, ?, ?, ?, ? FROM repos WHERE id = ?`, randomId(), kind, Math.round(quantity), subjectId, Date.now(), repoId, ); } catch (error) { console.error("Failed to record usage", kind, repoId, error); } } export async function meterCacheStorage(repoId: string, bytes: number): Promise { await recordRunnerUsage(repoId, "cache_bytes_stored", bytes, null); } export async function meterCacheFreed(repoId: string, bytes: number): Promise { await recordRunnerUsage(repoId, "cache_bytes_freed", bytes, null); }