diff --git a/LifeOS/install/LIFEOS/PULSE/Observability/src/app/conduit/page.tsx b/LifeOS/install/LIFEOS/PULSE/Observability/src/app/conduit/page.tsx index b580b6ac3f..3288c62de4 100644 --- a/LifeOS/install/LIFEOS/PULSE/Observability/src/app/conduit/page.tsx +++ b/LifeOS/install/LIFEOS/PULSE/Observability/src/app/conduit/page.tsx @@ -75,6 +75,24 @@ export default function ConduitPage() { const [sources, setSources] = useState(null); const [insight, setInsight] = useState(null); const [error, setError] = useState(null); + const [building, setBuilding] = useState(false); + const [buildMsg, setBuildMsg] = useState(null); + + async function runBuildInsights() { + setBuilding(true); + setBuildMsg(null); + try { + const res = await fetch("/api/conduit/build", { method: "POST" }); + const data = await res.json().catch(() => ({ ok: res.ok })); + setBuildMsg(data.ok ? "Insights rebuilt." : "Build failed — see server log."); + const fresh = await fetch("/api/conduit/insight").then((r) => r.json()); + setInsight(fresh); + } catch (e) { + setBuildMsg("Build error: " + String(e)); + } finally { + setBuilding(false); + } + } useEffect(() => { fetch("/api/conduit/today").then((r) => r.json()).then(setToday).catch((e) => setError(String(e))); @@ -136,9 +154,21 @@ export default function ConduitPage() { ) : ( !insight.available && ( -
- The insight job runs on the hour. Run it now:{" "} - bun Conduit/BuildInsight.ts +
+
The insight job runs on the hour. Build it now:
+ + {buildMsg &&
{buildMsg}
} +
+ Or from a terminal:{" "} + bun ~/.claude/LIFEOS/PULSE/Conduit/BuildInsight.ts +
) )} diff --git a/LifeOS/install/LIFEOS/PULSE/modules/conduit.ts b/LifeOS/install/LIFEOS/PULSE/modules/conduit.ts index 199c062067..de738c234f 100644 --- a/LifeOS/install/LIFEOS/PULSE/modules/conduit.ts +++ b/LifeOS/install/LIFEOS/PULSE/modules/conduit.ts @@ -116,8 +116,29 @@ export function todayRecordPublic(): DailyRecord { return todayRecord() } +/** Run BuildInsight.ts on demand (the "Build insights now" button). Spawns the + * standalone script — same as the hourly launchd job — and returns its output. */ +async function buildInsightNow(): Promise { + const script = join(import.meta.dir, "..", "Conduit", "BuildInsight.ts") + try { + const proc = Bun.spawn(["bun", script], { stdout: "pipe", stderr: "pipe" }) + const [out, err, code] = await Promise.all([ + new Response(proc.stdout).text(), + new Response(proc.stderr).text(), + proc.exited, + ]) + return Response.json( + { ok: code === 0, output: ((out || "") + (err || "")).slice(-1500) }, + { status: code === 0 ? 200 : 500 }, + ) + } catch (e) { + return Response.json({ ok: false, output: String(e) }, { status: 500 }) + } +} + export async function handleRequest(req: Request, pathname: string): Promise { const sub = pathname.replace(/^\/api\/conduit/, "") || "/" + if (sub === "/build" && req.method === "POST") return buildInsightNow() if (sub === "/" || sub === "/today") return Response.json(todayRecord()) if (sub === "/recent") { const days = Math.max(1, Math.min(90, Number(new URL(req.url).searchParams.get("days")) || 7))