import { accountDb } from "../scoped-db";
import { repoOwnerId } from "./scope";
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 {
const ownerId = await repoOwnerId(repoId);
if (!ownerId) return;
await accountDb(ownerId).run(
`INSERT INTO runner_usage_events (id, owner_id, repo_id, kind, quantity, subject_id, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
randomId(),
ownerId,
repoId,
kind,
Math.round(quantity),
subjectId,
Date.now(),
);
} catch (error) {
console.error("Failed to record usage", kind, repoId, error);
}
}
export async function meterCacheStorage(repoId: string, bytes: number): Promise<void> {
await recordRunnerUsage(repoId, "cache_bytes_stored", bytes, null);
}
export async function meterCacheFreed(repoId: string, bytes: number): Promise<void> {
await recordRunnerUsage(repoId, "cache_bytes_freed", bytes, null);
}