@essentialai/cogent-server 2.0.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 (166) hide show
  1. package/.env.example +68 -0
  2. package/CHANGELOG.md +16 -0
  3. package/Caddyfile +8 -0
  4. package/Dockerfile +46 -0
  5. package/LICENSE +190 -0
  6. package/README.md +89 -0
  7. package/config.json.example +16 -0
  8. package/dist/__tests__/helpers.d.ts +56 -0
  9. package/dist/__tests__/helpers.d.ts.map +1 -0
  10. package/dist/__tests__/helpers.js +138 -0
  11. package/dist/__tests__/helpers.js.map +1 -0
  12. package/dist/app.d.ts +38 -0
  13. package/dist/app.d.ts.map +1 -0
  14. package/dist/app.js +60 -0
  15. package/dist/app.js.map +1 -0
  16. package/dist/config.d.ts +88 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +148 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +102 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/middleware/auth.d.ts +15 -0
  25. package/dist/middleware/auth.d.ts.map +1 -0
  26. package/dist/middleware/auth.js +47 -0
  27. package/dist/middleware/auth.js.map +1 -0
  28. package/dist/middleware/error-handler.d.ts +14 -0
  29. package/dist/middleware/error-handler.d.ts.map +1 -0
  30. package/dist/middleware/error-handler.js +26 -0
  31. package/dist/middleware/error-handler.js.map +1 -0
  32. package/dist/middleware/not-found.d.ts +8 -0
  33. package/dist/middleware/not-found.d.ts.map +1 -0
  34. package/dist/middleware/not-found.js +12 -0
  35. package/dist/middleware/not-found.js.map +1 -0
  36. package/dist/middleware/request-logger.d.ts +17 -0
  37. package/dist/middleware/request-logger.d.ts.map +1 -0
  38. package/dist/middleware/request-logger.js +65 -0
  39. package/dist/middleware/request-logger.js.map +1 -0
  40. package/dist/middleware/ws-auth.d.ts +21 -0
  41. package/dist/middleware/ws-auth.d.ts.map +1 -0
  42. package/dist/middleware/ws-auth.js +59 -0
  43. package/dist/middleware/ws-auth.js.map +1 -0
  44. package/dist/routes/health.d.ts +11 -0
  45. package/dist/routes/health.d.ts.map +1 -0
  46. package/dist/routes/health.js +34 -0
  47. package/dist/routes/health.js.map +1 -0
  48. package/dist/routes/messages.d.ts +19 -0
  49. package/dist/routes/messages.d.ts.map +1 -0
  50. package/dist/routes/messages.js +154 -0
  51. package/dist/routes/messages.js.map +1 -0
  52. package/dist/routes/peers.d.ts +17 -0
  53. package/dist/routes/peers.d.ts.map +1 -0
  54. package/dist/routes/peers.js +169 -0
  55. package/dist/routes/peers.js.map +1 -0
  56. package/dist/routes/poll.d.ts +15 -0
  57. package/dist/routes/poll.d.ts.map +1 -0
  58. package/dist/routes/poll.js +97 -0
  59. package/dist/routes/poll.js.map +1 -0
  60. package/dist/routes/sessions.d.ts +14 -0
  61. package/dist/routes/sessions.d.ts.map +1 -0
  62. package/dist/routes/sessions.js +113 -0
  63. package/dist/routes/sessions.js.map +1 -0
  64. package/dist/routes/ui.d.ts +21 -0
  65. package/dist/routes/ui.d.ts.map +1 -0
  66. package/dist/routes/ui.js +173 -0
  67. package/dist/routes/ui.js.map +1 -0
  68. package/dist/routes/validation-hook.d.ts +18 -0
  69. package/dist/routes/validation-hook.d.ts.map +1 -0
  70. package/dist/routes/validation-hook.js +24 -0
  71. package/dist/routes/validation-hook.js.map +1 -0
  72. package/dist/services/auth-service.d.ts +48 -0
  73. package/dist/services/auth-service.d.ts.map +1 -0
  74. package/dist/services/auth-service.js +63 -0
  75. package/dist/services/auth-service.js.map +1 -0
  76. package/dist/services/connection-manager.d.ts +108 -0
  77. package/dist/services/connection-manager.d.ts.map +1 -0
  78. package/dist/services/connection-manager.js +216 -0
  79. package/dist/services/connection-manager.js.map +1 -0
  80. package/dist/services/message-queue.d.ts +56 -0
  81. package/dist/services/message-queue.d.ts.map +1 -0
  82. package/dist/services/message-queue.js +164 -0
  83. package/dist/services/message-queue.js.map +1 -0
  84. package/dist/services/peer-cleanup.d.ts +39 -0
  85. package/dist/services/peer-cleanup.d.ts.map +1 -0
  86. package/dist/services/peer-cleanup.js +96 -0
  87. package/dist/services/peer-cleanup.js.map +1 -0
  88. package/dist/services/session-cleanup.d.ts +44 -0
  89. package/dist/services/session-cleanup.d.ts.map +1 -0
  90. package/dist/services/session-cleanup.js +100 -0
  91. package/dist/services/session-cleanup.js.map +1 -0
  92. package/dist/services/session-store.d.ts +103 -0
  93. package/dist/services/session-store.d.ts.map +1 -0
  94. package/dist/services/session-store.js +292 -0
  95. package/dist/services/session-store.js.map +1 -0
  96. package/dist/services/stats-service.d.ts +48 -0
  97. package/dist/services/stats-service.d.ts.map +1 -0
  98. package/dist/services/stats-service.js +77 -0
  99. package/dist/services/stats-service.js.map +1 -0
  100. package/dist/types.d.ts +60 -0
  101. package/dist/types.d.ts.map +1 -0
  102. package/dist/types.js +2 -0
  103. package/dist/types.js.map +1 -0
  104. package/dist/ui/components/Footer.d.ts +7 -0
  105. package/dist/ui/components/Footer.d.ts.map +1 -0
  106. package/dist/ui/components/Footer.js +17 -0
  107. package/dist/ui/components/Footer.js.map +1 -0
  108. package/dist/ui/components/Layout.d.ts +13 -0
  109. package/dist/ui/components/Layout.d.ts.map +1 -0
  110. package/dist/ui/components/Layout.js +11 -0
  111. package/dist/ui/components/Layout.js.map +1 -0
  112. package/dist/ui/components/NavBar.d.ts +12 -0
  113. package/dist/ui/components/NavBar.d.ts.map +1 -0
  114. package/dist/ui/components/NavBar.js +60 -0
  115. package/dist/ui/components/NavBar.js.map +1 -0
  116. package/dist/ui/components/StatCard.d.ts +14 -0
  117. package/dist/ui/components/StatCard.d.ts.map +1 -0
  118. package/dist/ui/components/StatCard.js +32 -0
  119. package/dist/ui/components/StatCard.js.map +1 -0
  120. package/dist/ui/components/Terminal.d.ts +13 -0
  121. package/dist/ui/components/Terminal.d.ts.map +1 -0
  122. package/dist/ui/components/Terminal.js +37 -0
  123. package/dist/ui/components/Terminal.js.map +1 -0
  124. package/dist/ui/pages/AdminDashboard.d.ts +13 -0
  125. package/dist/ui/pages/AdminDashboard.d.ts.map +1 -0
  126. package/dist/ui/pages/AdminDashboard.js +59 -0
  127. package/dist/ui/pages/AdminDashboard.js.map +1 -0
  128. package/dist/ui/pages/HowToPage.d.ts +8 -0
  129. package/dist/ui/pages/HowToPage.d.ts.map +1 -0
  130. package/dist/ui/pages/HowToPage.js +312 -0
  131. package/dist/ui/pages/HowToPage.js.map +1 -0
  132. package/dist/ui/pages/LandingPage.d.ts +13 -0
  133. package/dist/ui/pages/LandingPage.d.ts.map +1 -0
  134. package/dist/ui/pages/LandingPage.js +160 -0
  135. package/dist/ui/pages/LandingPage.js.map +1 -0
  136. package/dist/ui/pages/MessageLog.d.ts +25 -0
  137. package/dist/ui/pages/MessageLog.d.ts.map +1 -0
  138. package/dist/ui/pages/MessageLog.js +146 -0
  139. package/dist/ui/pages/MessageLog.js.map +1 -0
  140. package/dist/ui/pages/SessionDetail.d.ts +14 -0
  141. package/dist/ui/pages/SessionDetail.d.ts.map +1 -0
  142. package/dist/ui/pages/SessionDetail.js +165 -0
  143. package/dist/ui/pages/SessionDetail.js.map +1 -0
  144. package/dist/ui/pages/SessionList.d.ts +22 -0
  145. package/dist/ui/pages/SessionList.d.ts.map +1 -0
  146. package/dist/ui/pages/SessionList.js +88 -0
  147. package/dist/ui/pages/SessionList.js.map +1 -0
  148. package/dist/ui/styles/theme.d.ts +35 -0
  149. package/dist/ui/styles/theme.d.ts.map +1 -0
  150. package/dist/ui/styles/theme.js +65 -0
  151. package/dist/ui/styles/theme.js.map +1 -0
  152. package/dist/ws/frames.d.ts +82 -0
  153. package/dist/ws/frames.d.ts.map +1 -0
  154. package/dist/ws/frames.js +68 -0
  155. package/dist/ws/frames.js.map +1 -0
  156. package/dist/ws/handler.d.ts +26 -0
  157. package/dist/ws/handler.d.ts.map +1 -0
  158. package/dist/ws/handler.js +72 -0
  159. package/dist/ws/handler.js.map +1 -0
  160. package/dist/ws/heartbeat.d.ts +18 -0
  161. package/dist/ws/heartbeat.d.ts.map +1 -0
  162. package/dist/ws/heartbeat.js +39 -0
  163. package/dist/ws/heartbeat.js.map +1 -0
  164. package/docker-compose.yml +38 -0
  165. package/nginx.conf.example +63 -0
  166. package/package.json +61 -0
@@ -0,0 +1,15 @@
1
+ import { Hono } from "hono";
2
+ import type { SessionStore } from "../services/session-store.js";
3
+ import type { AuthService } from "../services/auth-service.js";
4
+ import type { ServerEnv } from "../types.js";
5
+ /**
6
+ * Create poll routes sub-router.
7
+ *
8
+ * GET /:sessionId/poll -> Polling fallback for environments without WebSocket
9
+ *
10
+ * Returns new messages and peer lifecycle events since the last poll,
11
+ * filtered for the requesting peer. Updates the peer's lastSeenAt
12
+ * to keep it alive for stale cleanup.
13
+ */
14
+ export declare function createPollRoutes(sessionStore: SessionStore, authService: AuthService): Hono<ServerEnv>;
15
+ //# sourceMappingURL=poll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../src/routes/poll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,GACvB,IAAI,CAAC,SAAS,CAAC,CA6GjB"}
@@ -0,0 +1,97 @@
1
+ import { Hono } from "hono";
2
+ import { zValidator } from "@hono/zod-validator";
3
+ import { BridgeError, ErrorCode, PollQuerySchema, } from "@essentialai/cogent";
4
+ import { createAuthMiddleware } from "../middleware/auth.js";
5
+ import { validationHook } from "./validation-hook.js";
6
+ /**
7
+ * Create poll routes sub-router.
8
+ *
9
+ * GET /:sessionId/poll -> Polling fallback for environments without WebSocket
10
+ *
11
+ * Returns new messages and peer lifecycle events since the last poll,
12
+ * filtered for the requesting peer. Updates the peer's lastSeenAt
13
+ * to keep it alive for stale cleanup.
14
+ */
15
+ export function createPollRoutes(sessionStore, authService) {
16
+ const poll = new Hono();
17
+ // Auth middleware applied per-route (not wildcard) to avoid intercepting
18
+ // unauthenticated session routes mounted at the same /api/sessions prefix.
19
+ const auth = createAuthMiddleware(sessionStore, authService);
20
+ /**
21
+ * GET /:sessionId/poll - Poll for new messages and peer events.
22
+ *
23
+ * If lastMessageId is provided, returns messages after that ID.
24
+ * If lastMessageId is not found (truncated by cap), returns all messages.
25
+ * If lastMessageId is not provided (first poll), returns all messages.
26
+ *
27
+ * Messages are filtered to those relevant to the polling peer:
28
+ * sent by, addressed to, or broadcast to this peer.
29
+ *
30
+ * Peer events are filtered by timestamp (after the lastMessageId's timestamp).
31
+ * Limited to the last 100 events to prevent unbounded growth.
32
+ */
33
+ poll.get("/:sessionId/poll", auth, zValidator("query", PollQuerySchema, validationHook), async (c) => {
34
+ const authSessionId = c.get("sessionId");
35
+ const pathSessionId = c.req.param("sessionId");
36
+ // Verify path param matches authenticated session
37
+ if (pathSessionId !== authSessionId) {
38
+ throw new BridgeError(ErrorCode.AUTH_INVALID_TOKEN, "Token does not belong to this session");
39
+ }
40
+ const { lastMessageId, peerId } = c.req.valid("query");
41
+ // Update the polling peer's lastSeenAt to keep them alive for stale cleanup
42
+ await sessionStore.updateSession(authSessionId, (state) => {
43
+ if (state.peers[peerId]) {
44
+ state.peers[peerId].lastSeenAt = new Date().toISOString();
45
+ }
46
+ return state;
47
+ });
48
+ // Fresh read from store after the update
49
+ const session = await sessionStore.getSession(authSessionId);
50
+ if (!session) {
51
+ throw new BridgeError(ErrorCode.SESSION_NOT_FOUND, `Session ${authSessionId} not found`);
52
+ }
53
+ // Find new messages since lastMessageId
54
+ let newMessages;
55
+ let referenceTimestamp = null;
56
+ if (lastMessageId) {
57
+ const idx = session.messages.findIndex((msg) => msg.id === lastMessageId);
58
+ if (idx !== -1) {
59
+ // Found the message -- return everything after it
60
+ referenceTimestamp = session.messages[idx].timestamp;
61
+ newMessages = session.messages.slice(idx + 1);
62
+ }
63
+ else {
64
+ // Message not found (may have been truncated by cap) -- return all
65
+ newMessages = session.messages;
66
+ }
67
+ }
68
+ else {
69
+ // First poll -- return all messages
70
+ newMessages = session.messages;
71
+ }
72
+ // Filter messages relevant to this peer
73
+ const filteredMessages = newMessages.filter((msg) => msg.toPeerId === peerId ||
74
+ msg.toPeerId === "*" ||
75
+ msg.fromPeerId === peerId);
76
+ // Find peer events since the reference timestamp
77
+ let events;
78
+ if (referenceTimestamp) {
79
+ const refTime = new Date(referenceTimestamp).getTime();
80
+ events = session.peerEvents.filter((evt) => new Date(evt.timestamp).getTime() > refTime);
81
+ }
82
+ else {
83
+ // No reference -- return all events
84
+ events = session.peerEvents;
85
+ }
86
+ // Limit to last 100 events to prevent unbounded growth
87
+ if (events.length > 100) {
88
+ events = events.slice(-100);
89
+ }
90
+ return c.json({
91
+ messages: filteredMessages,
92
+ events,
93
+ });
94
+ });
95
+ return poll;
96
+ }
97
+ //# sourceMappingURL=poll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll.js","sourceRoot":"","sources":["../../src/routes/poll.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Hono } from "hono";
2
+ import type { SessionStore } from "../services/session-store.js";
3
+ import type { AuthService } from "../services/auth-service.js";
4
+ import type { ServerEnv } from "../types.js";
5
+ /**
6
+ * Create session routes sub-router.
7
+ *
8
+ * POST / -> Create a new session (maps to /api/sessions via app.route)
9
+ * POST /:sessionId/join -> Join an existing session
10
+ *
11
+ * Both endpoints are unauthenticated (they issue tokens, not consume them).
12
+ */
13
+ export declare function createSessionRoutes(sessionStore: SessionStore, authService: AuthService): Hono<ServerEnv>;
14
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,aAAa,CAAC;AAGzD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,GACvB,IAAI,CAAC,SAAS,CAAC,CA0JjB"}
@@ -0,0 +1,113 @@
1
+ import { Hono } from "hono";
2
+ import { zValidator } from "@hono/zod-validator";
3
+ import { BridgeError, ErrorCode, CreateSessionRequestSchema, JoinSessionRequestSchema, generateSessionId, generateSessionLabel, isValidSessionLabel, } from "@essentialai/cogent";
4
+ import { validationHook } from "./validation-hook.js";
5
+ /**
6
+ * Create session routes sub-router.
7
+ *
8
+ * POST / -> Create a new session (maps to /api/sessions via app.route)
9
+ * POST /:sessionId/join -> Join an existing session
10
+ *
11
+ * Both endpoints are unauthenticated (they issue tokens, not consume them).
12
+ */
13
+ export function createSessionRoutes(sessionStore, authService) {
14
+ const sessions = new Hono();
15
+ /**
16
+ * POST / - Create a new session.
17
+ *
18
+ * Receives { secret, label? }, bcrypt-hashes the secret, generates a bearer
19
+ * token, creates the session on disk, and returns { sessionId, token, createdAt }.
20
+ */
21
+ sessions.post("/", zValidator("json", CreateSessionRequestSchema, validationHook), async (c) => {
22
+ const { secret, label } = c.req.valid("json");
23
+ // Generate unique session ID
24
+ const sessionId = generateSessionId();
25
+ // Auto-generate label if not provided, with collision retry
26
+ let sessionLabel = label;
27
+ if (!sessionLabel) {
28
+ for (let i = 0; i < 5; i++) {
29
+ const candidate = generateSessionLabel();
30
+ if (!sessionStore.isLabelTaken(candidate)) {
31
+ sessionLabel = candidate;
32
+ break;
33
+ }
34
+ }
35
+ if (!sessionLabel)
36
+ sessionLabel = generateSessionLabel(); // last resort, collision unlikely
37
+ }
38
+ // Hash secret with bcrypt for secure storage
39
+ const secretHash = await authService.hashSecret(secret);
40
+ // Generate bearer token (returns plaintext + SHA-256 hash)
41
+ const { token, tokenHash } = authService.generateToken();
42
+ // Build token entry for storage
43
+ const tokenEntry = {
44
+ tokenHash,
45
+ createdAt: new Date().toISOString(),
46
+ };
47
+ // Capture creator IP (behind nginx: X-Forwarded-For or X-Real-IP)
48
+ const creatorIp = c.req.header("x-forwarded-for")?.split(",")[0].trim() ||
49
+ c.req.header("x-real-ip") ||
50
+ undefined;
51
+ // Persist the session
52
+ const session = await sessionStore.createSession(sessionId, sessionLabel, secretHash, tokenEntry, creatorIp);
53
+ return c.json({
54
+ sessionId,
55
+ token,
56
+ label: sessionLabel,
57
+ createdAt: session.createdAt,
58
+ }, 201);
59
+ });
60
+ /**
61
+ * GET /resolve/:label - Resolve a session label to its UUID.
62
+ *
63
+ * Unauthenticated -- a sessionId alone grants no access (token required).
64
+ * Returns { sessionId, label } or 400/404.
65
+ */
66
+ sessions.get("/resolve/:label", async (c) => {
67
+ const label = c.req.param("label");
68
+ if (!isValidSessionLabel(label)) {
69
+ return c.json({ error: "Invalid label format. Must be 3-32 lowercase alphanumeric characters with internal hyphens." }, 400);
70
+ }
71
+ const sessionId = sessionStore.getSessionIdByLabel(label);
72
+ if (!sessionId) {
73
+ throw new BridgeError(ErrorCode.SESSION_NOT_FOUND, `No session with label "${label}"`, "Check the label or use the full session ID");
74
+ }
75
+ return c.json({ sessionId, label });
76
+ });
77
+ /**
78
+ * POST /:sessionId/join - Join an existing session.
79
+ *
80
+ * Receives { secret }, verifies against bcrypt hash, generates a new bearer
81
+ * token, and returns { sessionId, token, peerCount }.
82
+ */
83
+ sessions.post("/:sessionId/join", zValidator("json", JoinSessionRequestSchema, validationHook), async (c) => {
84
+ const sessionId = c.req.param("sessionId");
85
+ const { secret } = c.req.valid("json");
86
+ // Look up the session
87
+ const session = await sessionStore.getSession(sessionId);
88
+ if (!session) {
89
+ throw new BridgeError(ErrorCode.SESSION_NOT_FOUND, `Session ${sessionId} not found`, "Check the session ID or create a new session");
90
+ }
91
+ // Verify the provided secret against the stored bcrypt hash
92
+ const isValid = await authService.verifySecret(secret, session.secretHash);
93
+ if (!isValid) {
94
+ throw new BridgeError(ErrorCode.AUTH_INVALID_TOKEN, "Invalid session secret", "Verify you are using the correct shared secret for this session");
95
+ }
96
+ // Generate a new bearer token for this joining client
97
+ const { token, tokenHash } = authService.generateToken();
98
+ const tokenEntry = {
99
+ tokenHash,
100
+ createdAt: new Date().toISOString(),
101
+ };
102
+ // Add the token to the session
103
+ await sessionStore.addToken(sessionId, tokenEntry);
104
+ return c.json({
105
+ sessionId,
106
+ token,
107
+ label: session.label ?? "",
108
+ peerCount: Object.keys(session.peers).length,
109
+ });
110
+ });
111
+ return sessions;
112
+ }
113
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { Hono } from "hono";
2
+ import type { ServerEnv } from "../types.js";
3
+ import type { StatsService } from "../services/stats-service.js";
4
+ import type { SessionStore } from "../services/session-store.js";
5
+ import type { ConnectionManager } from "../services/connection-manager.js";
6
+ /** Dependencies for UI route handlers. */
7
+ export interface UiRouteDeps {
8
+ statsService: StatsService;
9
+ adminPassword: string | null;
10
+ sessionStore: SessionStore;
11
+ connectionManager: ConnectionManager;
12
+ }
13
+ /**
14
+ * Create UI route handlers for the public landing page, SSE stats stream,
15
+ * and password-protected admin dashboard with session/peer CRUD and message log.
16
+ *
17
+ * Admin routes are only mounted when adminPassword is set (non-null).
18
+ * When disabled, /admin/* returns 404 via Hono's default handler.
19
+ */
20
+ export declare function createUiRoutes(deps: UiRouteDeps): Hono<ServerEnv>;
21
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/routes/ui.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAU3E,0CAA0C;AAC1C,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,iBAAiB,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,CAkMjE"}
@@ -0,0 +1,173 @@
1
+ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
2
+ import { Hono } from "hono";
3
+ import { streamSSE } from "hono/streaming";
4
+ import { basicAuth } from "hono/basic-auth";
5
+ import { LandingPage } from "../ui/pages/LandingPage.js";
6
+ import { HowToPage } from "../ui/pages/HowToPage.js";
7
+ import { Layout } from "../ui/components/Layout.js";
8
+ import { AdminDashboard } from "../ui/pages/AdminDashboard.js";
9
+ import { SessionList } from "../ui/pages/SessionList.js";
10
+ import { SessionDetail } from "../ui/pages/SessionDetail.js";
11
+ import { MessageLog } from "../ui/pages/MessageLog.js";
12
+ /**
13
+ * Create UI route handlers for the public landing page, SSE stats stream,
14
+ * and password-protected admin dashboard with session/peer CRUD and message log.
15
+ *
16
+ * Admin routes are only mounted when adminPassword is set (non-null).
17
+ * When disabled, /admin/* returns 404 via Hono's default handler.
18
+ */
19
+ export function createUiRoutes(deps) {
20
+ const { statsService, adminPassword, sessionStore, connectionManager } = deps;
21
+ const ui = new Hono();
22
+ // GET / -- Landing page with server-rendered stats
23
+ ui.get("/", (c) => {
24
+ const stats = statsService.getStats();
25
+ return c.html(_jsx(LandingPage, { stats: stats }));
26
+ });
27
+ // GET /how-to -- Comprehensive how-to guide
28
+ ui.get("/how-to", (c) => {
29
+ return c.html(_jsx(HowToPage, {}));
30
+ });
31
+ // GET /ui/stats/stream -- SSE endpoint pushing stats every 5 seconds
32
+ ui.get("/ui/stats/stream", (c) => {
33
+ return streamSSE(c, async (stream) => {
34
+ while (true) {
35
+ const stats = statsService.getStats();
36
+ await stream.writeSSE({
37
+ data: JSON.stringify(stats),
38
+ event: "stats",
39
+ id: String(Date.now()),
40
+ });
41
+ await stream.sleep(5000);
42
+ }
43
+ });
44
+ });
45
+ // --- Admin routes (only when password is configured) ---
46
+ if (adminPassword) {
47
+ const admin = new Hono();
48
+ // Basic Auth middleware for all admin routes
49
+ admin.use("*", basicAuth({
50
+ verifyUser: (username, password) => {
51
+ return username === "admin" && password === adminPassword;
52
+ },
53
+ realm: "COGENT Admin",
54
+ }));
55
+ // GET /admin/ or /admin -- Dashboard overview
56
+ admin.get("/", async (_c) => {
57
+ const stats = statsService.getStats();
58
+ return _c.html(_jsx(AdminDashboard, { stats: stats }));
59
+ });
60
+ // GET /admin/sessions -- Session list
61
+ admin.get("/sessions", async (c) => {
62
+ const sessionIds = await sessionStore.listSessions();
63
+ const sessions = [];
64
+ for (const id of sessionIds) {
65
+ const session = await sessionStore.getSession(id);
66
+ if (session) {
67
+ sessions.push({
68
+ sessionId: id,
69
+ label: session.label,
70
+ createdAt: session.createdAt,
71
+ creatorIp: session.creatorIp,
72
+ peerCount: Object.keys(session.peers).length,
73
+ messageCount: session.messages.length,
74
+ connectedPeerIds: connectionManager.getConnectedPeerIds(id),
75
+ });
76
+ }
77
+ }
78
+ return c.html(_jsx(SessionList, { sessions: sessions }));
79
+ });
80
+ // GET /admin/sessions/:id -- Session detail
81
+ admin.get("/sessions/:id", async (c) => {
82
+ const sessionId = c.req.param("id");
83
+ const session = await sessionStore.getSession(sessionId);
84
+ if (!session) {
85
+ return c.html(_jsx(Layout, { title: "Not Found", children: _jsx("p", { children: "Session not found" }) }), 404);
86
+ }
87
+ const connectedPeerIds = connectionManager.getConnectedPeerIds(sessionId);
88
+ return c.html(_jsx(SessionDetail, { session: session, connectedPeerIds: connectedPeerIds }));
89
+ });
90
+ // POST /admin/sessions/:id/delete -- Delete session
91
+ admin.post("/sessions/:id/delete", async (c) => {
92
+ const sessionId = c.req.param("id");
93
+ // Close all WS connections for this session before deleting
94
+ const connectedPeers = connectionManager.getConnectedPeerIds(sessionId);
95
+ for (const peerId of connectedPeers) {
96
+ const conns = connectionManager.getConnections(sessionId, peerId);
97
+ for (const conn of conns) {
98
+ try {
99
+ conn.ws.close(1000, "Session deleted by admin");
100
+ }
101
+ catch {
102
+ // Ignore errors from already-closed sockets
103
+ }
104
+ }
105
+ }
106
+ await sessionStore.deleteSession(sessionId);
107
+ return c.redirect("/admin/sessions");
108
+ });
109
+ // POST /admin/sessions/:id/peers/:peerId/delete -- Delete peer from session
110
+ admin.post("/sessions/:id/peers/:peerId/delete", async (c) => {
111
+ const sessionId = c.req.param("id");
112
+ const peerId = c.req.param("peerId");
113
+ // Close WS connections for this peer
114
+ const conns = connectionManager.getConnections(sessionId, peerId);
115
+ for (const conn of conns) {
116
+ try {
117
+ conn.ws.close(1000, "Peer removed by admin");
118
+ }
119
+ catch {
120
+ // Ignore errors from already-closed sockets
121
+ }
122
+ }
123
+ // Remove peer from session state
124
+ await sessionStore.updateSession(sessionId, (state) => {
125
+ const { [peerId]: _, ...remainingPeers } = state.peers;
126
+ return { ...state, peers: remainingPeers };
127
+ });
128
+ return c.redirect("/admin/sessions/" + sessionId);
129
+ });
130
+ // GET /admin/messages -- Message log with filtering
131
+ admin.get("/messages", async (c) => {
132
+ const filterSessionId = c.req.query("sessionId") || undefined;
133
+ const filterPeerId = c.req.query("peerId") || undefined;
134
+ const filterQuery = c.req.query("q") || undefined;
135
+ const sessionIds = filterSessionId
136
+ ? [filterSessionId]
137
+ : await sessionStore.listSessions();
138
+ const allMessages = [];
139
+ for (const id of sessionIds) {
140
+ const session = await sessionStore.getSession(id);
141
+ if (session) {
142
+ for (const msg of session.messages) {
143
+ allMessages.push({ ...msg, sessionId: id });
144
+ }
145
+ }
146
+ }
147
+ // Apply filters
148
+ let filtered = allMessages;
149
+ if (filterPeerId) {
150
+ filtered = filtered.filter((m) => m.fromPeerId === filterPeerId || m.toPeerId === filterPeerId);
151
+ }
152
+ if (filterQuery) {
153
+ const q = filterQuery.toLowerCase();
154
+ filtered = filtered.filter((m) => m.message.toLowerCase().includes(q));
155
+ }
156
+ // Sort by timestamp descending (newest first), take last 200
157
+ filtered.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
158
+ filtered = filtered.slice(0, 200);
159
+ const allSessionIds = await sessionStore.listSessions();
160
+ return c.html(_jsx(MessageLog, { messages: filtered, filters: {
161
+ sessionId: filterSessionId,
162
+ peerId: filterPeerId,
163
+ query: filterQuery,
164
+ }, sessions: allSessionIds }));
165
+ });
166
+ // Mount admin sub-router
167
+ ui.route("/admin", admin);
168
+ // Hono sub-routers don't match trailing slash on root — redirect /admin/ → /admin
169
+ ui.get("/admin/", (c) => c.redirect("/admin"));
170
+ }
171
+ return ui;
172
+ }
173
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/routes/ui.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Context, Env } from "hono";
2
+ /**
3
+ * Shared validation hook for @hono/zod-validator.
4
+ *
5
+ * Converts Zod validation failures into structured BridgeError JSON
6
+ * responses with error code INVALID_INPUT and HTTP 400. This ensures
7
+ * all validation errors across routes use a consistent format.
8
+ *
9
+ * Reusable by all route files that use zValidator.
10
+ *
11
+ * Note: The hook uses the base Env type (not ServerEnv) because
12
+ * @hono/zod-validator's Hook type narrows Env generically.
13
+ */
14
+ export declare function validationHook(result: {
15
+ success: boolean;
16
+ error?: unknown;
17
+ }, c: Context<Env>): Response | void;
18
+ //# sourceMappingURL=validation-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-hook.d.ts","sourceRoot":"","sources":["../../src/routes/validation-hook.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC7C,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GACd,QAAQ,GAAG,IAAI,CAejB"}
@@ -0,0 +1,24 @@
1
+ import { BridgeError, ErrorCode } from "@essentialai/cogent";
2
+ /**
3
+ * Shared validation hook for @hono/zod-validator.
4
+ *
5
+ * Converts Zod validation failures into structured BridgeError JSON
6
+ * responses with error code INVALID_INPUT and HTTP 400. This ensures
7
+ * all validation errors across routes use a consistent format.
8
+ *
9
+ * Reusable by all route files that use zValidator.
10
+ *
11
+ * Note: The hook uses the base Env type (not ServerEnv) because
12
+ * @hono/zod-validator's Hook type narrows Env generically.
13
+ */
14
+ export function validationHook(result, c) {
15
+ if (!result.success) {
16
+ const zodError = result.error;
17
+ const message = zodError instanceof Error
18
+ ? zodError.message
19
+ : "Invalid request body";
20
+ const err = new BridgeError(ErrorCode.INVALID_INPUT, `Validation failed: ${message}`, "Check request body matches the expected schema");
21
+ return c.json(err.toJSON(), 400);
22
+ }
23
+ }
24
+ //# sourceMappingURL=validation-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-hook.js","sourceRoot":"","sources":["../../src/routes/validation-hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAG7D;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC5B,MAA6C,EAC7C,CAAe;IAEf,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;QAC9B,MAAM,OAAO,GACX,QAAQ,YAAY,KAAK;YACvB,CAAC,CAAC,QAAQ,CAAC,OAAO;YAClB,CAAC,CAAC,sBAAsB,CAAC;QAE7B,MAAM,GAAG,GAAG,IAAI,WAAW,CACzB,SAAS,CAAC,aAAa,EACvB,sBAAsB,OAAO,EAAE,EAC/B,gDAAgD,CACjD,CAAC;QACF,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;IACnC,CAAC;AACH,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Authentication service for the Cogent server.
3
+ *
4
+ * Responsibilities:
5
+ * - bcrypt hashing of session secrets (one-time, during create/join)
6
+ * - Random bearer token generation with SHA-256 hashing for storage
7
+ * - Timing-safe comparison for token verification (every request)
8
+ *
9
+ * bcrypt is used for secrets (slow, salted -- appropriate for passwords).
10
+ * SHA-256 is used for tokens (fast -- tokens are high-entropy random values).
11
+ * Timing-safe comparison prevents timing attacks on token verification.
12
+ */
13
+ export declare class AuthService {
14
+ private readonly bcryptRounds;
15
+ constructor(bcryptRounds: number);
16
+ /**
17
+ * Hash a plaintext secret using bcrypt.
18
+ * Used during session creation and join operations.
19
+ */
20
+ hashSecret(plainSecret: string): Promise<string>;
21
+ /**
22
+ * Verify a plaintext secret against a bcrypt hash.
23
+ * Used during session join to validate the provided secret.
24
+ */
25
+ verifySecret(plainSecret: string, hash: string): Promise<boolean>;
26
+ /**
27
+ * Generate a cryptographically random bearer token.
28
+ * Returns both the plaintext token (to send to client) and its
29
+ * SHA-256 hash (to store on disk for lookup).
30
+ */
31
+ generateToken(): {
32
+ token: string;
33
+ tokenHash: string;
34
+ };
35
+ /**
36
+ * Compute the SHA-256 hash of a token string.
37
+ * Used by auth middleware to look up tokens in the session store index.
38
+ */
39
+ hashToken(token: string): string;
40
+ /**
41
+ * Timing-safe string comparison.
42
+ * Hashes both strings with SHA-256 to normalize length, then uses
43
+ * crypto.timingSafeEqual on the resulting buffers. This prevents
44
+ * timing attacks regardless of input string lengths.
45
+ */
46
+ timingSafeCompare(a: string, b: string): boolean;
47
+ }
48
+ //# sourceMappingURL=auth-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../../src/services/auth-service.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,YAAY,EAAE,MAAM;IAIhC;;;OAGG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAItD;;;OAGG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIvE;;;;OAIG;IACH,aAAa,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAMrD;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIhC;;;;;OAKG;IACH,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;CAKjD"}
@@ -0,0 +1,63 @@
1
+ import crypto from "node:crypto";
2
+ import bcrypt from "bcryptjs";
3
+ /**
4
+ * Authentication service for the Cogent server.
5
+ *
6
+ * Responsibilities:
7
+ * - bcrypt hashing of session secrets (one-time, during create/join)
8
+ * - Random bearer token generation with SHA-256 hashing for storage
9
+ * - Timing-safe comparison for token verification (every request)
10
+ *
11
+ * bcrypt is used for secrets (slow, salted -- appropriate for passwords).
12
+ * SHA-256 is used for tokens (fast -- tokens are high-entropy random values).
13
+ * Timing-safe comparison prevents timing attacks on token verification.
14
+ */
15
+ export class AuthService {
16
+ bcryptRounds;
17
+ constructor(bcryptRounds) {
18
+ this.bcryptRounds = bcryptRounds;
19
+ }
20
+ /**
21
+ * Hash a plaintext secret using bcrypt.
22
+ * Used during session creation and join operations.
23
+ */
24
+ async hashSecret(plainSecret) {
25
+ return bcrypt.hash(plainSecret, this.bcryptRounds);
26
+ }
27
+ /**
28
+ * Verify a plaintext secret against a bcrypt hash.
29
+ * Used during session join to validate the provided secret.
30
+ */
31
+ async verifySecret(plainSecret, hash) {
32
+ return bcrypt.compare(plainSecret, hash);
33
+ }
34
+ /**
35
+ * Generate a cryptographically random bearer token.
36
+ * Returns both the plaintext token (to send to client) and its
37
+ * SHA-256 hash (to store on disk for lookup).
38
+ */
39
+ generateToken() {
40
+ const token = crypto.randomBytes(32).toString("hex");
41
+ const tokenHash = this.hashToken(token);
42
+ return { token, tokenHash };
43
+ }
44
+ /**
45
+ * Compute the SHA-256 hash of a token string.
46
+ * Used by auth middleware to look up tokens in the session store index.
47
+ */
48
+ hashToken(token) {
49
+ return crypto.createHash("sha256").update(token).digest("hex");
50
+ }
51
+ /**
52
+ * Timing-safe string comparison.
53
+ * Hashes both strings with SHA-256 to normalize length, then uses
54
+ * crypto.timingSafeEqual on the resulting buffers. This prevents
55
+ * timing attacks regardless of input string lengths.
56
+ */
57
+ timingSafeCompare(a, b) {
58
+ const hashA = crypto.createHash("sha256").update(a).digest();
59
+ const hashB = crypto.createHash("sha256").update(b).digest();
60
+ return crypto.timingSafeEqual(hashA, hashB);
61
+ }
62
+ }
63
+ //# sourceMappingURL=auth-service.js.map