@agentlensai/server 0.3.0 → 0.6.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.
Files changed (259) hide show
  1. package/dist/db/embedding-store.d.ts +74 -0
  2. package/dist/db/embedding-store.d.ts.map +1 -0
  3. package/dist/db/embedding-store.js +177 -0
  4. package/dist/db/embedding-store.js.map +1 -0
  5. package/dist/db/health-snapshot-store.d.ts +33 -0
  6. package/dist/db/health-snapshot-store.d.ts.map +1 -0
  7. package/dist/db/health-snapshot-store.js +112 -0
  8. package/dist/db/health-snapshot-store.js.map +1 -0
  9. package/dist/db/lesson-store.d.ts +57 -0
  10. package/dist/db/lesson-store.d.ts.map +1 -0
  11. package/dist/db/lesson-store.js +217 -0
  12. package/dist/db/lesson-store.js.map +1 -0
  13. package/dist/db/migrate.d.ts.map +1 -1
  14. package/dist/db/migrate.js +256 -8
  15. package/dist/db/migrate.js.map +1 -1
  16. package/dist/db/schema.sqlite.d.ts +822 -47
  17. package/dist/db/schema.sqlite.d.ts.map +1 -1
  18. package/dist/db/schema.sqlite.js +79 -4
  19. package/dist/db/schema.sqlite.js.map +1 -1
  20. package/dist/db/session-summary-store.d.ts +45 -0
  21. package/dist/db/session-summary-store.d.ts.map +1 -0
  22. package/dist/db/session-summary-store.js +112 -0
  23. package/dist/db/session-summary-store.js.map +1 -0
  24. package/dist/db/sqlite-store.d.ts +19 -12
  25. package/dist/db/sqlite-store.d.ts.map +1 -1
  26. package/dist/db/sqlite-store.js +145 -44
  27. package/dist/db/sqlite-store.js.map +1 -1
  28. package/dist/db/tenant-scoped-store.d.ts +61 -0
  29. package/dist/db/tenant-scoped-store.d.ts.map +1 -0
  30. package/dist/db/tenant-scoped-store.js +94 -0
  31. package/dist/db/tenant-scoped-store.js.map +1 -0
  32. package/dist/index.d.ts +18 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +78 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/lib/alert-engine.d.ts +11 -0
  37. package/dist/lib/alert-engine.d.ts.map +1 -1
  38. package/dist/lib/alert-engine.js +65 -24
  39. package/dist/lib/alert-engine.js.map +1 -1
  40. package/dist/lib/analysis/cost-analysis.d.ts +20 -0
  41. package/dist/lib/analysis/cost-analysis.d.ts.map +1 -0
  42. package/dist/lib/analysis/cost-analysis.js +161 -0
  43. package/dist/lib/analysis/cost-analysis.js.map +1 -0
  44. package/dist/lib/analysis/error-patterns.d.ts +26 -0
  45. package/dist/lib/analysis/error-patterns.d.ts.map +1 -0
  46. package/dist/lib/analysis/error-patterns.js +158 -0
  47. package/dist/lib/analysis/error-patterns.js.map +1 -0
  48. package/dist/lib/analysis/index.d.ts +23 -0
  49. package/dist/lib/analysis/index.d.ts.map +1 -0
  50. package/dist/lib/analysis/index.js +144 -0
  51. package/dist/lib/analysis/index.js.map +1 -0
  52. package/dist/lib/analysis/performance-trends.d.ts +19 -0
  53. package/dist/lib/analysis/performance-trends.d.ts.map +1 -0
  54. package/dist/lib/analysis/performance-trends.js +121 -0
  55. package/dist/lib/analysis/performance-trends.js.map +1 -0
  56. package/dist/lib/analysis/tool-sequences.d.ts +19 -0
  57. package/dist/lib/analysis/tool-sequences.d.ts.map +1 -0
  58. package/dist/lib/analysis/tool-sequences.js +148 -0
  59. package/dist/lib/analysis/tool-sequences.js.map +1 -0
  60. package/dist/lib/context/index.d.ts +5 -0
  61. package/dist/lib/context/index.d.ts.map +1 -0
  62. package/dist/lib/context/index.js +5 -0
  63. package/dist/lib/context/index.js.map +1 -0
  64. package/dist/lib/context/retrieval.d.ts +60 -0
  65. package/dist/lib/context/retrieval.d.ts.map +1 -0
  66. package/dist/lib/context/retrieval.js +233 -0
  67. package/dist/lib/context/retrieval.js.map +1 -0
  68. package/dist/lib/embeddings/index.d.ts +31 -0
  69. package/dist/lib/embeddings/index.d.ts.map +1 -0
  70. package/dist/lib/embeddings/index.js +31 -0
  71. package/dist/lib/embeddings/index.js.map +1 -0
  72. package/dist/lib/embeddings/local.d.ts +15 -0
  73. package/dist/lib/embeddings/local.d.ts.map +1 -0
  74. package/dist/lib/embeddings/local.js +65 -0
  75. package/dist/lib/embeddings/local.js.map +1 -0
  76. package/dist/lib/embeddings/math.d.ts +13 -0
  77. package/dist/lib/embeddings/math.d.ts.map +1 -0
  78. package/dist/lib/embeddings/math.js +35 -0
  79. package/dist/lib/embeddings/math.js.map +1 -0
  80. package/dist/lib/embeddings/openai.d.ts +15 -0
  81. package/dist/lib/embeddings/openai.d.ts.map +1 -0
  82. package/dist/lib/embeddings/openai.js +58 -0
  83. package/dist/lib/embeddings/openai.js.map +1 -0
  84. package/dist/lib/embeddings/summarizer.d.ts +26 -0
  85. package/dist/lib/embeddings/summarizer.d.ts.map +1 -0
  86. package/dist/lib/embeddings/summarizer.js +181 -0
  87. package/dist/lib/embeddings/summarizer.js.map +1 -0
  88. package/dist/lib/embeddings/types.d.ts +17 -0
  89. package/dist/lib/embeddings/types.d.ts.map +1 -0
  90. package/dist/lib/embeddings/types.js +5 -0
  91. package/dist/lib/embeddings/types.js.map +1 -0
  92. package/dist/lib/embeddings/worker.d.ts +56 -0
  93. package/dist/lib/embeddings/worker.d.ts.map +1 -0
  94. package/dist/lib/embeddings/worker.js +109 -0
  95. package/dist/lib/embeddings/worker.js.map +1 -0
  96. package/dist/lib/health/computer.d.ts +28 -0
  97. package/dist/lib/health/computer.d.ts.map +1 -0
  98. package/dist/lib/health/computer.js +270 -0
  99. package/dist/lib/health/computer.js.map +1 -0
  100. package/dist/lib/optimization/classifier.d.ts +34 -0
  101. package/dist/lib/optimization/classifier.d.ts.map +1 -0
  102. package/dist/lib/optimization/classifier.js +108 -0
  103. package/dist/lib/optimization/classifier.js.map +1 -0
  104. package/dist/lib/optimization/engine.d.ts +24 -0
  105. package/dist/lib/optimization/engine.d.ts.map +1 -0
  106. package/dist/lib/optimization/engine.js +202 -0
  107. package/dist/lib/optimization/engine.js.map +1 -0
  108. package/dist/lib/optimization/index.d.ts +10 -0
  109. package/dist/lib/optimization/index.d.ts.map +1 -0
  110. package/dist/lib/optimization/index.js +9 -0
  111. package/dist/lib/optimization/index.js.map +1 -0
  112. package/dist/lib/sse.d.ts +1 -0
  113. package/dist/lib/sse.d.ts.map +1 -1
  114. package/dist/lib/sse.js +8 -1
  115. package/dist/lib/sse.js.map +1 -1
  116. package/dist/middleware/auth.d.ts +1 -0
  117. package/dist/middleware/auth.d.ts.map +1 -1
  118. package/dist/middleware/auth.js +2 -1
  119. package/dist/middleware/auth.js.map +1 -1
  120. package/dist/routes/agents.d.ts.map +1 -1
  121. package/dist/routes/agents.js +6 -3
  122. package/dist/routes/agents.js.map +1 -1
  123. package/dist/routes/alerts.d.ts.map +1 -1
  124. package/dist/routes/alerts.js +15 -7
  125. package/dist/routes/alerts.js.map +1 -1
  126. package/dist/routes/analytics.d.ts.map +1 -1
  127. package/dist/routes/analytics.js +16 -2
  128. package/dist/routes/analytics.js.map +1 -1
  129. package/dist/routes/api-keys.d.ts.map +1 -1
  130. package/dist/routes/api-keys.js +30 -5
  131. package/dist/routes/api-keys.js.map +1 -1
  132. package/dist/routes/context.d.ts +23 -0
  133. package/dist/routes/context.d.ts.map +1 -0
  134. package/dist/routes/context.js +52 -0
  135. package/dist/routes/context.js.map +1 -0
  136. package/dist/routes/events.d.ts +6 -1
  137. package/dist/routes/events.d.ts.map +1 -1
  138. package/dist/routes/events.js +61 -6
  139. package/dist/routes/events.js.map +1 -1
  140. package/dist/routes/health.d.ts +21 -0
  141. package/dist/routes/health.d.ts.map +1 -0
  142. package/dist/routes/health.js +142 -0
  143. package/dist/routes/health.js.map +1 -0
  144. package/dist/routes/ingest.d.ts +6 -0
  145. package/dist/routes/ingest.d.ts.map +1 -1
  146. package/dist/routes/ingest.js +23 -4
  147. package/dist/routes/ingest.js.map +1 -1
  148. package/dist/routes/lessons.d.ts +19 -0
  149. package/dist/routes/lessons.d.ts.map +1 -0
  150. package/dist/routes/lessons.js +164 -0
  151. package/dist/routes/lessons.js.map +1 -0
  152. package/dist/routes/optimize.d.ts +15 -0
  153. package/dist/routes/optimize.d.ts.map +1 -0
  154. package/dist/routes/optimize.js +55 -0
  155. package/dist/routes/optimize.js.map +1 -0
  156. package/dist/routes/recall.d.ts +22 -0
  157. package/dist/routes/recall.d.ts.map +1 -0
  158. package/dist/routes/recall.js +91 -0
  159. package/dist/routes/recall.js.map +1 -0
  160. package/dist/routes/reflect.d.ts +15 -0
  161. package/dist/routes/reflect.d.ts.map +1 -0
  162. package/dist/routes/reflect.js +54 -0
  163. package/dist/routes/reflect.js.map +1 -0
  164. package/dist/routes/sessions.d.ts.map +1 -1
  165. package/dist/routes/sessions.js +8 -6
  166. package/dist/routes/sessions.js.map +1 -1
  167. package/dist/routes/stats.d.ts.map +1 -1
  168. package/dist/routes/stats.js +3 -1
  169. package/dist/routes/stats.js.map +1 -1
  170. package/dist/routes/stream.d.ts +9 -2
  171. package/dist/routes/stream.d.ts.map +1 -1
  172. package/dist/routes/stream.js +55 -2
  173. package/dist/routes/stream.js.map +1 -1
  174. package/dist/routes/tenant-helper.d.ts +20 -0
  175. package/dist/routes/tenant-helper.d.ts.map +1 -0
  176. package/dist/routes/tenant-helper.js +35 -0
  177. package/dist/routes/tenant-helper.js.map +1 -0
  178. package/package.json +11 -11
  179. package/LICENSE +0 -21
  180. package/dist/__tests__/agents-stats.test.d.ts +0 -5
  181. package/dist/__tests__/agents-stats.test.d.ts.map +0 -1
  182. package/dist/__tests__/agents-stats.test.js +0 -134
  183. package/dist/__tests__/agents-stats.test.js.map +0 -1
  184. package/dist/__tests__/alerts.test.d.ts +0 -5
  185. package/dist/__tests__/alerts.test.d.ts.map +0 -1
  186. package/dist/__tests__/alerts.test.js +0 -245
  187. package/dist/__tests__/alerts.test.js.map +0 -1
  188. package/dist/__tests__/analytics.test.d.ts +0 -5
  189. package/dist/__tests__/analytics.test.d.ts.map +0 -1
  190. package/dist/__tests__/analytics.test.js +0 -218
  191. package/dist/__tests__/analytics.test.js.map +0 -1
  192. package/dist/__tests__/api-keys.test.d.ts +0 -5
  193. package/dist/__tests__/api-keys.test.d.ts.map +0 -1
  194. package/dist/__tests__/api-keys.test.js +0 -118
  195. package/dist/__tests__/api-keys.test.js.map +0 -1
  196. package/dist/__tests__/auth-no-db.test.d.ts +0 -2
  197. package/dist/__tests__/auth-no-db.test.d.ts.map +0 -1
  198. package/dist/__tests__/auth-no-db.test.js +0 -43
  199. package/dist/__tests__/auth-no-db.test.js.map +0 -1
  200. package/dist/__tests__/auth.test.d.ts +0 -5
  201. package/dist/__tests__/auth.test.d.ts.map +0 -1
  202. package/dist/__tests__/auth.test.js +0 -86
  203. package/dist/__tests__/auth.test.js.map +0 -1
  204. package/dist/__tests__/config.test.d.ts +0 -2
  205. package/dist/__tests__/config.test.d.ts.map +0 -1
  206. package/dist/__tests__/config.test.js +0 -37
  207. package/dist/__tests__/config.test.js.map +0 -1
  208. package/dist/__tests__/events-ingest.test.d.ts +0 -5
  209. package/dist/__tests__/events-ingest.test.d.ts.map +0 -1
  210. package/dist/__tests__/events-ingest.test.js +0 -248
  211. package/dist/__tests__/events-ingest.test.js.map +0 -1
  212. package/dist/__tests__/events-query.test.d.ts +0 -5
  213. package/dist/__tests__/events-query.test.d.ts.map +0 -1
  214. package/dist/__tests__/events-query.test.js +0 -205
  215. package/dist/__tests__/events-query.test.js.map +0 -1
  216. package/dist/__tests__/health.test.d.ts +0 -5
  217. package/dist/__tests__/health.test.d.ts.map +0 -1
  218. package/dist/__tests__/health.test.js +0 -40
  219. package/dist/__tests__/health.test.js.map +0 -1
  220. package/dist/__tests__/ingest.test.d.ts +0 -8
  221. package/dist/__tests__/ingest.test.d.ts.map +0 -1
  222. package/dist/__tests__/ingest.test.js +0 -469
  223. package/dist/__tests__/ingest.test.js.map +0 -1
  224. package/dist/__tests__/llm-tracking.test.d.ts +0 -10
  225. package/dist/__tests__/llm-tracking.test.d.ts.map +0 -1
  226. package/dist/__tests__/llm-tracking.test.js +0 -602
  227. package/dist/__tests__/llm-tracking.test.js.map +0 -1
  228. package/dist/__tests__/sessions.test.d.ts +0 -5
  229. package/dist/__tests__/sessions.test.d.ts.map +0 -1
  230. package/dist/__tests__/sessions.test.js +0 -176
  231. package/dist/__tests__/sessions.test.js.map +0 -1
  232. package/dist/__tests__/stream.test.d.ts +0 -5
  233. package/dist/__tests__/stream.test.d.ts.map +0 -1
  234. package/dist/__tests__/stream.test.js +0 -352
  235. package/dist/__tests__/stream.test.js.map +0 -1
  236. package/dist/__tests__/test-helpers.d.ts +0 -24
  237. package/dist/__tests__/test-helpers.d.ts.map +0 -1
  238. package/dist/__tests__/test-helpers.js +0 -45
  239. package/dist/__tests__/test-helpers.js.map +0 -1
  240. package/dist/db/__tests__/init.test.d.ts +0 -2
  241. package/dist/db/__tests__/init.test.d.ts.map +0 -1
  242. package/dist/db/__tests__/init.test.js +0 -73
  243. package/dist/db/__tests__/init.test.js.map +0 -1
  244. package/dist/db/__tests__/sqlite-store-read.test.d.ts +0 -2
  245. package/dist/db/__tests__/sqlite-store-read.test.d.ts.map +0 -1
  246. package/dist/db/__tests__/sqlite-store-read.test.js +0 -749
  247. package/dist/db/__tests__/sqlite-store-read.test.js.map +0 -1
  248. package/dist/db/__tests__/sqlite-store-write.test.d.ts +0 -2
  249. package/dist/db/__tests__/sqlite-store-write.test.d.ts.map +0 -1
  250. package/dist/db/__tests__/sqlite-store-write.test.js +0 -418
  251. package/dist/db/__tests__/sqlite-store-write.test.js.map +0 -1
  252. package/dist/lib/__tests__/alert-engine.test.d.ts +0 -5
  253. package/dist/lib/__tests__/alert-engine.test.d.ts.map +0 -1
  254. package/dist/lib/__tests__/alert-engine.test.js +0 -211
  255. package/dist/lib/__tests__/alert-engine.test.js.map +0 -1
  256. package/dist/lib/__tests__/retention.test.d.ts +0 -2
  257. package/dist/lib/__tests__/retention.test.d.ts.map +0 -1
  258. package/dist/lib/__tests__/retention.test.js +0 -238
  259. package/dist/lib/__tests__/retention.test.js.map +0 -1
@@ -3,17 +3,68 @@
3
3
  *
4
4
  * GET /api/stream — Server-Sent Events endpoint for real-time dashboard updates.
5
5
  *
6
+ * Authentication:
7
+ * - Bearer token via Authorization header, OR
8
+ * - ?token=als_xxx query param (for EventSource compatibility — browsers can't set headers)
9
+ * - In dev mode (authDisabled), authentication is skipped
10
+ *
6
11
  * Query params:
12
+ * token? — API key for auth (EventSource compat)
7
13
  * sessionId? — filter to a specific session
8
14
  * agentId? — filter to a specific agent
9
15
  * eventType? — filter by event type (comma-separated)
10
16
  */
11
17
  import { Hono } from 'hono';
18
+ import { apiKeys } from '../db/schema.sqlite.js';
19
+ import { eq, and, isNull } from 'drizzle-orm';
20
+ import { hashApiKey } from '../middleware/auth.js';
12
21
  import { createSSEStream } from '../lib/sse.js';
13
- export function streamRoutes() {
22
+ export function streamRoutes(db, authDisabled) {
14
23
  const app = new Hono();
15
24
  app.get('/', (c) => {
16
- // Parse filter params
25
+ // ─── Authenticate ────────────────────────────────────
26
+ let apiKeyInfo;
27
+ if (authDisabled) {
28
+ // Dev mode: allow unauthenticated access with default tenant
29
+ apiKeyInfo = { id: 'dev', name: 'dev-mode', scopes: ['*'], tenantId: 'default' };
30
+ }
31
+ else if (db) {
32
+ // Try Authorization header first
33
+ const authHeader = c.req.header('Authorization');
34
+ const headerMatch = authHeader?.match(/^Bearer\s+(als_\w+)$/);
35
+ const rawKey = headerMatch?.[1] ?? c.req.query('token');
36
+ if (!rawKey || !rawKey.startsWith('als_')) {
37
+ return c.json({ error: 'Authentication required. Provide Bearer token or ?token=als_xxx query param.', status: 401 }, 401);
38
+ }
39
+ const keyHash = hashApiKey(rawKey);
40
+ const row = db
41
+ .select()
42
+ .from(apiKeys)
43
+ .where(and(eq(apiKeys.keyHash, keyHash), isNull(apiKeys.revokedAt)))
44
+ .get();
45
+ if (!row) {
46
+ return c.json({ error: 'Invalid or revoked API key', status: 401 }, 401);
47
+ }
48
+ const scopes = (() => {
49
+ try {
50
+ return JSON.parse(row.scopes);
51
+ }
52
+ catch {
53
+ return [];
54
+ }
55
+ })();
56
+ apiKeyInfo = {
57
+ id: row.id,
58
+ name: row.name,
59
+ scopes,
60
+ tenantId: row.tenantId,
61
+ };
62
+ }
63
+ else {
64
+ // No db and not dev mode — cannot authenticate
65
+ return c.json({ error: 'Authentication not available', status: 500 }, 500);
66
+ }
67
+ // ─── Parse filter params ─────────────────────────────
17
68
  const filters = {};
18
69
  const sessionId = c.req.query('sessionId');
19
70
  if (sessionId)
@@ -25,6 +76,8 @@ export function streamRoutes() {
25
76
  if (eventType) {
26
77
  filters.eventTypes = eventType.split(',').filter(Boolean);
27
78
  }
79
+ // Tenant filtering — ALWAYS from authenticated context, never from query param
80
+ filters.tenantId = apiKeyInfo.tenantId;
28
81
  // Create SSE stream
29
82
  const stream = createSSEStream(filters, c.req.raw.signal);
30
83
  return new Response(stream, {
@@ -1 +1 @@
1
- {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/routes/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,eAAe,EAAmB,MAAM,eAAe,CAAC;AAEjE,MAAM,UAAU,YAAY;IAC1B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAgC,CAAC;IAErD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;QACjB,sBAAsB;QACtB,MAAM,OAAO,GAAe,EAAE,CAAC;QAE/B,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;QAE7C,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,OAAO;YAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QAEvC,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC5D,CAAC;QAED,oBAAoB;QACpB,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAE1D,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE;YAC1B,OAAO,EAAE;gBACP,cAAc,EAAE,mBAAmB;gBACnC,eAAe,EAAE,UAAU;gBAC3B,UAAU,EAAE,YAAY;gBACxB,mBAAmB,EAAE,IAAI,EAAE,0BAA0B;aACtD;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/routes/stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Helper to create a TenantScopedStore from request context.
3
+ *
4
+ * If the store is a SqliteEventStore and the request has an API key
5
+ * with a tenantId, returns a TenantScopedStore. Otherwise returns
6
+ * the store as-is (backward compatibility for tests, etc.).
7
+ */
8
+ import type { Context } from 'hono';
9
+ import type { IEventStore } from '@agentlensai/core';
10
+ import type { AuthVariables } from '../middleware/auth.js';
11
+ /**
12
+ * Get a tenant-scoped store from the request context.
13
+ *
14
+ * For SqliteEventStore: wraps in TenantScopedStore with the tenant from auth.
15
+ * For other IEventStore impls (tests, mocks): returns as-is.
16
+ */
17
+ export declare function getTenantStore(store: IEventStore, c: Context<{
18
+ Variables: AuthVariables;
19
+ }>): IEventStore;
20
+ //# sourceMappingURL=tenant-helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-helper.d.ts","sourceRoot":"","sources":["../../src/routes/tenant-helper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI3D;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,WAAW,EAClB,CAAC,EAAE,OAAO,CAAC;IAAE,SAAS,EAAE,aAAa,CAAA;CAAE,CAAC,GACvC,WAAW,CAuBb"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Helper to create a TenantScopedStore from request context.
3
+ *
4
+ * If the store is a SqliteEventStore and the request has an API key
5
+ * with a tenantId, returns a TenantScopedStore. Otherwise returns
6
+ * the store as-is (backward compatibility for tests, etc.).
7
+ */
8
+ import { SqliteEventStore } from '../db/sqlite-store.js';
9
+ import { TenantScopedStore } from '../db/tenant-scoped-store.js';
10
+ /**
11
+ * Get a tenant-scoped store from the request context.
12
+ *
13
+ * For SqliteEventStore: wraps in TenantScopedStore with the tenant from auth.
14
+ * For other IEventStore impls (tests, mocks): returns as-is.
15
+ */
16
+ export function getTenantStore(store, c) {
17
+ const apiKeyInfo = c.get('apiKey');
18
+ if (apiKeyInfo?.tenantId && store instanceof SqliteEventStore) {
19
+ return new TenantScopedStore(store, apiKeyInfo.tenantId);
20
+ }
21
+ // If auth provided a tenantId but the store isn't SqliteEventStore,
22
+ // tenant isolation cannot be applied — warn or reject.
23
+ if (apiKeyInfo?.tenantId && !(store instanceof SqliteEventStore)) {
24
+ const isTest = process.env['NODE_ENV'] === 'test' || process.env['VITEST'];
25
+ if (isTest) {
26
+ console.warn(`[tenant-helper] Store is not SqliteEventStore; tenant isolation skipped for tenant "${apiKeyInfo.tenantId}".`);
27
+ }
28
+ else {
29
+ throw new Error(`Tenant isolation requires SqliteEventStore but got ${store.constructor.name}. ` +
30
+ `Cannot safely scope data for tenant "${apiKeyInfo.tenantId}".`);
31
+ }
32
+ }
33
+ return store;
34
+ }
35
+ //# sourceMappingURL=tenant-helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-helper.js","sourceRoot":"","sources":["../../src/routes/tenant-helper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAkB,EAClB,CAAwC;IAExC,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,UAAU,EAAE,QAAQ,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;QAC9D,OAAO,IAAI,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC3D,CAAC;IAED,oEAAoE;IACpE,uDAAuD;IACvD,IAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,CAAC,KAAK,YAAY,gBAAgB,CAAC,EAAE,CAAC;QACjE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3E,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CACV,uFAAuF,UAAU,CAAC,QAAQ,IAAI,CAC/G,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,sDAAsD,KAAK,CAAC,WAAW,CAAC,IAAI,IAAI;gBAC9E,wCAAwC,UAAU,CAAC,QAAQ,IAAI,CAClE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlensai/server",
3
- "version": "0.3.0",
3
+ "version": "0.6.0",
4
4
  "description": "AgentLens API server — event ingestion, querying, dashboard, and alerting for AI agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,25 +37,25 @@
37
37
  "files": [
38
38
  "dist"
39
39
  ],
40
+ "scripts": {
41
+ "build": "tsc",
42
+ "typecheck": "tsc --noEmit",
43
+ "test": "vitest run",
44
+ "test:watch": "vitest",
45
+ "dev": "tsx watch src/index.ts"
46
+ },
40
47
  "dependencies": {
48
+ "@agentlensai/core": "workspace:*",
41
49
  "drizzle-orm": "^0.44.2",
42
50
  "better-sqlite3": "^11.9.1",
43
51
  "hono": "^4.7.10",
44
52
  "@hono/node-server": "^1.14.1",
45
53
  "ulid": "^3.0.2",
46
- "zod": "^3.25.76",
47
- "@agentlensai/core": "0.3.0"
54
+ "zod": "^3.25.76"
48
55
  },
49
56
  "devDependencies": {
50
57
  "@types/better-sqlite3": "^7.6.13",
51
58
  "drizzle-kit": "^0.31.4",
52
59
  "vitest": "^3.2.3"
53
- },
54
- "scripts": {
55
- "build": "tsc",
56
- "typecheck": "tsc --noEmit",
57
- "test": "vitest run",
58
- "test:watch": "vitest",
59
- "dev": "tsx watch src/index.ts"
60
60
  }
61
- }
61
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Amit Paz
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,5 +0,0 @@
1
- /**
2
- * Tests for Story 4.7: Agent and Stats Endpoints
3
- */
4
- export {};
5
- //# sourceMappingURL=agents-stats.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agents-stats.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/agents-stats.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -1,134 +0,0 @@
1
- /**
2
- * Tests for Story 4.7: Agent and Stats Endpoints
3
- */
4
- import { describe, it, expect, beforeEach } from 'vitest';
5
- import { createTestApp, authHeaders } from './test-helpers.js';
6
- async function ingestEvents(app, apiKey, events) {
7
- return app.request('/api/events', {
8
- method: 'POST',
9
- headers: authHeaders(apiKey),
10
- body: JSON.stringify({ events }),
11
- });
12
- }
13
- describe('Agent Endpoints (Story 4.7)', () => {
14
- let app;
15
- let apiKey;
16
- beforeEach(async () => {
17
- const ctx = createTestApp();
18
- app = ctx.app;
19
- apiKey = ctx.apiKey;
20
- await ingestEvents(app, apiKey, [
21
- {
22
- sessionId: 'sess_001',
23
- agentId: 'agent_001',
24
- eventType: 'session_started',
25
- payload: { agentName: 'Agent One', tags: [] },
26
- },
27
- ]);
28
- await ingestEvents(app, apiKey, [
29
- {
30
- sessionId: 'sess_002',
31
- agentId: 'agent_002',
32
- eventType: 'session_started',
33
- payload: { agentName: 'Agent Two', tags: [] },
34
- },
35
- ]);
36
- });
37
- describe('GET /api/agents', () => {
38
- it('lists all agents', async () => {
39
- const res = await app.request('/api/agents', {
40
- headers: authHeaders(apiKey),
41
- });
42
- expect(res.status).toBe(200);
43
- const body = await res.json();
44
- expect(body.agents).toBeInstanceOf(Array);
45
- expect(body.agents.length).toBe(2);
46
- const names = body.agents.map((a) => a.name);
47
- expect(names).toContain('Agent One');
48
- expect(names).toContain('Agent Two');
49
- });
50
- it('includes agent metadata', async () => {
51
- const res = await app.request('/api/agents', {
52
- headers: authHeaders(apiKey),
53
- });
54
- const body = await res.json();
55
- const agent = body.agents.find((a) => a.id === 'agent_001');
56
- expect(agent.id).toBe('agent_001');
57
- expect(agent.name).toBe('Agent One');
58
- expect(agent.firstSeenAt).toBeTruthy();
59
- expect(agent.lastSeenAt).toBeTruthy();
60
- expect(agent.sessionCount).toBe(1);
61
- });
62
- });
63
- describe('GET /api/agents/:id', () => {
64
- it('returns a single agent', async () => {
65
- const res = await app.request('/api/agents/agent_001', {
66
- headers: authHeaders(apiKey),
67
- });
68
- expect(res.status).toBe(200);
69
- const body = await res.json();
70
- expect(body.id).toBe('agent_001');
71
- expect(body.name).toBe('Agent One');
72
- });
73
- it('returns 404 for non-existent agent', async () => {
74
- const res = await app.request('/api/agents/nonexistent', {
75
- headers: authHeaders(apiKey),
76
- });
77
- expect(res.status).toBe(404);
78
- const body = await res.json();
79
- expect(body.error).toBe('Agent not found');
80
- });
81
- });
82
- });
83
- describe('Stats Endpoint (Story 4.7)', () => {
84
- it('returns storage statistics for empty database', async () => {
85
- const { app, apiKey } = createTestApp();
86
- const res = await app.request('/api/stats', {
87
- headers: authHeaders(apiKey),
88
- });
89
- expect(res.status).toBe(200);
90
- const body = await res.json();
91
- expect(body.totalEvents).toBe(0);
92
- expect(body.totalSessions).toBe(0);
93
- expect(body.totalAgents).toBe(0);
94
- });
95
- it('returns accurate stats after ingestion', async () => {
96
- const { app, apiKey } = createTestApp();
97
- await ingestEvents(app, apiKey, [
98
- {
99
- sessionId: 'sess_001',
100
- agentId: 'agent_001',
101
- eventType: 'session_started',
102
- timestamp: '2026-01-01T10:00:00Z',
103
- payload: { tags: [] },
104
- },
105
- {
106
- sessionId: 'sess_001',
107
- agentId: 'agent_001',
108
- eventType: 'tool_call',
109
- timestamp: '2026-01-01T10:01:00Z',
110
- payload: { toolName: 'search', arguments: {}, callId: 'c1' },
111
- },
112
- ]);
113
- await ingestEvents(app, apiKey, [
114
- {
115
- sessionId: 'sess_002',
116
- agentId: 'agent_002',
117
- eventType: 'session_started',
118
- timestamp: '2026-01-02T10:00:00Z',
119
- payload: { tags: [] },
120
- },
121
- ]);
122
- const res = await app.request('/api/stats', {
123
- headers: authHeaders(apiKey),
124
- });
125
- const body = await res.json();
126
- expect(body.totalEvents).toBe(3);
127
- expect(body.totalSessions).toBe(2);
128
- expect(body.totalAgents).toBe(2);
129
- expect(body.oldestEvent).toBeTruthy();
130
- expect(body.newestEvent).toBeTruthy();
131
- expect(typeof body.storageSizeBytes).toBe('number');
132
- });
133
- });
134
- //# sourceMappingURL=agents-stats.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agents-stats.test.js","sourceRoot":"","sources":["../../src/__tests__/agents-stats.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +0,0 @@
1
- /**
2
- * Tests for Alert Rule CRUD endpoints (Story 12.1) and alert history.
3
- */
4
- export {};
5
- //# sourceMappingURL=alerts.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"alerts.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/alerts.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -1,245 +0,0 @@
1
- /**
2
- * Tests for Alert Rule CRUD endpoints (Story 12.1) and alert history.
3
- */
4
- import { describe, it, expect, beforeEach } from 'vitest';
5
- import { createTestApp, authHeaders } from './test-helpers.js';
6
- let ctx;
7
- beforeEach(() => {
8
- ctx = createTestApp();
9
- });
10
- describe('POST /api/alerts/rules', () => {
11
- it('creates an alert rule with valid data', async () => {
12
- const res = await ctx.app.request('/api/alerts/rules', {
13
- method: 'POST',
14
- headers: authHeaders(ctx.apiKey),
15
- body: JSON.stringify({
16
- name: 'High Error Rate',
17
- condition: 'error_rate_exceeds',
18
- threshold: 0.1,
19
- windowMinutes: 60,
20
- }),
21
- });
22
- expect(res.status).toBe(201);
23
- const body = await res.json();
24
- expect(body.id).toBeTruthy();
25
- expect(body.name).toBe('High Error Rate');
26
- expect(body.condition).toBe('error_rate_exceeds');
27
- expect(body.threshold).toBe(0.1);
28
- expect(body.windowMinutes).toBe(60);
29
- expect(body.enabled).toBe(true);
30
- expect(body.scope).toEqual({});
31
- expect(body.notifyChannels).toEqual([]);
32
- });
33
- it('creates a rule with all fields', async () => {
34
- const res = await ctx.app.request('/api/alerts/rules', {
35
- method: 'POST',
36
- headers: authHeaders(ctx.apiKey),
37
- body: JSON.stringify({
38
- name: 'Cost Alert',
39
- condition: 'cost_exceeds',
40
- threshold: 10.0,
41
- windowMinutes: 1440,
42
- enabled: false,
43
- scope: { agentId: 'my-agent' },
44
- notifyChannels: ['https://hooks.slack.com/xxx'],
45
- }),
46
- });
47
- expect(res.status).toBe(201);
48
- const body = await res.json();
49
- expect(body.name).toBe('Cost Alert');
50
- expect(body.enabled).toBe(false);
51
- expect(body.scope).toEqual({ agentId: 'my-agent' });
52
- expect(body.notifyChannels).toEqual(['https://hooks.slack.com/xxx']);
53
- });
54
- it('rejects invalid condition', async () => {
55
- const res = await ctx.app.request('/api/alerts/rules', {
56
- method: 'POST',
57
- headers: authHeaders(ctx.apiKey),
58
- body: JSON.stringify({
59
- name: 'Bad Rule',
60
- condition: 'invalid_condition',
61
- threshold: 1,
62
- windowMinutes: 60,
63
- }),
64
- });
65
- expect(res.status).toBe(400);
66
- });
67
- it('rejects missing name', async () => {
68
- const res = await ctx.app.request('/api/alerts/rules', {
69
- method: 'POST',
70
- headers: authHeaders(ctx.apiKey),
71
- body: JSON.stringify({
72
- condition: 'error_rate_exceeds',
73
- threshold: 0.1,
74
- windowMinutes: 60,
75
- }),
76
- });
77
- expect(res.status).toBe(400);
78
- });
79
- it('rejects negative threshold', async () => {
80
- const res = await ctx.app.request('/api/alerts/rules', {
81
- method: 'POST',
82
- headers: authHeaders(ctx.apiKey),
83
- body: JSON.stringify({
84
- name: 'Bad',
85
- condition: 'error_rate_exceeds',
86
- threshold: -1,
87
- windowMinutes: 60,
88
- }),
89
- });
90
- expect(res.status).toBe(400);
91
- });
92
- });
93
- describe('GET /api/alerts/rules', () => {
94
- it('returns empty list initially', async () => {
95
- const res = await ctx.app.request('/api/alerts/rules', {
96
- headers: authHeaders(ctx.apiKey),
97
- });
98
- expect(res.status).toBe(200);
99
- const body = await res.json();
100
- expect(body.rules).toEqual([]);
101
- });
102
- it('lists created rules', async () => {
103
- // Create two rules
104
- await ctx.app.request('/api/alerts/rules', {
105
- method: 'POST',
106
- headers: authHeaders(ctx.apiKey),
107
- body: JSON.stringify({
108
- name: 'Rule 1',
109
- condition: 'error_rate_exceeds',
110
- threshold: 0.1,
111
- windowMinutes: 60,
112
- }),
113
- });
114
- await ctx.app.request('/api/alerts/rules', {
115
- method: 'POST',
116
- headers: authHeaders(ctx.apiKey),
117
- body: JSON.stringify({
118
- name: 'Rule 2',
119
- condition: 'cost_exceeds',
120
- threshold: 5.0,
121
- windowMinutes: 1440,
122
- }),
123
- });
124
- const res = await ctx.app.request('/api/alerts/rules', {
125
- headers: authHeaders(ctx.apiKey),
126
- });
127
- expect(res.status).toBe(200);
128
- const body = await res.json();
129
- expect(body.rules).toHaveLength(2);
130
- });
131
- });
132
- describe('GET /api/alerts/rules/:id', () => {
133
- it('returns a single rule', async () => {
134
- const createRes = await ctx.app.request('/api/alerts/rules', {
135
- method: 'POST',
136
- headers: authHeaders(ctx.apiKey),
137
- body: JSON.stringify({
138
- name: 'Test Rule',
139
- condition: 'latency_exceeds',
140
- threshold: 500,
141
- windowMinutes: 30,
142
- }),
143
- });
144
- const created = await createRes.json();
145
- const res = await ctx.app.request(`/api/alerts/rules/${created.id}`, {
146
- headers: authHeaders(ctx.apiKey),
147
- });
148
- expect(res.status).toBe(200);
149
- const body = await res.json();
150
- expect(body.id).toBe(created.id);
151
- expect(body.name).toBe('Test Rule');
152
- });
153
- it('returns 404 for nonexistent rule', async () => {
154
- const res = await ctx.app.request('/api/alerts/rules/nonexistent', {
155
- headers: authHeaders(ctx.apiKey),
156
- });
157
- expect(res.status).toBe(404);
158
- });
159
- });
160
- describe('PUT /api/alerts/rules/:id', () => {
161
- it('updates a rule', async () => {
162
- const createRes = await ctx.app.request('/api/alerts/rules', {
163
- method: 'POST',
164
- headers: authHeaders(ctx.apiKey),
165
- body: JSON.stringify({
166
- name: 'Original',
167
- condition: 'error_rate_exceeds',
168
- threshold: 0.1,
169
- windowMinutes: 60,
170
- }),
171
- });
172
- const created = await createRes.json();
173
- const res = await ctx.app.request(`/api/alerts/rules/${created.id}`, {
174
- method: 'PUT',
175
- headers: authHeaders(ctx.apiKey),
176
- body: JSON.stringify({
177
- name: 'Updated',
178
- threshold: 0.2,
179
- enabled: false,
180
- }),
181
- });
182
- expect(res.status).toBe(200);
183
- const body = await res.json();
184
- expect(body.name).toBe('Updated');
185
- expect(body.threshold).toBe(0.2);
186
- expect(body.enabled).toBe(false);
187
- // Unchanged fields preserved
188
- expect(body.condition).toBe('error_rate_exceeds');
189
- expect(body.windowMinutes).toBe(60);
190
- });
191
- it('returns 404 for nonexistent rule', async () => {
192
- const res = await ctx.app.request('/api/alerts/rules/nonexistent', {
193
- method: 'PUT',
194
- headers: authHeaders(ctx.apiKey),
195
- body: JSON.stringify({ name: 'Updated' }),
196
- });
197
- expect(res.status).toBe(404);
198
- });
199
- });
200
- describe('DELETE /api/alerts/rules/:id', () => {
201
- it('deletes a rule', async () => {
202
- const createRes = await ctx.app.request('/api/alerts/rules', {
203
- method: 'POST',
204
- headers: authHeaders(ctx.apiKey),
205
- body: JSON.stringify({
206
- name: 'To Delete',
207
- condition: 'error_rate_exceeds',
208
- threshold: 0.1,
209
- windowMinutes: 60,
210
- }),
211
- });
212
- const created = await createRes.json();
213
- const delRes = await ctx.app.request(`/api/alerts/rules/${created.id}`, {
214
- method: 'DELETE',
215
- headers: authHeaders(ctx.apiKey),
216
- });
217
- expect(delRes.status).toBe(200);
218
- const body = await delRes.json();
219
- expect(body.deleted).toBe(true);
220
- // Verify it's gone
221
- const getRes = await ctx.app.request(`/api/alerts/rules/${created.id}`, {
222
- headers: authHeaders(ctx.apiKey),
223
- });
224
- expect(getRes.status).toBe(404);
225
- });
226
- it('returns 404 for nonexistent rule', async () => {
227
- const res = await ctx.app.request('/api/alerts/rules/nonexistent', {
228
- method: 'DELETE',
229
- headers: authHeaders(ctx.apiKey),
230
- });
231
- expect(res.status).toBe(404);
232
- });
233
- });
234
- describe('GET /api/alerts/history', () => {
235
- it('returns empty history initially', async () => {
236
- const res = await ctx.app.request('/api/alerts/history', {
237
- headers: authHeaders(ctx.apiKey),
238
- });
239
- expect(res.status).toBe(200);
240
- const body = await res.json();
241
- expect(body.entries).toEqual([]);
242
- expect(body.total).toBe(0);
243
- });
244
- });
245
- //# sourceMappingURL=alerts.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"alerts.test.js","sourceRoot":"","sources":["../../src/__tests__/alerts.test.ts"],"names":[],"mappings":"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"}