summaryrefslogtreecommitdiffhomepage
path: root/packages/api/src/app.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-01 09:28:30 +0900
committerAdam Malczewski <[email protected]>2026-06-01 09:28:30 +0900
commit21cdb1199599c4dc6e2a941e52713ba6511cd675 (patch)
treee3711384f16c476bf5d8def148dc922f9ceb5f3d /packages/api/src/app.ts
parent5e72191cac9469c2ade91aaba1e62f69fa1ad94a (diff)
downloaddispatch-21cdb1199599c4dc6e2a941e52713ba6511cd675.tar.gz
dispatch-21cdb1199599c4dc6e2a941e52713ba6511cd675.zip
feat(api): wire notification dispatcher into app + /notifications routes
PermissionManager: add onPromptAdded(listener) callback. Fires exactly once per unique pending prompt id, even when broadcastPending is called repeatedly for unrelated mutations (e.g. another prompt resolving while this one is still pending). app.ts: instantiate NotificationDispatcher, attach to both AgentManager and PermissionManager. Tab-title lookup via core's getTab so the notifications carry human-readable context instead of raw UUIDs. routes/notifications.ts: - GET /notifications — current config (auth token redacted) plus the event-type catalog and defaults - PUT /notifications — partial update; auth token semantics are undefined=keep, ''=clear, otherwise replace - POST /notifications/test — sends a test notification with the current config (rejects if disabled or topic invalid) Tests: - new permission-manager.test.ts covers the onPromptAdded contract (one-fire-per-prompt, dedup across rebroadcasts, unsubscribe, listener throws don't break siblings) - existing routes.test.ts gets stubs for the new core notification exports so the @dispatch/core mock stays complete
Diffstat (limited to 'packages/api/src/app.ts')
-rw-r--r--packages/api/src/app.ts18
1 files changed, 18 insertions, 0 deletions
diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts
index 19cc193..24cef24 100644
--- a/packages/api/src/app.ts
+++ b/packages/api/src/app.ts
@@ -1,3 +1,4 @@
+import { getTab, NotificationDispatcher } from "@dispatch/core";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { AgentManager } from "./agent-manager.js";
@@ -5,12 +6,28 @@ import { PermissionManager } from "./permission-manager.js";
import { agentsRoutes } from "./routes/agents.js";
import { configRoutes } from "./routes/config.js";
import { modelsRoutes, startWakeScheduler } from "./routes/models.js";
+import { notificationsRoutes } from "./routes/notifications.js";
import { skillsRoutes } from "./routes/skills.js";
import { tabsRoutes } from "./routes/tabs.js";
export const permissionManager = new PermissionManager();
export const agentManager = new AgentManager(permissionManager);
+// ntfy.sh push notifications. The dispatcher reads its config from the
+// `settings` table on every send, so config changes apply immediately —
+// no restart, no re-attach needed.
+export const notificationDispatcher = new NotificationDispatcher({
+ getTabTitle: (tabId) => {
+ try {
+ return getTab(tabId)?.title ?? null;
+ } catch {
+ return null;
+ }
+ },
+});
+notificationDispatcher.attachToAgentManager(agentManager);
+notificationDispatcher.attachToPermissionManager(permissionManager);
+
export const app = new Hono();
app.use(
@@ -112,6 +129,7 @@ app.route("/skills", skillsRoutes);
app.route("/models", modelsRoutes);
app.route("/tabs", tabsRoutes);
app.route("/agents", agentsRoutes);
+app.route("/notifications", notificationsRoutes);
// Start the wake scheduler on boot (restores persisted schedule)
startWakeScheduler();