agentic-notify 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +111 -2
- package/dist/chunk-3N77SYRA.js +168 -0
- package/dist/chunk-6QCYA3X7.js +401 -0
- package/dist/chunk-FJPEUDTG.js +64 -0
- package/dist/chunk-KJKM56FT.js +31 -0
- package/dist/chunk-POSHKDNY.js +86 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +226 -0
- package/dist/dispatch-LBnkLOQg.d.ts +77 -0
- package/dist/gdpr-CUq5MUwo.d.ts +9 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +44 -0
- package/dist/mcp-bin.d.ts +1 -0
- package/dist/mcp-bin.js +18 -0
- package/dist/mcp.d.ts +11 -0
- package/dist/mcp.js +12 -0
- package/dist/sdk.d.ts +110 -0
- package/dist/sdk.js +8 -0
- package/dist/testing-B5slvYDV.d.ts +27 -0
- package/dist/testing.d.ts +5 -0
- package/dist/testing.js +10 -0
- package/dist/types-C842YAMe.d.ts +217 -0
- package/dist/types-DONKNIOo.d.ts +20 -0
- package/dist/worker.d.ts +65 -0
- package/dist/worker.js +178 -0
- package/package.json +88 -4
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const CHANNELS: readonly ["inbox", "webhook", "email"];
|
|
4
|
+
type Channel = (typeof CHANNELS)[number];
|
|
5
|
+
declare const recipientSchema: z.ZodObject<{
|
|
6
|
+
kind: z.ZodEnum<["human", "agent"]>;
|
|
7
|
+
id: z.ZodString;
|
|
8
|
+
}, "strict", z.ZodTypeAny, {
|
|
9
|
+
id: string;
|
|
10
|
+
kind: "human" | "agent";
|
|
11
|
+
}, {
|
|
12
|
+
id: string;
|
|
13
|
+
kind: "human" | "agent";
|
|
14
|
+
}>;
|
|
15
|
+
type Recipient = z.infer<typeof recipientSchema>;
|
|
16
|
+
declare const resourceRefSchema: z.ZodObject<{
|
|
17
|
+
kind: z.ZodString;
|
|
18
|
+
id: z.ZodString;
|
|
19
|
+
url: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
20
|
+
}, "strict", z.ZodTypeAny, {
|
|
21
|
+
id: string;
|
|
22
|
+
kind: string;
|
|
23
|
+
url?: string | undefined;
|
|
24
|
+
}, {
|
|
25
|
+
id: string;
|
|
26
|
+
kind: string;
|
|
27
|
+
url?: string | undefined;
|
|
28
|
+
}>;
|
|
29
|
+
type ResourceRef = z.infer<typeof resourceRefSchema>;
|
|
30
|
+
declare const actorSchema: z.ZodObject<{
|
|
31
|
+
kind: z.ZodEnum<["human", "agent"]>;
|
|
32
|
+
id: z.ZodString;
|
|
33
|
+
}, "strict", z.ZodTypeAny, {
|
|
34
|
+
id: string;
|
|
35
|
+
kind: "human" | "agent";
|
|
36
|
+
}, {
|
|
37
|
+
id: string;
|
|
38
|
+
kind: "human" | "agent";
|
|
39
|
+
}>;
|
|
40
|
+
type Actor = z.infer<typeof actorSchema>;
|
|
41
|
+
declare const notificationSchema: z.ZodObject<{
|
|
42
|
+
id: z.ZodString;
|
|
43
|
+
recipient: z.ZodObject<{
|
|
44
|
+
kind: z.ZodEnum<["human", "agent"]>;
|
|
45
|
+
id: z.ZodString;
|
|
46
|
+
}, "strict", z.ZodTypeAny, {
|
|
47
|
+
id: string;
|
|
48
|
+
kind: "human" | "agent";
|
|
49
|
+
}, {
|
|
50
|
+
id: string;
|
|
51
|
+
kind: "human" | "agent";
|
|
52
|
+
}>;
|
|
53
|
+
type: z.ZodString;
|
|
54
|
+
resource: z.ZodObject<{
|
|
55
|
+
kind: z.ZodString;
|
|
56
|
+
id: z.ZodString;
|
|
57
|
+
url: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
58
|
+
}, "strict", z.ZodTypeAny, {
|
|
59
|
+
id: string;
|
|
60
|
+
kind: string;
|
|
61
|
+
url?: string | undefined;
|
|
62
|
+
}, {
|
|
63
|
+
id: string;
|
|
64
|
+
kind: string;
|
|
65
|
+
url?: string | undefined;
|
|
66
|
+
}>;
|
|
67
|
+
actor: z.ZodObject<{
|
|
68
|
+
kind: z.ZodEnum<["human", "agent"]>;
|
|
69
|
+
id: z.ZodString;
|
|
70
|
+
}, "strict", z.ZodTypeAny, {
|
|
71
|
+
id: string;
|
|
72
|
+
kind: "human" | "agent";
|
|
73
|
+
}, {
|
|
74
|
+
id: string;
|
|
75
|
+
kind: "human" | "agent";
|
|
76
|
+
}>;
|
|
77
|
+
body: z.ZodString;
|
|
78
|
+
createdAt: z.ZodNumber;
|
|
79
|
+
readAt: z.ZodNullable<z.ZodNumber>;
|
|
80
|
+
}, "strict", z.ZodTypeAny, {
|
|
81
|
+
type: string;
|
|
82
|
+
id: string;
|
|
83
|
+
recipient: {
|
|
84
|
+
id: string;
|
|
85
|
+
kind: "human" | "agent";
|
|
86
|
+
};
|
|
87
|
+
resource: {
|
|
88
|
+
id: string;
|
|
89
|
+
kind: string;
|
|
90
|
+
url?: string | undefined;
|
|
91
|
+
};
|
|
92
|
+
actor: {
|
|
93
|
+
id: string;
|
|
94
|
+
kind: "human" | "agent";
|
|
95
|
+
};
|
|
96
|
+
body: string;
|
|
97
|
+
createdAt: number;
|
|
98
|
+
readAt: number | null;
|
|
99
|
+
}, {
|
|
100
|
+
type: string;
|
|
101
|
+
id: string;
|
|
102
|
+
recipient: {
|
|
103
|
+
id: string;
|
|
104
|
+
kind: "human" | "agent";
|
|
105
|
+
};
|
|
106
|
+
resource: {
|
|
107
|
+
id: string;
|
|
108
|
+
kind: string;
|
|
109
|
+
url?: string | undefined;
|
|
110
|
+
};
|
|
111
|
+
actor: {
|
|
112
|
+
id: string;
|
|
113
|
+
kind: "human" | "agent";
|
|
114
|
+
};
|
|
115
|
+
body: string;
|
|
116
|
+
createdAt: number;
|
|
117
|
+
readAt: number | null;
|
|
118
|
+
}>;
|
|
119
|
+
type Notification = z.infer<typeof notificationSchema>;
|
|
120
|
+
declare const sendInputSchema: z.ZodObject<{
|
|
121
|
+
recipient: z.ZodObject<{
|
|
122
|
+
kind: z.ZodEnum<["human", "agent"]>;
|
|
123
|
+
id: z.ZodString;
|
|
124
|
+
}, "strict", z.ZodTypeAny, {
|
|
125
|
+
id: string;
|
|
126
|
+
kind: "human" | "agent";
|
|
127
|
+
}, {
|
|
128
|
+
id: string;
|
|
129
|
+
kind: "human" | "agent";
|
|
130
|
+
}>;
|
|
131
|
+
type: z.ZodString;
|
|
132
|
+
resource: z.ZodObject<{
|
|
133
|
+
kind: z.ZodString;
|
|
134
|
+
id: z.ZodString;
|
|
135
|
+
url: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
136
|
+
}, "strict", z.ZodTypeAny, {
|
|
137
|
+
id: string;
|
|
138
|
+
kind: string;
|
|
139
|
+
url?: string | undefined;
|
|
140
|
+
}, {
|
|
141
|
+
id: string;
|
|
142
|
+
kind: string;
|
|
143
|
+
url?: string | undefined;
|
|
144
|
+
}>;
|
|
145
|
+
actor: z.ZodObject<{
|
|
146
|
+
kind: z.ZodEnum<["human", "agent"]>;
|
|
147
|
+
id: z.ZodString;
|
|
148
|
+
}, "strict", z.ZodTypeAny, {
|
|
149
|
+
id: string;
|
|
150
|
+
kind: "human" | "agent";
|
|
151
|
+
}, {
|
|
152
|
+
id: string;
|
|
153
|
+
kind: "human" | "agent";
|
|
154
|
+
}>;
|
|
155
|
+
body: z.ZodString;
|
|
156
|
+
}, "strict", z.ZodTypeAny, {
|
|
157
|
+
type: string;
|
|
158
|
+
recipient: {
|
|
159
|
+
id: string;
|
|
160
|
+
kind: "human" | "agent";
|
|
161
|
+
};
|
|
162
|
+
resource: {
|
|
163
|
+
id: string;
|
|
164
|
+
kind: string;
|
|
165
|
+
url?: string | undefined;
|
|
166
|
+
};
|
|
167
|
+
actor: {
|
|
168
|
+
id: string;
|
|
169
|
+
kind: "human" | "agent";
|
|
170
|
+
};
|
|
171
|
+
body: string;
|
|
172
|
+
}, {
|
|
173
|
+
type: string;
|
|
174
|
+
recipient: {
|
|
175
|
+
id: string;
|
|
176
|
+
kind: "human" | "agent";
|
|
177
|
+
};
|
|
178
|
+
resource: {
|
|
179
|
+
id: string;
|
|
180
|
+
kind: string;
|
|
181
|
+
url?: string | undefined;
|
|
182
|
+
};
|
|
183
|
+
actor: {
|
|
184
|
+
id: string;
|
|
185
|
+
kind: "human" | "agent";
|
|
186
|
+
};
|
|
187
|
+
body: string;
|
|
188
|
+
}>;
|
|
189
|
+
type SendInput = z.infer<typeof sendInputSchema>;
|
|
190
|
+
declare const preferencesSchema: z.ZodObject<{
|
|
191
|
+
channels: z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodArray<z.ZodEnum<["inbox", "webhook", "email"]>, "many">, ("inbox" | "webhook" | "email")[], ("inbox" | "webhook" | "email")[]>>;
|
|
192
|
+
muted: z.ZodBoolean;
|
|
193
|
+
mutedTypes: z.ZodArray<z.ZodString, "many">;
|
|
194
|
+
}, "strict", z.ZodTypeAny, {
|
|
195
|
+
channels: Record<string, ("inbox" | "webhook" | "email")[]>;
|
|
196
|
+
muted: boolean;
|
|
197
|
+
mutedTypes: string[];
|
|
198
|
+
}, {
|
|
199
|
+
channels: Record<string, ("inbox" | "webhook" | "email")[]>;
|
|
200
|
+
muted: boolean;
|
|
201
|
+
mutedTypes: string[];
|
|
202
|
+
}>;
|
|
203
|
+
type Preferences = z.infer<typeof preferencesSchema>;
|
|
204
|
+
declare const DEFAULT_CHANNELS: readonly Channel[];
|
|
205
|
+
declare function defaultPreferences(): Preferences;
|
|
206
|
+
declare function recipientKey(recipient: Recipient): string;
|
|
207
|
+
interface DigestGroup {
|
|
208
|
+
type: string;
|
|
209
|
+
count: number;
|
|
210
|
+
latest: Notification;
|
|
211
|
+
}
|
|
212
|
+
interface Digest {
|
|
213
|
+
total: number;
|
|
214
|
+
groups: DigestGroup[];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export { type Actor as A, type Channel as C, type Digest as D, type Notification as N, type Preferences as P, type Recipient as R, type SendInput as S, type ResourceRef as a, CHANNELS as b, DEFAULT_CHANNELS as c, type DigestGroup as d, defaultPreferences as e, recipientKey as r };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { N as Notification } from './types-C842YAMe.js';
|
|
2
|
+
|
|
3
|
+
/** A delivery channel other than the inbox. */
|
|
4
|
+
interface DeliveryChannel {
|
|
5
|
+
/** Resolves "skipped" when the recipient has no target (no endpoint, no address). Throws on failure. */
|
|
6
|
+
deliver(notification: Notification): Promise<"sent" | "skipped">;
|
|
7
|
+
}
|
|
8
|
+
declare class DeliveryError extends Error {
|
|
9
|
+
readonly attempts: number;
|
|
10
|
+
readonly code = "DELIVERY_FAILED";
|
|
11
|
+
constructor(message: string, attempts: number, options?: {
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** Logger that takes ids and counts only. Never pass content or addresses. */
|
|
16
|
+
interface Logger {
|
|
17
|
+
warn(message: string, fields?: Record<string, string | number>): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { type DeliveryChannel as D, type Logger as L, DeliveryError as a };
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { c as Notifier, e as createDispatcher } from './dispatch-LBnkLOQg.js';
|
|
3
|
+
import { R as Recipient } from './types-C842YAMe.js';
|
|
4
|
+
import { D1Like } from 'agentic-identity/cloudflare';
|
|
5
|
+
import './gdpr-CUq5MUwo.js';
|
|
6
|
+
import 'agentic-identity';
|
|
7
|
+
import './types-DONKNIOo.js';
|
|
8
|
+
|
|
9
|
+
/** Who is calling. `kind` has no default: a missing kind is an error. */
|
|
10
|
+
declare const principalSchema: z.ZodObject<{
|
|
11
|
+
kind: z.ZodEnum<["human", "agent"]>;
|
|
12
|
+
id: z.ZodString;
|
|
13
|
+
/** Allowed to send notifications to others. */
|
|
14
|
+
canSend: z.ZodOptional<z.ZodBoolean>;
|
|
15
|
+
}, "strict", z.ZodTypeAny, {
|
|
16
|
+
kind: "human" | "agent";
|
|
17
|
+
id: string;
|
|
18
|
+
canSend?: boolean | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
kind: "human" | "agent";
|
|
21
|
+
id: string;
|
|
22
|
+
canSend?: boolean | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
type Principal = z.infer<typeof principalSchema>;
|
|
25
|
+
/** Port: turn a request into a principal, or null for "not signed in". Plug in any identity system. */
|
|
26
|
+
type PrincipalResolver = (request: Request) => Promise<Principal | null> | Principal | null;
|
|
27
|
+
|
|
28
|
+
interface AppOptions {
|
|
29
|
+
notifier: Notifier;
|
|
30
|
+
/** When set, sends also go to webhook and email channels. */
|
|
31
|
+
dispatcher?: ReturnType<typeof createDispatcher>;
|
|
32
|
+
resolvePrincipal: PrincipalResolver;
|
|
33
|
+
/** May this principal send to this recipient? Default: only principals with `canSend: true`. */
|
|
34
|
+
authorizeSend?: (principal: Principal, recipient: Recipient) => Promise<boolean> | boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface WorkerEnv {
|
|
38
|
+
NOTIFY_NAME?: string;
|
|
39
|
+
/** D1 binding. */
|
|
40
|
+
DB?: D1Like;
|
|
41
|
+
/** Secret. JSON map of bearer token (32 bytes or more) to principal. */
|
|
42
|
+
NOTIFY_TOKENS?: string;
|
|
43
|
+
/** Data region of the store: "eu", "us" or "unknown". Default "eu". */
|
|
44
|
+
NOTIFY_REGION?: string;
|
|
45
|
+
/** Retention window in days. Unset keeps notifications. */
|
|
46
|
+
NOTIFY_RETENTION_DAYS?: string;
|
|
47
|
+
}
|
|
48
|
+
interface WorkerConfig {
|
|
49
|
+
/** Build the principal resolver from the environment. Null means not configured. */
|
|
50
|
+
resolver(env: WorkerEnv): Promise<PrincipalResolver | null>;
|
|
51
|
+
authorizeSend?: AppOptions["authorizeSend"];
|
|
52
|
+
}
|
|
53
|
+
/** Compose the Worker. Bring your own identity system through `resolver`. */
|
|
54
|
+
declare function createWorker(config: WorkerConfig): {
|
|
55
|
+
fetch(request: Request, env: WorkerEnv): Promise<Response>;
|
|
56
|
+
/** Delete notifications past the retention window. Wire to a cron trigger. */
|
|
57
|
+
scheduled(_event: unknown, env: WorkerEnv): Promise<void>;
|
|
58
|
+
};
|
|
59
|
+
declare const _default: {
|
|
60
|
+
fetch(request: Request, env: WorkerEnv): Promise<Response>;
|
|
61
|
+
/** Delete notifications past the retention window. Wire to a cron trigger. */
|
|
62
|
+
scheduled(_event: unknown, env: WorkerEnv): Promise<void>;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export { type WorkerConfig, type WorkerEnv, createWorker, _default as default };
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NOTES,
|
|
3
|
+
NotFoundError,
|
|
4
|
+
VERSION,
|
|
5
|
+
createNotifier,
|
|
6
|
+
preferencesSchema,
|
|
7
|
+
recipientSchema,
|
|
8
|
+
resourceRefSchema,
|
|
9
|
+
typeNameSchema
|
|
10
|
+
} from "./chunk-6QCYA3X7.js";
|
|
11
|
+
|
|
12
|
+
// src/api.ts
|
|
13
|
+
import { Hono } from "hono";
|
|
14
|
+
import { bodyLimit } from "hono/body-limit";
|
|
15
|
+
import { ZodError, z as z2 } from "zod";
|
|
16
|
+
|
|
17
|
+
// src/auth.ts
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
var principalSchema = z.object({
|
|
20
|
+
kind: z.enum(["human", "agent"]),
|
|
21
|
+
id: z.string().min(1).max(200),
|
|
22
|
+
/** Allowed to send notifications to others. */
|
|
23
|
+
canSend: z.boolean().optional()
|
|
24
|
+
}).strict();
|
|
25
|
+
var MIN_TOKEN_BYTES = 32;
|
|
26
|
+
var enc = new TextEncoder();
|
|
27
|
+
async function digest(token) {
|
|
28
|
+
const d = await crypto.subtle.digest("SHA-256", enc.encode(token));
|
|
29
|
+
return [...new Uint8Array(d)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
30
|
+
}
|
|
31
|
+
async function createStaticTokenResolver(tokens) {
|
|
32
|
+
const byDigest = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const [token, principal] of Object.entries(tokens)) {
|
|
34
|
+
if (enc.encode(token).length < MIN_TOKEN_BYTES) throw new Error(`tokens must hold at least ${MIN_TOKEN_BYTES} bytes`);
|
|
35
|
+
byDigest.set(await digest(token), principalSchema.parse(principal));
|
|
36
|
+
}
|
|
37
|
+
if (byDigest.size === 0) throw new Error("no tokens configured");
|
|
38
|
+
return async (request) => {
|
|
39
|
+
const m = /^Bearer ([^\s]+)$/.exec(request.headers.get("authorization") ?? "");
|
|
40
|
+
return m ? byDigest.get(await digest(m[1])) ?? null : null;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/api.ts
|
|
45
|
+
var sendBody = z2.object({ recipient: recipientSchema, type: typeNameSchema, resource: resourceRefSchema, body: z2.string().max(2e3) }).strict();
|
|
46
|
+
var num = z2.coerce.number().int().nonnegative();
|
|
47
|
+
function createApp(options) {
|
|
48
|
+
const { notifier } = options;
|
|
49
|
+
const authorizeSend = options.authorizeSend ?? ((p) => p.canSend === true);
|
|
50
|
+
const app = new Hono();
|
|
51
|
+
app.get("/health", (c) => c.json({ ok: true }));
|
|
52
|
+
const api = new Hono();
|
|
53
|
+
api.use("*", bodyLimit({ maxSize: 64 * 1024, onError: (c) => c.json({ error: "too_large" }, 413) }));
|
|
54
|
+
api.use("*", async (c, next) => {
|
|
55
|
+
const raw = await options.resolvePrincipal(c.req.raw);
|
|
56
|
+
const parsed = principalSchema.safeParse(raw);
|
|
57
|
+
if (!parsed.success) return c.json({ error: "unauthorized" }, 401);
|
|
58
|
+
c.set("principal", parsed.data);
|
|
59
|
+
c.set("me", { kind: parsed.data.kind, id: parsed.data.id });
|
|
60
|
+
await next();
|
|
61
|
+
});
|
|
62
|
+
async function json2(c) {
|
|
63
|
+
try {
|
|
64
|
+
return await c.req.json();
|
|
65
|
+
} catch {
|
|
66
|
+
throw new ZodError([{ code: "custom", path: [], message: "body must be JSON" }]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
api.get("/notifications", async (c) => {
|
|
70
|
+
const q = c.req.query();
|
|
71
|
+
const limit = q.limit === void 0 ? void 0 : z2.coerce.number().int().min(1).parse(q.limit);
|
|
72
|
+
try {
|
|
73
|
+
return c.json(await notifier.list(c.var.me, { unreadOnly: q.unread === "1" || q.unread === "true", limit, cursor: q.cursor }));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error instanceof RangeError) throw new ZodError([{ code: "custom", path: ["cursor"], message: "bad cursor" }]);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
api.post("/notifications", async (c) => {
|
|
80
|
+
const input = sendBody.parse(await json2(c));
|
|
81
|
+
if (!await authorizeSend(c.var.principal, input.recipient)) return c.json({ error: "forbidden" }, 403);
|
|
82
|
+
const full = { ...input, actor: c.var.me };
|
|
83
|
+
if (options.dispatcher) return c.json(await options.dispatcher.notify(full), 201);
|
|
84
|
+
const sent = await notifier.send(full);
|
|
85
|
+
const deliveries = sent.channels.filter((channel) => channel !== "inbox").map((channel) => ({ channel, ok: false, skipped: "no_channel" }));
|
|
86
|
+
return c.json({ ...sent, deliveries }, 201);
|
|
87
|
+
});
|
|
88
|
+
api.post("/notifications/read-all", async (c) => c.json({ marked: await notifier.markAllRead(c.var.me) }));
|
|
89
|
+
api.get("/notifications/:id", async (c) => c.json(await notifier.get(c.var.me, c.req.param("id"))));
|
|
90
|
+
api.post("/notifications/:id/read", async (c) => c.json(await notifier.markRead(c.var.me, c.req.param("id"))));
|
|
91
|
+
api.get("/unread-count", async (c) => c.json({ count: await notifier.unreadCount(c.var.me) }));
|
|
92
|
+
api.get("/digest", async (c) => {
|
|
93
|
+
const since = c.req.query("since");
|
|
94
|
+
return c.json(await notifier.digest(c.var.me, since === void 0 ? 0 : num.parse(since)));
|
|
95
|
+
});
|
|
96
|
+
api.get("/preferences", async (c) => c.json(await notifier.getPreferences(c.var.me)));
|
|
97
|
+
api.put("/preferences", async (c) => c.json(await notifier.setPreferences(c.var.me, preferencesSchema.parse(await json2(c)))));
|
|
98
|
+
api.get("/me/export", async (c) => c.json(await notifier.exportData(c.var.me)));
|
|
99
|
+
api.delete("/me", async (c) => c.json(await notifier.erase(c.var.me)));
|
|
100
|
+
app.route("/api/v1", api);
|
|
101
|
+
app.notFound((c) => c.json({ error: "not_found" }, 404));
|
|
102
|
+
app.onError((error, c) => {
|
|
103
|
+
if (error instanceof NotFoundError) return c.json({ error: "not_found" }, 404);
|
|
104
|
+
if (error instanceof ZodError) {
|
|
105
|
+
return c.json({ error: "invalid_request", issues: error.issues.map((i) => ({ path: i.path.join("."), message: i.message })) }, 400);
|
|
106
|
+
}
|
|
107
|
+
console.error("internal error", error instanceof Error ? error.name : "unknown");
|
|
108
|
+
return c.json({ error: "internal_error" }, 500);
|
|
109
|
+
});
|
|
110
|
+
return app;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/worker.ts
|
|
114
|
+
import { parseRegion } from "agentic-identity";
|
|
115
|
+
import { createD1Store, D1_SCHEMA } from "agentic-identity/cloudflare";
|
|
116
|
+
function json(value, status = 200) {
|
|
117
|
+
return new Response(JSON.stringify(value), { status, headers: { "content-type": "application/json" } });
|
|
118
|
+
}
|
|
119
|
+
async function hasLegacyRows(db) {
|
|
120
|
+
const table = await db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'notify_docs'").bind().first();
|
|
121
|
+
if (!table) return false;
|
|
122
|
+
return await db.prepare("SELECT 1 AS found FROM notify_docs LIMIT 1").bind().first() !== null;
|
|
123
|
+
}
|
|
124
|
+
function createWorker(config) {
|
|
125
|
+
const apps = /* @__PURE__ */ new WeakMap();
|
|
126
|
+
async function buildNotifier(env) {
|
|
127
|
+
if (!env.DB) return null;
|
|
128
|
+
if (await hasLegacyRows(env.DB)) throw new Error("notify_docs holds rows that were not copied to store_docs");
|
|
129
|
+
await env.DB.prepare(D1_SCHEMA).bind().run();
|
|
130
|
+
const days = env.NOTIFY_RETENTION_DAYS === void 0 ? void 0 : Number(env.NOTIFY_RETENTION_DAYS);
|
|
131
|
+
const store = createD1Store(env.DB, { region: parseRegion(env.NOTIFY_REGION ?? "eu") });
|
|
132
|
+
await store.query(NOTES, { limit: 1 });
|
|
133
|
+
return createNotifier({
|
|
134
|
+
store,
|
|
135
|
+
retentionMs: days === void 0 ? void 0 : days * 864e5
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async function build(env) {
|
|
139
|
+
const resolvePrincipal = await config.resolver(env);
|
|
140
|
+
const notifier = resolvePrincipal ? await buildNotifier(env) : null;
|
|
141
|
+
if (!resolvePrincipal || !notifier) return null;
|
|
142
|
+
return createApp({ notifier, resolvePrincipal, authorizeSend: config.authorizeSend });
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
async fetch(request, env) {
|
|
146
|
+
const url = new URL(request.url);
|
|
147
|
+
if ((request.method === "GET" || request.method === "HEAD") && url.pathname === "/health") {
|
|
148
|
+
return json({ ok: true, name: env.NOTIFY_NAME ?? "agentic-notify", version: VERSION });
|
|
149
|
+
}
|
|
150
|
+
if (!url.pathname.startsWith("/api/")) return json({ error: "not_found" }, 404);
|
|
151
|
+
let app;
|
|
152
|
+
try {
|
|
153
|
+
let p = apps.get(env);
|
|
154
|
+
if (!p) apps.set(env, p = build(env));
|
|
155
|
+
app = await p;
|
|
156
|
+
} catch {
|
|
157
|
+
apps.delete(env);
|
|
158
|
+
return json({ error: "not_configured" }, 503);
|
|
159
|
+
}
|
|
160
|
+
if (!app) return json({ error: "not_configured" }, 503);
|
|
161
|
+
return app.fetch(request);
|
|
162
|
+
},
|
|
163
|
+
/** Delete notifications past the retention window. Wire to a cron trigger. */
|
|
164
|
+
async scheduled(_event, env) {
|
|
165
|
+
await (await buildNotifier(env))?.sweep();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
var worker_default = createWorker({
|
|
170
|
+
async resolver(env) {
|
|
171
|
+
if (!env.NOTIFY_TOKENS) return null;
|
|
172
|
+
return createStaticTokenResolver(JSON.parse(env.NOTIFY_TOKENS));
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
export {
|
|
176
|
+
createWorker,
|
|
177
|
+
worker_default as default
|
|
178
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,90 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-notify",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Notifications for humans and agents: in-app inbox, webhook and email delivery, with SDK, CLI, MCP server and Worker API.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"notifications",
|
|
7
|
+
"inbox",
|
|
8
|
+
"webhook",
|
|
9
|
+
"agents",
|
|
10
|
+
"mcp",
|
|
11
|
+
"cli",
|
|
12
|
+
"cloudflare-workers",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Pooria Arab",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/pooriaarab/agentic-notify.git"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"private": false,
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"mcpName": "io.github.pooriaarab/agentic-notify",
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./testing": {
|
|
41
|
+
"types": "./dist/testing.d.ts",
|
|
42
|
+
"import": "./dist/testing.js"
|
|
43
|
+
},
|
|
44
|
+
"./sdk": {
|
|
45
|
+
"types": "./dist/sdk.d.ts",
|
|
46
|
+
"import": "./dist/sdk.js"
|
|
47
|
+
},
|
|
48
|
+
"./worker": {
|
|
49
|
+
"types": "./dist/worker.d.ts",
|
|
50
|
+
"import": "./dist/worker.js"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"bin": {
|
|
54
|
+
"agentic-notify": "./dist/cli.js",
|
|
55
|
+
"agentic-notify-mcp": "./dist/mcp-bin.js"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsup",
|
|
59
|
+
"typecheck": "tsc --noEmit",
|
|
60
|
+
"test": "vitest run",
|
|
61
|
+
"clean": "rm -rf dist",
|
|
62
|
+
"prepack": "npm run build",
|
|
63
|
+
"ci:local": "npm run build && npm run typecheck && npm run check:worker && npm test",
|
|
64
|
+
"deploy:demo": "wrangler deploy --config wrangler.demo.jsonc",
|
|
65
|
+
"check:worker": "esbuild src/worker.ts --bundle --platform=neutral --format=esm --log-level=warning --outfile=node_modules/.cache/worker-check.js"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
69
|
+
"agentic-identity": "^0.4.0",
|
|
70
|
+
"hono": "^4.13.14",
|
|
71
|
+
"zod": "^3.25.0"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@types/node": "^22.0.0",
|
|
75
|
+
"esbuild": "^0.28.2",
|
|
76
|
+
"miniflare": "^4.20260730.0",
|
|
77
|
+
"tsup": "^8.0.0",
|
|
78
|
+
"typescript": "^5.6.0",
|
|
79
|
+
"vitest": "^3.0.0",
|
|
80
|
+
"wrangler": "^4.155.0"
|
|
81
|
+
},
|
|
82
|
+
"peerDependencies": {
|
|
83
|
+
"vitest": ">=3"
|
|
84
|
+
},
|
|
85
|
+
"peerDependenciesMeta": {
|
|
86
|
+
"vitest": {
|
|
87
|
+
"optional": true
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|