@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
package/dist/app.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { Hono } from "hono";
2
+ import type { Server } from "node:http";
3
+ import type { Http2Server, Http2SecureServer } from "node:http2";
4
+ import type { SessionStore } from "./services/session-store.js";
5
+ import type { AuthService } from "./services/auth-service.js";
6
+ import type { ConnectionManager } from "./services/connection-manager.js";
7
+ import type { MessageQueueService } from "./services/message-queue.js";
8
+ import type { StatsService } from "./services/stats-service.js";
9
+ import type { ServerEnv } from "./types.js";
10
+ /** Dependencies required to create the Hono app. */
11
+ export interface AppDeps {
12
+ sessionStore: SessionStore;
13
+ authService: AuthService;
14
+ startTime: number;
15
+ maxMessages: number;
16
+ connectionManager: ConnectionManager;
17
+ messageQueue: MessageQueueService;
18
+ statsService: StatsService;
19
+ adminPassword: string | null;
20
+ heartbeatIntervalMs: number;
21
+ heartbeatMaxMissed: number;
22
+ }
23
+ /** Return type of createApp -- app + injectWebSocket for index.ts wiring. */
24
+ export interface AppResult {
25
+ app: Hono<ServerEnv>;
26
+ injectWebSocket: (server: Server | Http2Server | Http2SecureServer) => void;
27
+ }
28
+ /**
29
+ * Create and configure the Hono app with all middleware and routes.
30
+ *
31
+ * Uses an app factory pattern so the app can be tested via app.request()
32
+ * without starting a real HTTP server.
33
+ *
34
+ * Returns both the Hono app and the injectWebSocket function that must
35
+ * be called with the HTTP server after serve() -- see RESEARCH.md Pitfall 2.
36
+ */
37
+ export declare function createApp(deps: AppDeps): AppResult;
38
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAa5C,oDAAoD;AACpD,MAAM,WAAW,OAAO;IACtB,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,YAAY,EAAE,mBAAmB,CAAC;IAClC,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,6EAA6E;AAC7E,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACrB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,iBAAiB,KAAK,IAAI,CAAC;CAC7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CA6DlD"}
package/dist/app.js ADDED
@@ -0,0 +1,60 @@
1
+ import { Hono } from "hono";
2
+ import { createNodeWebSocket } from "@hono/node-ws";
3
+ import { requestLogger } from "./middleware/request-logger.js";
4
+ import { errorHandler } from "./middleware/error-handler.js";
5
+ import { notFoundHandler } from "./middleware/not-found.js";
6
+ import { createSessionRoutes } from "./routes/sessions.js";
7
+ import { createHealthRoutes } from "./routes/health.js";
8
+ import { createPeerRoutes } from "./routes/peers.js";
9
+ import { createMessageRoutes } from "./routes/messages.js";
10
+ import { createPollRoutes } from "./routes/poll.js";
11
+ import { createUiRoutes } from "./routes/ui.js";
12
+ import { createWsAuthMiddleware } from "./middleware/ws-auth.js";
13
+ import { createWsHandler } from "./ws/handler.js";
14
+ /**
15
+ * Create and configure the Hono app with all middleware and routes.
16
+ *
17
+ * Uses an app factory pattern so the app can be tested via app.request()
18
+ * without starting a real HTTP server.
19
+ *
20
+ * Returns both the Hono app and the injectWebSocket function that must
21
+ * be called with the HTTP server after serve() -- see RESEARCH.md Pitfall 2.
22
+ */
23
+ export function createApp(deps) {
24
+ const { sessionStore, authService, startTime, maxMessages, connectionManager, messageQueue, statsService, adminPassword, heartbeatIntervalMs, heartbeatMaxMissed, } = deps;
25
+ const app = new Hono();
26
+ const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
27
+ // --- Error handling ---
28
+ app.onError(errorHandler);
29
+ app.notFound(notFoundHandler);
30
+ // --- WebSocket route (registered BEFORE request logger to avoid header conflicts) ---
31
+ // Auth middleware reads token from query param; runs before WS upgrade handshake.
32
+ app.get("/ws/:sessionId", createWsAuthMiddleware(sessionStore, authService), upgradeWebSocket((c) => {
33
+ const sessionId = c.get("sessionId");
34
+ const peerId = c.req.query("peerId");
35
+ return createWsHandler(sessionId, peerId, {
36
+ connectionManager,
37
+ messageQueue,
38
+ sessionStore,
39
+ heartbeatIntervalMs,
40
+ heartbeatMaxMissed,
41
+ });
42
+ }));
43
+ // --- Global middleware (applied after WS route to avoid header conflicts per RESEARCH.md Pitfall 1) ---
44
+ app.use("*", requestLogger());
45
+ // --- UI routes (after requestLogger, before API routes) ---
46
+ const uiRoutes = createUiRoutes({ statsService, adminPassword, sessionStore, connectionManager });
47
+ app.route("/", uiRoutes);
48
+ // --- Unauthenticated routes ---
49
+ app.route("/api/sessions", createSessionRoutes(sessionStore, authService));
50
+ app.route("/api/health", createHealthRoutes(sessionStore, startTime, connectionManager));
51
+ // --- Authenticated routes ---
52
+ // Peer routes: /:sessionId/peers (POST, GET), /:sessionId/peers/:peerId (DELETE)
53
+ app.route("/api/sessions", createPeerRoutes(sessionStore, authService, connectionManager));
54
+ // Message routes: /:sessionId/messages (POST, GET)
55
+ app.route("/api/sessions", createMessageRoutes(sessionStore, authService, maxMessages, connectionManager, messageQueue));
56
+ // Poll routes: /:sessionId/poll (GET)
57
+ app.route("/api/sessions", createPollRoutes(sessionStore, authService));
58
+ return { app, injectWebSocket };
59
+ }
60
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Zod schema for server configuration.
4
+ * All values loadable from COGENT_SERVER_* environment variables.
5
+ */
6
+ declare const ServerConfigSchema: z.ZodObject<{
7
+ PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
8
+ HOST: z.ZodDefault<z.ZodString>;
9
+ STATE_DIR: z.ZodDefault<z.ZodString>;
10
+ LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
11
+ debug: "debug";
12
+ error: "error";
13
+ info: "info";
14
+ warn: "warn";
15
+ }>>;
16
+ BCRYPT_ROUNDS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
17
+ STALE_PEER_TIMEOUT_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
18
+ MAX_MESSAGES_PER_SESSION: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
19
+ CORS_ORIGIN: z.ZodDefault<z.ZodString>;
20
+ WS_HEARTBEAT_INTERVAL_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
21
+ WS_HEARTBEAT_MAX_MISSED: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
22
+ WS_GRACE_PERIOD_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
23
+ QUEUE_TTL_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
24
+ QUEUE_CLEANUP_INTERVAL_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
25
+ SESSION_INACTIVITY_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
26
+ SESSION_CLEANUP_INTERVAL_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
27
+ ADMIN_PASSWORD: z.ZodOptional<z.ZodString>;
28
+ }, z.core.$strip>;
29
+ /** Inferred server configuration type. */
30
+ export type ServerConfig = z.infer<typeof ServerConfigSchema>;
31
+ /**
32
+ * Shape of camelCase keys accepted in config.json.
33
+ * Each key maps to a COGENT_SERVER_* env var equivalent.
34
+ */
35
+ export interface ConfigFileOptions {
36
+ port?: number;
37
+ host?: string;
38
+ stateDir?: string;
39
+ logLevel?: string;
40
+ bcryptRounds?: number;
41
+ stalePeerTimeoutMs?: number;
42
+ maxMessagesPerSession?: number;
43
+ corsOrigin?: string;
44
+ wsHeartbeatIntervalMs?: number;
45
+ wsHeartbeatMaxMissed?: number;
46
+ wsGracePeriodMs?: number;
47
+ queueTtlMs?: number;
48
+ queueCleanupIntervalMs?: number;
49
+ sessionInactivityMs?: number;
50
+ sessionCleanupIntervalMs?: number;
51
+ adminPassword?: string;
52
+ }
53
+ /**
54
+ * Load a config.json file and return its values mapped to COGENT_SERVER_* env var names.
55
+ *
56
+ * - If the file does not exist, returns an empty object (no error).
57
+ * - If the file contains invalid JSON, throws with a clear error message.
58
+ * - Only non-undefined values are included in the result.
59
+ */
60
+ export declare function loadConfigFile(configPath: string): Record<string, string>;
61
+ /**
62
+ * Load server configuration from COGENT_SERVER_* environment variables,
63
+ * with optional config.json support.
64
+ *
65
+ * Precedence (highest to lowest):
66
+ * 1. Environment variables (COGENT_SERVER_*)
67
+ * 2. config.json values (if file exists)
68
+ * 3. Zod schema defaults
69
+ *
70
+ * Strips the prefix, validates with Zod, freezes the result.
71
+ * Can only be called once -- subsequent calls throw.
72
+ *
73
+ * @param env - Environment variable source (defaults to process.env)
74
+ * @param configPath - Path to config.json file (default: "config.json" relative to cwd)
75
+ */
76
+ export declare function loadServerConfig(env?: Record<string, string | undefined>, configPath?: string): Readonly<ServerConfig>;
77
+ /**
78
+ * Get the previously loaded server configuration.
79
+ * Throws if loadServerConfig() has not been called.
80
+ */
81
+ export declare function getServerConfig(): Readonly<ServerConfig>;
82
+ /**
83
+ * Reset the loaded config (for testing only).
84
+ * @internal
85
+ */
86
+ export declare function _resetConfig(): void;
87
+ export {};
88
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;GAGG;AACH,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;iBA2BtB,CAAC;AAEH,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAsBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8BzE;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,EACrD,UAAU,GAAE,MAAgD,GAC3D,QAAQ,CAAC,YAAY,CAAC,CA2BxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,QAAQ,CAAC,YAAY,CAAC,CAOxD;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
package/dist/config.js ADDED
@@ -0,0 +1,148 @@
1
+ import { z } from "zod";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ /**
6
+ * Zod schema for server configuration.
7
+ * All values loadable from COGENT_SERVER_* environment variables.
8
+ */
9
+ const ServerConfigSchema = z.object({
10
+ PORT: z.coerce.number().default(3000),
11
+ HOST: z.string().default("0.0.0.0"),
12
+ STATE_DIR: z.string().default(path.join(os.homedir(), ".cogent-server", "sessions")),
13
+ LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
14
+ BCRYPT_ROUNDS: z.coerce.number().int().min(4).max(31).default(12),
15
+ STALE_PEER_TIMEOUT_MS: z.coerce.number().default(1_800_000),
16
+ MAX_MESSAGES_PER_SESSION: z.coerce.number().int().min(1).default(1500),
17
+ CORS_ORIGIN: z.string().default("*"),
18
+ /** Ping interval for WebSocket heartbeat (default: 30s). */
19
+ WS_HEARTBEAT_INTERVAL_MS: z.coerce.number().default(30_000),
20
+ /** Number of missed pongs before terminating a WebSocket connection (default: 3, giving 90s window). */
21
+ WS_HEARTBEAT_MAX_MISSED: z.coerce.number().int().min(1).default(3),
22
+ /** Grace period after last WS connection closes before marking peer offline (default: 30s). */
23
+ WS_GRACE_PERIOD_MS: z.coerce.number().default(30_000),
24
+ /** Queue message TTL in ms (default: 7 days). */
25
+ QUEUE_TTL_MS: z.coerce.number().default(604_800_000),
26
+ /** Queue cleanup interval in ms (default: 6 hours). */
27
+ QUEUE_CLEANUP_INTERVAL_MS: z.coerce.number().default(21_600_000),
28
+ /** Inactivity threshold before a session is eligible for automatic deletion (default: 7 days). */
29
+ SESSION_INACTIVITY_MS: z.coerce.number().default(604_800_000),
30
+ /** How often to scan for and delete inactive sessions (default: 1 hour). */
31
+ SESSION_CLEANUP_INTERVAL_MS: z.coerce.number().default(3_600_000),
32
+ /** Admin dashboard password. When undefined, admin dashboard is disabled entirely. */
33
+ ADMIN_PASSWORD: z.string().optional(),
34
+ });
35
+ const ENV_PREFIX = "COGENT_SERVER_";
36
+ /** Maps camelCase config.json keys to COGENT_SERVER_* env var names. */
37
+ const CONFIG_KEY_MAP = {
38
+ port: "COGENT_SERVER_PORT",
39
+ host: "COGENT_SERVER_HOST",
40
+ stateDir: "COGENT_SERVER_STATE_DIR",
41
+ logLevel: "COGENT_SERVER_LOG_LEVEL",
42
+ bcryptRounds: "COGENT_SERVER_BCRYPT_ROUNDS",
43
+ stalePeerTimeoutMs: "COGENT_SERVER_STALE_PEER_TIMEOUT_MS",
44
+ maxMessagesPerSession: "COGENT_SERVER_MAX_MESSAGES_PER_SESSION",
45
+ corsOrigin: "COGENT_SERVER_CORS_ORIGIN",
46
+ wsHeartbeatIntervalMs: "COGENT_SERVER_WS_HEARTBEAT_INTERVAL_MS",
47
+ wsHeartbeatMaxMissed: "COGENT_SERVER_WS_HEARTBEAT_MAX_MISSED",
48
+ wsGracePeriodMs: "COGENT_SERVER_WS_GRACE_PERIOD_MS",
49
+ queueTtlMs: "COGENT_SERVER_QUEUE_TTL_MS",
50
+ queueCleanupIntervalMs: "COGENT_SERVER_QUEUE_CLEANUP_INTERVAL_MS",
51
+ sessionInactivityMs: "COGENT_SERVER_SESSION_INACTIVITY_MS",
52
+ sessionCleanupIntervalMs: "COGENT_SERVER_SESSION_CLEANUP_INTERVAL_MS",
53
+ adminPassword: "COGENT_SERVER_ADMIN_PASSWORD",
54
+ };
55
+ /**
56
+ * Load a config.json file and return its values mapped to COGENT_SERVER_* env var names.
57
+ *
58
+ * - If the file does not exist, returns an empty object (no error).
59
+ * - If the file contains invalid JSON, throws with a clear error message.
60
+ * - Only non-undefined values are included in the result.
61
+ */
62
+ export function loadConfigFile(configPath) {
63
+ let content;
64
+ try {
65
+ content = fs.readFileSync(configPath, "utf-8");
66
+ }
67
+ catch (err) {
68
+ // File not found is OK -- config.json is optional
69
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
70
+ return {};
71
+ }
72
+ throw err;
73
+ }
74
+ let parsed;
75
+ try {
76
+ parsed = JSON.parse(content);
77
+ }
78
+ catch {
79
+ throw new Error(`Invalid JSON in config file: ${configPath}`);
80
+ }
81
+ const configObj = parsed;
82
+ const result = {};
83
+ for (const [key, envKey] of Object.entries(CONFIG_KEY_MAP)) {
84
+ const value = configObj[key];
85
+ if (value !== undefined) {
86
+ result[envKey] = String(value);
87
+ }
88
+ }
89
+ return result;
90
+ }
91
+ let _config = null;
92
+ /**
93
+ * Load server configuration from COGENT_SERVER_* environment variables,
94
+ * with optional config.json support.
95
+ *
96
+ * Precedence (highest to lowest):
97
+ * 1. Environment variables (COGENT_SERVER_*)
98
+ * 2. config.json values (if file exists)
99
+ * 3. Zod schema defaults
100
+ *
101
+ * Strips the prefix, validates with Zod, freezes the result.
102
+ * Can only be called once -- subsequent calls throw.
103
+ *
104
+ * @param env - Environment variable source (defaults to process.env)
105
+ * @param configPath - Path to config.json file (default: "config.json" relative to cwd)
106
+ */
107
+ export function loadServerConfig(env = process.env, configPath = path.join(process.cwd(), "config.json")) {
108
+ if (_config !== null) {
109
+ throw new Error("Server config already loaded. Use getServerConfig().");
110
+ }
111
+ // Load config.json values (returns empty object if file missing)
112
+ const fileValues = loadConfigFile(configPath);
113
+ // Strip COGENT_SERVER_ prefix from matching env vars
114
+ const raw = {};
115
+ for (const [key, value] of Object.entries(env)) {
116
+ if (key.startsWith(ENV_PREFIX) && value !== undefined) {
117
+ raw[key.slice(ENV_PREFIX.length)] = value;
118
+ }
119
+ }
120
+ // Merge config.json values UNDER env vars: only set if env var is not already present
121
+ for (const [envKey, fileValue] of Object.entries(fileValues)) {
122
+ const stripped = envKey.slice(ENV_PREFIX.length);
123
+ if (!(stripped in raw)) {
124
+ raw[stripped] = fileValue;
125
+ }
126
+ }
127
+ const parsed = ServerConfigSchema.parse(raw);
128
+ _config = Object.freeze(parsed);
129
+ return _config;
130
+ }
131
+ /**
132
+ * Get the previously loaded server configuration.
133
+ * Throws if loadServerConfig() has not been called.
134
+ */
135
+ export function getServerConfig() {
136
+ if (_config === null) {
137
+ throw new Error("Server config not loaded. Call loadServerConfig() first.");
138
+ }
139
+ return _config;
140
+ }
141
+ /**
142
+ * Reset the loaded config (for testing only).
143
+ * @internal
144
+ */
145
+ export function _resetConfig() {
146
+ _config = null;
147
+ }
148
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,102 @@
1
+ import { serve } from "@hono/node-server";
2
+ import { loadServerConfig, getServerConfig } from "./config.js";
3
+ import { AuthService } from "./services/auth-service.js";
4
+ import { SessionStore } from "./services/session-store.js";
5
+ import { PeerCleanup } from "./services/peer-cleanup.js";
6
+ import { ConnectionManager } from "./services/connection-manager.js";
7
+ import { MessageQueueService } from "./services/message-queue.js";
8
+ import { StatsService } from "./services/stats-service.js";
9
+ import { createPeerDisconnectedFrame } from "./ws/frames.js";
10
+ import { SessionCleanup } from "./services/session-cleanup.js";
11
+ import { createApp } from "./app.js";
12
+ /**
13
+ * Cogent Server entry point.
14
+ *
15
+ * 1. Load and validate server configuration from env vars
16
+ * 2. Initialize services (auth, session store, connection manager, message queue)
17
+ * 3. Create and wire the Hono app with WebSocket support
18
+ * 4. Start the HTTP server and inject WebSocket handler
19
+ * 5. Start background services (peer cleanup, queue cleanup)
20
+ * 6. Register graceful shutdown handlers
21
+ */
22
+ async function main() {
23
+ // Load config from COGENT_SERVER_* environment variables
24
+ loadServerConfig();
25
+ const config = getServerConfig();
26
+ // Initialize services
27
+ const authService = new AuthService(config.BCRYPT_ROUNDS);
28
+ const sessionStore = new SessionStore(config.STATE_DIR, config.MAX_MESSAGES_PER_SESSION);
29
+ await sessionStore.init();
30
+ // Create WebSocket-aware services
31
+ const connectionManager = new ConnectionManager(config.WS_GRACE_PERIOD_MS);
32
+ const messageQueue = new MessageQueueService(sessionStore, config.QUEUE_TTL_MS);
33
+ // Set up peer offline callback: broadcast peer_disconnected when grace period expires
34
+ connectionManager.setOnPeerOffline((sessionId, peerId) => {
35
+ const frame = createPeerDisconnectedFrame(peerId);
36
+ connectionManager.broadcastToSession(sessionId, JSON.stringify(frame));
37
+ });
38
+ // Record server start time for uptime calculation
39
+ const startTime = Date.now();
40
+ // Admin dashboard password (null = admin disabled)
41
+ const adminPassword = config.ADMIN_PASSWORD ?? null;
42
+ // Stats aggregation service for landing page and SSE stream
43
+ const statsService = new StatsService(sessionStore, connectionManager, startTime);
44
+ // Create the Hono app with all middleware, routes, and WebSocket support
45
+ const { app, injectWebSocket } = createApp({
46
+ sessionStore,
47
+ authService,
48
+ startTime,
49
+ maxMessages: config.MAX_MESSAGES_PER_SESSION,
50
+ connectionManager,
51
+ messageQueue,
52
+ statsService,
53
+ adminPassword,
54
+ heartbeatIntervalMs: config.WS_HEARTBEAT_INTERVAL_MS,
55
+ heartbeatMaxMissed: config.WS_HEARTBEAT_MAX_MISSED,
56
+ });
57
+ // Start stale peer cleanup background service
58
+ const peerCleanup = new PeerCleanup(sessionStore, config.STALE_PEER_TIMEOUT_MS);
59
+ peerCleanup.start();
60
+ // Start message queue cleanup background service
61
+ messageQueue.startCleanup(config.QUEUE_CLEANUP_INTERVAL_MS);
62
+ // Start inactive session cleanup background service (default: scan every hour, delete after 7 days)
63
+ const sessionCleanup = new SessionCleanup(sessionStore, connectionManager, config.SESSION_INACTIVITY_MS);
64
+ sessionCleanup.start(config.SESSION_CLEANUP_INTERVAL_MS);
65
+ // Start the HTTP server
66
+ const server = serve({
67
+ fetch: app.fetch,
68
+ port: config.PORT,
69
+ hostname: config.HOST,
70
+ });
71
+ // CRITICAL: inject WebSocket handler into the HTTP server (see RESEARCH.md Pitfall 2)
72
+ // Without this call, WebSocket upgrade requests would return 404.
73
+ injectWebSocket(server);
74
+ console.error(`Cogent Server listening on ${config.HOST}:${config.PORT} (WebSocket enabled)`);
75
+ // Start stats refresh after server is listening
76
+ statsService.start();
77
+ // Graceful shutdown on SIGTERM and SIGINT
78
+ const shutdown = () => {
79
+ console.error("Shutting down Cogent Server...");
80
+ // Stop background services
81
+ statsService.stop();
82
+ peerCleanup.stop();
83
+ sessionCleanup.stop();
84
+ messageQueue.stopCleanup();
85
+ // Close all WebSocket connections with 1012 (Service Restart) before HTTP server shutdown
86
+ connectionManager.closeAll(1012, "Service restart");
87
+ server.close((err) => {
88
+ if (err) {
89
+ console.error("Error during shutdown:", err);
90
+ process.exit(1);
91
+ }
92
+ process.exit(0);
93
+ });
94
+ };
95
+ process.on("SIGTERM", shutdown);
96
+ process.on("SIGINT", shutdown);
97
+ }
98
+ main().catch((err) => {
99
+ console.error("Fatal error starting Cogent Server:", err);
100
+ process.exit(1);
101
+ });
102
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { SessionStore } from "../services/session-store.js";
2
+ import type { AuthService } from "../services/auth-service.js";
3
+ import type { ServerEnv } from "../types.js";
4
+ /**
5
+ * Create bearer token auth middleware.
6
+ *
7
+ * Extracts the Authorization header, hashes the token with SHA-256,
8
+ * looks up the session via the token index, performs a timing-safe
9
+ * comparison for defense-in-depth, and sets sessionId + session on
10
+ * the Hono context for downstream handlers.
11
+ *
12
+ * Uses dependency injection so the middleware is fully testable.
13
+ */
14
+ export declare function createAuthMiddleware(sessionStore: SessionStore, authService: AuthService): import("hono").MiddlewareHandler<ServerEnv, string, {}, Response>;
15
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAEA,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;AAE7C;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,qEA4DzB"}
@@ -0,0 +1,47 @@
1
+ import { createMiddleware } from "hono/factory";
2
+ import { BridgeError, ErrorCode } from "@essentialai/cogent";
3
+ /**
4
+ * Create bearer token auth middleware.
5
+ *
6
+ * Extracts the Authorization header, hashes the token with SHA-256,
7
+ * looks up the session via the token index, performs a timing-safe
8
+ * comparison for defense-in-depth, and sets sessionId + session on
9
+ * the Hono context for downstream handlers.
10
+ *
11
+ * Uses dependency injection so the middleware is fully testable.
12
+ */
13
+ export function createAuthMiddleware(sessionStore, authService) {
14
+ return createMiddleware(async (c, next) => {
15
+ // 1. Extract Authorization header
16
+ const authHeader = c.req.header("Authorization");
17
+ if (!authHeader) {
18
+ throw new BridgeError(ErrorCode.AUTH_MISSING_TOKEN, "Authorization header required", "Include 'Authorization: Bearer <token>' header");
19
+ }
20
+ // 2. Parse Bearer <token> format
21
+ const match = authHeader.match(/^Bearer\s+(.+)$/i);
22
+ if (!match) {
23
+ throw new BridgeError(ErrorCode.AUTH_INVALID_TOKEN, "Invalid Authorization header format", "Use 'Bearer <token>' format");
24
+ }
25
+ const token = match[1];
26
+ // 3. Hash the token with SHA-256 for index lookup
27
+ const tokenHash = authService.hashToken(token);
28
+ // 4. Look up session by token hash
29
+ const result = await sessionStore.getSessionByTokenHash(tokenHash);
30
+ if (!result) {
31
+ throw new BridgeError(ErrorCode.AUTH_INVALID_TOKEN, "Invalid or expired token");
32
+ }
33
+ // 5. Defense-in-depth: timing-safe comparison of the token hash
34
+ // Even though we looked up by hash, this guards against hash collisions.
35
+ const storedTokenHash = result.state.tokens.find((t) => t.tokenHash === tokenHash)?.tokenHash;
36
+ if (!storedTokenHash ||
37
+ !authService.timingSafeCompare(tokenHash, storedTokenHash)) {
38
+ throw new BridgeError(ErrorCode.AUTH_INVALID_TOKEN, "Invalid or expired token");
39
+ }
40
+ // 6. Set session context for downstream handlers
41
+ c.set("sessionId", result.sessionId);
42
+ c.set("session", result.state);
43
+ // 7. Continue to next middleware/handler
44
+ await next();
45
+ });
46
+ }
47
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAK7D;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAA0B,EAC1B,WAAwB;IAExB,OAAO,gBAAgB,CAAY,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACnD,kCAAkC;QAClC,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,WAAW,CACnB,SAAS,CAAC,kBAAkB,EAC5B,+BAA+B,EAC/B,gDAAgD,CACjD,CAAC;QACJ,CAAC;QAED,iCAAiC;QACjC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,WAAW,CACnB,SAAS,CAAC,kBAAkB,EAC5B,qCAAqC,EACrC,6BAA6B,CAC9B,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAEvB,kDAAkD;QAClD,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAE/C,mCAAmC;QACnC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,WAAW,CACnB,SAAS,CAAC,kBAAkB,EAC5B,0BAA0B,CAC3B,CAAC;QACJ,CAAC;QAED,gEAAgE;QAChE,4EAA4E;QAC5E,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAC9C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CACjC,EAAE,SAAS,CAAC;QAEb,IACE,CAAC,eAAe;YAChB,CAAC,WAAW,CAAC,iBAAiB,CAAC,SAAS,EAAE,eAAe,CAAC,EAC1D,CAAC;YACD,MAAM,IAAI,WAAW,CACnB,SAAS,CAAC,kBAAkB,EAC5B,0BAA0B,CAC3B,CAAC;QACJ,CAAC;QAED,iDAAiD;QACjD,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;QACrC,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAE/B,yCAAyC;QACzC,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { ErrorHandler } from "hono";
2
+ import type { ServerEnv } from "../types.js";
3
+ /**
4
+ * Global error handler for the Hono app.
5
+ *
6
+ * - HTTPException instances (from middleware like basicAuth) are passed
7
+ * through as-is using getResponse() to preserve correct status codes.
8
+ * - BridgeError instances are serialized to structured JSON using toJSON()
9
+ * with the correct HTTP status code from the ErrorHttpStatus mapping.
10
+ * - All other errors are wrapped in a generic INTERNAL_SERVER_ERROR
11
+ * BridgeError, with the original error logged to stderr for debugging.
12
+ */
13
+ export declare const errorHandler: ErrorHandler<ServerEnv>;
14
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAGzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,EAAE,YAAY,CAAC,SAAS,CAkBhD,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { BridgeError, ErrorCode } from "@essentialai/cogent";
2
+ import { HTTPException } from "hono/http-exception";
3
+ /**
4
+ * Global error handler for the Hono app.
5
+ *
6
+ * - HTTPException instances (from middleware like basicAuth) are passed
7
+ * through as-is using getResponse() to preserve correct status codes.
8
+ * - BridgeError instances are serialized to structured JSON using toJSON()
9
+ * with the correct HTTP status code from the ErrorHttpStatus mapping.
10
+ * - All other errors are wrapped in a generic INTERNAL_SERVER_ERROR
11
+ * BridgeError, with the original error logged to stderr for debugging.
12
+ */
13
+ export const errorHandler = (err, c) => {
14
+ // Pass through Hono HTTPException responses (e.g. 401 from basicAuth)
15
+ if (err instanceof HTTPException) {
16
+ return err.getResponse();
17
+ }
18
+ if (err instanceof BridgeError) {
19
+ return c.json(err.toJSON(), err.httpStatus);
20
+ }
21
+ // Unexpected error -- wrap and log
22
+ const bridgeErr = new BridgeError(ErrorCode.INTERNAL_SERVER_ERROR, "An unexpected error occurred", "Please try again or contact support");
23
+ console.error("Unhandled error:", err);
24
+ return c.json(bridgeErr.toJSON(), 500);
25
+ };
26
+ //# sourceMappingURL=error-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIpD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAA4B,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;IAC9D,sEAAsE;IACtE,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;QACjC,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;IAC3B,CAAC;IAED,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;QAC/B,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,UAAkC,CAAC,CAAC;IACtE,CAAC;IAED,mCAAmC;IACnC,MAAM,SAAS,GAAG,IAAI,WAAW,CAC/B,SAAS,CAAC,qBAAqB,EAC/B,8BAA8B,EAC9B,qCAAqC,CACtC,CAAC;IACF,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACvC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;AACzC,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { NotFoundHandler } from "hono";
2
+ import type { ServerEnv } from "../types.js";
3
+ /**
4
+ * 404 handler for unknown routes.
5
+ * Returns a structured BridgeError JSON response (not default HTML).
6
+ */
7
+ export declare const notFoundHandler: NotFoundHandler<ServerEnv>;
8
+ //# sourceMappingURL=not-found.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"not-found.d.ts","sourceRoot":"","sources":["../../src/middleware/not-found.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,eAAe,CAAC,SAAS,CAWtD,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { BridgeError, ErrorCode } from "@essentialai/cogent";
2
+ /**
3
+ * 404 handler for unknown routes.
4
+ * Returns a structured BridgeError JSON response (not default HTML).
5
+ */
6
+ export const notFoundHandler = (c) => {
7
+ const method = c.req.method;
8
+ const path = c.req.path;
9
+ const err = new BridgeError(ErrorCode.ENDPOINT_NOT_FOUND, `Route not found: ${method} ${path}`, "Check the URL and HTTP method");
10
+ return c.json(err.toJSON(), 404);
11
+ };
12
+ //# sourceMappingURL=not-found.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"not-found.js","sourceRoot":"","sources":["../../src/middleware/not-found.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAI7D;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAA+B,CAAC,CAAC,EAAE,EAAE;IAC/D,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;IAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAExB,MAAM,GAAG,GAAG,IAAI,WAAW,CACzB,SAAS,CAAC,kBAAkB,EAC5B,oBAAoB,MAAM,IAAI,IAAI,EAAE,EACpC,+BAA+B,CAChC,CAAC;IAEF,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC,CAAC"}