From d06c29e6a7c4b6f843294c2084fd6cb2a9c6098a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Andr=C3=A9?= Date: Sun, 26 Jul 2026 15:57:20 +0000 Subject: [PATCH 1/2] feat(conduit): Build-insights button + endpoint; relative command path --- .../install/LIFEOS/PULSE/modules/conduit.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) 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)) From 9a0002c465114bc1130b5bccb9be2ae404df4d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Andr=C3=A9?= Date: Sun, 26 Jul 2026 15:57:21 +0000 Subject: [PATCH 2/2] feat(conduit): Build-insights button + endpoint; relative command path --- .../Observability/src/app/conduit/page.tsx | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) 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 +
) )}