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 { 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); } }