singularity-forge/web/app/api/notifications/route.ts
Jeremy 8078755e4b feat(gsd): persistent notification panel with TUI overlay, widget, and web API
Notifications from ctx.ui.notify() and workflow-logger now persist to
.gsd/notifications.jsonl instead of evaporating as transient toasts.

- notification-store: JSONL persistence with 500-entry rotation, atomic
  temp+rename rewrites, ref-counted suppress API, disk-synced counters
- notify-interceptor: WeakSet-guarded monkey-patch on ctx.ui.notify
  installed at session_start and session_switch
- notification-widget: always-on belowEditor strip showing unread count
- notification-overlay: scrollable Ctrl+Alt+N panel with severity filter
- /gsd notifications command: clear, tail, filter subcommands
- workflow-logger: warnings now also persist to notification store
- web API: GET/DELETE /api/notifications with ?countOnly support
- 16 unit tests covering store, suppress, project isolation, resync
2026-04-05 22:13:28 -05:00

49 lines
1.5 KiB
TypeScript

import { collectNotificationsData, clearNotificationsData } from "../../../../src/web/notifications-service.ts"
import { requireProjectCwd } from "../../../../src/web/bridge-service.ts"
export const runtime = "nodejs"
export const dynamic = "force-dynamic"
export async function GET(request: Request): Promise<Response> {
try {
const projectCwd = requireProjectCwd(request);
const url = new URL(request.url)
const countOnly = url.searchParams.get("countOnly") === "true"
const payload = await collectNotificationsData(projectCwd)
if (countOnly) {
return Response.json(
{ unreadCount: payload.unreadCount },
{ headers: { "Cache-Control": "no-store" } },
)
}
return Response.json(payload, {
headers: { "Cache-Control": "no-store" },
})
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return Response.json(
{ error: message },
{ status: 500, headers: { "Cache-Control": "no-store" } },
)
}
}
export async function DELETE(request: Request): Promise<Response> {
try {
const projectCwd = requireProjectCwd(request);
await clearNotificationsData(projectCwd)
return Response.json(
{ ok: true },
{ headers: { "Cache-Control": "no-store" } },
)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return Response.json(
{ error: message },
{ status: 500, headers: { "Cache-Control": "no-store" } },
)
}
}