@antasphere/chassis-server 1.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 (798) hide show
  1. package/LICENSE +82 -0
  2. package/dist/accounts/deletion.d.ts +163 -0
  3. package/dist/accounts/deletion.d.ts.map +1 -0
  4. package/dist/accounts/deletion.js +360 -0
  5. package/dist/accounts/deletion.js.map +1 -0
  6. package/dist/accounts/erasure-log.d.ts +66 -0
  7. package/dist/accounts/erasure-log.d.ts.map +1 -0
  8. package/dist/accounts/erasure-log.js +117 -0
  9. package/dist/accounts/erasure-log.js.map +1 -0
  10. package/dist/accounts/index.d.ts +6 -0
  11. package/dist/accounts/index.d.ts.map +1 -0
  12. package/dist/accounts/index.js +6 -0
  13. package/dist/accounts/index.js.map +1 -0
  14. package/dist/accounts/mint-refusal.d.ts +47 -0
  15. package/dist/accounts/mint-refusal.d.ts.map +1 -0
  16. package/dist/accounts/mint-refusal.js +59 -0
  17. package/dist/accounts/mint-refusal.js.map +1 -0
  18. package/dist/accounts/signup-duplicate.d.ts +14 -0
  19. package/dist/accounts/signup-duplicate.d.ts.map +1 -0
  20. package/dist/accounts/signup-duplicate.js +25 -0
  21. package/dist/accounts/signup-duplicate.js.map +1 -0
  22. package/dist/accounts/superadmin.d.ts +27 -0
  23. package/dist/accounts/superadmin.d.ts.map +1 -0
  24. package/dist/accounts/superadmin.js +46 -0
  25. package/dist/accounts/superadmin.js.map +1 -0
  26. package/dist/api/apikeys.d.ts +14 -0
  27. package/dist/api/apikeys.d.ts.map +1 -0
  28. package/dist/api/apikeys.js +124 -0
  29. package/dist/api/apikeys.js.map +1 -0
  30. package/dist/api/audit.d.ts +4 -0
  31. package/dist/api/audit.d.ts.map +1 -0
  32. package/dist/api/audit.js +75 -0
  33. package/dist/api/audit.js.map +1 -0
  34. package/dist/api/break-glass.d.ts +48 -0
  35. package/dist/api/break-glass.d.ts.map +1 -0
  36. package/dist/api/break-glass.js +217 -0
  37. package/dist/api/break-glass.js.map +1 -0
  38. package/dist/api/cli-auth.d.ts +84 -0
  39. package/dist/api/cli-auth.d.ts.map +1 -0
  40. package/dist/api/cli-auth.js +177 -0
  41. package/dist/api/cli-auth.js.map +1 -0
  42. package/dist/api/create-api.d.ts +123 -0
  43. package/dist/api/create-api.d.ts.map +1 -0
  44. package/dist/api/create-api.js +1165 -0
  45. package/dist/api/create-api.js.map +1 -0
  46. package/dist/api/demo-passes.d.ts +45 -0
  47. package/dist/api/demo-passes.d.ts.map +1 -0
  48. package/dist/api/demo-passes.js +143 -0
  49. package/dist/api/demo-passes.js.map +1 -0
  50. package/dist/api/errors.d.ts +5 -0
  51. package/dist/api/errors.d.ts.map +1 -0
  52. package/dist/api/errors.js +5 -0
  53. package/dist/api/errors.js.map +1 -0
  54. package/dist/api/export.d.ts +63 -0
  55. package/dist/api/export.d.ts.map +1 -0
  56. package/dist/api/export.js +393 -0
  57. package/dist/api/export.js.map +1 -0
  58. package/dist/api/files.d.ts +40 -0
  59. package/dist/api/files.d.ts.map +1 -0
  60. package/dist/api/files.js +150 -0
  61. package/dist/api/files.js.map +1 -0
  62. package/dist/api/index.d.ts +17 -0
  63. package/dist/api/index.d.ts.map +1 -0
  64. package/dist/api/index.js +17 -0
  65. package/dist/api/index.js.map +1 -0
  66. package/dist/api/invitations.d.ts +50 -0
  67. package/dist/api/invitations.d.ts.map +1 -0
  68. package/dist/api/invitations.js +313 -0
  69. package/dist/api/invitations.js.map +1 -0
  70. package/dist/api/members.d.ts +25 -0
  71. package/dist/api/members.d.ts.map +1 -0
  72. package/dist/api/members.js +522 -0
  73. package/dist/api/members.js.map +1 -0
  74. package/dist/api/onboarding.d.ts +26 -0
  75. package/dist/api/onboarding.d.ts.map +1 -0
  76. package/dist/api/onboarding.js +23 -0
  77. package/dist/api/onboarding.js.map +1 -0
  78. package/dist/api/openapi-doc.d.ts +21 -0
  79. package/dist/api/openapi-doc.d.ts.map +1 -0
  80. package/dist/api/openapi-doc.js +25 -0
  81. package/dist/api/openapi-doc.js.map +1 -0
  82. package/dist/api/projects.d.ts +24 -0
  83. package/dist/api/projects.d.ts.map +1 -0
  84. package/dist/api/projects.js +624 -0
  85. package/dist/api/projects.js.map +1 -0
  86. package/dist/api/scope-routes.d.ts +12 -0
  87. package/dist/api/scope-routes.d.ts.map +1 -0
  88. package/dist/api/scope-routes.js +2 -0
  89. package/dist/api/scope-routes.js.map +1 -0
  90. package/dist/api/sso-connect.d.ts +70 -0
  91. package/dist/api/sso-connect.d.ts.map +1 -0
  92. package/dist/api/sso-connect.js +164 -0
  93. package/dist/api/sso-connect.js.map +1 -0
  94. package/dist/api/sso-logout.d.ts +41 -0
  95. package/dist/api/sso-logout.d.ts.map +1 -0
  96. package/dist/api/sso-logout.js +34 -0
  97. package/dist/api/sso-logout.js.map +1 -0
  98. package/dist/api/teams.d.ts +43 -0
  99. package/dist/api/teams.d.ts.map +1 -0
  100. package/dist/api/teams.js +447 -0
  101. package/dist/api/teams.js.map +1 -0
  102. package/dist/api/workspaces.d.ts +114 -0
  103. package/dist/api/workspaces.d.ts.map +1 -0
  104. package/dist/api/workspaces.js +462 -0
  105. package/dist/api/workspaces.js.map +1 -0
  106. package/dist/apikeys/index.d.ts +3 -0
  107. package/dist/apikeys/index.d.ts.map +1 -0
  108. package/dist/apikeys/index.js +3 -0
  109. package/dist/apikeys/index.js.map +1 -0
  110. package/dist/apikeys/peppers.d.ts +53 -0
  111. package/dist/apikeys/peppers.d.ts.map +1 -0
  112. package/dist/apikeys/peppers.js +97 -0
  113. package/dist/apikeys/peppers.js.map +1 -0
  114. package/dist/apikeys/service.d.ts +56 -0
  115. package/dist/apikeys/service.d.ts.map +1 -0
  116. package/dist/apikeys/service.js +196 -0
  117. package/dist/apikeys/service.js.map +1 -0
  118. package/dist/app.d.ts +65 -0
  119. package/dist/app.d.ts.map +1 -0
  120. package/dist/app.js +142 -0
  121. package/dist/app.js.map +1 -0
  122. package/dist/audit/filters.d.ts +38 -0
  123. package/dist/audit/filters.d.ts.map +1 -0
  124. package/dist/audit/filters.js +97 -0
  125. package/dist/audit/filters.js.map +1 -0
  126. package/dist/audit/index.d.ts +3 -0
  127. package/dist/audit/index.d.ts.map +1 -0
  128. package/dist/audit/index.js +3 -0
  129. package/dist/audit/index.js.map +1 -0
  130. package/dist/audit/service.d.ts +65 -0
  131. package/dist/audit/service.d.ts.map +1 -0
  132. package/dist/audit/service.js +103 -0
  133. package/dist/audit/service.js.map +1 -0
  134. package/dist/boot.d.ts +8 -0
  135. package/dist/boot.d.ts.map +1 -0
  136. package/dist/boot.js +822 -0
  137. package/dist/boot.js.map +1 -0
  138. package/dist/constant-time.d.ts +3 -0
  139. package/dist/constant-time.d.ts.map +1 -0
  140. package/dist/constant-time.js +10 -0
  141. package/dist/constant-time.js.map +1 -0
  142. package/dist/email/driver.d.ts +16 -0
  143. package/dist/email/driver.d.ts.map +1 -0
  144. package/dist/email/driver.js +136 -0
  145. package/dist/email/driver.js.map +1 -0
  146. package/dist/email/index.d.ts +4 -0
  147. package/dist/email/index.d.ts.map +1 -0
  148. package/dist/email/index.js +4 -0
  149. package/dist/email/index.js.map +1 -0
  150. package/dist/email/shell.d.ts +72 -0
  151. package/dist/email/shell.d.ts.map +1 -0
  152. package/dist/email/shell.js +146 -0
  153. package/dist/email/shell.js.map +1 -0
  154. package/dist/email/templates.d.ts +83 -0
  155. package/dist/email/templates.d.ts.map +1 -0
  156. package/dist/email/templates.js +96 -0
  157. package/dist/email/templates.js.map +1 -0
  158. package/dist/entitlements/check.d.ts +140 -0
  159. package/dist/entitlements/check.d.ts.map +1 -0
  160. package/dist/entitlements/check.js +276 -0
  161. package/dist/entitlements/check.js.map +1 -0
  162. package/dist/entitlements/clock-skew.d.ts +27 -0
  163. package/dist/entitlements/clock-skew.d.ts.map +1 -0
  164. package/dist/entitlements/clock-skew.js +58 -0
  165. package/dist/entitlements/clock-skew.js.map +1 -0
  166. package/dist/entitlements/gate.d.ts +113 -0
  167. package/dist/entitlements/gate.d.ts.map +1 -0
  168. package/dist/entitlements/gate.js +430 -0
  169. package/dist/entitlements/gate.js.map +1 -0
  170. package/dist/entitlements/hub-machine-token.d.ts +83 -0
  171. package/dist/entitlements/hub-machine-token.d.ts.map +1 -0
  172. package/dist/entitlements/hub-machine-token.js +175 -0
  173. package/dist/entitlements/hub-machine-token.js.map +1 -0
  174. package/dist/entitlements/hub-subject.d.ts +32 -0
  175. package/dist/entitlements/hub-subject.d.ts.map +1 -0
  176. package/dist/entitlements/hub-subject.js +68 -0
  177. package/dist/entitlements/hub-subject.js.map +1 -0
  178. package/dist/entitlements/index.d.ts +9 -0
  179. package/dist/entitlements/index.d.ts.map +1 -0
  180. package/dist/entitlements/index.js +9 -0
  181. package/dist/entitlements/index.js.map +1 -0
  182. package/dist/entitlements/poster.d.ts +81 -0
  183. package/dist/entitlements/poster.d.ts.map +1 -0
  184. package/dist/entitlements/poster.js +169 -0
  185. package/dist/entitlements/poster.js.map +1 -0
  186. package/dist/entitlements/profiles.d.ts +121 -0
  187. package/dist/entitlements/profiles.d.ts.map +1 -0
  188. package/dist/entitlements/profiles.js +212 -0
  189. package/dist/entitlements/profiles.js.map +1 -0
  190. package/dist/entitlements/slot.d.ts +33 -0
  191. package/dist/entitlements/slot.d.ts.map +1 -0
  192. package/dist/entitlements/slot.js +75 -0
  193. package/dist/entitlements/slot.js.map +1 -0
  194. package/dist/env.d.ts +293 -0
  195. package/dist/env.d.ts.map +1 -0
  196. package/dist/env.js +383 -0
  197. package/dist/env.js.map +1 -0
  198. package/dist/files/http.d.ts +23 -0
  199. package/dist/files/http.d.ts.map +1 -0
  200. package/dist/files/http.js +66 -0
  201. package/dist/files/http.js.map +1 -0
  202. package/dist/files/index.d.ts +5 -0
  203. package/dist/files/index.d.ts.map +1 -0
  204. package/dist/files/index.js +5 -0
  205. package/dist/files/index.js.map +1 -0
  206. package/dist/files/serve.d.ts +43 -0
  207. package/dist/files/serve.d.ts.map +1 -0
  208. package/dist/files/serve.js +71 -0
  209. package/dist/files/serve.js.map +1 -0
  210. package/dist/files/service.d.ts +69 -0
  211. package/dist/files/service.d.ts.map +1 -0
  212. package/dist/files/service.js +188 -0
  213. package/dist/files/service.js.map +1 -0
  214. package/dist/files/spool.d.ts +21 -0
  215. package/dist/files/spool.d.ts.map +1 -0
  216. package/dist/files/spool.js +47 -0
  217. package/dist/files/spool.js.map +1 -0
  218. package/dist/identity/better-auth.d.ts +3927 -0
  219. package/dist/identity/better-auth.d.ts.map +1 -0
  220. package/dist/identity/better-auth.js +896 -0
  221. package/dist/identity/better-auth.js.map +1 -0
  222. package/dist/identity/demo-pass-plugin.d.ts +29 -0
  223. package/dist/identity/demo-pass-plugin.d.ts.map +1 -0
  224. package/dist/identity/demo-pass-plugin.js +88 -0
  225. package/dist/identity/demo-pass-plugin.js.map +1 -0
  226. package/dist/identity/demo-pass-rules.d.ts +84 -0
  227. package/dist/identity/demo-pass-rules.d.ts.map +1 -0
  228. package/dist/identity/demo-pass-rules.js +223 -0
  229. package/dist/identity/demo-pass-rules.js.map +1 -0
  230. package/dist/identity/demo-pass.d.ts +159 -0
  231. package/dist/identity/demo-pass.d.ts.map +1 -0
  232. package/dist/identity/demo-pass.js +319 -0
  233. package/dist/identity/demo-pass.js.map +1 -0
  234. package/dist/identity/hub-grant.d.ts +182 -0
  235. package/dist/identity/hub-grant.d.ts.map +1 -0
  236. package/dist/identity/hub-grant.js +594 -0
  237. package/dist/identity/hub-grant.js.map +1 -0
  238. package/dist/identity/hub-jwt.d.ts +22 -0
  239. package/dist/identity/hub-jwt.d.ts.map +1 -0
  240. package/dist/identity/hub-jwt.js +77 -0
  241. package/dist/identity/hub-jwt.js.map +1 -0
  242. package/dist/identity/hub-live-gate.d.ts +17 -0
  243. package/dist/identity/hub-live-gate.d.ts.map +1 -0
  244. package/dist/identity/hub-live-gate.js +146 -0
  245. package/dist/identity/hub-live-gate.js.map +1 -0
  246. package/dist/identity/hub-logout.d.ts +88 -0
  247. package/dist/identity/hub-logout.d.ts.map +1 -0
  248. package/dist/identity/hub-logout.js +158 -0
  249. package/dist/identity/hub-logout.js.map +1 -0
  250. package/dist/identity/hub-projection.d.ts +103 -0
  251. package/dist/identity/hub-projection.d.ts.map +1 -0
  252. package/dist/identity/hub-projection.js +130 -0
  253. package/dist/identity/hub-projection.js.map +1 -0
  254. package/dist/identity/hub-reconcile.d.ts +151 -0
  255. package/dist/identity/hub-reconcile.d.ts.map +1 -0
  256. package/dist/identity/hub-reconcile.js +400 -0
  257. package/dist/identity/hub-reconcile.js.map +1 -0
  258. package/dist/identity/hub-sso.d.ts +363 -0
  259. package/dist/identity/hub-sso.d.ts.map +1 -0
  260. package/dist/identity/hub-sso.js +536 -0
  261. package/dist/identity/hub-sso.js.map +1 -0
  262. package/dist/identity/hub-user-client.d.ts +222 -0
  263. package/dist/identity/hub-user-client.d.ts.map +1 -0
  264. package/dist/identity/hub-user-client.js +406 -0
  265. package/dist/identity/hub-user-client.js.map +1 -0
  266. package/dist/identity/index.d.ts +13 -0
  267. package/dist/identity/index.d.ts.map +1 -0
  268. package/dist/identity/index.js +13 -0
  269. package/dist/identity/index.js.map +1 -0
  270. package/dist/identity/oauth-jwt.d.ts +32 -0
  271. package/dist/identity/oauth-jwt.d.ts.map +1 -0
  272. package/dist/identity/oauth-jwt.js +122 -0
  273. package/dist/identity/oauth-jwt.js.map +1 -0
  274. package/dist/identity/resolve-membership.d.ts +28 -0
  275. package/dist/identity/resolve-membership.d.ts.map +1 -0
  276. package/dist/identity/resolve-membership.js +69 -0
  277. package/dist/identity/resolve-membership.js.map +1 -0
  278. package/dist/identity/signing-key.d.ts +49 -0
  279. package/dist/identity/signing-key.d.ts.map +1 -0
  280. package/dist/identity/signing-key.js +144 -0
  281. package/dist/identity/signing-key.js.map +1 -0
  282. package/dist/index.d.ts +21 -0
  283. package/dist/index.d.ts.map +1 -0
  284. package/dist/index.js +11 -0
  285. package/dist/index.js.map +1 -0
  286. package/dist/invitations/index.d.ts +2 -0
  287. package/dist/invitations/index.d.ts.map +1 -0
  288. package/dist/invitations/index.js +2 -0
  289. package/dist/invitations/index.js.map +1 -0
  290. package/dist/invitations/service.d.ts +36 -0
  291. package/dist/invitations/service.d.ts.map +1 -0
  292. package/dist/invitations/service.js +122 -0
  293. package/dist/invitations/service.js.map +1 -0
  294. package/dist/jobs/index.d.ts +3 -0
  295. package/dist/jobs/index.d.ts.map +1 -0
  296. package/dist/jobs/index.js +3 -0
  297. package/dist/jobs/index.js.map +1 -0
  298. package/dist/jobs/pgboss.d.ts +119 -0
  299. package/dist/jobs/pgboss.d.ts.map +1 -0
  300. package/dist/jobs/pgboss.js +521 -0
  301. package/dist/jobs/pgboss.js.map +1 -0
  302. package/dist/jobs/timers.d.ts +127 -0
  303. package/dist/jobs/timers.d.ts.map +1 -0
  304. package/dist/jobs/timers.js +228 -0
  305. package/dist/jobs/timers.js.map +1 -0
  306. package/dist/logger.d.ts +17 -0
  307. package/dist/logger.d.ts.map +1 -0
  308. package/dist/logger.js +64 -0
  309. package/dist/logger.js.map +1 -0
  310. package/dist/mcp/errors.d.ts +37 -0
  311. package/dist/mcp/errors.d.ts.map +1 -0
  312. package/dist/mcp/errors.js +77 -0
  313. package/dist/mcp/errors.js.map +1 -0
  314. package/dist/mcp/http.d.ts +35 -0
  315. package/dist/mcp/http.d.ts.map +1 -0
  316. package/dist/mcp/http.js +80 -0
  317. package/dist/mcp/http.js.map +1 -0
  318. package/dist/mcp/index.d.ts +8 -0
  319. package/dist/mcp/index.d.ts.map +1 -0
  320. package/dist/mcp/index.js +8 -0
  321. package/dist/mcp/index.js.map +1 -0
  322. package/dist/mcp/inputs.d.ts +19 -0
  323. package/dist/mcp/inputs.d.ts.map +1 -0
  324. package/dist/mcp/inputs.js +28 -0
  325. package/dist/mcp/inputs.js.map +1 -0
  326. package/dist/mcp/projects.d.ts +31 -0
  327. package/dist/mcp/projects.d.ts.map +1 -0
  328. package/dist/mcp/projects.js +315 -0
  329. package/dist/mcp/projects.js.map +1 -0
  330. package/dist/mcp/server.d.ts +52 -0
  331. package/dist/mcp/server.d.ts.map +1 -0
  332. package/dist/mcp/server.js +93 -0
  333. package/dist/mcp/server.js.map +1 -0
  334. package/dist/mcp/teams.d.ts +7 -0
  335. package/dist/mcp/teams.d.ts.map +1 -0
  336. package/dist/mcp/teams.js +62 -0
  337. package/dist/mcp/teams.js.map +1 -0
  338. package/dist/mcp/tool-kit.d.ts +67 -0
  339. package/dist/mcp/tool-kit.d.ts.map +1 -0
  340. package/dist/mcp/tool-kit.js +74 -0
  341. package/dist/mcp/tool-kit.js.map +1 -0
  342. package/dist/members/removal.d.ts +95 -0
  343. package/dist/members/removal.d.ts.map +1 -0
  344. package/dist/members/removal.js +143 -0
  345. package/dist/members/removal.js.map +1 -0
  346. package/dist/middleware/auth-body.d.ts +25 -0
  347. package/dist/middleware/auth-body.d.ts.map +1 -0
  348. package/dist/middleware/auth-body.js +55 -0
  349. package/dist/middleware/auth-body.js.map +1 -0
  350. package/dist/middleware/auth-context.d.ts +170 -0
  351. package/dist/middleware/auth-context.d.ts.map +1 -0
  352. package/dist/middleware/auth-context.js +251 -0
  353. package/dist/middleware/auth-context.js.map +1 -0
  354. package/dist/middleware/body-cap.d.ts +17 -0
  355. package/dist/middleware/body-cap.d.ts.map +1 -0
  356. package/dist/middleware/body-cap.js +31 -0
  357. package/dist/middleware/body-cap.js.map +1 -0
  358. package/dist/middleware/body-refusal.d.ts +20 -0
  359. package/dist/middleware/body-refusal.d.ts.map +1 -0
  360. package/dist/middleware/body-refusal.js +5 -0
  361. package/dist/middleware/body-refusal.js.map +1 -0
  362. package/dist/middleware/cross-site.d.ts +61 -0
  363. package/dist/middleware/cross-site.d.ts.map +1 -0
  364. package/dist/middleware/cross-site.js +59 -0
  365. package/dist/middleware/cross-site.js.map +1 -0
  366. package/dist/middleware/hub-managed.d.ts +45 -0
  367. package/dist/middleware/hub-managed.d.ts.map +1 -0
  368. package/dist/middleware/hub-managed.js +56 -0
  369. package/dist/middleware/hub-managed.js.map +1 -0
  370. package/dist/middleware/idempotency.d.ts +14 -0
  371. package/dist/middleware/idempotency.d.ts.map +1 -0
  372. package/dist/middleware/idempotency.js +222 -0
  373. package/dist/middleware/idempotency.js.map +1 -0
  374. package/dist/middleware/index.d.ts +12 -0
  375. package/dist/middleware/index.d.ts.map +1 -0
  376. package/dist/middleware/index.js +12 -0
  377. package/dist/middleware/index.js.map +1 -0
  378. package/dist/middleware/json-depth.d.ts +32 -0
  379. package/dist/middleware/json-depth.d.ts.map +1 -0
  380. package/dist/middleware/json-depth.js +80 -0
  381. package/dist/middleware/json-depth.js.map +1 -0
  382. package/dist/middleware/no-store.d.ts +21 -0
  383. package/dist/middleware/no-store.d.ts.map +1 -0
  384. package/dist/middleware/no-store.js +32 -0
  385. package/dist/middleware/no-store.js.map +1 -0
  386. package/dist/middleware/oauth-public.d.ts +16 -0
  387. package/dist/middleware/oauth-public.d.ts.map +1 -0
  388. package/dist/middleware/oauth-public.js +50 -0
  389. package/dist/middleware/oauth-public.js.map +1 -0
  390. package/dist/middleware/rate-limit.d.ts +182 -0
  391. package/dist/middleware/rate-limit.d.ts.map +1 -0
  392. package/dist/middleware/rate-limit.js +277 -0
  393. package/dist/middleware/rate-limit.js.map +1 -0
  394. package/dist/middleware/request-id.d.ts +10 -0
  395. package/dist/middleware/request-id.d.ts.map +1 -0
  396. package/dist/middleware/request-id.js +42 -0
  397. package/dist/middleware/request-id.js.map +1 -0
  398. package/dist/middleware/scopes.d.ts +33 -0
  399. package/dist/middleware/scopes.d.ts.map +1 -0
  400. package/dist/middleware/scopes.js +139 -0
  401. package/dist/middleware/scopes.js.map +1 -0
  402. package/dist/middleware/security-headers.d.ts +53 -0
  403. package/dist/middleware/security-headers.d.ts.map +1 -0
  404. package/dist/middleware/security-headers.js +96 -0
  405. package/dist/middleware/security-headers.js.map +1 -0
  406. package/dist/observability/index.d.ts +3 -0
  407. package/dist/observability/index.d.ts.map +1 -0
  408. package/dist/observability/index.js +3 -0
  409. package/dist/observability/index.js.map +1 -0
  410. package/dist/observability/metrics.d.ts +17 -0
  411. package/dist/observability/metrics.d.ts.map +1 -0
  412. package/dist/observability/metrics.js +82 -0
  413. package/dist/observability/metrics.js.map +1 -0
  414. package/dist/observability/otel.d.ts +20 -0
  415. package/dist/observability/otel.d.ts.map +1 -0
  416. package/dist/observability/otel.js +54 -0
  417. package/dist/observability/otel.js.map +1 -0
  418. package/dist/pagination.d.ts +57 -0
  419. package/dist/pagination.d.ts.map +1 -0
  420. package/dist/pagination.js +90 -0
  421. package/dist/pagination.js.map +1 -0
  422. package/dist/platform/edition.d.ts +57 -0
  423. package/dist/platform/edition.d.ts.map +1 -0
  424. package/dist/platform/edition.js +90 -0
  425. package/dist/platform/edition.js.map +1 -0
  426. package/dist/platform/entitlements.d.ts +26 -0
  427. package/dist/platform/entitlements.d.ts.map +1 -0
  428. package/dist/platform/entitlements.js +28 -0
  429. package/dist/platform/entitlements.js.map +1 -0
  430. package/dist/platform/events.d.ts +87 -0
  431. package/dist/platform/events.d.ts.map +1 -0
  432. package/dist/platform/events.js +32 -0
  433. package/dist/platform/events.js.map +1 -0
  434. package/dist/platform/index.d.ts +8 -0
  435. package/dist/platform/index.d.ts.map +1 -0
  436. package/dist/platform/index.js +8 -0
  437. package/dist/platform/index.js.map +1 -0
  438. package/dist/platform/local-identity.d.ts +39 -0
  439. package/dist/platform/local-identity.d.ts.map +1 -0
  440. package/dist/platform/local-identity.js +87 -0
  441. package/dist/platform/local-identity.js.map +1 -0
  442. package/dist/platform/registry.d.ts +23 -0
  443. package/dist/platform/registry.d.ts.map +1 -0
  444. package/dist/platform/registry.js +5 -0
  445. package/dist/platform/registry.js.map +1 -0
  446. package/dist/platform/usage.d.ts +11 -0
  447. package/dist/platform/usage.d.ts.map +1 -0
  448. package/dist/platform/usage.js +12 -0
  449. package/dist/platform/usage.js.map +1 -0
  450. package/dist/platform/workspaces.d.ts +25 -0
  451. package/dist/platform/workspaces.d.ts.map +1 -0
  452. package/dist/platform/workspaces.js +44 -0
  453. package/dist/platform/workspaces.js.map +1 -0
  454. package/dist/projects/access.d.ts +40 -0
  455. package/dist/projects/access.d.ts.map +1 -0
  456. package/dist/projects/access.js +133 -0
  457. package/dist/projects/access.js.map +1 -0
  458. package/dist/projects/index.d.ts +2 -0
  459. package/dist/projects/index.d.ts.map +1 -0
  460. package/dist/projects/index.js +2 -0
  461. package/dist/projects/index.js.map +1 -0
  462. package/dist/route-label.d.ts +23 -0
  463. package/dist/route-label.d.ts.map +1 -0
  464. package/dist/route-label.js +29 -0
  465. package/dist/route-label.js.map +1 -0
  466. package/dist/routes/health.d.ts +30 -0
  467. package/dist/routes/health.d.ts.map +1 -0
  468. package/dist/routes/health.js +54 -0
  469. package/dist/routes/health.js.map +1 -0
  470. package/dist/routes/index.d.ts +3 -0
  471. package/dist/routes/index.d.ts.map +1 -0
  472. package/dist/routes/index.js +3 -0
  473. package/dist/routes/index.js.map +1 -0
  474. package/dist/routes/wellknown.d.ts +11 -0
  475. package/dist/routes/wellknown.d.ts.map +1 -0
  476. package/dist/routes/wellknown.js +45 -0
  477. package/dist/routes/wellknown.js.map +1 -0
  478. package/dist/secret.d.ts +30 -0
  479. package/dist/secret.d.ts.map +1 -0
  480. package/dist/secret.js +97 -0
  481. package/dist/secret.js.map +1 -0
  482. package/dist/setup-transport.d.ts +23 -0
  483. package/dist/setup-transport.d.ts.map +1 -0
  484. package/dist/setup-transport.js +52 -0
  485. package/dist/setup-transport.js.map +1 -0
  486. package/dist/state.d.ts +21 -0
  487. package/dist/state.d.ts.map +1 -0
  488. package/dist/state.js +4 -0
  489. package/dist/state.js.map +1 -0
  490. package/dist/storage/driver.d.ts +38 -0
  491. package/dist/storage/driver.d.ts.map +1 -0
  492. package/dist/storage/driver.js +14 -0
  493. package/dist/storage/driver.js.map +1 -0
  494. package/dist/storage/factory.d.ts +4 -0
  495. package/dist/storage/factory.d.ts.map +1 -0
  496. package/dist/storage/factory.js +20 -0
  497. package/dist/storage/factory.js.map +1 -0
  498. package/dist/storage/index.d.ts +4 -0
  499. package/dist/storage/index.d.ts.map +1 -0
  500. package/dist/storage/index.js +4 -0
  501. package/dist/storage/index.js.map +1 -0
  502. package/dist/storage/local.d.ts +21 -0
  503. package/dist/storage/local.d.ts.map +1 -0
  504. package/dist/storage/local.js +88 -0
  505. package/dist/storage/local.js.map +1 -0
  506. package/dist/storage/s3.d.ts +29 -0
  507. package/dist/storage/s3.d.ts.map +1 -0
  508. package/dist/storage/s3.js +82 -0
  509. package/dist/storage/s3.js.map +1 -0
  510. package/dist/testing/fake-hub.d.ts +425 -0
  511. package/dist/testing/fake-hub.d.ts.map +1 -0
  512. package/dist/testing/fake-hub.js +1074 -0
  513. package/dist/testing/fake-hub.js.map +1 -0
  514. package/dist/testing/helpers.d.ts +124 -0
  515. package/dist/testing/helpers.d.ts.map +1 -0
  516. package/dist/testing/helpers.js +174 -0
  517. package/dist/testing/helpers.js.map +1 -0
  518. package/dist/testing/index.d.ts +12 -0
  519. package/dist/testing/index.d.ts.map +1 -0
  520. package/dist/testing/index.js +12 -0
  521. package/dist/testing/index.js.map +1 -0
  522. package/dist/testing/setup-origins.d.ts +21 -0
  523. package/dist/testing/setup-origins.d.ts.map +1 -0
  524. package/dist/testing/setup-origins.js +57 -0
  525. package/dist/testing/setup-origins.js.map +1 -0
  526. package/dist/testing/sso-helpers.d.ts +43 -0
  527. package/dist/testing/sso-helpers.d.ts.map +1 -0
  528. package/dist/testing/sso-helpers.js +159 -0
  529. package/dist/testing/sso-helpers.js.map +1 -0
  530. package/dist/tool-definition.d.ts +382 -0
  531. package/dist/tool-definition.d.ts.map +1 -0
  532. package/dist/tool-definition.js +46 -0
  533. package/dist/tool-definition.js.map +1 -0
  534. package/dist/util/index.d.ts +8 -0
  535. package/dist/util/index.d.ts.map +1 -0
  536. package/dist/util/index.js +8 -0
  537. package/dist/util/index.js.map +1 -0
  538. package/package.json +161 -0
  539. package/src/accounts/deletion.ts +389 -0
  540. package/src/accounts/erasure-log.ts +140 -0
  541. package/src/accounts/index.ts +5 -0
  542. package/src/accounts/mint-refusal.ts +67 -0
  543. package/src/accounts/signup-duplicate.ts +21 -0
  544. package/src/accounts/superadmin.ts +46 -0
  545. package/src/api/apikeys.ts +147 -0
  546. package/src/api/audit.ts +83 -0
  547. package/src/api/break-glass.ts +294 -0
  548. package/src/api/cli-auth.ts +308 -0
  549. package/src/api/create-api.ts +1403 -0
  550. package/src/api/demo-passes.ts +207 -0
  551. package/src/api/errors.ts +13 -0
  552. package/src/api/export.ts +482 -0
  553. package/src/api/files.ts +200 -0
  554. package/src/api/index.ts +29 -0
  555. package/src/api/invitations.ts +396 -0
  556. package/src/api/members.ts +617 -0
  557. package/src/api/onboarding.ts +50 -0
  558. package/src/api/openapi-doc.ts +34 -0
  559. package/src/api/projects.ts +788 -0
  560. package/src/api/scope-routes.ts +12 -0
  561. package/src/api/sso-connect.ts +246 -0
  562. package/src/api/sso-logout.ts +76 -0
  563. package/src/api/teams.ts +575 -0
  564. package/src/api/workspaces.ts +649 -0
  565. package/src/apikeys/index.ts +2 -0
  566. package/src/apikeys/peppers.ts +118 -0
  567. package/src/apikeys/service.ts +223 -0
  568. package/src/app.ts +228 -0
  569. package/src/audit/filters.ts +101 -0
  570. package/src/audit/index.ts +9 -0
  571. package/src/audit/service.ts +154 -0
  572. package/src/boot.ts +953 -0
  573. package/src/constant-time.ts +9 -0
  574. package/src/email/driver.ts +139 -0
  575. package/src/email/index.ts +26 -0
  576. package/src/email/shell.ts +201 -0
  577. package/src/email/templates.ts +184 -0
  578. package/src/entitlements/check.ts +435 -0
  579. package/src/entitlements/clock-skew.ts +58 -0
  580. package/src/entitlements/gate.ts +636 -0
  581. package/src/entitlements/hub-machine-token.ts +205 -0
  582. package/src/entitlements/hub-subject.ts +77 -0
  583. package/src/entitlements/index.ts +37 -0
  584. package/src/entitlements/poster.ts +265 -0
  585. package/src/entitlements/profiles.ts +303 -0
  586. package/src/entitlements/slot.ts +105 -0
  587. package/src/env.ts +456 -0
  588. package/src/files/http.ts +77 -0
  589. package/src/files/index.ts +4 -0
  590. package/src/files/serve.ts +115 -0
  591. package/src/files/service.ts +209 -0
  592. package/src/files/spool.ts +58 -0
  593. package/src/identity/better-auth.ts +1103 -0
  594. package/src/identity/demo-pass-plugin.ts +121 -0
  595. package/src/identity/demo-pass-rules.ts +229 -0
  596. package/src/identity/demo-pass.ts +390 -0
  597. package/src/identity/hub-grant.ts +704 -0
  598. package/src/identity/hub-jwt.ts +78 -0
  599. package/src/identity/hub-live-gate.ts +167 -0
  600. package/src/identity/hub-logout.ts +174 -0
  601. package/src/identity/hub-projection.ts +251 -0
  602. package/src/identity/hub-reconcile.ts +572 -0
  603. package/src/identity/hub-sso.ts +696 -0
  604. package/src/identity/hub-user-client.ts +554 -0
  605. package/src/identity/index.ts +56 -0
  606. package/src/identity/oauth-jwt.ts +128 -0
  607. package/src/identity/resolve-membership.ts +104 -0
  608. package/src/identity/signing-key.ts +164 -0
  609. package/src/index.ts +65 -0
  610. package/src/invitations/index.ts +1 -0
  611. package/src/invitations/service.ts +171 -0
  612. package/src/jobs/index.ts +25 -0
  613. package/src/jobs/pgboss.ts +594 -0
  614. package/src/jobs/timers.ts +287 -0
  615. package/src/logger.ts +67 -0
  616. package/src/mcp/errors.ts +111 -0
  617. package/src/mcp/http.ts +129 -0
  618. package/src/mcp/index.ts +28 -0
  619. package/src/mcp/inputs.ts +33 -0
  620. package/src/mcp/projects.ts +480 -0
  621. package/src/mcp/server.ts +174 -0
  622. package/src/mcp/teams.ts +97 -0
  623. package/src/mcp/tool-kit.ts +148 -0
  624. package/src/members/removal.ts +216 -0
  625. package/src/middleware/auth-body.ts +58 -0
  626. package/src/middleware/auth-context.ts +384 -0
  627. package/src/middleware/body-cap.ts +33 -0
  628. package/src/middleware/body-refusal.ts +23 -0
  629. package/src/middleware/cross-site.ts +122 -0
  630. package/src/middleware/hub-managed.ts +61 -0
  631. package/src/middleware/idempotency.ts +264 -0
  632. package/src/middleware/index.ts +33 -0
  633. package/src/middleware/json-depth.ts +74 -0
  634. package/src/middleware/no-store.ts +30 -0
  635. package/src/middleware/oauth-public.ts +54 -0
  636. package/src/middleware/rate-limit.ts +432 -0
  637. package/src/middleware/request-id.ts +54 -0
  638. package/src/middleware/scopes.ts +150 -0
  639. package/src/middleware/security-headers.ts +129 -0
  640. package/src/observability/index.ts +2 -0
  641. package/src/observability/metrics.ts +102 -0
  642. package/src/observability/otel.ts +79 -0
  643. package/src/pagination.ts +118 -0
  644. package/src/platform/edition.ts +140 -0
  645. package/src/platform/entitlements.ts +47 -0
  646. package/src/platform/events.ts +71 -0
  647. package/src/platform/index.ts +7 -0
  648. package/src/platform/local-identity.ts +90 -0
  649. package/src/platform/registry.ts +28 -0
  650. package/src/platform/usage.ts +13 -0
  651. package/src/platform/workspaces.ts +41 -0
  652. package/src/projects/access.ts +160 -0
  653. package/src/projects/index.ts +7 -0
  654. package/src/route-label.ts +29 -0
  655. package/src/routes/health.ts +79 -0
  656. package/src/routes/index.ts +2 -0
  657. package/src/routes/wellknown.ts +67 -0
  658. package/src/secret.ts +115 -0
  659. package/src/setup-transport.ts +46 -0
  660. package/src/state.ts +23 -0
  661. package/src/storage/driver.ts +48 -0
  662. package/src/storage/factory.ts +34 -0
  663. package/src/storage/index.ts +3 -0
  664. package/src/storage/local.ts +93 -0
  665. package/src/storage/s3.ts +109 -0
  666. package/src/testing/fake-hub.ts +1316 -0
  667. package/src/testing/helpers.ts +254 -0
  668. package/src/testing/index.ts +11 -0
  669. package/src/testing/setup-origins.ts +58 -0
  670. package/src/testing/sso-helpers.ts +178 -0
  671. package/src/tool-definition.ts +463 -0
  672. package/src/util/index.ts +17 -0
  673. package/test/fixtures/onboarding-backfill.sql +13 -0
  674. package/test/fixtures/operator-user-id-backfill.sql +18 -0
  675. package/test/host/dr-lib.sh +253 -0
  676. package/test/host/drizzle/0000_chassis_baseline.sql +438 -0
  677. package/test/host/drizzle/0001_last_owner_guard.sql +42 -0
  678. package/test/host/drizzle/meta/0000_snapshot.json +3566 -0
  679. package/test/host/drizzle/meta/_journal.json +20 -0
  680. package/test/host/drizzle.config.ts +17 -0
  681. package/test/host/identity.ts +41 -0
  682. package/test/host/minimal-host.ts +35 -0
  683. package/test/host/minimal-tool.ts +123 -0
  684. package/test/integration/account-oracle-uniformity.test.ts +284 -0
  685. package/test/integration/account-recovery.test.ts +236 -0
  686. package/test/integration/account.test.ts +120 -0
  687. package/test/integration/api-quota.test.ts +208 -0
  688. package/test/integration/apikey-expiry.test.ts +178 -0
  689. package/test/integration/apikey-pepper-rotation.test.ts +190 -0
  690. package/test/integration/boot-posture.test.ts +169 -0
  691. package/test/integration/break-glass.test.ts +484 -0
  692. package/test/integration/cloud-lifecycle.test.ts +233 -0
  693. package/test/integration/credential-boundaries.test.ts +195 -0
  694. package/test/integration/default-workspace.test.ts +414 -0
  695. package/test/integration/demo-pass-rules.test.ts +332 -0
  696. package/test/integration/demo-pass.test.ts +1222 -0
  697. package/test/integration/dr-verify-real-dump.test.ts +154 -0
  698. package/test/integration/edition.test.ts +868 -0
  699. package/test/integration/email-change.test.ts +394 -0
  700. package/test/integration/empty-tool.test.ts +267 -0
  701. package/test/integration/entitlements-null-hook.test.ts +187 -0
  702. package/test/integration/entitlements.test.ts +693 -0
  703. package/test/integration/export-entries.test.ts +321 -0
  704. package/test/integration/files.test.ts +320 -0
  705. package/test/integration/first-boot-claim.test.ts +131 -0
  706. package/test/integration/gdpr.test.ts +532 -0
  707. package/test/integration/helpers.ts +29 -0
  708. package/test/integration/hub-grant.test.ts +505 -0
  709. package/test/integration/hub-live-gate.test.ts +612 -0
  710. package/test/integration/hub-managed-membership.test.ts +559 -0
  711. package/test/integration/hub-org-selector.test.ts +281 -0
  712. package/test/integration/hub-reconcile.test.ts +646 -0
  713. package/test/integration/hub-sso.test.ts +704 -0
  714. package/test/integration/hub-teams.test.ts +771 -0
  715. package/test/integration/idempotency.test.ts +261 -0
  716. package/test/integration/invite-verification.test.ts +210 -0
  717. package/test/integration/last-owner-race.test.ts +263 -0
  718. package/test/integration/m9-fixes.test.ts +290 -0
  719. package/test/integration/mcp-projects.test.ts +463 -0
  720. package/test/integration/member-removal-race.test.ts +466 -0
  721. package/test/integration/member-removal.test.ts +794 -0
  722. package/test/integration/oauth-mcp.test.ts +477 -0
  723. package/test/integration/oauth-workspace.test.ts +371 -0
  724. package/test/integration/observability.test.ts +183 -0
  725. package/test/integration/onboarding.test.ts +268 -0
  726. package/test/integration/orphan-purge.test.ts +451 -0
  727. package/test/integration/pagination.test.ts +344 -0
  728. package/test/integration/platform-core.test.ts +305 -0
  729. package/test/integration/platform.test.ts +246 -0
  730. package/test/integration/pool-survives-restart.test.ts +80 -0
  731. package/test/integration/projects-cloud.test.ts +193 -0
  732. package/test/integration/projects.test.ts +1310 -0
  733. package/test/integration/security-fixes.test.ts +190 -0
  734. package/test/integration/setup-transport.test.ts +118 -0
  735. package/test/integration/signin-wall.test.ts +103 -0
  736. package/test/integration/signing-key-rotation.test.ts +349 -0
  737. package/test/integration/sso-helpers.ts +15 -0
  738. package/test/integration/sso-logout.test.ts +283 -0
  739. package/test/integration/teams-cloud.test.ts +194 -0
  740. package/test/integration/teams.test.ts +551 -0
  741. package/test/integration/timers.test.ts +377 -0
  742. package/test/integration/two-factor.test.ts +301 -0
  743. package/test/integration/usage-window.test.ts +161 -0
  744. package/test/integration/workspace-create-cloud.test.ts +523 -0
  745. package/test/integration/workspace-create.test.ts +510 -0
  746. package/test/integration/workspace-scoping.test.ts +530 -0
  747. package/test/integration/zz-adv-replay-owner.test.ts +205 -0
  748. package/test/unit/apikey-peppers.test.ts +110 -0
  749. package/test/unit/apikey-prefix.test.ts +34 -0
  750. package/test/unit/audit-filters.test.ts +135 -0
  751. package/test/unit/auth-origins.test.ts +92 -0
  752. package/test/unit/body-cap.test.ts +84 -0
  753. package/test/unit/clock-skew.test.ts +68 -0
  754. package/test/unit/cross-site.test.ts +166 -0
  755. package/test/unit/declared-credits.test.ts +13 -0
  756. package/test/unit/demo-pass-rules.test.ts +192 -0
  757. package/test/unit/email-driver.test.ts +127 -0
  758. package/test/unit/end-pool.test.ts +181 -0
  759. package/test/unit/entitlement-check.test.ts +492 -0
  760. package/test/unit/entitlement-gate.test.ts +1333 -0
  761. package/test/unit/entitlement-profiles.test.ts +289 -0
  762. package/test/unit/erasure-log.test.ts +72 -0
  763. package/test/unit/error-mapping.test.ts +89 -0
  764. package/test/unit/export-entries.test.ts +85 -0
  765. package/test/unit/export-sanitize.test.ts +27 -0
  766. package/test/unit/files-head-route.test.ts +155 -0
  767. package/test/unit/files-http.test.ts +97 -0
  768. package/test/unit/head-rewrite.test.ts +72 -0
  769. package/test/unit/hub-create-org.test.ts +181 -0
  770. package/test/unit/hub-jwt.test.ts +93 -0
  771. package/test/unit/hub-sso-params.test.ts +39 -0
  772. package/test/unit/hub-subject.test.ts +83 -0
  773. package/test/unit/idempotency-crypto.test.ts +45 -0
  774. package/test/unit/json-depth.test.ts +88 -0
  775. package/test/unit/logger-redaction.test.ts +66 -0
  776. package/test/unit/mcp-errors.test.ts +110 -0
  777. package/test/unit/mcp-inputs.test.ts +106 -0
  778. package/test/unit/mcp-project-hints.test.ts +26 -0
  779. package/test/unit/no-store.test.ts +49 -0
  780. package/test/unit/no-tool-migration-filename.test.ts +70 -0
  781. package/test/unit/observability.test.ts +64 -0
  782. package/test/unit/openapi-doc.test.ts +93 -0
  783. package/test/unit/rate-limit-consume-on.test.ts +76 -0
  784. package/test/unit/rate-limit.test.ts +39 -0
  785. package/test/unit/readyz-storage.test.ts +157 -0
  786. package/test/unit/request-quota.test.ts +146 -0
  787. package/test/unit/scopes.test.ts +66 -0
  788. package/test/unit/security-headers.test.ts +131 -0
  789. package/test/unit/serve-content-type.test.ts +74 -0
  790. package/test/unit/setup-transport.test.ts +29 -0
  791. package/test/unit/storage-local.test.ts +53 -0
  792. package/test/unit/superadmin.test.ts +32 -0
  793. package/test/unit/timers.test.ts +287 -0
  794. package/test/unit/tool-definition-guards.test.ts +166 -0
  795. package/test/unit/tool-entitlements-guard.test.ts +168 -0
  796. package/test/unit/usage-poster.test.ts +542 -0
  797. package/test/unit/usage-window.test.ts +48 -0
  798. package/test/unit/workspace-create-audit.test.ts +48 -0
@@ -0,0 +1,287 @@
1
+ import type PgBoss from 'pg-boss';
2
+ import type pg from 'pg';
3
+ import type { Logger } from '../logger.js';
4
+
5
+ /**
6
+ * One-off timers (PRDCT-3254): a tool runs a function ONCE at an instant or
7
+ * after a delay, with a JSON payload, on a queue its `jobs` slot declared.
8
+ * The runtime is pg-boss, the same durable queue the crons ride: a timer is a
9
+ * job row with `start_after` in the future, so it lives in the tool's own
10
+ * Postgres, survives a restart and a redeploy, and is taken by exactly one
11
+ * worker replica (pg-boss fetches with SKIP LOCKED). Edition-neutral, and an
12
+ * api-role replica schedules and cancels like any other: neither needs DDL.
13
+ *
14
+ * A KEY makes a timer replaceable: at most one PENDING timer per (queue, key).
15
+ * `schedule` with a key withdraws the pending timer of that key and inserts
16
+ * the new one in ONE transaction under a per-key advisory lock, so two
17
+ * replicas re-arming the same thing at once never leave two timers, and
18
+ * `cancel({ key })` withdraws whatever is pending under it. A timer that is
19
+ * already RUNNING is neither replaced nor cancelled (its handler is on it;
20
+ * the handler's own claim decides what it does), so a consumer keeps its
21
+ * at-most-once guard in the data it writes (meet's claim-before-send stamps),
22
+ * never in the timer alone. What a replace or a cancel DOES do to a running
23
+ * timer is take its retries away (`retry_limit = retry_count`): pg-boss's
24
+ * fail path re-inserts a failed run as a pending `retry` row with the same
25
+ * key, which would put the withdrawn payload back beside the new one
26
+ * (verifier round 1, F1). A withdrawn run that throws therefore ends
27
+ * `failed`, and "one pending per key" holds across the run's failure too.
28
+ * `cancel` itself runs without the per-key lock: a cancel concurrent with a
29
+ * replace may answer 0 and leave the replace's fresh timer pending (it ran
30
+ * first); a consumer that wants "nothing pending" cancels after its last
31
+ * schedule, as every path does.
32
+ *
33
+ * The payload is a plain JSON object (not an array, not a Date, nothing a
34
+ * `JSON.stringify` refuses): node-postgres serialises anything else into a
35
+ * value the `jsonb` column refuses with a raw database error, so the check is
36
+ * made here and answers `invalid_argument` before any statement runs.
37
+ *
38
+ * What a timer costs in pg-boss's terms: `keep_until` is `start_after` plus
39
+ * the queue's retention (fourteen days by default), so a timer months ahead is
40
+ * not archived before its time; the handler's run is bounded by the queue's
41
+ * `expireIn` (fifteen minutes by default); a handler that throws fails the
42
+ * job, retried per `retry` or the queue's own settings (pg-boss's queue
43
+ * default is two retries with no delay, so a handler that always throws runs
44
+ * three times back to back), then left `failed`.
45
+ *
46
+ * The pending-state SQL reads pg-boss's own `job` table (its schema is
47
+ * `PGBOSS_SCHEMA`, the column names pg-boss 10's), because pg-boss exposes no
48
+ * lookup by key; `timers.test.ts` pins the statements against the installed
49
+ * version. A pg-boss bump re-verifies them.
50
+ */
51
+
52
+ /** The pg-boss schema the chassis installs (`createJobs`). */
53
+ export const PGBOSS_SCHEMA = 'pgboss';
54
+
55
+ /**
56
+ * Advisory-lock class serializing the replace of a keyed timer, beside the
57
+ * migration lock (7432001), the last-owner locks (7432002 xact / 7432003
58
+ * session) and the pg-boss install lock (7432004). Transaction-scoped, two-int
59
+ * form: the class and `hashtext(queue + US + key)`.
60
+ */
61
+ export const TIMER_KEY_LOCK_CLASS = 7_432_005;
62
+
63
+ /** What a handler learns about the run beside its payload. */
64
+ export interface TimerJob {
65
+ /** pg-boss's job id, the one `schedule` answered. */
66
+ id: string;
67
+ queue: string;
68
+ /** The dedupe key the timer was scheduled with; null without one, and on a cron's run. */
69
+ key: string | null;
70
+ }
71
+
72
+ /** When the timer fires: an instant, or a delay from now in seconds. Exactly one. */
73
+ export type TimerWhen = { at: Date; inSeconds?: never } | { inSeconds: number; at?: never };
74
+
75
+ /** How a failing handler is retried: `limit` more attempts, `delaySeconds` apart (doubled each time with `backoff`). */
76
+ export interface TimerRetry {
77
+ limit: number;
78
+ delaySeconds?: number;
79
+ backoff?: boolean;
80
+ }
81
+
82
+ export type TimerOptions = TimerWhen & {
83
+ /** The dedupe key: at most one pending timer per (queue, key); a new schedule under it replaces the pending one. */
84
+ key?: string;
85
+ /** Absent: the queue's own retry settings. */
86
+ retry?: TimerRetry;
87
+ };
88
+
89
+ export type TimerRef = { id: string; key?: never } | { key: string; id?: never };
90
+
91
+ export interface Timers {
92
+ /**
93
+ * Run the queue's handler once with `data`, at `at` or `inSeconds` from
94
+ * now. Answers the job id. Throws `TimerError` (`unknown_queue`) when the
95
+ * queue is not one the tool's `jobs` slot declared, `invalid_argument` on a
96
+ * malformed call.
97
+ */
98
+ schedule<TData extends object>(queue: string, data: TData, options: TimerOptions): Promise<string>;
99
+ /**
100
+ * Withdraw a timer that has not run yet, by its id or by its key. Answers
101
+ * how many pending timers were withdrawn (0 when none was pending: already
102
+ * run, already withdrawn, running right now, or never scheduled).
103
+ */
104
+ cancel(queue: string, ref: TimerRef): Promise<number>;
105
+ }
106
+
107
+ export class TimerError extends Error {
108
+ constructor(
109
+ readonly code: 'unknown_queue' | 'invalid_argument',
110
+ message: string
111
+ ) {
112
+ super(message);
113
+ this.name = 'TimerError';
114
+ }
115
+ }
116
+
117
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
118
+ /** pg-boss's own rule for a queue name (`Attorney.assertQueueName`), applied before any statement runs. */
119
+ const QUEUE_NAME = /^[\w-]+$/;
120
+ /** Unit separator: joins the queue and the key under one lock hash, and can appear in neither. */
121
+ const US = '\u001f';
122
+
123
+ const invalid = (message: string) => new TimerError('invalid_argument', `timers: ${message}`);
124
+ /** The longest delay a timer takes, in seconds: a hundred years. Postgres refuses an interval written in exponent form. */
125
+ export const MAX_IN_SECONDS = 100 * 365.25 * 24 * 3600;
126
+
127
+ function assertQueue(queue: unknown): asserts queue is string {
128
+ if (typeof queue !== 'string' || !QUEUE_NAME.test(queue)) {
129
+ throw invalid('the queue must be a name of letters, digits, underscores and hyphens');
130
+ }
131
+ }
132
+
133
+ /** The pg-boss `startAfter` for `options`: the Date of `at`, or the delay in seconds. */
134
+ export function startAfterOf(options: TimerWhen): Date | number {
135
+ const hasAt = options.at !== undefined;
136
+ const hasIn = options.inSeconds !== undefined;
137
+ if (hasAt === hasIn) throw invalid('exactly one of `at` and `inSeconds` is required');
138
+ if (hasAt) {
139
+ const at = options.at as Date;
140
+ if (!(at instanceof Date) || !Number.isFinite(at.getTime())) throw invalid('`at` must be a valid Date');
141
+ return at;
142
+ }
143
+ const seconds = options.inSeconds as number;
144
+ if (!Number.isFinite(seconds) || seconds < 0 || seconds > MAX_IN_SECONDS)
145
+ throw invalid('`inSeconds` must be a finite number, between 0 and a hundred years');
146
+ return seconds;
147
+ }
148
+
149
+ function retryOptionsOf(retry: TimerRetry | undefined): PgBoss.RetryOptions {
150
+ if (retry === undefined) return {};
151
+ if (!Number.isInteger(retry.limit) || retry.limit < 0)
152
+ throw invalid('`retry.limit` must be an integer, 0 or more');
153
+ const delay = retry.delaySeconds ?? 0;
154
+ if (!Number.isInteger(delay) || delay < 0)
155
+ throw invalid('`retry.delaySeconds` must be an integer, 0 or more');
156
+ return { retryLimit: retry.limit, retryDelay: delay, retryBackoff: retry.backoff ?? false };
157
+ }
158
+
159
+ function assertData(data: unknown): void {
160
+ const proto = typeof data === 'object' && data !== null ? Object.getPrototypeOf(data) : undefined;
161
+ if (proto !== Object.prototype && proto !== null) {
162
+ throw invalid('the payload must be a plain object');
163
+ }
164
+ try {
165
+ JSON.stringify(data);
166
+ } catch {
167
+ throw invalid('the payload must be JSON (no bigint, no circular reference)');
168
+ }
169
+ }
170
+
171
+ function assertKey(key: unknown): asserts key is string {
172
+ if (typeof key !== 'string' || key.length === 0 || key.includes(US)) {
173
+ throw invalid('the key must be a non-empty string');
174
+ }
175
+ }
176
+
177
+ /** The one statement that withdraws pending timers: created or waiting for a retry, never running. */
178
+ const CANCEL_PENDING = (where: string) => `
179
+ UPDATE ${PGBOSS_SCHEMA}.job
180
+ SET state = 'cancelled', completed_on = now()
181
+ WHERE name = $1 AND ${where} AND state IN ('created', 'retry')
182
+ `;
183
+ /**
184
+ * The statement that takes a RUNNING timer's retries away, so that if the
185
+ * run fails it ends `failed` and never comes back as a pending `retry` row
186
+ * under the withdrawn key or id (pg-boss retries while
187
+ * `retry_count < retry_limit`).
188
+ */
189
+ const DISARM_ACTIVE = (where: string) => `
190
+ UPDATE ${PGBOSS_SCHEMA}.job
191
+ SET retry_limit = retry_count
192
+ WHERE name = $1 AND ${where} AND state = 'active'
193
+ `;
194
+
195
+ interface Queryable {
196
+ query(text: string, values: unknown[]): Promise<{ rowCount: number | null }>;
197
+ }
198
+
199
+ /** Withdraw the pending timers `ref` names and disarm a running one; answers the pending count withdrawn. */
200
+ async function cancelPending(client: Queryable, queue: string, ref: TimerRef): Promise<number> {
201
+ let where: string;
202
+ let value: string;
203
+ if (ref.id !== undefined) {
204
+ if (!UUID.test(ref.id)) throw invalid('the id must be a job id');
205
+ where = 'id = $2::uuid';
206
+ value = ref.id;
207
+ } else {
208
+ assertKey(ref.key);
209
+ where = 'singleton_key = $2';
210
+ value = ref.key;
211
+ }
212
+ // The disarm FIRST, then the withdraw (verifier round 2, F7): pg-boss's fail
213
+ // path deletes the active row and re-inserts it as `retry` under the same
214
+ // key. Disarm first either takes the row lock before that fail (which then
215
+ // waits for the commit and computes `failed` on the disarmed row) or runs
216
+ // after it, and the withdraw, a new statement with a new snapshot, then
217
+ // sees the re-inserted `retry` row and cancels it. In the other order both
218
+ // statements could miss the row, one round trip wide. A replace whose
219
+ // insert then fails rolls the disarm back with it: the old run keeps its
220
+ // retries, a failed replace changes nothing.
221
+ await client.query(DISARM_ACTIVE(where), [queue, value]);
222
+ const res = await client.query(CANCEL_PENDING(where), [queue, value]);
223
+ return res.rowCount ?? 0;
224
+ }
225
+
226
+ export class PgBossTimers implements Timers {
227
+ constructor(
228
+ private readonly boss: PgBoss,
229
+ private readonly pool: pg.Pool,
230
+ private readonly logger: Logger
231
+ ) {}
232
+
233
+ async schedule<TData extends object>(queue: string, data: TData, options: TimerOptions): Promise<string> {
234
+ assertQueue(queue);
235
+ assertData(data);
236
+ const send: PgBoss.SendOptions = { startAfter: startAfterOf(options), ...retryOptionsOf(options.retry) };
237
+ if (options.key === undefined) {
238
+ return this.insert(queue, data, send);
239
+ }
240
+ assertKey(options.key);
241
+ const key = options.key;
242
+ // Replace = withdraw + insert in one transaction, serialized per key
243
+ // across replicas by a transaction-scoped advisory lock on a client of
244
+ // our own: pg-boss runs the insert through the `db` executor we hand it.
245
+ const client = await this.pool.connect();
246
+ try {
247
+ await client.query('BEGIN');
248
+ await client.query('SELECT pg_advisory_xact_lock($1, hashtext($2))', [
249
+ TIMER_KEY_LOCK_CLASS,
250
+ queue + US + key
251
+ ]);
252
+ const replaced = await cancelPending(client, queue, { key });
253
+ const id = await this.insert(queue, data, {
254
+ ...send,
255
+ singletonKey: key,
256
+ db: { executeSql: (text, values) => client.query(text, values) }
257
+ });
258
+ await client.query('COMMIT');
259
+ if (replaced > 0) this.logger.debug({ queue, key, replaced }, 'timers: a pending timer was replaced');
260
+ return id;
261
+ } catch (err) {
262
+ await client.query('ROLLBACK').catch(() => {});
263
+ throw err;
264
+ } finally {
265
+ client.release();
266
+ }
267
+ }
268
+
269
+ async cancel(queue: string, ref: TimerRef): Promise<number> {
270
+ assertQueue(queue);
271
+ if ((ref.id === undefined) === (ref.key === undefined))
272
+ throw invalid('exactly one of `id` and `key` is required');
273
+ return cancelPending(this.pool, queue, ref);
274
+ }
275
+
276
+ /** pg-boss answers null for a queue that does not exist (its insert joins the queue row): that is an unknown queue, named. */
277
+ private async insert(queue: string, data: object, send: PgBoss.SendOptions): Promise<string> {
278
+ const id = await this.boss.send(queue, data, send);
279
+ if (id === null) {
280
+ throw new TimerError(
281
+ 'unknown_queue',
282
+ `timers: the queue "${queue}" is not declared by the tool's jobs slot (a timer rides a declared queue)`
283
+ );
284
+ }
285
+ return id;
286
+ }
287
+ }
package/src/logger.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { pino, type Logger } from 'pino';
2
+ import type { Env } from './env.js';
3
+
4
+ export type { Logger };
5
+
6
+ /**
7
+ * The single redaction list — secrets never reach the log stream.
8
+ *
9
+ * Two rules when adding to it. (1) pino's `*.x` glob matches the key `x` at
10
+ * depth 1 ONLY, and it is an EXACT key match: `*.secret` does NOT cover
11
+ * `clientSecret`, `client_secret`, or `apiSecret`, so every camel/snake spelling
12
+ * a payload can carry needs its own entry. (2) Redaction keys off object
13
+ * PROPERTIES — it can do nothing about a secret interpolated into a message
14
+ * string. Never build a log message out of a config payload; log the shape
15
+ * (`{ length, path }`), never the bytes.
16
+ */
17
+ export const REDACT_PATHS = [
18
+ 'req.headers.authorization',
19
+ 'req.headers.cookie',
20
+ '*.password',
21
+ '*.secret',
22
+ '*.token',
23
+ // Exact-key spellings the `*.secret` glob misses — OAuth/hub-federation
24
+ // payloads carry these verbatim.
25
+ '*.clientSecret',
26
+ '*.client_secret',
27
+ '*.accessToken',
28
+ '*.access_token',
29
+ '*.refreshToken',
30
+ '*.refresh_token',
31
+ '*.apiKey',
32
+ '*.api_key',
33
+ 'DATABASE_URL',
34
+ 'AUTH_SECRET',
35
+ 'API_KEY_PEPPERS',
36
+ 'SETUP_TOKEN',
37
+ 'METRICS_TOKEN',
38
+ 'SMTP_URL',
39
+ 'RESEND_API_KEY',
40
+ 'BREVO_API_KEY',
41
+ 'S3_SECRET_ACCESS_KEY',
42
+ 'S3_ACCESS_KEY_ID',
43
+ // Optional social credential — read in env.ts, so any "here is the parsed
44
+ // env" line would otherwise carry it.
45
+ 'GOOGLE_CLIENT_SECRET',
46
+ // Cloud edition: the hub-federation client credential (ADR 015 / user-scoped
47
+ // federation). It is THIS tool's identity at the Antasphere hub — leaking it
48
+ // lets anyone impersonate the tool at the authorization server.
49
+ 'HUB_CLIENT_SECRET'
50
+ ];
51
+
52
+ export function createLogger(env: Pick<Env, 'LOG_LEVEL' | 'NODE_ENV'>): Logger {
53
+ const options = {
54
+ level: env.LOG_LEVEL,
55
+ redact: { paths: REDACT_PATHS, censor: '[redacted]' }
56
+ };
57
+ if (env.NODE_ENV === 'development') {
58
+ // pino-pretty is a devDependency; if it is absent (e.g. NODE_ENV flipped
59
+ // on a pruned production image) fall back to JSON instead of crashing.
60
+ try {
61
+ return pino({ ...options, transport: { target: 'pino-pretty', options: { colorize: true } } });
62
+ } catch {
63
+ // fall through to JSON output
64
+ }
65
+ }
66
+ return pino(options);
67
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * API error → model-readable hint mapping (the predecessor MCP template's
3
+ * DOMAIN_HINTS pattern). The API returns `{ error: { code, message } }` on
4
+ * failure; each domain code can carry an actionable hint so the model
5
+ * self-corrects instead of retrying blindly. The table below holds the hints of
6
+ * the chassis' own codes; a tool adds its domain's through
7
+ * {@link mergeErrorHints} (the `errorHints` of its MCP definition).
8
+ */
9
+ export type ErrorHints = Readonly<Record<string, string>>;
10
+
11
+ const DOMAIN_HINTS: Record<string, string> = {
12
+ insufficient_scope:
13
+ 'Reconnect the MCP server and approve the permission on the consent screen ' +
14
+ '(or mint an API key that includes the scope).',
15
+ endpoint_not_allowed:
16
+ 'This endpoint is not opened to machine credentials — a human must do this in the dashboard.',
17
+ invalid_token: 'The OAuth token expired or was revoked. Reconnect the MCP server.',
18
+ unauthenticated: 'No valid credential reached the API. Reconnect the MCP server.',
19
+ rate_limited: 'Rate limited — wait before retrying.',
20
+ plan_required:
21
+ 'The workspace\u2019s plan does not allow this — the upgrade link in this message is where a human raises it.',
22
+ // The same code on both editions: the cloud's credit refusal (402, with the
23
+ // top-up link) and a self-hosted instance's cap (413, no details).
24
+ entitlement_denied:
25
+ 'The instance refused this action: on the cloud the organization lacks the credits and the top-up link in this message is where a human adds them, or the quantity is beyond what can be priced and no top-up covers it (no link then); on a self-hosted instance the request exceeds the operator’s cap.'
26
+ };
27
+
28
+ /** The chassis hints plus a tool's own: ONE table, the one every lookup reads. */
29
+ export function mergeErrorHints(toolHints: ErrorHints): ErrorHints {
30
+ return { ...DOMAIN_HINTS, ...toolHints };
31
+ }
32
+
33
+ /** Thrown when the in-process API call returns a non-2xx wire-shape response. */
34
+ export class ApiToolError extends Error {
35
+ readonly status: number;
36
+ readonly code: string | null;
37
+
38
+ /** The wire's `error.details`, when the API sent any (a plan refusal's upgrade link rides here). */
39
+ readonly details: unknown;
40
+
41
+ constructor(status: number, code: string | null, message: string, details?: unknown) {
42
+ super(message);
43
+ this.name = 'ApiToolError';
44
+ this.status = status;
45
+ this.code = code;
46
+ this.details = details;
47
+ }
48
+
49
+ toUserFacingText(hints: ErrorHints = DOMAIN_HINTS): string {
50
+ const hint = this.code ? hints[this.code] : undefined;
51
+ const codePart = this.code ? `, code: ${this.code}` : '';
52
+ // A plan refusal carries its upgrade link (the billing rail, §7): the
53
+ // agent relays it, a human follows it. Text, never a structured field.
54
+ const upgradeUrl = (this.details as { upgradeUrl?: unknown } | null)?.upgradeUrl;
55
+ const upgrade = typeof upgradeUrl === 'string' && upgradeUrl ? ` Upgrade: ${upgradeUrl}` : '';
56
+ // A credit refusal (402 `entitlement_denied`, PRDCT-2664) carries its
57
+ // top-up link, the price and the balance: the same, as text. The
58
+ // self-hosted cap's 413 has no details, so no top-up sentence.
59
+ const { topUpUrl, credits, balance } = (this.details ?? {}) as {
60
+ topUpUrl?: unknown;
61
+ credits?: unknown;
62
+ balance?: unknown;
63
+ };
64
+ let topUp = '';
65
+ // The gate's message already carries the link, the price and the balance;
66
+ // the sentence is added only for a message that does not (verifier
67
+ // round 1: the URL was printed twice).
68
+ if (typeof topUpUrl === 'string' && topUpUrl && !this.message.includes(topUpUrl)) {
69
+ const needs = typeof credits === 'number' ? [`this needs ${credits} credits`] : [];
70
+ const holds = typeof balance === 'number' ? [`the organization holds ${balance}`] : [];
71
+ const numbers = [...needs, ...holds].join(', ');
72
+ topUp = ` Top up: ${topUpUrl}${numbers ? ` (${numbers}).` : ''}`;
73
+ }
74
+ return `API error (HTTP ${this.status}${codePart}): ${this.message}${hint ? ` — Next: ${hint}` : ''}${upgrade}${topUp}`;
75
+ }
76
+ }
77
+
78
+ export interface ToolTextResult {
79
+ // Index signature: the MCP SDK's CallToolResult carries extra open fields.
80
+ [key: string]: unknown;
81
+ content: Array<{ type: 'text'; text: string }>;
82
+ isError?: true;
83
+ }
84
+
85
+ export const jsonText = (result: unknown): ToolTextResult => ({
86
+ content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }]
87
+ });
88
+
89
+ export const deny = (text: string): ToolTextResult => ({
90
+ content: [{ type: 'text' as const, text }],
91
+ isError: true as const
92
+ });
93
+
94
+ /**
95
+ * Wrap a tool body so any thrown ApiToolError surfaces as a structured MCP
96
+ * tool error (`isError: true` + text) rather than a protocol-level exception.
97
+ * Other errors propagate to the SDK.
98
+ */
99
+ export async function wrapToolErrors(
100
+ fn: () => Promise<ToolTextResult>,
101
+ hints: ErrorHints = DOMAIN_HINTS
102
+ ): Promise<ToolTextResult> {
103
+ try {
104
+ return await fn();
105
+ } catch (err) {
106
+ if (err instanceof ApiToolError) {
107
+ return { content: [{ type: 'text', text: err.toUserFacingText(hints) }], isError: true };
108
+ }
109
+ throw err;
110
+ }
111
+ }
@@ -0,0 +1,129 @@
1
+ import { Hono } from 'hono';
2
+ import type { MiddlewareHandler } from 'hono';
3
+ import { bodyLimit } from 'hono/body-limit';
4
+ import { HTTPException } from 'hono/http-exception';
5
+ import { StreamableHTTPTransport } from '@hono/mcp';
6
+ import type { Principal } from '@antasphere/chassis-contract';
7
+ import { looksLikeJwt } from '../middleware/scopes.js';
8
+ import { buildMcpServer, type McpIdentity, type McpToolDefinition } from './server.js';
9
+
10
+ /**
11
+ * The bundled /mcp endpoint (streamable HTTP, STATELESS — see ADR 002).
12
+ *
13
+ * Auth gate first: a valid OAuth bearer (token this instance minted) or an
14
+ * API key — both machine principals, resolved through the same machinery as
15
+ * /api/v1. Anything else gets the RFC 9728 401 challenge whose
16
+ * `resource_metadata` URL drives the whole client OAuth dance.
17
+ *
18
+ * Stateless transport: a fresh McpServer + transport per request, no session
19
+ * ids, no in-process stream state — the platform's statelessness invariant
20
+ * applies to MCP too (any replica can serve any request).
21
+ */
22
+ export interface McpHttpDeps {
23
+ publicBaseUrl: string;
24
+ version: string;
25
+ /** In-process fetch against the root app; tools forward the caller's bearer. */
26
+ fetchApi: (path: string, init?: RequestInit) => Promise<Response>;
27
+ resolveOauthJwt: (token: string) => Promise<Principal | null>;
28
+ resolveApiKey: (token: string) => Promise<Principal | null>;
29
+ isApiKeyToken: (token: string) => boolean;
30
+ /** Instance display name for the server instructions (cached by the caller). */
31
+ instanceName: () => Promise<string>;
32
+ /** Per-IP rate limit, applied before the auth gate (floods rejected cheaply). */
33
+ limiter: MiddlewareHandler;
34
+ /** The tool's MCP definition: its tools, instructions, error hints and scope names. */
35
+ tool: McpToolDefinition;
36
+ /** The server's name and the tool names' prefix (`identity.mcp`). */
37
+ identity: McpIdentity;
38
+ }
39
+
40
+ export function mcpRoutes(deps: McpHttpDeps): Hono {
41
+ const app = new Hono();
42
+ const challengeUrl = `${deps.publicBaseUrl.replace(/\/+$/, '')}/.well-known/oauth-protected-resource/mcp`;
43
+
44
+ app.use('*', deps.limiter);
45
+ // JSON-RPC bodies are small; cap them so a caller can't force the transport
46
+ // to buffer an arbitrarily large body (the /api/v1 cap does not cover /mcp).
47
+ app.use(
48
+ '*',
49
+ bodyLimit({
50
+ maxSize: 1024 * 1024,
51
+ onError: (c) =>
52
+ c.json({ error: 'payload_too_large', error_description: 'MCP request body exceeds 1 MiB' }, 413)
53
+ })
54
+ );
55
+
56
+ const unauthorized = (description: string) =>
57
+ new Response(JSON.stringify({ error: 'invalid_token', error_description: description }), {
58
+ status: 401,
59
+ headers: {
60
+ 'content-type': 'application/json',
61
+ 'www-authenticate': `Bearer resource_metadata="${challengeUrl}"`
62
+ }
63
+ });
64
+
65
+ app.post('/', async (c) => {
66
+ const authHeader = c.req.header('authorization')?.trim() ?? '';
67
+ const bearer = /^Bearer\s+(.+)$/i.exec(authHeader)?.[1]?.trim() ?? null;
68
+ if (!bearer) {
69
+ return unauthorized('Authentication required');
70
+ }
71
+
72
+ let principal: Principal | null = null;
73
+ if (deps.isApiKeyToken(bearer)) {
74
+ principal = await deps.resolveApiKey(bearer);
75
+ } else if (looksLikeJwt(bearer)) {
76
+ principal = await deps.resolveOauthJwt(bearer);
77
+ }
78
+ if (!principal) {
79
+ return unauthorized('Bearer token is invalid, expired, or revoked');
80
+ }
81
+
82
+ const server = buildMcpServer(
83
+ {
84
+ fetchApi: deps.fetchApi,
85
+ principal,
86
+ authorizationHeader: `Bearer ${bearer}`,
87
+ publicBaseUrl: deps.publicBaseUrl
88
+ },
89
+ { version: deps.version, instanceName: await deps.instanceName() },
90
+ deps.tool,
91
+ deps.identity
92
+ );
93
+ // No sessionIdGenerator = stateless: every POST is self-contained.
94
+ // enableJsonResponse returns plain JSON bodies (no SSE), so the Response
95
+ // handed back to Hono is complete — no stream outlives the request,
96
+ // nothing to tear down (the per-request server + transport are
97
+ // unreferenced after this).
98
+ const transport = new StreamableHTTPTransport({ enableJsonResponse: true });
99
+ await server.connect(transport);
100
+ try {
101
+ const res = await transport.handleRequest(c);
102
+ return res ?? c.body(null, 204);
103
+ } catch (cause) {
104
+ // The transport reports protocol failures by THROWING HTTPException
105
+ // (400 -32700 for a malformed / non-JSON-RPC body, 406/415 for header
106
+ // problems), each carrying a complete JSON-RPC error response. Return
107
+ // that response instead of letting the root onError turn it into a
108
+ // generic 500.
109
+ if (cause instanceof HTTPException) {
110
+ return cause.getResponse();
111
+ }
112
+ throw cause;
113
+ }
114
+ });
115
+
116
+ // Non-POST is rejected here EXPLICITLY (ADR 002): the stateless transport
117
+ // does NOT 405 on its own — its GET handler would happily open a long-lived
118
+ // server-initiated SSE stream, and DELETE would answer a session teardown.
119
+ // Neither exists in stateless mode, so every non-POST method gets
120
+ // 405 + Allow: POST with a JSON-RPC error envelope (the shape the official
121
+ // SDK expects and tolerates).
122
+ app.all('/', (c) =>
123
+ c.json({ jsonrpc: '2.0', error: { code: -32000, message: 'Method not allowed' }, id: null }, 405, {
124
+ allow: 'POST'
125
+ })
126
+ );
127
+
128
+ return app;
129
+ }
@@ -0,0 +1,28 @@
1
+ export {
2
+ ApiToolError,
3
+ type ErrorHints,
4
+ type ToolTextResult,
5
+ deny,
6
+ jsonText,
7
+ mergeErrorHints,
8
+ wrapToolErrors
9
+ } from './errors.js';
10
+ export { mcpRoutes } from './http.js';
11
+ export { projectErrorHints, registerProjectTools } from './projects.js';
12
+ export { registerTeamTools } from './teams.js';
13
+ export { type McpInputs, mcpInputs } from './inputs.js';
14
+ export {
15
+ type McpIdentity,
16
+ type McpServerInfo,
17
+ type McpToolDefinition,
18
+ buildMcpServer,
19
+ composeErrorHints
20
+ } from './server.js';
21
+ export {
22
+ type McpToolContext,
23
+ callApi,
24
+ createScopeCheck,
25
+ fetchApiRaw,
26
+ forWorkspace,
27
+ pageQuery
28
+ } from './tool-kit.js';
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ import type { ToolIdentity } from '@antasphere/chassis-contract';
3
+
4
+ /**
5
+ * The inputs every MCP tool of an instance shares, chassis tools and the
6
+ * tool's own alike: ONE definition, so the same argument reads the same in
7
+ * every tool of `tools/list`. The workspace input names `<toolPrefix>whoami`
8
+ * (the identity tool the chassis registers), hence a factory over the MCP half
9
+ * of the tool's identity. Build it once per server, next to `registerTools`.
10
+ */
11
+ export function mcpInputs(identity: Pick<ToolIdentity['mcp'], 'toolPrefix'>) {
12
+ return {
13
+ /** The per-call target workspace; omitted, the caller's default one. */
14
+ workspaceInput: z
15
+ .uuid()
16
+ .optional()
17
+ .describe(
18
+ `Target organization (workspace id). Omit to use your default org — see ${identity.toolPrefix}whoami.`
19
+ ),
20
+ /** The cursor of a paginated list, passed through to the API. */
21
+ cursorInput: z.string().optional().describe('nextCursor from a previous page.'),
22
+ /** The page size of a paginated list. */
23
+ limitInput: z
24
+ .number()
25
+ .int()
26
+ .min(1)
27
+ .max(100)
28
+ .optional()
29
+ .describe('Page size (server max 100). Default 50.')
30
+ };
31
+ }
32
+
33
+ export type McpInputs = ReturnType<typeof mcpInputs>;