import { loadConfig } from './config.js'; import { Db } from './db.js'; import { runPoll } from './poller.js'; import { getLastRateLimit } from './github/client.js'; import { createApp, buildSyncPayload } from './routes.js'; import { broadcast } from './sse.js'; const config = loadConfig(); const db = new Db({ muteRules: config.timeline.mute }); let polling = false; async function poll(): Promise { if (polling) return; polling = true; try { const result = await runPoll(config, db); console.log( `[poll] ${result.prCount} PRs, ${result.changedPrIds.length} changed, ${result.newEventCount} new events`, ); broadcast(buildSyncPayload(db, result.changedPrIds, result.newEventCount)); } catch (err) { const message = err instanceof Error ? err.message : String(err); console.error(`[poll] failed: ${message}`); db.setMeta('last_sync_error', message); broadcast(buildSyncPayload(db)); } finally { polling = false; } } function scheduleNext(): void { // Back off to 5 minutes when the rate-limit budget runs low. const remaining = getLastRateLimit()?.remaining; const intervalMs = remaining !== undefined && remaining < 500 ? 5 * 60_000 : config.pollIntervalSeconds * 1000; setTimeout(async () => { await poll(); scheduleNext(); }, intervalMs); } const app = createApp({ db, user: config.user, triggerSync: poll }); app.listen(config.port, config.host, () => { console.log(`pr-monitor listening on http://${config.host}:${config.port}`); }); await poll(); scheduleNext();