@agentlensai/server 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.
Files changed (118) hide show
  1. package/LICENSE +21 -0
  2. package/dist/__tests__/agents-stats.test.d.ts +5 -0
  3. package/dist/__tests__/agents-stats.test.d.ts.map +1 -0
  4. package/dist/__tests__/agents-stats.test.js +134 -0
  5. package/dist/__tests__/agents-stats.test.js.map +1 -0
  6. package/dist/__tests__/api-keys.test.d.ts +5 -0
  7. package/dist/__tests__/api-keys.test.d.ts.map +1 -0
  8. package/dist/__tests__/api-keys.test.js +118 -0
  9. package/dist/__tests__/api-keys.test.js.map +1 -0
  10. package/dist/__tests__/auth-no-db.test.d.ts +2 -0
  11. package/dist/__tests__/auth-no-db.test.d.ts.map +1 -0
  12. package/dist/__tests__/auth-no-db.test.js +43 -0
  13. package/dist/__tests__/auth-no-db.test.js.map +1 -0
  14. package/dist/__tests__/auth.test.d.ts +5 -0
  15. package/dist/__tests__/auth.test.d.ts.map +1 -0
  16. package/dist/__tests__/auth.test.js +86 -0
  17. package/dist/__tests__/auth.test.js.map +1 -0
  18. package/dist/__tests__/config.test.d.ts +2 -0
  19. package/dist/__tests__/config.test.d.ts.map +1 -0
  20. package/dist/__tests__/config.test.js +37 -0
  21. package/dist/__tests__/config.test.js.map +1 -0
  22. package/dist/__tests__/events-ingest.test.d.ts +5 -0
  23. package/dist/__tests__/events-ingest.test.d.ts.map +1 -0
  24. package/dist/__tests__/events-ingest.test.js +248 -0
  25. package/dist/__tests__/events-ingest.test.js.map +1 -0
  26. package/dist/__tests__/events-query.test.d.ts +5 -0
  27. package/dist/__tests__/events-query.test.d.ts.map +1 -0
  28. package/dist/__tests__/events-query.test.js +205 -0
  29. package/dist/__tests__/events-query.test.js.map +1 -0
  30. package/dist/__tests__/health.test.d.ts +5 -0
  31. package/dist/__tests__/health.test.d.ts.map +1 -0
  32. package/dist/__tests__/health.test.js +40 -0
  33. package/dist/__tests__/health.test.js.map +1 -0
  34. package/dist/__tests__/sessions.test.d.ts +5 -0
  35. package/dist/__tests__/sessions.test.d.ts.map +1 -0
  36. package/dist/__tests__/sessions.test.js +176 -0
  37. package/dist/__tests__/sessions.test.js.map +1 -0
  38. package/dist/__tests__/test-helpers.d.ts +24 -0
  39. package/dist/__tests__/test-helpers.d.ts.map +1 -0
  40. package/dist/__tests__/test-helpers.js +45 -0
  41. package/dist/__tests__/test-helpers.js.map +1 -0
  42. package/dist/config.d.ts +20 -0
  43. package/dist/config.d.ts.map +1 -0
  44. package/dist/config.js +20 -0
  45. package/dist/config.js.map +1 -0
  46. package/dist/db/__tests__/init.test.d.ts +2 -0
  47. package/dist/db/__tests__/init.test.d.ts.map +1 -0
  48. package/dist/db/__tests__/init.test.js +73 -0
  49. package/dist/db/__tests__/init.test.js.map +1 -0
  50. package/dist/db/__tests__/sqlite-store-read.test.d.ts +2 -0
  51. package/dist/db/__tests__/sqlite-store-read.test.d.ts.map +1 -0
  52. package/dist/db/__tests__/sqlite-store-read.test.js +749 -0
  53. package/dist/db/__tests__/sqlite-store-read.test.js.map +1 -0
  54. package/dist/db/__tests__/sqlite-store-write.test.d.ts +2 -0
  55. package/dist/db/__tests__/sqlite-store-write.test.d.ts.map +1 -0
  56. package/dist/db/__tests__/sqlite-store-write.test.js +418 -0
  57. package/dist/db/__tests__/sqlite-store-write.test.js.map +1 -0
  58. package/dist/db/errors.d.ts +16 -0
  59. package/dist/db/errors.d.ts.map +1 -0
  60. package/dist/db/errors.js +22 -0
  61. package/dist/db/errors.js.map +1 -0
  62. package/dist/db/index.d.ts +33 -0
  63. package/dist/db/index.d.ts.map +1 -0
  64. package/dist/db/index.js +44 -0
  65. package/dist/db/index.js.map +1 -0
  66. package/dist/db/migrate.d.ts +26 -0
  67. package/dist/db/migrate.d.ts.map +1 -0
  68. package/dist/db/migrate.js +128 -0
  69. package/dist/db/migrate.js.map +1 -0
  70. package/dist/db/schema.sqlite.d.ts +1009 -0
  71. package/dist/db/schema.sqlite.d.ts.map +1 -0
  72. package/dist/db/schema.sqlite.js +96 -0
  73. package/dist/db/schema.sqlite.js.map +1 -0
  74. package/dist/db/sqlite-store.d.ts +68 -0
  75. package/dist/db/sqlite-store.d.ts.map +1 -0
  76. package/dist/db/sqlite-store.js +753 -0
  77. package/dist/db/sqlite-store.js.map +1 -0
  78. package/dist/index.d.ts +45 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +182 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/lib/__tests__/retention.test.d.ts +2 -0
  83. package/dist/lib/__tests__/retention.test.d.ts.map +1 -0
  84. package/dist/lib/__tests__/retention.test.js +238 -0
  85. package/dist/lib/__tests__/retention.test.js.map +1 -0
  86. package/dist/lib/retention.d.ts +31 -0
  87. package/dist/lib/retention.d.ts.map +1 -0
  88. package/dist/lib/retention.js +47 -0
  89. package/dist/lib/retention.js.map +1 -0
  90. package/dist/middleware/auth.d.ts +37 -0
  91. package/dist/middleware/auth.d.ts.map +1 -0
  92. package/dist/middleware/auth.js +78 -0
  93. package/dist/middleware/auth.js.map +1 -0
  94. package/dist/routes/agents.d.ts +13 -0
  95. package/dist/routes/agents.d.ts.map +1 -0
  96. package/dist/routes/agents.js +34 -0
  97. package/dist/routes/agents.js.map +1 -0
  98. package/dist/routes/api-keys.d.ts +14 -0
  99. package/dist/routes/api-keys.d.ts.map +1 -0
  100. package/dist/routes/api-keys.js +81 -0
  101. package/dist/routes/api-keys.js.map +1 -0
  102. package/dist/routes/config.d.ts +39 -0
  103. package/dist/routes/config.d.ts.map +1 -0
  104. package/dist/routes/config.js +97 -0
  105. package/dist/routes/config.js.map +1 -0
  106. package/dist/routes/events.d.ts +14 -0
  107. package/dist/routes/events.d.ts.map +1 -0
  108. package/dist/routes/events.js +164 -0
  109. package/dist/routes/events.js.map +1 -0
  110. package/dist/routes/sessions.d.ts +14 -0
  111. package/dist/routes/sessions.d.ts.map +1 -0
  112. package/dist/routes/sessions.js +72 -0
  113. package/dist/routes/sessions.js.map +1 -0
  114. package/dist/routes/stats.d.ts +12 -0
  115. package/dist/routes/stats.d.ts.map +1 -0
  116. package/dist/routes/stats.js +16 -0
  117. package/dist/routes/stats.js.map +1 -0
  118. package/package.json +61 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.js","sourceRoot":"","sources":["../../src/lib/retention.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAElC;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9C,MAAM,aAAa,GAAG,OAAO,KAAK,SAAS;QACzC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QACvB,CAAC,CAAC,sBAAsB,CAAC;IAE3B,OAAO;QACL,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,aAAa;KAC7E,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAkB,EAClB,MAAwB;IAExB,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAC;IAEtD,sCAAsC;IACtC,IAAI,cAAc,CAAC,aAAa,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC5C,CAAC;IAED,IAAI,cAAc,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC5C,CAAC;IAED,iCAAiC;IACjC,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;IAC9B,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAErD,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * API Key Authentication Middleware (Story 4.2)
3
+ *
4
+ * Validates `Authorization: Bearer als_xxx` header by hashing the key
5
+ * with SHA-256 and looking it up in the apiKeys table.
6
+ *
7
+ * When AUTH_DISABLED=true, authentication is skipped (dev mode).
8
+ */
9
+ import type { SqliteDb } from '../db/index.js';
10
+ /**
11
+ * API key info attached to the Hono context.
12
+ */
13
+ export interface ApiKeyInfo {
14
+ id: string;
15
+ name: string;
16
+ scopes: string[];
17
+ }
18
+ /**
19
+ * Type augmentation for Hono context variables.
20
+ */
21
+ export type AuthVariables = {
22
+ apiKey: ApiKeyInfo;
23
+ };
24
+ /**
25
+ * SHA-256 hash a raw API key string.
26
+ */
27
+ export declare function hashApiKey(raw: string): string;
28
+ /**
29
+ * Create the auth middleware.
30
+ *
31
+ * @param db - Drizzle SQLite database instance
32
+ * @param authDisabled - If true, skip authentication (dev mode)
33
+ */
34
+ export declare function authMiddleware(db: SqliteDb, authDisabled: boolean): import("hono").MiddlewareHandler<{
35
+ Variables: AuthVariables;
36
+ }, string, {}, Response>;
37
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO;eAC3B,aAAa;yBA0DnD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * API Key Authentication Middleware (Story 4.2)
3
+ *
4
+ * Validates `Authorization: Bearer als_xxx` header by hashing the key
5
+ * with SHA-256 and looking it up in the apiKeys table.
6
+ *
7
+ * When AUTH_DISABLED=true, authentication is skipped (dev mode).
8
+ */
9
+ import { createHash } from 'node:crypto';
10
+ import { createMiddleware } from 'hono/factory';
11
+ import { apiKeys } from '../db/schema.sqlite.js';
12
+ import { eq, and, isNull } from 'drizzle-orm';
13
+ /**
14
+ * SHA-256 hash a raw API key string.
15
+ */
16
+ export function hashApiKey(raw) {
17
+ return createHash('sha256').update(raw).digest('hex');
18
+ }
19
+ /**
20
+ * Create the auth middleware.
21
+ *
22
+ * @param db - Drizzle SQLite database instance
23
+ * @param authDisabled - If true, skip authentication (dev mode)
24
+ */
25
+ export function authMiddleware(db, authDisabled) {
26
+ return createMiddleware(async (c, next) => {
27
+ // Dev mode: skip auth
28
+ if (authDisabled) {
29
+ c.set('apiKey', { id: 'dev', name: 'dev-mode', scopes: ['*'] });
30
+ return next();
31
+ }
32
+ const authHeader = c.req.header('Authorization');
33
+ if (!authHeader) {
34
+ return c.json({ error: 'Missing Authorization header', status: 401 }, 401);
35
+ }
36
+ const match = authHeader.match(/^Bearer\s+(als_\w+)$/);
37
+ if (!match) {
38
+ return c.json({ error: 'Invalid Authorization header format. Expected: Bearer als_xxx', status: 401 }, 401);
39
+ }
40
+ const rawKey = match[1];
41
+ const keyHash = hashApiKey(rawKey);
42
+ // Look up the key by hash (not revoked)
43
+ const row = db
44
+ .select()
45
+ .from(apiKeys)
46
+ .where(and(eq(apiKeys.keyHash, keyHash), isNull(apiKeys.revokedAt)))
47
+ .get();
48
+ if (!row) {
49
+ return c.json({ error: 'Invalid or revoked API key', status: 401 }, 401);
50
+ }
51
+ // Fire-and-forget lastUsedAt update
52
+ const now = Math.floor(Date.now() / 1000);
53
+ try {
54
+ db.update(apiKeys)
55
+ .set({ lastUsedAt: now })
56
+ .where(eq(apiKeys.id, row.id))
57
+ .run();
58
+ }
59
+ catch {
60
+ // Non-critical — don't fail the request
61
+ }
62
+ const scopes = (() => {
63
+ try {
64
+ return JSON.parse(row.scopes);
65
+ }
66
+ catch {
67
+ return [];
68
+ }
69
+ })();
70
+ c.set('apiKey', {
71
+ id: row.id,
72
+ name: row.name,
73
+ scopes,
74
+ });
75
+ return next();
76
+ });
77
+ }
78
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Agent Endpoints (Story 4.7)
3
+ *
4
+ * GET /api/agents — list all agents (with error rate from sessions)
5
+ * GET /api/agents/:id — single agent
6
+ */
7
+ import { Hono } from 'hono';
8
+ import type { IEventStore } from '@agentlens/core';
9
+ import type { AuthVariables } from '../middleware/auth.js';
10
+ export declare function agentsRoutes(store: IEventStore): Hono<{
11
+ Variables: AuthVariables;
12
+ }, import("hono/types").BlankSchema, "/">;
13
+ //# sourceMappingURL=agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CAiChD"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Agent Endpoints (Story 4.7)
3
+ *
4
+ * GET /api/agents — list all agents (with error rate from sessions)
5
+ * GET /api/agents/:id — single agent
6
+ */
7
+ import { Hono } from 'hono';
8
+ export function agentsRoutes(store) {
9
+ const app = new Hono();
10
+ // GET /api/agents — list all agents with computed error rates
11
+ app.get('/', async (c) => {
12
+ const agents = await store.listAgents();
13
+ // Enrich agents with error rate computed from their sessions
14
+ const enriched = await Promise.all(agents.map(async (agent) => {
15
+ const { sessions } = await store.querySessions({ agentId: agent.id, limit: 10000 });
16
+ const totalErrors = sessions.reduce((sum, s) => sum + s.errorCount, 0);
17
+ const totalEvents = sessions.reduce((sum, s) => sum + s.eventCount, 0);
18
+ const errorRate = totalEvents > 0 ? totalErrors / totalEvents : 0;
19
+ return { ...agent, errorRate };
20
+ }));
21
+ return c.json({ agents: enriched });
22
+ });
23
+ // GET /api/agents/:id — single agent
24
+ app.get('/:id', async (c) => {
25
+ const id = c.req.param('id');
26
+ const agent = await store.getAgent(id);
27
+ if (!agent) {
28
+ return c.json({ error: 'Agent not found', status: 404 }, 404);
29
+ }
30
+ return c.json(agent);
31
+ });
32
+ return app;
33
+ }
34
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAgC,CAAC;IAErD,8DAA8D;IAC9D,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC;QAExC,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACzB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACpF,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YACvE,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YACvE,MAAM,SAAS,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC,CAAC,CACH,CAAC;QAEF,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,qCAAqC;IACrC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAEvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * API Key Management Endpoints (Story 4.3)
3
+ *
4
+ * POST /api/keys — create a new API key
5
+ * GET /api/keys — list all keys (no raw key exposed)
6
+ * DELETE /api/keys/:id — revoke (soft delete) a key
7
+ */
8
+ import { Hono } from 'hono';
9
+ import type { SqliteDb } from '../db/index.js';
10
+ import { type AuthVariables } from '../middleware/auth.js';
11
+ export declare function apiKeysRoutes(db: SqliteDb): Hono<{
12
+ Variables: AuthVariables;
13
+ }, import("hono/types").BlankSchema, "/">;
14
+ //# sourceMappingURL=api-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../../src/routes/api-keys.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEvE,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ;eACN,aAAa;0CAyEhD"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * API Key Management Endpoints (Story 4.3)
3
+ *
4
+ * POST /api/keys — create a new API key
5
+ * GET /api/keys — list all keys (no raw key exposed)
6
+ * DELETE /api/keys/:id — revoke (soft delete) a key
7
+ */
8
+ import { Hono } from 'hono';
9
+ import { randomBytes } from 'node:crypto';
10
+ import { ulid } from 'ulid';
11
+ import { apiKeys } from '../db/schema.sqlite.js';
12
+ import { eq } from 'drizzle-orm';
13
+ import { hashApiKey } from '../middleware/auth.js';
14
+ export function apiKeysRoutes(db) {
15
+ const app = new Hono();
16
+ // POST /api/keys — create a new API key
17
+ app.post('/', async (c) => {
18
+ const body = await c.req.json().catch(() => ({}));
19
+ const name = body.name;
20
+ const scopes = body.scopes;
21
+ const id = ulid();
22
+ const rawKey = `als_${randomBytes(32).toString('hex')}`;
23
+ const keyHash = hashApiKey(rawKey);
24
+ const now = Math.floor(Date.now() / 1000);
25
+ db.insert(apiKeys)
26
+ .values({
27
+ id,
28
+ keyHash,
29
+ name: name ?? 'Unnamed Key',
30
+ scopes: JSON.stringify(scopes ?? ['*']),
31
+ createdAt: now,
32
+ })
33
+ .run();
34
+ return c.json({
35
+ id,
36
+ key: rawKey,
37
+ name: name ?? 'Unnamed Key',
38
+ scopes: scopes ?? ['*'],
39
+ createdAt: new Date(now * 1000).toISOString(),
40
+ }, 201);
41
+ });
42
+ // GET /api/keys — list all keys
43
+ app.get('/', (c) => {
44
+ const rows = db.select().from(apiKeys).all();
45
+ const keys = rows.map((row) => ({
46
+ id: row.id,
47
+ name: row.name,
48
+ scopes: (() => {
49
+ try {
50
+ return JSON.parse(row.scopes);
51
+ }
52
+ catch {
53
+ return [];
54
+ }
55
+ })(),
56
+ createdAt: new Date(row.createdAt * 1000).toISOString(),
57
+ lastUsedAt: row.lastUsedAt ? new Date(row.lastUsedAt * 1000).toISOString() : null,
58
+ revokedAt: row.revokedAt ? new Date(row.revokedAt * 1000).toISOString() : null,
59
+ }));
60
+ return c.json({ keys });
61
+ });
62
+ // DELETE /api/keys/:id — revoke a key
63
+ app.delete('/:id', (c) => {
64
+ const id = c.req.param('id');
65
+ const now = Math.floor(Date.now() / 1000);
66
+ const existing = db.select().from(apiKeys).where(eq(apiKeys.id, id)).get();
67
+ if (!existing) {
68
+ return c.json({ error: 'API key not found', status: 404 }, 404);
69
+ }
70
+ if (existing.revokedAt) {
71
+ return c.json({ error: 'API key already revoked', status: 409 }, 409);
72
+ }
73
+ db.update(apiKeys)
74
+ .set({ revokedAt: now })
75
+ .where(eq(apiKeys.id, id))
76
+ .run();
77
+ return c.json({ id, revoked: true });
78
+ });
79
+ return app;
80
+ }
81
+ //# sourceMappingURL=api-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/routes/api-keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Config Endpoints (Story 8.4)
3
+ *
4
+ * GET /api/config — get current configuration
5
+ * PUT /api/config — update configuration values
6
+ *
7
+ * Configuration values are stored in a simple key-value table.
8
+ * Falls back to environment defaults when no override exists.
9
+ */
10
+ import { Hono } from 'hono';
11
+ import { z } from 'zod';
12
+ import type { AuthVariables } from '../middleware/auth.js';
13
+ import type { SqliteDb } from '../db/index.js';
14
+ /** Schema for config update request */
15
+ declare const configUpdateSchema: z.ZodObject<{
16
+ retentionDays: z.ZodOptional<z.ZodNumber>;
17
+ agentGateUrl: z.ZodOptional<z.ZodString>;
18
+ agentGateSecret: z.ZodOptional<z.ZodString>;
19
+ formBridgeUrl: z.ZodOptional<z.ZodString>;
20
+ formBridgeSecret: z.ZodOptional<z.ZodString>;
21
+ }, "strip", z.ZodTypeAny, {
22
+ retentionDays?: number | undefined;
23
+ agentGateUrl?: string | undefined;
24
+ agentGateSecret?: string | undefined;
25
+ formBridgeUrl?: string | undefined;
26
+ formBridgeSecret?: string | undefined;
27
+ }, {
28
+ retentionDays?: number | undefined;
29
+ agentGateUrl?: string | undefined;
30
+ agentGateSecret?: string | undefined;
31
+ formBridgeUrl?: string | undefined;
32
+ formBridgeSecret?: string | undefined;
33
+ }>;
34
+ export type ConfigValues = z.infer<typeof configUpdateSchema>;
35
+ export declare function configRoutes(db: SqliteDb): Hono<{
36
+ Variables: AuthVariables;
37
+ }, import("hono/types").BlankSchema, "/">;
38
+ export {};
39
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,uCAAuC;AACvC,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;EAMtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAoC9D,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ;eACL,aAAa;0CAuDhD"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Config Endpoints (Story 8.4)
3
+ *
4
+ * GET /api/config — get current configuration
5
+ * PUT /api/config — update configuration values
6
+ *
7
+ * Configuration values are stored in a simple key-value table.
8
+ * Falls back to environment defaults when no override exists.
9
+ */
10
+ import { Hono } from 'hono';
11
+ import { sql } from 'drizzle-orm';
12
+ import { z } from 'zod';
13
+ import { getConfig } from '../config.js';
14
+ /** Schema for config update request */
15
+ const configUpdateSchema = z.object({
16
+ retentionDays: z.number().int().min(0).max(3650).optional(),
17
+ agentGateUrl: z.string().max(2048).optional(),
18
+ agentGateSecret: z.string().max(512).optional(),
19
+ formBridgeUrl: z.string().max(2048).optional(),
20
+ formBridgeSecret: z.string().max(512).optional(),
21
+ });
22
+ /**
23
+ * Ensure the config_kv table exists (simple key-value store).
24
+ */
25
+ function ensureConfigTable(db) {
26
+ db.run(sql `CREATE TABLE IF NOT EXISTS config_kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)`);
27
+ }
28
+ function getConfigValue(db, key) {
29
+ const row = db.get(sql `SELECT value FROM config_kv WHERE key = ${key}`);
30
+ return row?.value ?? null;
31
+ }
32
+ function setConfigValue(db, key, value) {
33
+ db.run(sql `INSERT INTO config_kv (key, value) VALUES (${key}, ${value}) ON CONFLICT(key) DO UPDATE SET value = ${value}`);
34
+ }
35
+ function getAllConfig(db) {
36
+ const serverConfig = getConfig();
37
+ return {
38
+ retentionDays: (() => {
39
+ const v = getConfigValue(db, 'retentionDays');
40
+ return v !== null ? parseInt(v, 10) : serverConfig.retentionDays;
41
+ })(),
42
+ agentGateUrl: getConfigValue(db, 'agentGateUrl') ?? '',
43
+ agentGateSecret: getConfigValue(db, 'agentGateSecret') ?? '',
44
+ formBridgeUrl: getConfigValue(db, 'formBridgeUrl') ?? '',
45
+ formBridgeSecret: getConfigValue(db, 'formBridgeSecret') ?? '',
46
+ };
47
+ }
48
+ export function configRoutes(db) {
49
+ const app = new Hono();
50
+ ensureConfigTable(db);
51
+ // GET /api/config — current config
52
+ app.get('/', (c) => {
53
+ const config = getAllConfig(db);
54
+ // Mask secrets in response
55
+ return c.json({
56
+ retentionDays: config.retentionDays,
57
+ agentGateUrl: config.agentGateUrl,
58
+ agentGateSecret: config.agentGateSecret ? '••••••••' : '',
59
+ formBridgeUrl: config.formBridgeUrl,
60
+ formBridgeSecret: config.formBridgeSecret ? '••••••••' : '',
61
+ });
62
+ });
63
+ // PUT /api/config — update config values
64
+ app.put('/', async (c) => {
65
+ const rawBody = await c.req.json().catch(() => null);
66
+ if (!rawBody) {
67
+ return c.json({ error: 'Invalid JSON body', status: 400 }, 400);
68
+ }
69
+ const parseResult = configUpdateSchema.safeParse(rawBody);
70
+ if (!parseResult.success) {
71
+ return c.json({
72
+ error: 'Validation failed',
73
+ status: 400,
74
+ details: parseResult.error.issues,
75
+ }, 400);
76
+ }
77
+ const updates = parseResult.data;
78
+ if (updates.retentionDays !== undefined) {
79
+ setConfigValue(db, 'retentionDays', String(updates.retentionDays));
80
+ }
81
+ if (updates.agentGateUrl !== undefined) {
82
+ setConfigValue(db, 'agentGateUrl', updates.agentGateUrl);
83
+ }
84
+ if (updates.agentGateSecret !== undefined) {
85
+ setConfigValue(db, 'agentGateSecret', updates.agentGateSecret);
86
+ }
87
+ if (updates.formBridgeUrl !== undefined) {
88
+ setConfigValue(db, 'formBridgeUrl', updates.formBridgeUrl);
89
+ }
90
+ if (updates.formBridgeSecret !== undefined) {
91
+ setConfigValue(db, 'formBridgeSecret', updates.formBridgeSecret);
92
+ }
93
+ return c.json({ ok: true });
94
+ });
95
+ return app;
96
+ }
97
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,uCAAuC;AACvC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IAC3D,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IAC7C,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC/C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IAC9C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACjD,CAAC,CAAC;AAIH;;GAEG;AACH,SAAS,iBAAiB,CAAC,EAAY;IACrC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAA,kFAAkF,CAAC,CAAC;AAChG,CAAC;AAED,SAAS,cAAc,CAAC,EAAY,EAAE,GAAW;IAC/C,MAAM,GAAG,GAAG,EAAE,CAAC,GAAG,CAChB,GAAG,CAAA,2CAA2C,GAAG,EAAE,CACpD,CAAC;IACF,OAAO,GAAG,EAAE,KAAK,IAAI,IAAI,CAAC;AAC5B,CAAC;AAED,SAAS,cAAc,CAAC,EAAY,EAAE,GAAW,EAAE,KAAa;IAC9D,EAAE,CAAC,GAAG,CACJ,GAAG,CAAA,8CAA8C,GAAG,KAAK,KAAK,4CAA4C,KAAK,EAAE,CAClH,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,EAAY;IAChC,MAAM,YAAY,GAAG,SAAS,EAAE,CAAC;IACjC,OAAO;QACL,aAAa,EAAE,CAAC,GAAG,EAAE;YACnB,MAAM,CAAC,GAAG,cAAc,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC;YAC9C,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC;QACnE,CAAC,CAAC,EAAE;QACJ,YAAY,EAAE,cAAc,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,EAAE;QACtD,eAAe,EAAE,cAAc,CAAC,EAAE,EAAE,iBAAiB,CAAC,IAAI,EAAE;QAC5D,aAAa,EAAE,cAAc,CAAC,EAAE,EAAE,eAAe,CAAC,IAAI,EAAE;QACxD,gBAAgB,EAAE,cAAc,CAAC,EAAE,EAAE,kBAAkB,CAAC,IAAI,EAAE;KAC/D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAY;IACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAgC,CAAC;IAErD,iBAAiB,CAAC,EAAE,CAAC,CAAC;IAEtB,mCAAmC;IACnC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,2BAA2B;QAC3B,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,eAAe,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;YACzD,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;SAC5D,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,yCAAyC;IACzC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAClE,CAAC;QAED,MAAM,WAAW,GAAG,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1D,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACzB,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,mBAAmB;gBAC1B,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM;aAClC,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC;QAEjC,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACxC,cAAc,CAAC,EAAE,EAAE,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;QACrE,CAAC;QACD,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACvC,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YAC1C,cAAc,CAAC,EAAE,EAAE,iBAAiB,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACxC,cAAc,CAAC,EAAE,EAAE,eAAe,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,OAAO,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC3C,cAAc,CAAC,EAAE,EAAE,kBAAkB,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Event Endpoints (Stories 4.4 + 4.5)
3
+ *
4
+ * POST /api/events — ingest events
5
+ * GET /api/events — query events with filters
6
+ * GET /api/events/:id — get single event
7
+ */
8
+ import { Hono } from 'hono';
9
+ import type { IEventStore } from '@agentlens/core';
10
+ import type { AuthVariables } from '../middleware/auth.js';
11
+ export declare function eventsRoutes(store: IEventStore): Hono<{
12
+ Variables: AuthVariables;
13
+ }, import("hono/types").BlankSchema, "/">;
14
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/routes/events.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAO3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CAyKhD"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Event Endpoints (Stories 4.4 + 4.5)
3
+ *
4
+ * POST /api/events — ingest events
5
+ * GET /api/events — query events with filters
6
+ * GET /api/events/:id — get single event
7
+ */
8
+ import { Hono } from 'hono';
9
+ import { ulid } from 'ulid';
10
+ import { z } from 'zod';
11
+ import { ingestEventSchema, computeEventHash, truncatePayload, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, } from '@agentlens/core';
12
+ /** Schema for the batch ingestion request body */
13
+ const ingestBatchSchema = z.object({
14
+ events: z.array(ingestEventSchema).min(1).max(1000),
15
+ });
16
+ export function eventsRoutes(store) {
17
+ const app = new Hono();
18
+ // POST /api/events — ingest events
19
+ app.post('/', async (c) => {
20
+ const rawBody = await c.req.json().catch(() => null);
21
+ if (!rawBody) {
22
+ return c.json({ error: 'Invalid JSON body', status: 400 }, 400);
23
+ }
24
+ const parseResult = ingestBatchSchema.safeParse(rawBody);
25
+ if (!parseResult.success) {
26
+ return c.json({
27
+ error: 'Validation failed',
28
+ status: 400,
29
+ details: parseResult.error.issues.map((issue) => ({
30
+ path: issue.path.join('.'),
31
+ message: issue.message,
32
+ })),
33
+ }, 400);
34
+ }
35
+ const { events: inputEvents } = parseResult.data;
36
+ // Group events by sessionId to handle per-session hash chains
37
+ const bySession = new Map();
38
+ for (const ev of inputEvents) {
39
+ const arr = bySession.get(ev.sessionId) ?? [];
40
+ arr.push(ev);
41
+ bySession.set(ev.sessionId, arr);
42
+ }
43
+ const allProcessed = [];
44
+ // Phase 1: Build all events (validate and compute hashes) without writing
45
+ for (const [sessionId, sessionEvents] of bySession) {
46
+ // Get the last event hash for this session to chain from
47
+ const timeline = await store.getSessionTimeline(sessionId);
48
+ let prevHash = timeline.length > 0 ? timeline[timeline.length - 1].hash : null;
49
+ for (const input of sessionEvents) {
50
+ const id = ulid();
51
+ const timestamp = input.timestamp ?? new Date().toISOString();
52
+ const severity = input.severity ?? 'info';
53
+ const metadata = input.metadata ?? {};
54
+ const payload = truncatePayload(input.payload);
55
+ const hash = computeEventHash({
56
+ id,
57
+ timestamp,
58
+ sessionId: input.sessionId,
59
+ agentId: input.agentId,
60
+ eventType: input.eventType,
61
+ severity,
62
+ payload,
63
+ metadata,
64
+ prevHash,
65
+ });
66
+ const event = {
67
+ id,
68
+ timestamp,
69
+ sessionId: input.sessionId,
70
+ agentId: input.agentId,
71
+ eventType: input.eventType,
72
+ severity: severity,
73
+ payload,
74
+ metadata,
75
+ prevHash,
76
+ hash,
77
+ };
78
+ allProcessed.push(event);
79
+ prevHash = hash;
80
+ }
81
+ }
82
+ // Phase 2: Insert all events atomically per session group.
83
+ // Group by session for hash-chain integrity, but insertEvents already
84
+ // runs inside a transaction, and if any session group fails the whole
85
+ // request is an error (no partial success).
86
+ const sessionGroups = new Map();
87
+ for (const event of allProcessed) {
88
+ const arr = sessionGroups.get(event.sessionId) ?? [];
89
+ arr.push(event);
90
+ sessionGroups.set(event.sessionId, arr);
91
+ }
92
+ try {
93
+ for (const [, sessionProcessed] of sessionGroups) {
94
+ await store.insertEvents(sessionProcessed);
95
+ }
96
+ }
97
+ catch (error) {
98
+ // If any session group fails, the entire batch is rejected
99
+ const message = error instanceof Error ? error.message : 'Unknown error';
100
+ return c.json({ error: `Batch insert failed: ${message}`, status: 500 }, 500);
101
+ }
102
+ return c.json({
103
+ ingested: allProcessed.length,
104
+ events: allProcessed.map((e) => ({ id: e.id, hash: e.hash })),
105
+ }, 201);
106
+ });
107
+ // GET /api/events — query events
108
+ app.get('/', async (c) => {
109
+ const query = {};
110
+ const sessionId = c.req.query('sessionId');
111
+ if (sessionId)
112
+ query.sessionId = sessionId;
113
+ const agentId = c.req.query('agentId');
114
+ if (agentId)
115
+ query.agentId = agentId;
116
+ const eventType = c.req.query('eventType');
117
+ if (eventType) {
118
+ query.eventType = eventType.includes(',')
119
+ ? eventType.split(',')
120
+ : eventType;
121
+ }
122
+ const severity = c.req.query('severity');
123
+ if (severity) {
124
+ query.severity = severity.includes(',')
125
+ ? severity.split(',')
126
+ : severity;
127
+ }
128
+ const from = c.req.query('from');
129
+ if (from)
130
+ query.from = from;
131
+ const to = c.req.query('to');
132
+ if (to)
133
+ query.to = to;
134
+ const search = c.req.query('search');
135
+ if (search)
136
+ query.search = search;
137
+ const limitStr = c.req.query('limit');
138
+ query.limit = limitStr
139
+ ? Math.max(1, Math.min(parseInt(limitStr, 10) || DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE))
140
+ : DEFAULT_PAGE_SIZE;
141
+ const offsetStr = c.req.query('offset');
142
+ query.offset = offsetStr ? Math.max(0, parseInt(offsetStr, 10) || 0) : 0;
143
+ const order = c.req.query('order');
144
+ if (order === 'asc' || order === 'desc')
145
+ query.order = order;
146
+ const result = await store.queryEvents(query);
147
+ return c.json({
148
+ events: result.events,
149
+ total: result.total,
150
+ hasMore: result.hasMore,
151
+ });
152
+ });
153
+ // GET /api/events/:id — single event
154
+ app.get('/:id', async (c) => {
155
+ const id = c.req.param('id');
156
+ const event = await store.getEvent(id);
157
+ if (!event) {
158
+ return c.json({ error: 'Event not found', status: 404 }, 404);
159
+ }
160
+ return c.json(event);
161
+ });
162
+ return app;
163
+ }
164
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/routes/events.ts"],"names":[],"mappings":"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"}