@data-club/ai-hub-server 0.0.1

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 (248) hide show
  1. package/dist/auth/argon2.d.ts +72 -0
  2. package/dist/auth/argon2.d.ts.map +1 -0
  3. package/dist/auth/argon2.js +145 -0
  4. package/dist/auth/argon2.js.map +1 -0
  5. package/dist/auth/encryption.d.ts +52 -0
  6. package/dist/auth/encryption.d.ts.map +1 -0
  7. package/dist/auth/encryption.js +98 -0
  8. package/dist/auth/encryption.js.map +1 -0
  9. package/dist/auth/jwt.d.ts +133 -0
  10. package/dist/auth/jwt.d.ts.map +1 -0
  11. package/dist/auth/jwt.js +147 -0
  12. package/dist/auth/jwt.js.map +1 -0
  13. package/dist/auth/middleware.d.ts +101 -0
  14. package/dist/auth/middleware.d.ts.map +1 -0
  15. package/dist/auth/middleware.js +161 -0
  16. package/dist/auth/middleware.js.map +1 -0
  17. package/dist/auth/passwordPolicy.d.ts +72 -0
  18. package/dist/auth/passwordPolicy.d.ts.map +1 -0
  19. package/dist/auth/passwordPolicy.js +88 -0
  20. package/dist/auth/passwordPolicy.js.map +1 -0
  21. package/dist/auth/roleRules.d.ts +200 -0
  22. package/dist/auth/roleRules.d.ts.map +1 -0
  23. package/dist/auth/roleRules.js +365 -0
  24. package/dist/auth/roleRules.js.map +1 -0
  25. package/dist/auth/userView.d.ts +55 -0
  26. package/dist/auth/userView.d.ts.map +1 -0
  27. package/dist/auth/userView.js +77 -0
  28. package/dist/auth/userView.js.map +1 -0
  29. package/dist/bootstrap.d.ts +104 -0
  30. package/dist/bootstrap.d.ts.map +1 -0
  31. package/dist/bootstrap.js +160 -0
  32. package/dist/bootstrap.js.map +1 -0
  33. package/dist/db/connection.d.ts +39 -0
  34. package/dist/db/connection.d.ts.map +1 -0
  35. package/dist/db/connection.js +62 -0
  36. package/dist/db/connection.js.map +1 -0
  37. package/dist/db/migrate.d.ts +31 -0
  38. package/dist/db/migrate.d.ts.map +1 -0
  39. package/dist/db/migrate.js +197 -0
  40. package/dist/db/migrate.js.map +1 -0
  41. package/dist/db/schema.d.ts +1902 -0
  42. package/dist/db/schema.d.ts.map +1 -0
  43. package/dist/db/schema.js +662 -0
  44. package/dist/db/schema.js.map +1 -0
  45. package/dist/db/seed.d.ts +92 -0
  46. package/dist/db/seed.d.ts.map +1 -0
  47. package/dist/db/seed.js +160 -0
  48. package/dist/db/seed.js.map +1 -0
  49. package/dist/handlers/fetch.d.ts +89 -0
  50. package/dist/handlers/fetch.d.ts.map +1 -0
  51. package/dist/handlers/fetch.js +173 -0
  52. package/dist/handlers/fetch.js.map +1 -0
  53. package/dist/handlers/nextjs.d.ts +40 -0
  54. package/dist/handlers/nextjs.d.ts.map +1 -0
  55. package/dist/handlers/nextjs.js +42 -0
  56. package/dist/handlers/nextjs.js.map +1 -0
  57. package/dist/handlers/responses.d.ts +46 -0
  58. package/dist/handlers/responses.d.ts.map +1 -0
  59. package/dist/handlers/responses.js +74 -0
  60. package/dist/handlers/responses.js.map +1 -0
  61. package/dist/handlers/routeTable.d.ts +21 -0
  62. package/dist/handlers/routeTable.d.ts.map +1 -0
  63. package/dist/handlers/routeTable.js +133 -0
  64. package/dist/handlers/routeTable.js.map +1 -0
  65. package/dist/handlers/router.d.ts +37 -0
  66. package/dist/handlers/router.d.ts.map +1 -0
  67. package/dist/handlers/router.js +82 -0
  68. package/dist/handlers/router.js.map +1 -0
  69. package/dist/handlers/sse.d.ts +38 -0
  70. package/dist/handlers/sse.d.ts.map +1 -0
  71. package/dist/handlers/sse.js +52 -0
  72. package/dist/handlers/sse.js.map +1 -0
  73. package/dist/handlers/types.d.ts +96 -0
  74. package/dist/handlers/types.d.ts.map +1 -0
  75. package/dist/handlers/types.js +14 -0
  76. package/dist/handlers/types.js.map +1 -0
  77. package/dist/index.d.ts +25 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +24 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/letta/client.d.ts +355 -0
  82. package/dist/letta/client.d.ts.map +1 -0
  83. package/dist/letta/client.js +529 -0
  84. package/dist/letta/client.js.map +1 -0
  85. package/dist/letta/eventTranslator.d.ts +166 -0
  86. package/dist/letta/eventTranslator.d.ts.map +1 -0
  87. package/dist/letta/eventTranslator.js +532 -0
  88. package/dist/letta/eventTranslator.js.map +1 -0
  89. package/dist/letta/index.d.ts +13 -0
  90. package/dist/letta/index.d.ts.map +1 -0
  91. package/dist/letta/index.js +12 -0
  92. package/dist/letta/index.js.map +1 -0
  93. package/dist/letta/modelSettings.d.ts +176 -0
  94. package/dist/letta/modelSettings.d.ts.map +1 -0
  95. package/dist/letta/modelSettings.js +314 -0
  96. package/dist/letta/modelSettings.js.map +1 -0
  97. package/dist/letta/promptWrapping.d.ts +141 -0
  98. package/dist/letta/promptWrapping.d.ts.map +1 -0
  99. package/dist/letta/promptWrapping.js +213 -0
  100. package/dist/letta/promptWrapping.js.map +1 -0
  101. package/dist/letta/sandbox.d.ts +100 -0
  102. package/dist/letta/sandbox.d.ts.map +1 -0
  103. package/dist/letta/sandbox.js +151 -0
  104. package/dist/letta/sandbox.js.map +1 -0
  105. package/dist/letta/settingsFileSeed.d.ts +146 -0
  106. package/dist/letta/settingsFileSeed.d.ts.map +1 -0
  107. package/dist/letta/settingsFileSeed.js +177 -0
  108. package/dist/letta/settingsFileSeed.js.map +1 -0
  109. package/dist/letta/subprocessThrottle.d.ts +151 -0
  110. package/dist/letta/subprocessThrottle.d.ts.map +1 -0
  111. package/dist/letta/subprocessThrottle.js +195 -0
  112. package/dist/letta/subprocessThrottle.js.map +1 -0
  113. package/dist/letta/tpmRecovery.d.ts +202 -0
  114. package/dist/letta/tpmRecovery.d.ts.map +1 -0
  115. package/dist/letta/tpmRecovery.js +314 -0
  116. package/dist/letta/tpmRecovery.js.map +1 -0
  117. package/dist/letta/types.d.ts +54 -0
  118. package/dist/letta/types.d.ts.map +1 -0
  119. package/dist/letta/types.js +16 -0
  120. package/dist/letta/types.js.map +1 -0
  121. package/dist/observability/logger.d.ts +48 -0
  122. package/dist/observability/logger.d.ts.map +1 -0
  123. package/dist/observability/logger.js +81 -0
  124. package/dist/observability/logger.js.map +1 -0
  125. package/dist/observability/metrics.d.ts +53 -0
  126. package/dist/observability/metrics.d.ts.map +1 -0
  127. package/dist/observability/metrics.js +92 -0
  128. package/dist/observability/metrics.js.map +1 -0
  129. package/dist/routes/admin/agents.d.ts +63 -0
  130. package/dist/routes/admin/agents.d.ts.map +1 -0
  131. package/dist/routes/admin/agents.js +461 -0
  132. package/dist/routes/admin/agents.js.map +1 -0
  133. package/dist/routes/admin/rotatingMessages.d.ts +51 -0
  134. package/dist/routes/admin/rotatingMessages.d.ts.map +1 -0
  135. package/dist/routes/admin/rotatingMessages.js +292 -0
  136. package/dist/routes/admin/rotatingMessages.js.map +1 -0
  137. package/dist/routes/admin/sessions.d.ts +61 -0
  138. package/dist/routes/admin/sessions.d.ts.map +1 -0
  139. package/dist/routes/admin/sessions.js +232 -0
  140. package/dist/routes/admin/sessions.js.map +1 -0
  141. package/dist/routes/admin/settings.d.ts +110 -0
  142. package/dist/routes/admin/settings.d.ts.map +1 -0
  143. package/dist/routes/admin/settings.js +401 -0
  144. package/dist/routes/admin/settings.js.map +1 -0
  145. package/dist/routes/admin/users.d.ts +115 -0
  146. package/dist/routes/admin/users.d.ts.map +1 -0
  147. package/dist/routes/admin/users.js +374 -0
  148. package/dist/routes/admin/users.js.map +1 -0
  149. package/dist/routes/agents.d.ts +30 -0
  150. package/dist/routes/agents.d.ts.map +1 -0
  151. package/dist/routes/agents.js +55 -0
  152. package/dist/routes/agents.js.map +1 -0
  153. package/dist/routes/auth.d.ts +121 -0
  154. package/dist/routes/auth.d.ts.map +1 -0
  155. package/dist/routes/auth.js +422 -0
  156. package/dist/routes/auth.js.map +1 -0
  157. package/dist/routes/chat.d.ts +97 -0
  158. package/dist/routes/chat.d.ts.map +1 -0
  159. package/dist/routes/chat.js +484 -0
  160. package/dist/routes/chat.js.map +1 -0
  161. package/dist/routes/featureToggles.d.ts +34 -0
  162. package/dist/routes/featureToggles.d.ts.map +1 -0
  163. package/dist/routes/featureToggles.js +47 -0
  164. package/dist/routes/featureToggles.js.map +1 -0
  165. package/dist/routes/health.d.ts +24 -0
  166. package/dist/routes/health.d.ts.map +1 -0
  167. package/dist/routes/health.js +37 -0
  168. package/dist/routes/health.js.map +1 -0
  169. package/dist/routes/metrics.d.ts +35 -0
  170. package/dist/routes/metrics.d.ts.map +1 -0
  171. package/dist/routes/metrics.js +106 -0
  172. package/dist/routes/metrics.js.map +1 -0
  173. package/dist/routes/sessions.d.ts +161 -0
  174. package/dist/routes/sessions.d.ts.map +1 -0
  175. package/dist/routes/sessions.js +1501 -0
  176. package/dist/routes/sessions.js.map +1 -0
  177. package/dist/routes/stubs.d.ts +25 -0
  178. package/dist/routes/stubs.d.ts.map +1 -0
  179. package/dist/routes/stubs.js +25 -0
  180. package/dist/routes/stubs.js.map +1 -0
  181. package/dist/state/cron.d.ts +58 -0
  182. package/dist/state/cron.d.ts.map +1 -0
  183. package/dist/state/cron.js +160 -0
  184. package/dist/state/cron.js.map +1 -0
  185. package/dist/state/rotationRunner.d.ts +96 -0
  186. package/dist/state/rotationRunner.d.ts.map +1 -0
  187. package/dist/state/rotationRunner.js +246 -0
  188. package/dist/state/rotationRunner.js.map +1 -0
  189. package/dist/state/rotationScheduler.d.ts +76 -0
  190. package/dist/state/rotationScheduler.d.ts.map +1 -0
  191. package/dist/state/rotationScheduler.js +124 -0
  192. package/dist/state/rotationScheduler.js.map +1 -0
  193. package/dist/state/turnRegistry.d.ts +95 -0
  194. package/dist/state/turnRegistry.d.ts.map +1 -0
  195. package/dist/state/turnRegistry.js +149 -0
  196. package/dist/state/turnRegistry.js.map +1 -0
  197. package/dist/state/turnRunner.d.ts +134 -0
  198. package/dist/state/turnRunner.d.ts.map +1 -0
  199. package/dist/state/turnRunner.js +476 -0
  200. package/dist/state/turnRunner.js.map +1 -0
  201. package/package.json +47 -0
  202. package/src/auth/argon2.ts +152 -0
  203. package/src/auth/encryption.ts +110 -0
  204. package/src/auth/jwt.ts +217 -0
  205. package/src/auth/middleware.ts +250 -0
  206. package/src/auth/passwordPolicy.ts +99 -0
  207. package/src/auth/roleRules.ts +497 -0
  208. package/src/auth/userView.ts +88 -0
  209. package/src/bootstrap.ts +254 -0
  210. package/src/db/connection.ts +75 -0
  211. package/src/db/migrate.ts +212 -0
  212. package/src/db/schema.ts +750 -0
  213. package/src/db/seed.ts +236 -0
  214. package/src/handlers/fetch.ts +251 -0
  215. package/src/handlers/nextjs.ts +49 -0
  216. package/src/handlers/responses.ts +95 -0
  217. package/src/handlers/routeTable.ts +188 -0
  218. package/src/handlers/router.ts +96 -0
  219. package/src/handlers/sse.ts +55 -0
  220. package/src/handlers/types.ts +104 -0
  221. package/src/index.ts +60 -0
  222. package/src/letta/client.ts +860 -0
  223. package/src/letta/eventTranslator.ts +667 -0
  224. package/src/letta/index.ts +46 -0
  225. package/src/letta/lettaCliShim.mjs +40 -0
  226. package/src/letta/sandbox.ts +167 -0
  227. package/src/letta/settingsFileSeed.ts +254 -0
  228. package/src/letta/subprocessThrottle.ts +251 -0
  229. package/src/letta/types.ts +65 -0
  230. package/src/observability/logger.ts +103 -0
  231. package/src/observability/metrics.ts +114 -0
  232. package/src/routes/admin/agents.ts +574 -0
  233. package/src/routes/admin/rotatingMessages.ts +351 -0
  234. package/src/routes/admin/sessions.ts +283 -0
  235. package/src/routes/admin/settings.ts +463 -0
  236. package/src/routes/admin/users.ts +455 -0
  237. package/src/routes/agents.ts +66 -0
  238. package/src/routes/auth.ts +513 -0
  239. package/src/routes/chat.ts +534 -0
  240. package/src/routes/featureToggles.ts +52 -0
  241. package/src/routes/health.ts +44 -0
  242. package/src/routes/metrics.ts +111 -0
  243. package/src/routes/sessions.ts +1878 -0
  244. package/src/state/cron.ts +179 -0
  245. package/src/state/rotationRunner.ts +326 -0
  246. package/src/state/rotationScheduler.ts +164 -0
  247. package/src/state/turnRegistry.ts +210 -0
  248. package/src/state/turnRunner.ts +588 -0
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Canonical route table.
3
+ *
4
+ * Order matters — the matcher walks top-to-bottom and returns the first
5
+ * pattern that matches. Concrete paths come before any future wildcards.
6
+ *
7
+ * Entries are paired with the step that wires the real handler. After Step 5
8
+ * every endpoint is real; no stubs remain.
9
+ *
10
+ * @see SPECIFICATIONS.md §6.3 — API surface
11
+ * @see docs/IMPLEMENTATION_PLAN.md — per-endpoint step assignment
12
+ */
13
+
14
+ import { handleListAgents as handleListAgentsUser } from "../routes/agents.js";
15
+ import { handleGetPublicFeatureToggles } from "../routes/featureToggles.js";
16
+ import {
17
+ handleCreateAgent,
18
+ handleListAgents as handleListAgentsAdmin,
19
+ handlePatchAgent,
20
+ } from "../routes/admin/agents.js";
21
+ import {
22
+ handleListSessionsOverview,
23
+ handlePurgeAdminSessionSandbox,
24
+ } from "../routes/admin/sessions.js";
25
+ import {
26
+ handleCreateRotatingMessage,
27
+ handleListRotatingMessages,
28
+ handlePatchRotatingMessage,
29
+ handleRotateRotatingMessageNow,
30
+ } from "../routes/admin/rotatingMessages.js";
31
+ import {
32
+ handleGetFeatureToggles,
33
+ handleGetSettings,
34
+ handlePatchFeatureToggles,
35
+ handlePatchSettings,
36
+ handleRotateMetricsToken,
37
+ handleRotateSigningKey,
38
+ } from "../routes/admin/settings.js";
39
+ import {
40
+ handleCreateUser,
41
+ handleDeleteUser,
42
+ handleListUsers,
43
+ handlePatchUserRole,
44
+ handleResetUserPassword,
45
+ } from "../routes/admin/users.js";
46
+ import {
47
+ handleChangePassword,
48
+ handleLogin,
49
+ handleLogout,
50
+ handleMe,
51
+ handleMePreferences,
52
+ } from "../routes/auth.js";
53
+ import { handleHealth, handleHealthReady } from "../routes/health.js";
54
+ import { handleMetrics } from "../routes/metrics.js";
55
+ import {
56
+ handleGetChatEvents,
57
+ handlePostChat,
58
+ handlePostChatCancel,
59
+ } from "../routes/chat.js";
60
+ import {
61
+ handleAdminGetSessionMessages,
62
+ handleCreateSession,
63
+ handleDeleteMessageOverride,
64
+ handleGetSession,
65
+ handleGetSessionMessages,
66
+ handleListMessageOverrides,
67
+ handleListSessions,
68
+ handlePatchSession,
69
+ handlePatchSessionReasoning,
70
+ handlePutMessageOverride,
71
+ } from "../routes/sessions.js";
72
+
73
+ import type { RouteEntry } from "./types.js";
74
+
75
+ /**
76
+ * Reads as the canonical Step-5 surface in one place. Step 6 adds no new
77
+ * routes — it extends the chat path with TPM-recovery internals.
78
+ *
79
+ * @returns the immutable route table.
80
+ */
81
+ export function buildRouteTable(): ReadonlyArray<RouteEntry> {
82
+ return [
83
+ // Health probes — public.
84
+ { method: "GET", path: "/health", handler: handleHealth },
85
+ { method: "GET", path: "/health/ready", handler: handleHealthReady },
86
+
87
+ // Auth — full surface after Step 3.
88
+ { method: "POST", path: "/auth/login", handler: handleLogin },
89
+ { method: "POST", path: "/auth/change-password", handler: handleChangePassword },
90
+ { method: "POST", path: "/auth/logout", handler: handleLogout },
91
+ { method: "GET", path: "/auth/me", handler: handleMe },
92
+ { method: "PATCH", path: "/auth/me/preferences", handler: handleMePreferences },
93
+
94
+ // Public — Step 10 (deployment-wide UI feature toggles per §8.4).
95
+ { method: "GET", path: "/feature-toggles", handler: handleGetPublicFeatureToggles },
96
+
97
+ // User-facing — Step 4.
98
+ { method: "GET", path: "/agents", handler: handleListAgentsUser },
99
+ { method: "GET", path: "/sessions", handler: handleListSessions },
100
+ { method: "POST", path: "/sessions", handler: handleCreateSession },
101
+ { method: "GET", path: "/sessions/:id", handler: handleGetSession },
102
+ { method: "PATCH", path: "/sessions/:id", handler: handlePatchSession },
103
+ {
104
+ method: "PATCH",
105
+ path: "/sessions/:id/reasoning",
106
+ handler: handlePatchSessionReasoning,
107
+ },
108
+ { method: "GET", path: "/sessions/:id/messages", handler: handleGetSessionMessages },
109
+ {
110
+ method: "GET",
111
+ path: "/sessions/:id/message-overrides",
112
+ handler: handleListMessageOverrides,
113
+ },
114
+ {
115
+ method: "PUT",
116
+ path: "/sessions/:id/message-overrides/:position",
117
+ handler: handlePutMessageOverride,
118
+ },
119
+ {
120
+ method: "DELETE",
121
+ path: "/sessions/:id/message-overrides/:position",
122
+ handler: handleDeleteMessageOverride,
123
+ },
124
+
125
+ // Chat — Step 5 (SSE wire protocol per §9).
126
+ { method: "POST", path: "/chat", handler: handlePostChat },
127
+ { method: "GET", path: "/chat/:turn_id/events", handler: handleGetChatEvents },
128
+ { method: "POST", path: "/chat/:turn_id/cancel", handler: handlePostChatCancel },
129
+
130
+ // Admin — Step 4.
131
+ { method: "GET", path: "/admin/users", handler: handleListUsers },
132
+ { method: "POST", path: "/admin/users", handler: handleCreateUser },
133
+ { method: "PATCH", path: "/admin/users/:id/role", handler: handlePatchUserRole },
134
+ { method: "POST", path: "/admin/users/:id/reset-password", handler: handleResetUserPassword },
135
+ { method: "DELETE", path: "/admin/users/:id", handler: handleDeleteUser },
136
+ { method: "GET", path: "/admin/agents", handler: handleListAgentsAdmin },
137
+ { method: "POST", path: "/admin/agents", handler: handleCreateAgent },
138
+ { method: "PATCH", path: "/admin/agents/:id", handler: handlePatchAgent },
139
+ {
140
+ method: "GET",
141
+ path: "/admin/rotating-messages",
142
+ handler: handleListRotatingMessages,
143
+ },
144
+ {
145
+ method: "POST",
146
+ path: "/admin/rotating-messages",
147
+ handler: handleCreateRotatingMessage,
148
+ },
149
+ {
150
+ method: "PATCH",
151
+ path: "/admin/rotating-messages/:id",
152
+ handler: handlePatchRotatingMessage,
153
+ },
154
+ {
155
+ method: "POST",
156
+ path: "/admin/rotating-messages/:id/rotate-now",
157
+ handler: handleRotateRotatingMessageNow,
158
+ },
159
+ { method: "GET", path: "/admin/sessions", handler: handleListSessionsOverview },
160
+ {
161
+ method: "GET",
162
+ path: "/admin/sessions/:id/messages",
163
+ handler: handleAdminGetSessionMessages,
164
+ },
165
+ {
166
+ method: "POST",
167
+ path: "/admin/sessions/:id/purge-sandbox",
168
+ handler: handlePurgeAdminSessionSandbox,
169
+ },
170
+ { method: "GET", path: "/admin/settings", handler: handleGetSettings },
171
+ { method: "PATCH", path: "/admin/settings", handler: handlePatchSettings },
172
+ { method: "GET", path: "/admin/settings/feature-toggles", handler: handleGetFeatureToggles },
173
+ { method: "PATCH", path: "/admin/settings/feature-toggles", handler: handlePatchFeatureToggles },
174
+ {
175
+ method: "POST",
176
+ path: "/admin/settings/rotate-signing-key",
177
+ handler: handleRotateSigningKey,
178
+ },
179
+ {
180
+ method: "POST",
181
+ path: "/admin/settings/rotate-metrics-token",
182
+ handler: handleRotateMetricsToken,
183
+ },
184
+
185
+ // Metrics — Step 4 (bearer-token-gated, no JWT).
186
+ { method: "GET", path: "/metrics", handler: handleMetrics },
187
+ ];
188
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Tiny path + method matcher.
3
+ *
4
+ * Lives in-package — we explicitly do NOT depend on Hono / Express /
5
+ * Fastify (§6.2). The route table is flat, the patterns are simple, and a
6
+ * 50-line matcher beats a 500 KB framework runtime that brings its own
7
+ * plugin ecosystem we wouldn't use.
8
+ *
9
+ * Pattern syntax: `/sessions/:id` matches `/sessions/abc` and exposes
10
+ * `{ id: "abc" }` as the parameter map. The matcher does not support globs
11
+ * or regex — every spec'd route is a plain segment-by-segment match.
12
+ *
13
+ * The `*` method is the wildcard used by `OPTIONS` preflight handlers and
14
+ * the catch-all 404. First-registered-first-matched, so wildcards always go
15
+ * at the bottom of the table.
16
+ *
17
+ * @see SPECIFICATIONS.md §6.2 — stack (no framework runtime)
18
+ */
19
+
20
+ import type { RouteEntry } from "./types.js";
21
+
22
+ /** A successful match's output. */
23
+ export interface MatchResult {
24
+ /** The handler to invoke. */
25
+ entry: RouteEntry;
26
+ /** Path-parameter map (e.g. `{ id: "abc" }`). */
27
+ params: Record<string, string>;
28
+ }
29
+
30
+ /**
31
+ * Walks `entries` in order and returns the first one whose method + path
32
+ * pattern match `method` and `pathname`. Returns `null` if nothing matches.
33
+ *
34
+ * @param entries - the route table.
35
+ * @param method - request method (uppercased).
36
+ * @param pathname - request path with the basePath already stripped.
37
+ * @returns the matched entry + params, or `null`.
38
+ */
39
+ export function matchRoute(
40
+ entries: ReadonlyArray<RouteEntry>,
41
+ method: string,
42
+ pathname: string,
43
+ ): MatchResult | null {
44
+ for (const entry of entries) {
45
+ if (entry.method !== "*" && entry.method !== method) continue;
46
+ const params = matchPath(entry.path, pathname);
47
+ if (params) return { entry, params };
48
+ }
49
+ return null;
50
+ }
51
+
52
+ /**
53
+ * Compares a path template (with `:param` placeholders) against an actual
54
+ * pathname. Returns the parameter map on a match, `null` otherwise.
55
+ *
56
+ * Trailing slashes are tolerated — `/foo/` matches `/foo`.
57
+ *
58
+ * @param template - the route template (e.g. `/sessions/:id`).
59
+ * @param pathname - the actual path being matched.
60
+ * @returns parameter map on a match, else `null`.
61
+ */
62
+ function matchPath(
63
+ template: string,
64
+ pathname: string,
65
+ ): Record<string, string> | null {
66
+ const tplParts = trimSlashes(template).split("/");
67
+ const pathParts = trimSlashes(pathname).split("/");
68
+ if (tplParts.length !== pathParts.length) return null;
69
+
70
+ const params: Record<string, string> = {};
71
+ for (let i = 0; i < tplParts.length; i++) {
72
+ const tp = tplParts[i] as string;
73
+ const pp = pathParts[i] as string;
74
+ if (tp.startsWith(":")) {
75
+ const name = tp.slice(1);
76
+ params[name] = decodeURIComponent(pp);
77
+ continue;
78
+ }
79
+ if (tp !== pp) return null;
80
+ }
81
+ return params;
82
+ }
83
+
84
+ /**
85
+ * Trims a single leading and trailing `/` so `split("/")` yields the same
86
+ * segment count regardless of slash style.
87
+ *
88
+ * @param s - the path or template string.
89
+ * @returns the input with one leading/trailing slash removed.
90
+ */
91
+ function trimSlashes(s: string): string {
92
+ let out = s;
93
+ if (out.startsWith("/")) out = out.slice(1);
94
+ if (out.endsWith("/")) out = out.slice(0, -1);
95
+ return out;
96
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Small SSE writer helpers.
3
+ *
4
+ * The chat handler builds a `ReadableStream` of bytes and pipes named
5
+ * events into it. This file holds the encoding (utf-8 + `event:`/`data:`
6
+ * lines) and a `formatSseEvent` helper so the writer stays in one place.
7
+ *
8
+ * @see SPECIFICATIONS.md §9.5 — why SSE (not EventSource)
9
+ */
10
+
11
+ import type { AIHubEvent } from "@data-club/ai-hub";
12
+
13
+ /**
14
+ * Formats one ai-hub event into the byte payload an SSE client expects.
15
+ *
16
+ * Format per the W3C EventSource spec:
17
+ *
18
+ * ```
19
+ * event: <event-name>
20
+ * data: <json-payload>
21
+ *
22
+ * ```
23
+ *
24
+ * The blank line separator is required. Multi-line payloads get one
25
+ * `data:` line per newline; we shouldn't see any in practice (the events
26
+ * we emit are flat JSON), but the helper handles it defensively.
27
+ *
28
+ * @param event - the ai-hub event.
29
+ * @returns the SSE-formatted bytes.
30
+ */
31
+ export function formatSseEvent(event: AIHubEvent): Uint8Array {
32
+ // `heartbeat` events carry no `data` per the wire-protocol union — for
33
+ // every other variant `data` is present. We pull it out with a narrowing
34
+ // `in` check so we don't include `data: undefined` in the wire payload
35
+ // when there's nothing to send.
36
+ const payload = JSON.stringify({
37
+ seq: event.seq,
38
+ ...("data" in event ? { data: event.data } : {}),
39
+ state: event.state,
40
+ });
41
+ const dataLines = payload.split("\n").map((l) => `data: ${l}`).join("\n");
42
+ const text = `event: ${event.event}\n${dataLines}\n\n`;
43
+ return new TextEncoder().encode(text);
44
+ }
45
+
46
+ /**
47
+ * Formats a plain SSE comment-line (used for heartbeats). Browsers and
48
+ * proxies treat any line starting with `:` as a comment — keeps the
49
+ * connection alive without delivering an event to the consumer.
50
+ *
51
+ * @returns the bytes of a `:hb\n\n` heartbeat.
52
+ */
53
+ export function formatSseHeartbeat(): Uint8Array {
54
+ return new TextEncoder().encode(":hb\n\n");
55
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Shared types for the Fetch routing core.
3
+ *
4
+ * `AIHubServerContext` is the per-server-instance bundle: DB, logger, parsed
5
+ * encryption key, route prefix. Subsequent steps extend it (Letta client,
6
+ * mock-Letta toggle, etc.) — additions are additive.
7
+ *
8
+ * Handlers are Web-Standard `(Request, ctx) => Promise<Response>` per §6.2.
9
+ * No framework runtime, no plugin ecosystem.
10
+ *
11
+ * @see SPECIFICATIONS.md §6.2 — stack
12
+ */
13
+
14
+ import type Database from "better-sqlite3";
15
+
16
+ import type { DrizzleDB } from "../db/connection.js";
17
+ import type { LettaClient } from "../letta/client.js";
18
+ import type { SubprocessThrottle } from "../letta/subprocessThrottle.js";
19
+ import type { Logger } from "../observability/logger.js";
20
+
21
+ /** Per-server-instance context wired by `createFetchHandler`. */
22
+ export interface AIHubServerContext {
23
+ /** Drizzle client typed against the schema — call this for typed queries. */
24
+ db: DrizzleDB;
25
+ /** Raw better-sqlite3 handle — used only for `transaction()` + low-level ops. */
26
+ sqlite: Database.Database;
27
+ /** Pino logger with redaction applied. */
28
+ logger: Logger;
29
+ /** Parsed `AI_HUB_ENCRYPTION_KEY` (32 bytes). */
30
+ encryptionKey: Buffer;
31
+ /**
32
+ * The route prefix (default `/api/ai-hub`). The matcher peels this off
33
+ * before pattern-matching. Set via `createFetchHandler({ basePath })`.
34
+ */
35
+ basePath: string;
36
+ /**
37
+ * Project-shaped Letta client (§6.4). The handler imports types from
38
+ * `letta/client.ts` and calls this for every Letta interaction —
39
+ * **never raw HTTP**. Tests inject a mock via
40
+ * `createFetchHandler({ lettaClient })`.
41
+ */
42
+ lettaClient: LettaClient;
43
+ /**
44
+ * Per-process throttle that gates Letta Code subprocess spawning. The chat
45
+ * route's `streamUserMessage` path calls `acquire()` before spawning and
46
+ * `release()` in the finally; if acquire throws `ServerOverloadedError`
47
+ * the route returns HTTP 503 with an `error {server_overloaded}` SSE
48
+ * event. The throttle is per-server-instance (the live-count counter is
49
+ * in-memory) and is constructed once in `createFetchHandler` from env-
50
+ * derived config. Tests can inject a configured-for-the-test instance.
51
+ *
52
+ * @see ../letta/subprocessThrottle.ts
53
+ */
54
+ subprocessThrottle: SubprocessThrottle;
55
+ /**
56
+ * Env-var record used as the fallback source for the Letta API key when
57
+ * the `admin_setting.letta_api_key_encrypted` column is empty (see
58
+ * `letta/client.ts:resolveApiKey` for the precedence). Production passes
59
+ * `process.env`; tests pass the synthesized record they boot with so
60
+ * the fallback behavior is exercisable without touching the real
61
+ * environment.
62
+ */
63
+ envSource: Record<string, string | undefined>;
64
+ /**
65
+ * Step-12.5 — absolute base directory holding `seeds/` and `sandboxes/`.
66
+ * Resolved once by `resolveFilesDir(env)` at boot from
67
+ * `AI_HUB_FILES_DIR` (or the `${AI_HUB_DATA_DIR}/files` fallback) and
68
+ * threaded through every route that touches sandbox dirs: the admin
69
+ * agents validator (seed existence check), `POST /sessions`
70
+ * (seed → sandbox copy), and `POST /admin/sessions/:id/purge-sandbox`.
71
+ *
72
+ * @see ../letta/sandbox.ts
73
+ * @see docs/server/session-sandboxes.md
74
+ */
75
+ filesDir: string;
76
+ }
77
+
78
+ /**
79
+ * The shape every route handler implements. Mirrors the Web-Standard fetch
80
+ * function signature plus the per-instance context bundle and the matched
81
+ * path parameters.
82
+ *
83
+ * `params` is keyed by the `:name` placeholders in the route template — e.g.
84
+ * `/admin/users/:id/role` yields `{ id: "abc" }`. Handlers that don't use
85
+ * params can simply omit the third argument (TypeScript bivariance allows it).
86
+ */
87
+ export type RouteHandler = (
88
+ req: Request,
89
+ ctx: AIHubServerContext,
90
+ params: Readonly<Record<string, string>>,
91
+ ) => Promise<Response>;
92
+
93
+ /** A path-aware handler entry consumed by the matcher. */
94
+ export interface RouteEntry {
95
+ /** Uppercase HTTP method, or `"*"` to match any. */
96
+ method: string;
97
+ /**
98
+ * Path template with `:param` placeholders (e.g. `/sessions/:id`). Matched
99
+ * against the request path *after* the `basePath` prefix has been peeled.
100
+ */
101
+ path: string;
102
+ /** The handler. */
103
+ handler: RouteHandler;
104
+ }
package/src/index.ts ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Public entry point for `@data-club/ai-hub-server`.
3
+ *
4
+ * Step 2 ships:
5
+ *
6
+ * - `createFetchHandler({...})` — the canonical Web-Standard Fetch entry point.
7
+ * - `createNextRouteHandler({...})` — first-class Next.js App Router adapter.
8
+ * - `bootServer({...})` — convenience: env → DB → migrate → seed → handler.
9
+ * - The shared types re-exported from `@data-club/ai-hub`.
10
+ *
11
+ * Subsequent steps add: full `/auth/*` + role middleware (Step 3), `/admin/*`
12
+ * + `/sessions` + `/agents` + `/metrics` (Step 4), Letta integration + `/chat`
13
+ * SSE (Step 5), TPM recovery (Step 6).
14
+ *
15
+ * @see SPECIFICATIONS.md §6 — server responsibilities
16
+ * @see SPECIFICATIONS.md §6.2 — stack
17
+ * @see docs/IMPLEMENTATION_PLAN.md — step-by-step delivery schedule
18
+ */
19
+
20
+ export type {
21
+ AIHubRole,
22
+ AIHubUser,
23
+ Session,
24
+ AIHubMessage,
25
+ TurnStatus,
26
+ RunningPhase,
27
+ AIHubState,
28
+ AIHubEventState,
29
+ AIHubEvent,
30
+ AIHubError,
31
+ } from "@data-club/ai-hub";
32
+
33
+ export {
34
+ createFetchHandler,
35
+ type CreateFetchHandlerOptions,
36
+ type FetchHandler,
37
+ } from "./handlers/fetch.js";
38
+
39
+ export { createNextRouteHandler } from "./handlers/nextjs.js";
40
+
41
+ export {
42
+ bootServer,
43
+ parseEnvConfig,
44
+ type BootServerOptions,
45
+ type BootedServer,
46
+ type EnvConfig,
47
+ type EnvLike,
48
+ } from "./bootstrap.js";
49
+
50
+ export {
51
+ SUPER_ADMIN_SEED_PASSWORD,
52
+ SUPER_ADMIN_SEED_USERNAME,
53
+ } from "./db/seed.js";
54
+
55
+ export {
56
+ createDefaultLettaClient,
57
+ type CreateLettaClientDeps,
58
+ type LettaClient,
59
+ type StreamUserMessageOpts,
60
+ } from "./letta/client.js";