import { readFileSync, statSync } from 'fs'; import path from 'path'; import { countPendingLocate } from '@/lib/db'; import { corsJson, corsPreflight } from '../cors'; /** The extension version this checkout ships. * * Read out of the manifest rather than kept as a constant here: the manifest is * the file Chrome actually loads, so the two cannot drift apart. Cached on * mtime because every browser polls this every few seconds and the file only * changes when an update lands. Null when there is no extension folder beside * the server — the extension then leaves itself alone, which is the right * answer for a dashboard that cannot see what it is supposed to be running. */ let manifestCache: { mtimeMs: number; version: string | null } | null = null; function shippedExtensionVersion(): string | null { const file = path.join(process.cwd(), 'extension', 'manifest.json'); try { const { mtimeMs } = statSync(file); if (!manifestCache || manifestCache.mtimeMs !== mtimeMs) { const parsed = JSON.parse(readFileSync(file, 'utf8')) as { version?: string }; manifestCache = { mtimeMs, version: parsed.version ?? null }; } return manifestCache.version; } catch { return null; } } /** Polled by the Chromium extension every few seconds, and by the AutoBuyer page. * The AutoBuyer has no on/off switch — it is ready whenever the runner is — so * the only things worth reporting here are whether clicks are waiting to be * served, and which extension build this checkout expects to be talking to. */ export async function GET() { return corsJson({ pendingLocate: countPendingLocate(), extensionVersion: shippedExtensionVersion(), }); } export async function OPTIONS() { return corsPreflight(); }