Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgBEW6qAwgn2fcQbA2f4ZQ
50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
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();
|
|
|
|
let polling = false;
|
|
|
|
async function poll(): Promise<void> {
|
|
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, triggerSync: poll });
|
|
app.listen(config.port, () => {
|
|
console.log(`pr-monitor listening on http://localhost:${config.port}`);
|
|
});
|
|
|
|
await poll();
|
|
scheduleNext();
|