import { all, chunks, placeholders, run } from "../db";
const DAY_MS = 24 * 60 * 60 * 1000;
/** Dynamically registered clients that never completed an authorization are garbage after this long. */
const UNUSED_DCR_CLIENT_MS = 7 * DAY_MS;
const PRUNE_BATCH = 500;
/**
* Keeps the OAuth directory bounded (INT-A24): dynamic-client registrations nobody ever used. Codes and refresh
* tokens expire inside each account's AccountState (account-state/housekeeping.ts). A client with an unexpired
* refresh token can't match: issuing or rotating one sets `last_used_at`, and codes live a minute.
*/
export async function runMcpCron(now = Date.now()): Promise<void> {
const unused = await all<{ id: string }>(
"SELECT id FROM oauth_clients WHERE kind = 'dcr' AND last_used_at IS NULL AND created_at < ? LIMIT ?",
now - UNUSED_DCR_CLIENT_MS,
PRUNE_BATCH,
);
for (const ids of chunks(unused.map((row) => row.id))) {
await run(`DELETE FROM oauth_clients WHERE id IN (${placeholders(ids.length)})`, ...ids);
}
}