Files
career-ops-vbleroy/web/src/app/api/followups/route.ts
T
redlineo d295c7f139
Release Please / release-please (push) Failing after 4m2s
Web CI / web typecheck + build (push) Successful in 3m39s
CodeQL Analysis / Analyze (javascript-typescript) (push) Failing after 1h1m49s
CodeQL Analysis / Analyze (go) (push) Failing after 1h1m49s
init
2026-07-13 10:14:00 +03:00

29 lines
1.4 KiB
TypeScript

import { execFile } from "node:child_process";
import fs from "node:fs";
import { careerOpsRoot, rootScript } from "@/lib/career-ops";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
// The DEMAND loop: surface follow-ups due, via the core's own
// followup-cadence.mjs --json (the SAME calculator the CLI uses) — we never
// reimplement the cadence logic, we read its verdict (mirrors /api/doctor).
export async function GET() {
const script = rootScript("followup-cadence");
if (!fs.existsSync(script)) return Response.json({ available: false, metadata: null, entries: [] });
const stdout = await new Promise<string>((resolve) => {
execFile("node", [script, "--json"], { cwd: careerOpsRoot(), timeout: 12_000 }, (_e, out) => resolve(out || ""));
});
try {
const start = stdout.indexOf("{");
const j = JSON.parse(stdout.slice(start));
const entries = Array.isArray(j.entries) ? j.entries : [];
// Overdue first; cap for the home (full list lives in the tracker).
const overdue = entries.filter((e: { status?: string }) => /overdue|urgent/i.test(String(e.status))).slice(0, 8);
const top = (overdue.length ? overdue : entries).slice(0, 6);
return Response.json({ available: true, metadata: j.metadata ?? null, entries: top });
} catch {
return Response.json({ available: false, metadata: null, entries: [] });
}
}