palaryn 0.1.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 (607) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +716 -0
  3. package/dist/sdk/typescript/src/client.d.ts +71 -0
  4. package/dist/sdk/typescript/src/client.d.ts.map +1 -0
  5. package/dist/sdk/typescript/src/client.js +176 -0
  6. package/dist/sdk/typescript/src/client.js.map +1 -0
  7. package/dist/sdk/typescript/src/errors.d.ts +50 -0
  8. package/dist/sdk/typescript/src/errors.d.ts.map +1 -0
  9. package/dist/sdk/typescript/src/errors.js +103 -0
  10. package/dist/sdk/typescript/src/errors.js.map +1 -0
  11. package/dist/sdk/typescript/src/index.d.ts +4 -0
  12. package/dist/sdk/typescript/src/index.d.ts.map +1 -0
  13. package/dist/sdk/typescript/src/index.js +15 -0
  14. package/dist/sdk/typescript/src/index.js.map +1 -0
  15. package/dist/sdk/typescript/src/types.d.ts +101 -0
  16. package/dist/sdk/typescript/src/types.d.ts.map +1 -0
  17. package/dist/sdk/typescript/src/types.js +6 -0
  18. package/dist/sdk/typescript/src/types.js.map +1 -0
  19. package/dist/src/admin/index.d.ts +2 -0
  20. package/dist/src/admin/index.d.ts.map +1 -0
  21. package/dist/src/admin/index.js +6 -0
  22. package/dist/src/admin/index.js.map +1 -0
  23. package/dist/src/admin/routes.d.ts +5 -0
  24. package/dist/src/admin/routes.d.ts.map +1 -0
  25. package/dist/src/admin/routes.js +471 -0
  26. package/dist/src/admin/routes.js.map +1 -0
  27. package/dist/src/admin/templates.d.ts +51 -0
  28. package/dist/src/admin/templates.d.ts.map +1 -0
  29. package/dist/src/admin/templates.js +500 -0
  30. package/dist/src/admin/templates.js.map +1 -0
  31. package/dist/src/anomaly/detector.d.ts +141 -0
  32. package/dist/src/anomaly/detector.d.ts.map +1 -0
  33. package/dist/src/anomaly/detector.js +554 -0
  34. package/dist/src/anomaly/detector.js.map +1 -0
  35. package/dist/src/anomaly/index.d.ts +2 -0
  36. package/dist/src/anomaly/index.d.ts.map +1 -0
  37. package/dist/src/anomaly/index.js +7 -0
  38. package/dist/src/anomaly/index.js.map +1 -0
  39. package/dist/src/approval/manager.d.ts +147 -0
  40. package/dist/src/approval/manager.d.ts.map +1 -0
  41. package/dist/src/approval/manager.js +511 -0
  42. package/dist/src/approval/manager.js.map +1 -0
  43. package/dist/src/approval/webhook.d.ts +36 -0
  44. package/dist/src/approval/webhook.d.ts.map +1 -0
  45. package/dist/src/approval/webhook.js +135 -0
  46. package/dist/src/approval/webhook.js.map +1 -0
  47. package/dist/src/audit/logger.d.ts +70 -0
  48. package/dist/src/audit/logger.d.ts.map +1 -0
  49. package/dist/src/audit/logger.js +440 -0
  50. package/dist/src/audit/logger.js.map +1 -0
  51. package/dist/src/auth/index.d.ts +6 -0
  52. package/dist/src/auth/index.d.ts.map +1 -0
  53. package/dist/src/auth/index.js +22 -0
  54. package/dist/src/auth/index.js.map +1 -0
  55. package/dist/src/auth/password.d.ts +3 -0
  56. package/dist/src/auth/password.d.ts.map +1 -0
  57. package/dist/src/auth/password.js +25 -0
  58. package/dist/src/auth/password.js.map +1 -0
  59. package/dist/src/auth/pkce.d.ts +13 -0
  60. package/dist/src/auth/pkce.d.ts.map +1 -0
  61. package/dist/src/auth/pkce.js +58 -0
  62. package/dist/src/auth/pkce.js.map +1 -0
  63. package/dist/src/auth/providers.d.ts +28 -0
  64. package/dist/src/auth/providers.d.ts.map +1 -0
  65. package/dist/src/auth/providers.js +198 -0
  66. package/dist/src/auth/providers.js.map +1 -0
  67. package/dist/src/auth/routes.d.ts +14 -0
  68. package/dist/src/auth/routes.d.ts.map +1 -0
  69. package/dist/src/auth/routes.js +431 -0
  70. package/dist/src/auth/routes.js.map +1 -0
  71. package/dist/src/auth/session.d.ts +24 -0
  72. package/dist/src/auth/session.d.ts.map +1 -0
  73. package/dist/src/auth/session.js +105 -0
  74. package/dist/src/auth/session.js.map +1 -0
  75. package/dist/src/billing/index.d.ts +7 -0
  76. package/dist/src/billing/index.d.ts.map +1 -0
  77. package/dist/src/billing/index.js +14 -0
  78. package/dist/src/billing/index.js.map +1 -0
  79. package/dist/src/billing/plan-enforcer.d.ts +44 -0
  80. package/dist/src/billing/plan-enforcer.d.ts.map +1 -0
  81. package/dist/src/billing/plan-enforcer.js +110 -0
  82. package/dist/src/billing/plan-enforcer.js.map +1 -0
  83. package/dist/src/billing/routes.d.ts +15 -0
  84. package/dist/src/billing/routes.d.ts.map +1 -0
  85. package/dist/src/billing/routes.js +193 -0
  86. package/dist/src/billing/routes.js.map +1 -0
  87. package/dist/src/billing/stripe-client.d.ts +14 -0
  88. package/dist/src/billing/stripe-client.d.ts.map +1 -0
  89. package/dist/src/billing/stripe-client.js +51 -0
  90. package/dist/src/billing/stripe-client.js.map +1 -0
  91. package/dist/src/billing/webhook-handler.d.ts +19 -0
  92. package/dist/src/billing/webhook-handler.d.ts.map +1 -0
  93. package/dist/src/billing/webhook-handler.js +169 -0
  94. package/dist/src/billing/webhook-handler.js.map +1 -0
  95. package/dist/src/billing/webhook-routes.d.ts +5 -0
  96. package/dist/src/billing/webhook-routes.d.ts.map +1 -0
  97. package/dist/src/billing/webhook-routes.js +30 -0
  98. package/dist/src/billing/webhook-routes.js.map +1 -0
  99. package/dist/src/budget/manager.d.ts +95 -0
  100. package/dist/src/budget/manager.d.ts.map +1 -0
  101. package/dist/src/budget/manager.js +547 -0
  102. package/dist/src/budget/manager.js.map +1 -0
  103. package/dist/src/budget/usage-extractor.d.ts +38 -0
  104. package/dist/src/budget/usage-extractor.d.ts.map +1 -0
  105. package/dist/src/budget/usage-extractor.js +165 -0
  106. package/dist/src/budget/usage-extractor.js.map +1 -0
  107. package/dist/src/cli.d.ts +3 -0
  108. package/dist/src/cli.d.ts.map +1 -0
  109. package/dist/src/cli.js +115 -0
  110. package/dist/src/cli.js.map +1 -0
  111. package/dist/src/config/defaults.d.ts +3 -0
  112. package/dist/src/config/defaults.d.ts.map +1 -0
  113. package/dist/src/config/defaults.js +243 -0
  114. package/dist/src/config/defaults.js.map +1 -0
  115. package/dist/src/config/validate.d.ts +15 -0
  116. package/dist/src/config/validate.d.ts.map +1 -0
  117. package/dist/src/config/validate.js +105 -0
  118. package/dist/src/config/validate.js.map +1 -0
  119. package/dist/src/dlp/composite-scanner.d.ts +47 -0
  120. package/dist/src/dlp/composite-scanner.d.ts.map +1 -0
  121. package/dist/src/dlp/composite-scanner.js +186 -0
  122. package/dist/src/dlp/composite-scanner.js.map +1 -0
  123. package/dist/src/dlp/index.d.ts +10 -0
  124. package/dist/src/dlp/index.d.ts.map +1 -0
  125. package/dist/src/dlp/index.js +26 -0
  126. package/dist/src/dlp/index.js.map +1 -0
  127. package/dist/src/dlp/interfaces.d.ts +33 -0
  128. package/dist/src/dlp/interfaces.d.ts.map +1 -0
  129. package/dist/src/dlp/interfaces.js +3 -0
  130. package/dist/src/dlp/interfaces.js.map +1 -0
  131. package/dist/src/dlp/patterns.d.ts +9 -0
  132. package/dist/src/dlp/patterns.d.ts.map +1 -0
  133. package/dist/src/dlp/patterns.js +25 -0
  134. package/dist/src/dlp/patterns.js.map +1 -0
  135. package/dist/src/dlp/prompt-injection-backend.d.ts +68 -0
  136. package/dist/src/dlp/prompt-injection-backend.d.ts.map +1 -0
  137. package/dist/src/dlp/prompt-injection-backend.js +148 -0
  138. package/dist/src/dlp/prompt-injection-backend.js.map +1 -0
  139. package/dist/src/dlp/prompt-injection-patterns.d.ts +32 -0
  140. package/dist/src/dlp/prompt-injection-patterns.d.ts.map +1 -0
  141. package/dist/src/dlp/prompt-injection-patterns.js +290 -0
  142. package/dist/src/dlp/prompt-injection-patterns.js.map +1 -0
  143. package/dist/src/dlp/regex-backend.d.ts +32 -0
  144. package/dist/src/dlp/regex-backend.d.ts.map +1 -0
  145. package/dist/src/dlp/regex-backend.js +153 -0
  146. package/dist/src/dlp/regex-backend.js.map +1 -0
  147. package/dist/src/dlp/scanner.d.ts +122 -0
  148. package/dist/src/dlp/scanner.d.ts.map +1 -0
  149. package/dist/src/dlp/scanner.js +444 -0
  150. package/dist/src/dlp/scanner.js.map +1 -0
  151. package/dist/src/dlp/text-normalizer.d.ts +41 -0
  152. package/dist/src/dlp/text-normalizer.d.ts.map +1 -0
  153. package/dist/src/dlp/text-normalizer.js +203 -0
  154. package/dist/src/dlp/text-normalizer.js.map +1 -0
  155. package/dist/src/dlp/trufflehog-backend.d.ts +64 -0
  156. package/dist/src/dlp/trufflehog-backend.d.ts.map +1 -0
  157. package/dist/src/dlp/trufflehog-backend.js +151 -0
  158. package/dist/src/dlp/trufflehog-backend.js.map +1 -0
  159. package/dist/src/executor/http-executor.d.ts +25 -0
  160. package/dist/src/executor/http-executor.d.ts.map +1 -0
  161. package/dist/src/executor/http-executor.js +333 -0
  162. package/dist/src/executor/http-executor.js.map +1 -0
  163. package/dist/src/executor/index.d.ts +6 -0
  164. package/dist/src/executor/index.d.ts.map +1 -0
  165. package/dist/src/executor/index.js +12 -0
  166. package/dist/src/executor/index.js.map +1 -0
  167. package/dist/src/executor/interfaces.d.ts +11 -0
  168. package/dist/src/executor/interfaces.d.ts.map +1 -0
  169. package/dist/src/executor/interfaces.js +3 -0
  170. package/dist/src/executor/interfaces.js.map +1 -0
  171. package/dist/src/executor/noop-executor.d.ts +13 -0
  172. package/dist/src/executor/noop-executor.d.ts.map +1 -0
  173. package/dist/src/executor/noop-executor.js +21 -0
  174. package/dist/src/executor/noop-executor.js.map +1 -0
  175. package/dist/src/executor/registry.d.ts +30 -0
  176. package/dist/src/executor/registry.d.ts.map +1 -0
  177. package/dist/src/executor/registry.js +62 -0
  178. package/dist/src/executor/registry.js.map +1 -0
  179. package/dist/src/executor/slack-executor.d.ts +24 -0
  180. package/dist/src/executor/slack-executor.d.ts.map +1 -0
  181. package/dist/src/executor/slack-executor.js +147 -0
  182. package/dist/src/executor/slack-executor.js.map +1 -0
  183. package/dist/src/index.d.ts +25 -0
  184. package/dist/src/index.d.ts.map +1 -0
  185. package/dist/src/index.js +74 -0
  186. package/dist/src/index.js.map +1 -0
  187. package/dist/src/mcp/auth-verifier.d.ts +23 -0
  188. package/dist/src/mcp/auth-verifier.d.ts.map +1 -0
  189. package/dist/src/mcp/auth-verifier.js +162 -0
  190. package/dist/src/mcp/auth-verifier.js.map +1 -0
  191. package/dist/src/mcp/bridge.d.ts +132 -0
  192. package/dist/src/mcp/bridge.d.ts.map +1 -0
  193. package/dist/src/mcp/bridge.js +734 -0
  194. package/dist/src/mcp/bridge.js.map +1 -0
  195. package/dist/src/mcp/http-transport.d.ts +32 -0
  196. package/dist/src/mcp/http-transport.d.ts.map +1 -0
  197. package/dist/src/mcp/http-transport.js +538 -0
  198. package/dist/src/mcp/http-transport.js.map +1 -0
  199. package/dist/src/mcp/index.d.ts +10 -0
  200. package/dist/src/mcp/index.d.ts.map +1 -0
  201. package/dist/src/mcp/index.js +17 -0
  202. package/dist/src/mcp/index.js.map +1 -0
  203. package/dist/src/mcp/oauth-pages.d.ts +23 -0
  204. package/dist/src/mcp/oauth-pages.d.ts.map +1 -0
  205. package/dist/src/mcp/oauth-pages.js +121 -0
  206. package/dist/src/mcp/oauth-pages.js.map +1 -0
  207. package/dist/src/mcp/oauth-postgres-stores.d.ts +55 -0
  208. package/dist/src/mcp/oauth-postgres-stores.d.ts.map +1 -0
  209. package/dist/src/mcp/oauth-postgres-stores.js +226 -0
  210. package/dist/src/mcp/oauth-postgres-stores.js.map +1 -0
  211. package/dist/src/mcp/oauth-provider.d.ts +95 -0
  212. package/dist/src/mcp/oauth-provider.d.ts.map +1 -0
  213. package/dist/src/mcp/oauth-provider.js +360 -0
  214. package/dist/src/mcp/oauth-provider.js.map +1 -0
  215. package/dist/src/mcp/oauth-stores.d.ts +62 -0
  216. package/dist/src/mcp/oauth-stores.d.ts.map +1 -0
  217. package/dist/src/mcp/oauth-stores.js +154 -0
  218. package/dist/src/mcp/oauth-stores.js.map +1 -0
  219. package/dist/src/mcp/server.d.ts +18 -0
  220. package/dist/src/mcp/server.d.ts.map +1 -0
  221. package/dist/src/mcp/server.js +51 -0
  222. package/dist/src/mcp/server.js.map +1 -0
  223. package/dist/src/metrics/collector.d.ts +106 -0
  224. package/dist/src/metrics/collector.d.ts.map +1 -0
  225. package/dist/src/metrics/collector.js +311 -0
  226. package/dist/src/metrics/collector.js.map +1 -0
  227. package/dist/src/metrics/index.d.ts +2 -0
  228. package/dist/src/metrics/index.d.ts.map +1 -0
  229. package/dist/src/metrics/index.js +6 -0
  230. package/dist/src/metrics/index.js.map +1 -0
  231. package/dist/src/middleware/auth.d.ts +77 -0
  232. package/dist/src/middleware/auth.d.ts.map +1 -0
  233. package/dist/src/middleware/auth.js +720 -0
  234. package/dist/src/middleware/auth.js.map +1 -0
  235. package/dist/src/middleware/session.d.ts +18 -0
  236. package/dist/src/middleware/session.d.ts.map +1 -0
  237. package/dist/src/middleware/session.js +67 -0
  238. package/dist/src/middleware/session.js.map +1 -0
  239. package/dist/src/middleware/validate.d.ts +3 -0
  240. package/dist/src/middleware/validate.d.ts.map +1 -0
  241. package/dist/src/middleware/validate.js +85 -0
  242. package/dist/src/middleware/validate.js.map +1 -0
  243. package/dist/src/policy/engine.d.ts +107 -0
  244. package/dist/src/policy/engine.d.ts.map +1 -0
  245. package/dist/src/policy/engine.js +646 -0
  246. package/dist/src/policy/engine.js.map +1 -0
  247. package/dist/src/policy/index.d.ts +3 -0
  248. package/dist/src/policy/index.d.ts.map +1 -0
  249. package/dist/src/policy/index.js +8 -0
  250. package/dist/src/policy/index.js.map +1 -0
  251. package/dist/src/policy/opa-engine.d.ts +176 -0
  252. package/dist/src/policy/opa-engine.d.ts.map +1 -0
  253. package/dist/src/policy/opa-engine.js +790 -0
  254. package/dist/src/policy/opa-engine.js.map +1 -0
  255. package/dist/src/proxy/forward-proxy.d.ts +30 -0
  256. package/dist/src/proxy/forward-proxy.d.ts.map +1 -0
  257. package/dist/src/proxy/forward-proxy.js +580 -0
  258. package/dist/src/proxy/forward-proxy.js.map +1 -0
  259. package/dist/src/proxy/index.d.ts +2 -0
  260. package/dist/src/proxy/index.d.ts.map +1 -0
  261. package/dist/src/proxy/index.js +8 -0
  262. package/dist/src/proxy/index.js.map +1 -0
  263. package/dist/src/ratelimit/limiter.d.ts +45 -0
  264. package/dist/src/ratelimit/limiter.d.ts.map +1 -0
  265. package/dist/src/ratelimit/limiter.js +158 -0
  266. package/dist/src/ratelimit/limiter.js.map +1 -0
  267. package/dist/src/replay/engine.d.ts +40 -0
  268. package/dist/src/replay/engine.d.ts.map +1 -0
  269. package/dist/src/replay/engine.js +106 -0
  270. package/dist/src/replay/engine.js.map +1 -0
  271. package/dist/src/replay/index.d.ts +2 -0
  272. package/dist/src/replay/index.d.ts.map +1 -0
  273. package/dist/src/replay/index.js +6 -0
  274. package/dist/src/replay/index.js.map +1 -0
  275. package/dist/src/saas/index.d.ts +2 -0
  276. package/dist/src/saas/index.d.ts.map +1 -0
  277. package/dist/src/saas/index.js +18 -0
  278. package/dist/src/saas/index.js.map +1 -0
  279. package/dist/src/saas/routes.d.ts +18 -0
  280. package/dist/src/saas/routes.d.ts.map +1 -0
  281. package/dist/src/saas/routes.js +1566 -0
  282. package/dist/src/saas/routes.js.map +1 -0
  283. package/dist/src/server/app.d.ts +44 -0
  284. package/dist/src/server/app.d.ts.map +1 -0
  285. package/dist/src/server/app.js +854 -0
  286. package/dist/src/server/app.js.map +1 -0
  287. package/dist/src/server/errors.d.ts +32 -0
  288. package/dist/src/server/errors.d.ts.map +1 -0
  289. package/dist/src/server/errors.js +39 -0
  290. package/dist/src/server/errors.js.map +1 -0
  291. package/dist/src/server/gateway.d.ts +165 -0
  292. package/dist/src/server/gateway.d.ts.map +1 -0
  293. package/dist/src/server/gateway.js +964 -0
  294. package/dist/src/server/gateway.js.map +1 -0
  295. package/dist/src/server/index.d.ts +2 -0
  296. package/dist/src/server/index.d.ts.map +1 -0
  297. package/dist/src/server/index.js +295 -0
  298. package/dist/src/server/index.js.map +1 -0
  299. package/dist/src/server/logger.d.ts +33 -0
  300. package/dist/src/server/logger.d.ts.map +1 -0
  301. package/dist/src/server/logger.js +230 -0
  302. package/dist/src/server/logger.js.map +1 -0
  303. package/dist/src/server/stream-proxy.d.ts +32 -0
  304. package/dist/src/server/stream-proxy.d.ts.map +1 -0
  305. package/dist/src/server/stream-proxy.js +184 -0
  306. package/dist/src/server/stream-proxy.js.map +1 -0
  307. package/dist/src/storage/file-persistence.d.ts +48 -0
  308. package/dist/src/storage/file-persistence.d.ts.map +1 -0
  309. package/dist/src/storage/file-persistence.js +280 -0
  310. package/dist/src/storage/file-persistence.js.map +1 -0
  311. package/dist/src/storage/index.d.ts +5 -0
  312. package/dist/src/storage/index.d.ts.map +1 -0
  313. package/dist/src/storage/index.js +21 -0
  314. package/dist/src/storage/index.js.map +1 -0
  315. package/dist/src/storage/interfaces.d.ts +237 -0
  316. package/dist/src/storage/interfaces.d.ts.map +1 -0
  317. package/dist/src/storage/interfaces.js +3 -0
  318. package/dist/src/storage/interfaces.js.map +1 -0
  319. package/dist/src/storage/memory.d.ts +162 -0
  320. package/dist/src/storage/memory.d.ts.map +1 -0
  321. package/dist/src/storage/memory.js +603 -0
  322. package/dist/src/storage/memory.js.map +1 -0
  323. package/dist/src/storage/postgres.d.ts +267 -0
  324. package/dist/src/storage/postgres.d.ts.map +1 -0
  325. package/dist/src/storage/postgres.js +1555 -0
  326. package/dist/src/storage/postgres.js.map +1 -0
  327. package/dist/src/storage/redis.d.ts +202 -0
  328. package/dist/src/storage/redis.d.ts.map +1 -0
  329. package/dist/src/storage/redis.js +629 -0
  330. package/dist/src/storage/redis.js.map +1 -0
  331. package/dist/src/tracing/index.d.ts +2 -0
  332. package/dist/src/tracing/index.d.ts.map +1 -0
  333. package/dist/src/tracing/index.js +6 -0
  334. package/dist/src/tracing/index.js.map +1 -0
  335. package/dist/src/tracing/provider.d.ts +43 -0
  336. package/dist/src/tracing/provider.d.ts.map +1 -0
  337. package/dist/src/tracing/provider.js +74 -0
  338. package/dist/src/tracing/provider.js.map +1 -0
  339. package/dist/src/trust/calculator.d.ts +54 -0
  340. package/dist/src/trust/calculator.d.ts.map +1 -0
  341. package/dist/src/trust/calculator.js +102 -0
  342. package/dist/src/trust/calculator.js.map +1 -0
  343. package/dist/src/trust/index.d.ts +2 -0
  344. package/dist/src/trust/index.d.ts.map +1 -0
  345. package/dist/src/trust/index.js +7 -0
  346. package/dist/src/trust/index.js.map +1 -0
  347. package/dist/src/types/budget.d.ts +30 -0
  348. package/dist/src/types/budget.d.ts.map +1 -0
  349. package/dist/src/types/budget.js +3 -0
  350. package/dist/src/types/budget.js.map +1 -0
  351. package/dist/src/types/config.d.ts +176 -0
  352. package/dist/src/types/config.d.ts.map +1 -0
  353. package/dist/src/types/config.js +3 -0
  354. package/dist/src/types/config.js.map +1 -0
  355. package/dist/src/types/events.d.ts +24 -0
  356. package/dist/src/types/events.d.ts.map +1 -0
  357. package/dist/src/types/events.js +3 -0
  358. package/dist/src/types/events.js.map +1 -0
  359. package/dist/src/types/index.d.ts +8 -0
  360. package/dist/src/types/index.d.ts.map +1 -0
  361. package/dist/src/types/index.js +24 -0
  362. package/dist/src/types/index.js.map +1 -0
  363. package/dist/src/types/policy.d.ts +60 -0
  364. package/dist/src/types/policy.d.ts.map +1 -0
  365. package/dist/src/types/policy.js +3 -0
  366. package/dist/src/types/policy.js.map +1 -0
  367. package/dist/src/types/stripe-config.d.ts +12 -0
  368. package/dist/src/types/stripe-config.d.ts.map +1 -0
  369. package/dist/src/types/stripe-config.js +3 -0
  370. package/dist/src/types/stripe-config.js.map +1 -0
  371. package/dist/src/types/subscription.d.ts +24 -0
  372. package/dist/src/types/subscription.d.ts.map +1 -0
  373. package/dist/src/types/subscription.js +38 -0
  374. package/dist/src/types/subscription.js.map +1 -0
  375. package/dist/src/types/tool-call.d.ts +42 -0
  376. package/dist/src/types/tool-call.d.ts.map +1 -0
  377. package/dist/src/types/tool-call.js +3 -0
  378. package/dist/src/types/tool-call.js.map +1 -0
  379. package/dist/src/types/tool-result.d.ts +58 -0
  380. package/dist/src/types/tool-result.d.ts.map +1 -0
  381. package/dist/src/types/tool-result.js +3 -0
  382. package/dist/src/types/tool-result.js.map +1 -0
  383. package/dist/src/types/user.d.ts +101 -0
  384. package/dist/src/types/user.d.ts.map +1 -0
  385. package/dist/src/types/user.js +6 -0
  386. package/dist/src/types/user.js.map +1 -0
  387. package/dist/tests/integration/api.test.d.ts +2 -0
  388. package/dist/tests/integration/api.test.d.ts.map +1 -0
  389. package/dist/tests/integration/api.test.js +1199 -0
  390. package/dist/tests/integration/api.test.js.map +1 -0
  391. package/dist/tests/integration/proxy.test.d.ts +2 -0
  392. package/dist/tests/integration/proxy.test.d.ts.map +1 -0
  393. package/dist/tests/integration/proxy.test.js +251 -0
  394. package/dist/tests/integration/proxy.test.js.map +1 -0
  395. package/dist/tests/integration/storage.test.d.ts +16 -0
  396. package/dist/tests/integration/storage.test.d.ts.map +1 -0
  397. package/dist/tests/integration/storage.test.js +826 -0
  398. package/dist/tests/integration/storage.test.js.map +1 -0
  399. package/dist/tests/unit/admin.test.d.ts +2 -0
  400. package/dist/tests/unit/admin.test.d.ts.map +1 -0
  401. package/dist/tests/unit/admin.test.js +698 -0
  402. package/dist/tests/unit/admin.test.js.map +1 -0
  403. package/dist/tests/unit/anomaly-detector.test.d.ts +2 -0
  404. package/dist/tests/unit/anomaly-detector.test.d.ts.map +1 -0
  405. package/dist/tests/unit/anomaly-detector.test.js +903 -0
  406. package/dist/tests/unit/anomaly-detector.test.js.map +1 -0
  407. package/dist/tests/unit/approval-manager.test.d.ts +2 -0
  408. package/dist/tests/unit/approval-manager.test.d.ts.map +1 -0
  409. package/dist/tests/unit/approval-manager.test.js +528 -0
  410. package/dist/tests/unit/approval-manager.test.js.map +1 -0
  411. package/dist/tests/unit/approval-webhook.test.d.ts +2 -0
  412. package/dist/tests/unit/approval-webhook.test.d.ts.map +1 -0
  413. package/dist/tests/unit/approval-webhook.test.js +355 -0
  414. package/dist/tests/unit/approval-webhook.test.js.map +1 -0
  415. package/dist/tests/unit/audit-logger.test.d.ts +2 -0
  416. package/dist/tests/unit/audit-logger.test.d.ts.map +1 -0
  417. package/dist/tests/unit/audit-logger.test.js +635 -0
  418. package/dist/tests/unit/audit-logger.test.js.map +1 -0
  419. package/dist/tests/unit/auth-routes.test.d.ts +2 -0
  420. package/dist/tests/unit/auth-routes.test.d.ts.map +1 -0
  421. package/dist/tests/unit/auth-routes.test.js +281 -0
  422. package/dist/tests/unit/auth-routes.test.js.map +1 -0
  423. package/dist/tests/unit/auth.test.d.ts +2 -0
  424. package/dist/tests/unit/auth.test.d.ts.map +1 -0
  425. package/dist/tests/unit/auth.test.js +1382 -0
  426. package/dist/tests/unit/auth.test.js.map +1 -0
  427. package/dist/tests/unit/billing.test.d.ts +2 -0
  428. package/dist/tests/unit/billing.test.d.ts.map +1 -0
  429. package/dist/tests/unit/billing.test.js +579 -0
  430. package/dist/tests/unit/billing.test.js.map +1 -0
  431. package/dist/tests/unit/budget-manager.test.d.ts +2 -0
  432. package/dist/tests/unit/budget-manager.test.d.ts.map +1 -0
  433. package/dist/tests/unit/budget-manager.test.js +778 -0
  434. package/dist/tests/unit/budget-manager.test.js.map +1 -0
  435. package/dist/tests/unit/budget-race.test.d.ts +2 -0
  436. package/dist/tests/unit/budget-race.test.d.ts.map +1 -0
  437. package/dist/tests/unit/budget-race.test.js +58 -0
  438. package/dist/tests/unit/budget-race.test.js.map +1 -0
  439. package/dist/tests/unit/cli.test.d.ts +2 -0
  440. package/dist/tests/unit/cli.test.d.ts.map +1 -0
  441. package/dist/tests/unit/cli.test.js +93 -0
  442. package/dist/tests/unit/cli.test.js.map +1 -0
  443. package/dist/tests/unit/concurrency.test.d.ts +2 -0
  444. package/dist/tests/unit/concurrency.test.d.ts.map +1 -0
  445. package/dist/tests/unit/concurrency.test.js +1270 -0
  446. package/dist/tests/unit/concurrency.test.js.map +1 -0
  447. package/dist/tests/unit/config-validate.test.d.ts +2 -0
  448. package/dist/tests/unit/config-validate.test.d.ts.map +1 -0
  449. package/dist/tests/unit/config-validate.test.js +230 -0
  450. package/dist/tests/unit/config-validate.test.js.map +1 -0
  451. package/dist/tests/unit/defaults.test.d.ts +2 -0
  452. package/dist/tests/unit/defaults.test.d.ts.map +1 -0
  453. package/dist/tests/unit/defaults.test.js +364 -0
  454. package/dist/tests/unit/defaults.test.js.map +1 -0
  455. package/dist/tests/unit/dlp-backends.test.d.ts +2 -0
  456. package/dist/tests/unit/dlp-backends.test.d.ts.map +1 -0
  457. package/dist/tests/unit/dlp-backends.test.js +563 -0
  458. package/dist/tests/unit/dlp-backends.test.js.map +1 -0
  459. package/dist/tests/unit/dlp-scanner.test.d.ts +2 -0
  460. package/dist/tests/unit/dlp-scanner.test.d.ts.map +1 -0
  461. package/dist/tests/unit/dlp-scanner.test.js +739 -0
  462. package/dist/tests/unit/dlp-scanner.test.js.map +1 -0
  463. package/dist/tests/unit/error-responses.test.d.ts +2 -0
  464. package/dist/tests/unit/error-responses.test.d.ts.map +1 -0
  465. package/dist/tests/unit/error-responses.test.js +101 -0
  466. package/dist/tests/unit/error-responses.test.js.map +1 -0
  467. package/dist/tests/unit/executor-registry.test.d.ts +2 -0
  468. package/dist/tests/unit/executor-registry.test.d.ts.map +1 -0
  469. package/dist/tests/unit/executor-registry.test.js +390 -0
  470. package/dist/tests/unit/executor-registry.test.js.map +1 -0
  471. package/dist/tests/unit/forward-proxy.test.d.ts +2 -0
  472. package/dist/tests/unit/forward-proxy.test.d.ts.map +1 -0
  473. package/dist/tests/unit/forward-proxy.test.js +621 -0
  474. package/dist/tests/unit/forward-proxy.test.js.map +1 -0
  475. package/dist/tests/unit/gateway-features.test.d.ts +2 -0
  476. package/dist/tests/unit/gateway-features.test.d.ts.map +1 -0
  477. package/dist/tests/unit/gateway-features.test.js +753 -0
  478. package/dist/tests/unit/gateway-features.test.js.map +1 -0
  479. package/dist/tests/unit/http-executor.test.d.ts +2 -0
  480. package/dist/tests/unit/http-executor.test.d.ts.map +1 -0
  481. package/dist/tests/unit/http-executor.test.js +310 -0
  482. package/dist/tests/unit/http-executor.test.js.map +1 -0
  483. package/dist/tests/unit/mcp-bridge.test.d.ts +2 -0
  484. package/dist/tests/unit/mcp-bridge.test.d.ts.map +1 -0
  485. package/dist/tests/unit/mcp-bridge.test.js +1136 -0
  486. package/dist/tests/unit/mcp-bridge.test.js.map +1 -0
  487. package/dist/tests/unit/mcp-http-transport.test.d.ts +2 -0
  488. package/dist/tests/unit/mcp-http-transport.test.d.ts.map +1 -0
  489. package/dist/tests/unit/mcp-http-transport.test.js +899 -0
  490. package/dist/tests/unit/mcp-http-transport.test.js.map +1 -0
  491. package/dist/tests/unit/mcp-oauth.test.d.ts +2 -0
  492. package/dist/tests/unit/mcp-oauth.test.d.ts.map +1 -0
  493. package/dist/tests/unit/mcp-oauth.test.js +759 -0
  494. package/dist/tests/unit/mcp-oauth.test.js.map +1 -0
  495. package/dist/tests/unit/mcp-server.test.d.ts +15 -0
  496. package/dist/tests/unit/mcp-server.test.d.ts.map +1 -0
  497. package/dist/tests/unit/mcp-server.test.js +158 -0
  498. package/dist/tests/unit/mcp-server.test.js.map +1 -0
  499. package/dist/tests/unit/metrics.test.d.ts +2 -0
  500. package/dist/tests/unit/metrics.test.d.ts.map +1 -0
  501. package/dist/tests/unit/metrics.test.js +208 -0
  502. package/dist/tests/unit/metrics.test.js.map +1 -0
  503. package/dist/tests/unit/oauth.test.d.ts +2 -0
  504. package/dist/tests/unit/oauth.test.d.ts.map +1 -0
  505. package/dist/tests/unit/oauth.test.js +281 -0
  506. package/dist/tests/unit/oauth.test.js.map +1 -0
  507. package/dist/tests/unit/opa-circuit-breaker.test.d.ts +2 -0
  508. package/dist/tests/unit/opa-circuit-breaker.test.d.ts.map +1 -0
  509. package/dist/tests/unit/opa-circuit-breaker.test.js +297 -0
  510. package/dist/tests/unit/opa-circuit-breaker.test.js.map +1 -0
  511. package/dist/tests/unit/opa-engine.test.d.ts +2 -0
  512. package/dist/tests/unit/opa-engine.test.d.ts.map +1 -0
  513. package/dist/tests/unit/opa-engine.test.js +1813 -0
  514. package/dist/tests/unit/opa-engine.test.js.map +1 -0
  515. package/dist/tests/unit/pipeline-timing.test.d.ts +2 -0
  516. package/dist/tests/unit/pipeline-timing.test.d.ts.map +1 -0
  517. package/dist/tests/unit/pipeline-timing.test.js +528 -0
  518. package/dist/tests/unit/pipeline-timing.test.js.map +1 -0
  519. package/dist/tests/unit/policy-engine.test.d.ts +2 -0
  520. package/dist/tests/unit/policy-engine.test.d.ts.map +1 -0
  521. package/dist/tests/unit/policy-engine.test.js +1345 -0
  522. package/dist/tests/unit/policy-engine.test.js.map +1 -0
  523. package/dist/tests/unit/policy-store.test.d.ts +2 -0
  524. package/dist/tests/unit/policy-store.test.d.ts.map +1 -0
  525. package/dist/tests/unit/policy-store.test.js +60 -0
  526. package/dist/tests/unit/policy-store.test.js.map +1 -0
  527. package/dist/tests/unit/postgres-storage.test.d.ts +2 -0
  528. package/dist/tests/unit/postgres-storage.test.d.ts.map +1 -0
  529. package/dist/tests/unit/postgres-storage.test.js +614 -0
  530. package/dist/tests/unit/postgres-storage.test.js.map +1 -0
  531. package/dist/tests/unit/prompt-injection-backend.test.d.ts +2 -0
  532. package/dist/tests/unit/prompt-injection-backend.test.d.ts.map +1 -0
  533. package/dist/tests/unit/prompt-injection-backend.test.js +621 -0
  534. package/dist/tests/unit/prompt-injection-backend.test.js.map +1 -0
  535. package/dist/tests/unit/proxy-hardening.test.d.ts +2 -0
  536. package/dist/tests/unit/proxy-hardening.test.d.ts.map +1 -0
  537. package/dist/tests/unit/proxy-hardening.test.js +166 -0
  538. package/dist/tests/unit/proxy-hardening.test.js.map +1 -0
  539. package/dist/tests/unit/rate-limiter.test.d.ts +2 -0
  540. package/dist/tests/unit/rate-limiter.test.d.ts.map +1 -0
  541. package/dist/tests/unit/rate-limiter.test.js +443 -0
  542. package/dist/tests/unit/rate-limiter.test.js.map +1 -0
  543. package/dist/tests/unit/redis-storage.test.d.ts +2 -0
  544. package/dist/tests/unit/redis-storage.test.d.ts.map +1 -0
  545. package/dist/tests/unit/redis-storage.test.js +766 -0
  546. package/dist/tests/unit/redis-storage.test.js.map +1 -0
  547. package/dist/tests/unit/replay-engine.test.d.ts +2 -0
  548. package/dist/tests/unit/replay-engine.test.d.ts.map +1 -0
  549. package/dist/tests/unit/replay-engine.test.js +371 -0
  550. package/dist/tests/unit/replay-engine.test.js.map +1 -0
  551. package/dist/tests/unit/saas-routes.test.d.ts +2 -0
  552. package/dist/tests/unit/saas-routes.test.d.ts.map +1 -0
  553. package/dist/tests/unit/saas-routes.test.js +1399 -0
  554. package/dist/tests/unit/saas-routes.test.js.map +1 -0
  555. package/dist/tests/unit/session.test.d.ts +2 -0
  556. package/dist/tests/unit/session.test.d.ts.map +1 -0
  557. package/dist/tests/unit/session.test.js +532 -0
  558. package/dist/tests/unit/session.test.js.map +1 -0
  559. package/dist/tests/unit/slack-executor.test.d.ts +2 -0
  560. package/dist/tests/unit/slack-executor.test.d.ts.map +1 -0
  561. package/dist/tests/unit/slack-executor.test.js +209 -0
  562. package/dist/tests/unit/slack-executor.test.js.map +1 -0
  563. package/dist/tests/unit/storage-hardening.test.d.ts +2 -0
  564. package/dist/tests/unit/storage-hardening.test.d.ts.map +1 -0
  565. package/dist/tests/unit/storage-hardening.test.js +165 -0
  566. package/dist/tests/unit/storage-hardening.test.js.map +1 -0
  567. package/dist/tests/unit/storage.test.d.ts +2 -0
  568. package/dist/tests/unit/storage.test.d.ts.map +1 -0
  569. package/dist/tests/unit/storage.test.js +698 -0
  570. package/dist/tests/unit/storage.test.js.map +1 -0
  571. package/dist/tests/unit/text-normalizer.test.d.ts +2 -0
  572. package/dist/tests/unit/text-normalizer.test.d.ts.map +1 -0
  573. package/dist/tests/unit/text-normalizer.test.js +229 -0
  574. package/dist/tests/unit/text-normalizer.test.js.map +1 -0
  575. package/dist/tests/unit/tracing.test.d.ts +2 -0
  576. package/dist/tests/unit/tracing.test.d.ts.map +1 -0
  577. package/dist/tests/unit/tracing.test.js +611 -0
  578. package/dist/tests/unit/tracing.test.js.map +1 -0
  579. package/dist/tests/unit/trust-calculator.test.d.ts +2 -0
  580. package/dist/tests/unit/trust-calculator.test.d.ts.map +1 -0
  581. package/dist/tests/unit/trust-calculator.test.js +497 -0
  582. package/dist/tests/unit/trust-calculator.test.js.map +1 -0
  583. package/dist/tests/unit/ts-sdk.test.d.ts +2 -0
  584. package/dist/tests/unit/ts-sdk.test.d.ts.map +1 -0
  585. package/dist/tests/unit/ts-sdk.test.js +421 -0
  586. package/dist/tests/unit/ts-sdk.test.js.map +1 -0
  587. package/dist/tests/unit/usage-extractor-llm.test.d.ts +2 -0
  588. package/dist/tests/unit/usage-extractor-llm.test.d.ts.map +1 -0
  589. package/dist/tests/unit/usage-extractor-llm.test.js +139 -0
  590. package/dist/tests/unit/usage-extractor-llm.test.js.map +1 -0
  591. package/dist/tests/unit/usage-extractor.test.d.ts +2 -0
  592. package/dist/tests/unit/usage-extractor.test.d.ts.map +1 -0
  593. package/dist/tests/unit/usage-extractor.test.js +271 -0
  594. package/dist/tests/unit/usage-extractor.test.js.map +1 -0
  595. package/dist/tests/unit/user-stores.test.d.ts +2 -0
  596. package/dist/tests/unit/user-stores.test.d.ts.map +1 -0
  597. package/dist/tests/unit/user-stores.test.js +687 -0
  598. package/dist/tests/unit/user-stores.test.js.map +1 -0
  599. package/dist/tests/unit/validate.test.d.ts +2 -0
  600. package/dist/tests/unit/validate.test.d.ts.map +1 -0
  601. package/dist/tests/unit/validate.test.js +545 -0
  602. package/dist/tests/unit/validate.test.js.map +1 -0
  603. package/package.json +86 -0
  604. package/policy-packs/README.md +42 -0
  605. package/policy-packs/default.yaml +46 -0
  606. package/policy-packs/dev_fast.yaml +54 -0
  607. package/policy-packs/prod_strict.yaml +83 -0
@@ -0,0 +1,854 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createApp = createApp;
7
+ const express_1 = __importDefault(require("express"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const cors_1 = __importDefault(require("cors"));
10
+ const helmet_1 = __importDefault(require("helmet"));
11
+ const cookie_parser_1 = __importDefault(require("cookie-parser"));
12
+ const gateway_1 = require("./gateway");
13
+ const stream_proxy_1 = require("./stream-proxy");
14
+ const noop_executor_1 = require("../executor/noop-executor");
15
+ const proxy_1 = require("../proxy");
16
+ const auth_1 = require("../middleware/auth");
17
+ const session_1 = require("../middleware/session");
18
+ const validate_1 = require("../middleware/validate");
19
+ const metrics_1 = require("../metrics");
20
+ const tracing_1 = require("../tracing");
21
+ const admin_1 = require("../admin");
22
+ const errors_1 = require("./errors");
23
+ const routes_1 = require("../auth/routes");
24
+ const routes_2 = require("../saas/routes");
25
+ const http_transport_1 = require("../mcp/http-transport");
26
+ const router_js_1 = require("@modelcontextprotocol/sdk/server/auth/router.js");
27
+ const bearerAuth_js_1 = require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js");
28
+ const router_js_2 = require("@modelcontextprotocol/sdk/server/auth/router.js");
29
+ const oauth_stores_1 = require("../mcp/oauth-stores");
30
+ const oauth_provider_1 = require("../mcp/oauth-provider");
31
+ const auth_verifier_1 = require("../mcp/auth-verifier");
32
+ const session_2 = require("../auth/session");
33
+ const memory_1 = require("../storage/memory");
34
+ const billing_1 = require("../billing");
35
+ const plan_enforcer_1 = require("../billing/plan-enforcer");
36
+ const file_persistence_1 = require("../storage/file-persistence");
37
+ const password_1 = require("../auth/password");
38
+ const logger_1 = require("./logger");
39
+ const MAX_TRACKED_IPS = 10000;
40
+ /**
41
+ * Simple IP-based rate limiter for unauthenticated endpoints.
42
+ * Uses a sliding window with per-IP tracking.
43
+ */
44
+ function createIPRateLimiter(maxPerWindow, windowMs) {
45
+ const hits = new Map();
46
+ // Periodic cleanup to prevent memory leaks
47
+ setInterval(() => {
48
+ const now = Date.now();
49
+ for (const [ip, timestamps] of hits) {
50
+ const valid = timestamps.filter(t => now - t < windowMs);
51
+ if (valid.length === 0) {
52
+ hits.delete(ip);
53
+ }
54
+ else {
55
+ hits.set(ip, valid);
56
+ }
57
+ }
58
+ }, windowMs).unref();
59
+ return (req, res, next) => {
60
+ const ip = req.ip || req.socket.remoteAddress || 'unknown';
61
+ const now = Date.now();
62
+ const timestamps = hits.get(ip) || [];
63
+ const windowStart = now - windowMs;
64
+ const valid = timestamps.filter(t => t > windowStart);
65
+ if (valid.length >= maxPerWindow) {
66
+ (0, errors_1.sendError)(res, 429, errors_1.ErrorCode.RATE_LIMIT_EXCEEDED, 'Too many requests', {
67
+ details: { retry_after_ms: windowMs - (now - valid[0]) },
68
+ hint: 'Retry after the reset time or increase rate_limit config',
69
+ });
70
+ return;
71
+ }
72
+ // Reject new IPs when the map is at capacity to prevent unbounded growth
73
+ if (!hits.has(ip) && hits.size >= MAX_TRACKED_IPS) {
74
+ (0, errors_1.sendError)(res, 429, errors_1.ErrorCode.RATE_LIMIT_EXCEEDED, 'Too many requests', {
75
+ details: { retry_after_ms: windowMs },
76
+ hint: 'Retry after the reset time or increase rate_limit config',
77
+ });
78
+ return;
79
+ }
80
+ valid.push(now);
81
+ hits.set(ip, valid);
82
+ next();
83
+ };
84
+ }
85
+ function createApp(config, externalSaaSStores) {
86
+ const app = (0, express_1.default)();
87
+ const metrics = new metrics_1.GatewayMetrics();
88
+ const startTime = Date.now();
89
+ const healthChecks = [];
90
+ // Initialize SaaS stores (use provided, file-persisted, or default to in-memory)
91
+ let saasStores;
92
+ let filePersistence;
93
+ if (externalSaaSStores?.userStore) {
94
+ // External stores provided (e.g., PostgreSQL)
95
+ saasStores = {
96
+ userStore: externalSaaSStores.userStore,
97
+ oauthAccountStore: externalSaaSStores.oauthAccountStore || new memory_1.InMemoryOAuthAccountStore(),
98
+ workspaceStore: externalSaaSStores.workspaceStore || new memory_1.InMemoryWorkspaceStore(),
99
+ workspaceMemberStore: externalSaaSStores.workspaceMemberStore || new memory_1.InMemoryWorkspaceMemberStore(),
100
+ sessionStore: externalSaaSStores.sessionStore || new memory_1.InMemorySessionStore(),
101
+ userApiKeyStore: externalSaaSStores.userApiKeyStore || new memory_1.InMemoryUserApiKeyStore(),
102
+ subscriptionStore: externalSaaSStores.subscriptionStore || new memory_1.InMemorySubscriptionStore(),
103
+ policyStore: externalSaaSStores.policyStore,
104
+ rateLimitConfigStore: externalSaaSStores.rateLimitConfigStore,
105
+ budgetConfigStore: externalSaaSStores.budgetConfigStore,
106
+ };
107
+ }
108
+ else {
109
+ // File-persisted in-memory stores (survives server restarts)
110
+ const dataDir = process.env.PALARYN_DATA_DIR || './data';
111
+ filePersistence = new file_persistence_1.FilePersistedStores(dataDir);
112
+ saasStores = {
113
+ userStore: filePersistence.userStore,
114
+ oauthAccountStore: filePersistence.oauthAccountStore,
115
+ workspaceStore: filePersistence.workspaceStore,
116
+ workspaceMemberStore: filePersistence.workspaceMemberStore,
117
+ sessionStore: filePersistence.sessionStore,
118
+ userApiKeyStore: filePersistence.userApiKeyStore,
119
+ subscriptionStore: new memory_1.InMemorySubscriptionStore(),
120
+ policyStore: filePersistence.policyStore,
121
+ };
122
+ }
123
+ // Default OAuth config for session/auth routes (email+password always works)
124
+ const oauthConfig = config.oauth || {
125
+ enabled: false,
126
+ session_secret: 'palaryn-dev-session-secret',
127
+ session_ttl_seconds: 604800,
128
+ };
129
+ // Seed test account in non-production (async due to hashPassword)
130
+ if (process.env.NODE_ENV !== 'production') {
131
+ (async () => {
132
+ const existing = saasStores.userStore.getByEmail('test@palaryn.dev');
133
+ if (!existing) {
134
+ const now = new Date().toISOString();
135
+ saasStores.userStore.create({
136
+ id: 'usr_test_000',
137
+ email: 'test@palaryn.dev',
138
+ display_name: 'Test User',
139
+ password_hash: await (0, password_1.hashPassword)('test1234'),
140
+ status: 'active',
141
+ onboarding_completed: false,
142
+ created_at: now,
143
+ updated_at: now,
144
+ });
145
+ }
146
+ })();
147
+ }
148
+ // Seed admin account from env vars (for production with in-memory storage)
149
+ if (process.env.SEED_ADMIN_EMAIL && process.env.SEED_ADMIN_PASSWORD) {
150
+ (async () => {
151
+ const existing = saasStores.userStore.getByEmail(process.env.SEED_ADMIN_EMAIL);
152
+ if (!existing) {
153
+ const now = new Date().toISOString();
154
+ const userId = 'usr_admin_001';
155
+ const workspaceId = process.env.SEED_ADMIN_WORKSPACE || 'ws_default';
156
+ saasStores.userStore.create({
157
+ id: userId,
158
+ email: process.env.SEED_ADMIN_EMAIL,
159
+ display_name: process.env.SEED_ADMIN_NAME || 'Admin',
160
+ avatar_url: process.env.SEED_ADMIN_AVATAR_URL,
161
+ password_hash: await (0, password_1.hashPassword)(process.env.SEED_ADMIN_PASSWORD),
162
+ status: 'active',
163
+ onboarding_completed: true,
164
+ created_at: now,
165
+ updated_at: now,
166
+ });
167
+ // Create workspace if store supports it
168
+ if (saasStores.workspaceStore && typeof saasStores.workspaceStore.create === 'function') {
169
+ const existingWs = saasStores.workspaceStore.getById(workspaceId);
170
+ if (!existingWs) {
171
+ saasStores.workspaceStore.create({
172
+ id: workspaceId,
173
+ name: process.env.SEED_ADMIN_WORKSPACE_NAME || 'Default',
174
+ slug: 'default',
175
+ owner_user_id: userId,
176
+ plan: 'pro',
177
+ settings: {},
178
+ created_at: now,
179
+ updated_at: now,
180
+ });
181
+ }
182
+ }
183
+ // Add user as workspace owner
184
+ if (saasStores.workspaceMemberStore && typeof saasStores.workspaceMemberStore.create === 'function') {
185
+ saasStores.workspaceMemberStore.create({
186
+ id: `wm_${userId}_${workspaceId}`,
187
+ workspace_id: workspaceId,
188
+ user_id: userId,
189
+ role: 'owner',
190
+ joined_at: now,
191
+ });
192
+ }
193
+ }
194
+ })();
195
+ }
196
+ // Initialize OpenTelemetry tracer if tracing is configured
197
+ let tracer;
198
+ if (config.tracing?.enabled) {
199
+ tracer = new tracing_1.GatewayTracer(config.tracing);
200
+ tracer.setup();
201
+ }
202
+ const gateway = new gateway_1.Gateway(config, metrics, tracer);
203
+ // Inject workspace-level policy store so per-workspace policies are evaluated
204
+ if (saasStores.policyStore) {
205
+ gateway.setStores({ policyStore: saasStores.policyStore });
206
+ }
207
+ // Register noop executors for non-HTTP tools (e.g. pre-flight validation from Android)
208
+ gateway.registerExecutor('web_search', new noop_executor_1.NoopExecutor({ body: { validated: true, tool: 'web_search' } }));
209
+ gateway.registerExecutor('chat.completion', new noop_executor_1.NoopExecutor({ body: { validated: true, tool: 'chat.completion' } }));
210
+ // Rate limiter for unauthenticated endpoints (60 requests per minute per IP)
211
+ const publicLimitConfig = config.public_rate_limit || { max_per_window: 60, window_ms: 60000 };
212
+ const publicRateLimiter = createIPRateLimiter(publicLimitConfig.max_per_window, publicLimitConfig.window_ms);
213
+ // Middleware
214
+ const isProduction = process.env.NODE_ENV === 'production';
215
+ app.use((0, helmet_1.default)({
216
+ contentSecurityPolicy: {
217
+ directives: {
218
+ defaultSrc: ["'self'"],
219
+ scriptSrc: ["'self'", "'unsafe-inline'"],
220
+ styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
221
+ fontSrc: ["'self'", "https://fonts.gstatic.com", "data:"],
222
+ imgSrc: ["'self'", "data:", "https:"],
223
+ connectSrc: ["'self'"],
224
+ frameAncestors: ["'none'"],
225
+ upgradeInsecureRequests: isProduction ? [] : null,
226
+ },
227
+ },
228
+ hsts: isProduction ? { maxAge: 31536000, includeSubDomains: true } : false,
229
+ xFrameOptions: { action: 'deny' },
230
+ }));
231
+ app.use((0, cors_1.default)(config.cors_origins
232
+ ? { origin: config.cors_origins, credentials: true }
233
+ : isProduction
234
+ ? { origin: false } // Block all cross-origin in production when not configured
235
+ : { origin: ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:5173', 'http://127.0.0.1:3000', 'http://127.0.0.1:5173'], credentials: true }));
236
+ // Stripe webhook route (must be before express.json() for raw body signature verification)
237
+ if (config.stripe?.secret_key && config.stripe?.webhook_secret) {
238
+ const stripeClient = new billing_1.StripeClient(config.stripe);
239
+ const webhookHandler = new billing_1.WebhookHandler(saasStores.subscriptionStore, saasStores.workspaceStore, config.stripe);
240
+ app.use((0, billing_1.createWebhookRouter)(stripeClient, webhookHandler));
241
+ }
242
+ app.use(express_1.default.json({ limit: '10mb' }));
243
+ app.use((0, cookie_parser_1.default)());
244
+ // Server-side request timeout (30 seconds default, prevents hung connections)
245
+ app.use((req, res, next) => {
246
+ const timeoutMs = 30000;
247
+ res.setTimeout(timeoutMs, () => {
248
+ if (!res.headersSent) {
249
+ (0, errors_1.sendError)(res, 408, errors_1.ErrorCode.REQUEST_TIMEOUT, 'Request timeout', {
250
+ hint: 'The server did not complete the request within 30 seconds',
251
+ });
252
+ }
253
+ });
254
+ next();
255
+ });
256
+ // Dev request/response logging middleware
257
+ app.use((req, res, next) => {
258
+ const reqStart = Date.now();
259
+ // Skip noisy health/metrics polling
260
+ if (req.path === '/health' || req.path === '/ready' || req.path === '/metrics') {
261
+ return next();
262
+ }
263
+ logger_1.log.request(req.method, req.path, req.ip || req.socket.remoteAddress || 'unknown', req.body);
264
+ const origJson = res.json.bind(res);
265
+ res.json = (body) => {
266
+ logger_1.log.response(res.statusCode, Date.now() - reqStart, body);
267
+ return origJson(body);
268
+ };
269
+ const origSend = res.send.bind(res);
270
+ res.send = (body) => {
271
+ // Log HTML responses (from admin routes) without the full body
272
+ if (typeof body === 'string' && body.startsWith('<!DOCTYPE html>')) {
273
+ logger_1.log.response(res.statusCode, Date.now() - reqStart, { html: true, length: body.length });
274
+ }
275
+ return origSend(body);
276
+ };
277
+ next();
278
+ });
279
+ // Health check (no auth) - minimal response to avoid information disclosure
280
+ app.get('/health', publicRateLimiter, async (req, res) => {
281
+ // Internal dependency checks
282
+ const checks = [];
283
+ let overall = 'ok';
284
+ for (const hc of healthChecks) {
285
+ try {
286
+ const result = await hc.check();
287
+ checks.push({ name: hc.name, status: result.status });
288
+ if (result.status === 'unhealthy')
289
+ overall = 'unhealthy';
290
+ else if (result.status === 'degraded' && overall !== 'unhealthy')
291
+ overall = 'degraded';
292
+ }
293
+ catch {
294
+ checks.push({ name: hc.name, status: 'unhealthy' });
295
+ overall = 'unhealthy';
296
+ }
297
+ }
298
+ // Return 503 when any critical dependency is unhealthy
299
+ const httpStatus = overall === 'unhealthy' ? 503 : 200;
300
+ // Unauthenticated: minimal response (no version, uptime, or internal component names)
301
+ const authCtx = req.auth;
302
+ if (authCtx && authCtx.auth_method !== 'none') {
303
+ const uptimeSeconds = Math.floor((Date.now() - startTime) / 1000);
304
+ res.status(httpStatus).json({
305
+ status: overall,
306
+ version: '0.1.0',
307
+ timestamp: new Date().toISOString(),
308
+ uptime_seconds: uptimeSeconds,
309
+ checks,
310
+ });
311
+ }
312
+ else {
313
+ res.status(httpStatus).json({ status: overall });
314
+ }
315
+ });
316
+ // Readiness probe (no auth) - for K8s readiness checks
317
+ // Unlike /health which always returns 200, /ready returns 503 when unhealthy
318
+ app.get('/ready', publicRateLimiter, async (req, res) => {
319
+ const checks = [];
320
+ let ready = true;
321
+ // Check if gateway is shutting down
322
+ if (gateway.isShuttingDown) {
323
+ checks.push({ name: 'gateway', status: 'unhealthy', message: 'shutting down' });
324
+ ready = false;
325
+ }
326
+ else {
327
+ checks.push({ name: 'gateway', status: 'ok' });
328
+ }
329
+ // Check registered external dependencies (Redis, Postgres, etc.)
330
+ for (const hc of healthChecks) {
331
+ try {
332
+ const result = await hc.check();
333
+ checks.push({ name: hc.name, status: result.status, message: result.message });
334
+ if (result.status === 'unhealthy') {
335
+ ready = false;
336
+ }
337
+ }
338
+ catch (err) {
339
+ checks.push({ name: hc.name, status: 'unhealthy', message: err instanceof Error ? err.message : 'check failed' });
340
+ ready = false;
341
+ }
342
+ }
343
+ // Always check core components
344
+ checks.push({ name: 'policy_engine', status: 'ok' });
345
+ if (ready) {
346
+ res.json({ ready: true, checks });
347
+ }
348
+ else {
349
+ res.status(503).json({ ready: false, checks });
350
+ }
351
+ });
352
+ // Prometheus metrics endpoint (auth required in production, open in dev for scraping)
353
+ app.get('/metrics', publicRateLimiter, async (req, res) => {
354
+ // In production, require auth to prevent information disclosure
355
+ if (isProduction && config.auth.enabled) {
356
+ const apiKey = req.headers['x-api-key'];
357
+ const bearer = req.headers['authorization']?.startsWith('Bearer ') ? req.headers['authorization'].slice(7) : undefined;
358
+ if (!apiKey && !bearer) {
359
+ (0, errors_1.sendError)(res, 401, errors_1.ErrorCode.AUTH_REQUIRED, 'Authentication required for /metrics in production');
360
+ return;
361
+ }
362
+ }
363
+ try {
364
+ const metricsOutput = await metrics.getMetrics();
365
+ res.set('Content-Type', metrics.getContentType());
366
+ res.end(metricsOutput);
367
+ }
368
+ catch (err) {
369
+ res.status(500).end();
370
+ }
371
+ });
372
+ // Rate limiter for auth endpoints (10 requests per minute per IP — tighter than public)
373
+ const authRateLimiter = createIPRateLimiter(10, 60000);
374
+ // Stricter rate limiter for registration (3 per 15 minutes per IP)
375
+ const registerRateLimiter = createIPRateLimiter(3, 15 * 60 * 1000);
376
+ // Session middleware + auth routes (always mounted for email/password auth)
377
+ const sessionMiddleware = (0, session_1.createSessionMiddleware)({
378
+ oauthConfig: oauthConfig,
379
+ authConfig: config.auth,
380
+ sessionStore: saasStores.sessionStore,
381
+ userStore: saasStores.userStore,
382
+ workspaceMemberStore: saasStores.workspaceMemberStore,
383
+ });
384
+ app.use(sessionMiddleware);
385
+ const authRouter = (0, routes_1.createAuthRouter)({
386
+ config: oauthConfig,
387
+ userStore: saasStores.userStore,
388
+ oauthAccountStore: saasStores.oauthAccountStore,
389
+ sessionStore: saasStores.sessionStore,
390
+ workspaceStore: saasStores.workspaceStore,
391
+ workspaceMemberStore: saasStores.workspaceMemberStore,
392
+ userApiKeyStore: saasStores.userApiKeyStore,
393
+ });
394
+ app.use('/auth/register', registerRateLimiter);
395
+ app.use('/auth', authRateLimiter, authRouter);
396
+ // Auth middleware for API routes (bridges config keys + SaaS-generated keys)
397
+ const authMiddleware = (0, auth_1.createAuthMiddleware)(config.auth, saasStores.userApiKeyStore);
398
+ // RBAC middleware factories
399
+ const rbacToolExecute = (0, auth_1.createRBACMiddleware)(config.auth, 'tool:execute');
400
+ const rbacApprovalManage = (0, auth_1.createRBACMiddleware)(config.auth, 'approval:manage');
401
+ const rbacTraceRead = (0, auth_1.createRBACMiddleware)(config.auth, 'trace:read');
402
+ const rbacPolicyRead = (0, auth_1.createRBACMiddleware)(config.auth, 'policy:read');
403
+ const rbacPolicyWrite = (0, auth_1.createRBACMiddleware)(config.auth, 'policy:write');
404
+ // Admin routes (require auth + admin:full permission)
405
+ // Auth + RBAC middleware mounted directly with the router to prevent route decoupling
406
+ const adminRouter = (0, admin_1.createAdminRouter)(gateway, config);
407
+ const rbacAdmin = (0, auth_1.createRBACMiddleware)(config.auth, 'admin:full');
408
+ app.use('/admin', authMiddleware, rbacAdmin, adminRouter);
409
+ // Plan enforcer middleware — blocks calls when workspace exceeds plan limits
410
+ const planEnforcerMiddleware = (0, plan_enforcer_1.createPlanEnforcerMiddleware)({
411
+ workspaceStore: saasStores.workspaceStore,
412
+ getMonthlyCallCount: (workspaceId) => {
413
+ const now = new Date();
414
+ const monthStart = new Date(now.getFullYear(), now.getMonth(), 1).toISOString();
415
+ const events = gateway.getAuditLogger().getAllEvents();
416
+ return events.filter(e => e.workspace_id === workspaceId
417
+ && e.event_type === 'TOOL_CALL_RECEIVED'
418
+ && e.timestamp >= monthStart).length;
419
+ },
420
+ });
421
+ // POST /v1/tool/execute - Execute a tool call through the gateway
422
+ app.post('/v1/tool/execute', authMiddleware, rbacToolExecute, planEnforcerMiddleware, validate_1.validateToolCall, async (req, res) => {
423
+ try {
424
+ // Capability-specific RBAC check
425
+ const capability = req.body.tool?.capability;
426
+ if (capability && config.auth.rbac?.enabled) {
427
+ const authCtx = req.auth;
428
+ if (authCtx && authCtx.auth_method !== 'none') {
429
+ const { hasPermission } = require('../middleware/auth');
430
+ const capPerm = `tool:execute:${capability}`;
431
+ // Only enforce capability check if user does NOT have the broad tool:execute permission
432
+ // and does NOT have admin:full
433
+ if (!hasPermission(authCtx.permissions, capPerm)) {
434
+ (0, errors_1.sendError)(res, 403, errors_1.ErrorCode.AUTH_INSUFFICIENT_PERMS, `Insufficient permissions. Required: ${capPerm}`, {
435
+ details: { required_permission: capPerm, roles: authCtx.roles },
436
+ hint: 'Assign a role with the required permission or use an admin key',
437
+ });
438
+ return;
439
+ }
440
+ }
441
+ }
442
+ const toolCall = {
443
+ ...req.body,
444
+ workspace_id: req.auth?.workspace_id || req.body.workspace_id || req.workspace_id,
445
+ };
446
+ // Merge API key tags into context.labels
447
+ const apiKeyTags = req.auth?.api_key_tags;
448
+ if (apiKeyTags && apiKeyTags.length > 0) {
449
+ if (!toolCall.context)
450
+ toolCall.context = {};
451
+ const existing = toolCall.context.labels || [];
452
+ toolCall.context.labels = [...new Set([...existing, ...apiKeyTags])];
453
+ }
454
+ const requestingApiKeyId = req.auth?.api_key_id;
455
+ const result = await gateway.execute(toolCall, requestingApiKeyId);
456
+ // Use 429 for rate limit blocks, 403 for policy blocks
457
+ let httpStatus;
458
+ if (result.status === 'ok') {
459
+ httpStatus = 200;
460
+ }
461
+ else if (result.status === 'blocked') {
462
+ httpStatus = result.policy?.rule_id === 'rate_limit' ? 429 : 403;
463
+ }
464
+ else if (result.status === 'needs_approval') {
465
+ httpStatus = 202;
466
+ }
467
+ else {
468
+ httpStatus = 500;
469
+ }
470
+ // S3: Never leak raw error messages to clients (may contain internal paths, IPs, secrets)
471
+ if (result.error) {
472
+ console.error('[tool-execute] Gateway error:', result.error);
473
+ result.error = 'Tool execution failed';
474
+ }
475
+ res.status(httpStatus).json(result);
476
+ }
477
+ catch (err) {
478
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
479
+ console.error('[tool-execute] Execution error:', errorMessage);
480
+ (0, errors_1.sendError)(res, 500, errors_1.ErrorCode.TOOL_EXECUTION_ERROR, 'Tool execution failed');
481
+ }
482
+ });
483
+ // POST /v1/proxy/stream - SSE streaming proxy (dormant, feature-flagged)
484
+ // Kept for future use when gateway controls the upstream directly.
485
+ // Enable with PROXY_STREAM_ENABLED=true environment variable.
486
+ if (process.env.PROXY_STREAM_ENABLED === 'true') {
487
+ app.post('/v1/proxy/stream', authMiddleware, rbacToolExecute, async (req, res) => {
488
+ try {
489
+ const { tool_call_id, task_id, actor, source, tool, target, context, constraints } = req.body;
490
+ if (!tool_call_id || !task_id || !actor || !tool || !target?.url) {
491
+ (0, errors_1.sendError)(res, 400, errors_1.ErrorCode.VALIDATION_FAILED, 'Missing required fields: tool_call_id, task_id, actor, tool, target.url', {
492
+ hint: 'Check the stream proxy request schema',
493
+ });
494
+ return;
495
+ }
496
+ const toolCall = {
497
+ tool_call_id,
498
+ task_id,
499
+ workspace_id: req.auth?.workspace_id || req.body.workspace_id || 'unknown',
500
+ actor: { type: actor.type || 'agent', id: actor.id, display: actor.display },
501
+ source: { platform: source?.platform || 'unknown', session_id: source?.session_id },
502
+ tool: { name: tool.name, version: tool.version, capability: tool.capability || 'write' },
503
+ args: {
504
+ method: target.method || 'POST',
505
+ url: target.url,
506
+ headers: target.headers,
507
+ body: target.body,
508
+ },
509
+ constraints,
510
+ context,
511
+ };
512
+ const pre = await gateway.preExecute(toolCall);
513
+ if (!pre.allowed) {
514
+ const httpStatus = pre.result.status === 'blocked' ? 403 : pre.result.status === 'needs_approval' ? 202 : 500;
515
+ res.status(httpStatus).json(pre.result);
516
+ return;
517
+ }
518
+ res.setTimeout(0);
519
+ const proxyResult = await (0, stream_proxy_1.streamProxy)({
520
+ url: target.url,
521
+ method: target.method || 'POST',
522
+ headers: target.headers || {},
523
+ body: target.body,
524
+ timeoutMs: constraints?.timeout_ms || 120000,
525
+ }, res);
526
+ try {
527
+ await gateway.postExecute(toolCall, {
528
+ http_status: proxyResult.httpStatus,
529
+ body: proxyResult.accumulatedBody,
530
+ headers: proxyResult.headers,
531
+ }, pre);
532
+ }
533
+ catch (postErr) {
534
+ console.error('[post-execute] Failed:', postErr instanceof Error ? postErr.message : postErr);
535
+ // Don't fail the response — it's already sent for streaming
536
+ }
537
+ }
538
+ catch (err) {
539
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
540
+ console.error('[stream-proxy] Execution error:', errorMessage);
541
+ if (!res.headersSent) {
542
+ (0, errors_1.sendError)(res, 502, errors_1.ErrorCode.TOOL_EXECUTION_ERROR, 'Stream proxy failed');
543
+ }
544
+ else {
545
+ try {
546
+ res.write(`event: error\ndata: ${JSON.stringify({ error: 'Stream proxy failed' })}\n\n`);
547
+ res.end();
548
+ }
549
+ catch {
550
+ // Client already disconnected
551
+ }
552
+ }
553
+ }
554
+ });
555
+ }
556
+ // POST /v1/tool/approve - Approve or deny a pending action
557
+ app.post('/v1/tool/approve', authMiddleware, rbacApprovalManage, async (req, res) => {
558
+ try {
559
+ const { approval_token, approved, approver_id, reason } = req.body;
560
+ if (!approval_token) {
561
+ (0, errors_1.sendError)(res, 400, errors_1.ErrorCode.VALIDATION_FAILED, 'approval_token is required', {
562
+ hint: 'Include the approval_token from the needs_approval response',
563
+ });
564
+ return;
565
+ }
566
+ // Use auth context for approver identity (not user-supplied approver_id)
567
+ const authCtx = req.auth;
568
+ const approverApiKeyId = authCtx?.api_key_id;
569
+ const effectiveApproverId = authCtx?.actor_id || approver_id || 'unknown';
570
+ const result = await gateway.processApproval(approval_token, effectiveApproverId, approved !== false, reason, approverApiKeyId);
571
+ if (result.success) {
572
+ res.json({ status: 'ok', ...result });
573
+ }
574
+ else {
575
+ (0, errors_1.sendError)(res, 400, errors_1.ErrorCode.VALIDATION_FAILED, result.error || 'Approval processing failed');
576
+ }
577
+ }
578
+ catch (err) {
579
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
580
+ console.error('[tool-approve] Error:', errorMessage);
581
+ (0, errors_1.sendError)(res, 500, errors_1.ErrorCode.INTERNAL_ERROR, 'Approval processing failed');
582
+ }
583
+ });
584
+ // GET /v1/tasks/:task_id/trace - Get full trace for a task
585
+ // Workspace isolation: non-admin callers only see events for their own workspace
586
+ app.get('/v1/tasks/:task_id/trace', authMiddleware, rbacTraceRead, (req, res) => {
587
+ const taskId = Array.isArray(req.params.task_id) ? req.params.task_id[0] : req.params.task_id;
588
+ const authCtx = req.auth;
589
+ let events = gateway.getTaskTrace(taskId);
590
+ // Scope to workspace unless caller has admin:full permission
591
+ if (authCtx?.workspace_id && authCtx.auth_method !== 'none') {
592
+ const { hasPermission } = require('../middleware/auth');
593
+ if (!hasPermission(authCtx.permissions, 'admin:full')) {
594
+ events = events.filter(e => !e.workspace_id || e.workspace_id === authCtx.workspace_id);
595
+ }
596
+ }
597
+ res.json({ task_id: taskId, events });
598
+ });
599
+ // GET /v1/policies/current - Get active policy configuration
600
+ app.get('/v1/policies/current', authMiddleware, rbacPolicyRead, (req, res) => {
601
+ res.json(gateway.getCurrentPolicy());
602
+ });
603
+ // POST /v1/policies/validate - Validate a policy configuration
604
+ app.post('/v1/policies/validate', authMiddleware, rbacPolicyWrite, (req, res) => {
605
+ const result = gateway.validatePolicy(req.body);
606
+ res.json(result);
607
+ });
608
+ // POST /v1/policies/reload - Hot-reload policy from disk
609
+ app.post('/v1/policies/reload', authMiddleware, rbacPolicyWrite, (req, res) => {
610
+ const result = gateway.reloadPolicy();
611
+ if (result.success) {
612
+ res.json({ status: 'ok', rule_count: result.ruleCount });
613
+ }
614
+ else {
615
+ (0, errors_1.sendError)(res, 500, errors_1.ErrorCode.INTERNAL_ERROR, result.error || 'Policy reload failed');
616
+ }
617
+ });
618
+ // POST /v1/usage/report - Report actual usage/cost from clients
619
+ app.post('/v1/usage/report', authMiddleware, rbacToolExecute, (req, res) => {
620
+ try {
621
+ const { tool_call_id, task_id, actual_cost_usd, usage, workspace_id, actor_id } = req.body;
622
+ if (!tool_call_id || !task_id) {
623
+ (0, errors_1.sendError)(res, 400, errors_1.ErrorCode.VALIDATION_FAILED, 'tool_call_id and task_id are required', {
624
+ hint: 'Both tool_call_id and task_id must be provided in the request body',
625
+ });
626
+ return;
627
+ }
628
+ gateway.reportUsage({
629
+ tool_call_id,
630
+ task_id,
631
+ workspace_id: workspace_id || req.workspace_id,
632
+ actor_id: actor_id || req.auth?.actor_id,
633
+ actual_cost_usd,
634
+ usage,
635
+ });
636
+ res.json({ status: 'ok' });
637
+ }
638
+ catch (err) {
639
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
640
+ console.error('[usage-report] Error:', errorMessage);
641
+ (0, errors_1.sendError)(res, 500, errors_1.ErrorCode.INTERNAL_ERROR, 'Usage reporting failed');
642
+ }
643
+ });
644
+ // GET /v1/approvals/pending - List pending approvals
645
+ // Workspace isolation: non-admin callers are forced to their own workspace
646
+ app.get('/v1/approvals/pending', authMiddleware, rbacApprovalManage, (req, res) => {
647
+ const authCtx = req.auth;
648
+ let workspaceId = typeof req.query.workspace_id === 'string' ? req.query.workspace_id : undefined;
649
+ // Enforce workspace scope for non-admin callers
650
+ if (authCtx?.workspace_id && authCtx.auth_method !== 'none') {
651
+ const { hasPermission } = require('../middleware/auth');
652
+ if (!hasPermission(authCtx.permissions, 'admin:full')) {
653
+ workspaceId = authCtx.workspace_id;
654
+ }
655
+ }
656
+ const approvals = gateway.getPendingApprovals(workspaceId);
657
+ res.json({ approvals });
658
+ });
659
+ // GET /v1/config/active - View active configuration (admin-only, secrets redacted)
660
+ app.get('/v1/config/active', authMiddleware, rbacAdmin, (req, res) => {
661
+ const redacted = JSON.parse(JSON.stringify(config));
662
+ // Deep redaction: recursively redact any key containing 'secret', 'password', or 'token'
663
+ function deepRedact(obj) {
664
+ if (!obj || typeof obj !== 'object')
665
+ return;
666
+ for (const key of Object.keys(obj)) {
667
+ const lowerKey = key.toLowerCase();
668
+ if (lowerKey.includes('secret') || lowerKey.includes('password') || lowerKey.includes('token')) {
669
+ obj[key] = '[REDACTED]';
670
+ }
671
+ else if (lowerKey === 'last_used_at' || lowerKey === 'created_at') {
672
+ obj[key] = null;
673
+ }
674
+ else if (typeof obj[key] === 'object' && obj[key] !== null) {
675
+ deepRedact(obj[key]);
676
+ }
677
+ }
678
+ }
679
+ deepRedact(redacted);
680
+ // Redact API key values (show only key prefix, strip metadata timestamps)
681
+ if (redacted.auth?.api_keys) {
682
+ const redactedKeys = {};
683
+ for (const key of Object.keys(redacted.auth.api_keys)) {
684
+ const entry = redacted.auth.api_keys[key];
685
+ if (typeof entry === 'object' && entry !== null) {
686
+ delete entry.last_used_at;
687
+ delete entry.created_at;
688
+ }
689
+ redactedKeys[key.slice(0, 8) + '...'] = entry;
690
+ }
691
+ redacted.auth.api_keys = redactedKeys;
692
+ }
693
+ res.json(redacted);
694
+ });
695
+ // Inject per-workspace config stores into gateway if provided externally
696
+ if (saasStores.rateLimitConfigStore || saasStores.budgetConfigStore) {
697
+ gateway.setStores({
698
+ rateLimitConfigStore: saasStores.rateLimitConfigStore,
699
+ budgetConfigStore: saasStores.budgetConfigStore,
700
+ });
701
+ }
702
+ // SaaS API routes (require session auth)
703
+ const saasRouter = (0, routes_2.createSaaSRouter)({
704
+ config,
705
+ userStore: saasStores.userStore,
706
+ workspaceStore: saasStores.workspaceStore,
707
+ workspaceMemberStore: saasStores.workspaceMemberStore,
708
+ userApiKeyStore: saasStores.userApiKeyStore,
709
+ sessionStore: saasStores.sessionStore,
710
+ gateway,
711
+ policyStore: saasStores.policyStore,
712
+ rateLimitConfigStore: saasStores.rateLimitConfigStore,
713
+ budgetConfigStore: saasStores.budgetConfigStore,
714
+ });
715
+ app.use('/api/v1', authMiddleware, saasRouter);
716
+ // Billing API routes (require session auth, only when Stripe is configured)
717
+ if (config.stripe?.secret_key) {
718
+ const billingStripeClient = new billing_1.StripeClient(config.stripe);
719
+ const billingRouter = (0, billing_1.createBillingRouter)({
720
+ stripeClient: billingStripeClient,
721
+ stripeConfig: config.stripe,
722
+ subscriptionStore: saasStores.subscriptionStore,
723
+ workspaceStore: saasStores.workspaceStore,
724
+ workspaceMemberStore: saasStores.workspaceMemberStore,
725
+ gateway,
726
+ });
727
+ app.use('/api/v1', billingRouter);
728
+ }
729
+ // MCP HTTP transport with OAuth 2.0 support.
730
+ // When mcp_oauth is enabled, uses the MCP SDK's OAuth router + bearer auth.
731
+ // When disabled, falls back to existing auth middleware + RBAC.
732
+ const mcpHandler = (0, http_transport_1.createMCPHttpHandler)(gateway);
733
+ if (config.mcp_oauth?.enabled) {
734
+ // Create OAuth stores — prefer Postgres-backed stores when available
735
+ const oauthClientsStore = saasStores.mcpOAuthClientsStore
736
+ || filePersistence?.oauthClientsStore
737
+ || new oauth_stores_1.OAuthClientsStore();
738
+ const authCodeStore = new oauth_stores_1.AuthCodeStore();
739
+ const tokenStore = saasStores.mcpOAuthTokenStore
740
+ || new oauth_stores_1.OAuthTokenStore(config.mcp_oauth.access_token_ttl || 3600, config.mcp_oauth.refresh_token_ttl || 30 * 24 * 3600);
741
+ // Create OAuth provider
742
+ const oauthProvider = new oauth_provider_1.PalarynOAuthProvider({
743
+ clientsStore: oauthClientsStore,
744
+ authCodeStore,
745
+ tokenStore,
746
+ userStore: saasStores.userStore,
747
+ workspaceStore: saasStores.workspaceStore,
748
+ workspaceMemberStore: saasStores.workspaceMemberStore,
749
+ sessionStore: saasStores.sessionStore,
750
+ rbacConfig: config.auth.rbac,
751
+ });
752
+ // Determine base URL for OAuth metadata
753
+ const baseUrl = config.mcp_oauth.base_url
754
+ || (isProduction ? 'https://app.palaryn.com' : `http://localhost:${config.port}`);
755
+ const issuerUrl = new URL(baseUrl);
756
+ const resourceServerUrl = new URL('/mcp', baseUrl);
757
+ const resourceMetadataUrl = (0, router_js_2.getOAuthProtectedResourceMetadataUrl)(resourceServerUrl);
758
+ // Mount OAuth routes at app root (/.well-known/*, /authorize, /token, /register, /revoke)
759
+ app.use((0, router_js_1.mcpAuthRouter)({
760
+ provider: oauthProvider,
761
+ issuerUrl,
762
+ resourceServerUrl,
763
+ scopesSupported: ['mcp:tools'],
764
+ }));
765
+ // Consent decision endpoint (POST /authorize/decision from the consent form)
766
+ app.post('/authorize/decision', express_1.default.urlencoded({ extended: false }), async (req, res) => {
767
+ // Verify the user is logged in via session cookie
768
+ const sessionId = req.cookies?.[session_2.SESSION_COOKIE_NAME];
769
+ if (!sessionId) {
770
+ res.status(401).send('Not authenticated');
771
+ return;
772
+ }
773
+ const session = saasStores.sessionStore.getById(sessionId);
774
+ if (!session) {
775
+ res.status(401).send('Session expired');
776
+ return;
777
+ }
778
+ const result = await oauthProvider.handleConsentDecision(req.body, session.user_id);
779
+ if ('error' in result) {
780
+ res.status(result.status).send(result.error);
781
+ return;
782
+ }
783
+ res.redirect(result.redirectUrl);
784
+ });
785
+ // Create hybrid verifier (OAuth + API keys)
786
+ const hybridVerifier = new auth_verifier_1.HybridTokenVerifier({
787
+ oauthProvider,
788
+ authConfig: config.auth,
789
+ userApiKeyStore: saasStores.userApiKeyStore,
790
+ });
791
+ // MCP with SDK's bearer auth (returns proper WWW-Authenticate on 401)
792
+ app.use('/mcp', (0, bearerAuth_js_1.requireBearerAuth)({
793
+ verifier: hybridVerifier,
794
+ resourceMetadataUrl,
795
+ }), mcpHandler.router);
796
+ }
797
+ else {
798
+ // Legacy: use existing auth middleware + RBAC
799
+ app.use('/mcp', authMiddleware, rbacToolExecute, mcpHandler.router);
800
+ }
801
+ // Frontend SPA serving (must be last, serves index.html for all unmatched routes)
802
+ if (config.frontend?.enabled) {
803
+ const frontendPath = path_1.default.resolve(config.frontend.build_path);
804
+ app.use(express_1.default.static(frontendPath));
805
+ app.get('/{*splat}', (req, res) => {
806
+ // Don't serve SPA for API routes or known backend routes
807
+ if (req.path.startsWith('/v1/') || req.path.startsWith('/api/') ||
808
+ req.path.startsWith('/auth/') || req.path.startsWith('/admin/') ||
809
+ req.path === '/health' || req.path === '/metrics') {
810
+ (0, errors_1.sendError)(res, 404, errors_1.ErrorCode.NOT_FOUND, 'Not found');
811
+ return;
812
+ }
813
+ res.sendFile(path_1.default.join(frontendPath, 'index.html'));
814
+ });
815
+ }
816
+ // Global error handler — catches JSON parse errors and other unhandled errors
817
+ // Must be registered after all routes (Express identifies error handlers by 4 params)
818
+ app.use((err, req, res, next) => {
819
+ // JSON parse errors from body-parser
820
+ if (err.type === 'entity.parse.failed' || (err instanceof SyntaxError && 'body' in err)) {
821
+ (0, errors_1.sendError)(res, 400, errors_1.ErrorCode.VALIDATION_FAILED, 'Invalid JSON in request body', {
822
+ hint: 'Ensure the request body is valid JSON',
823
+ });
824
+ return;
825
+ }
826
+ // Payload too large
827
+ if (err.type === 'entity.too.large') {
828
+ (0, errors_1.sendError)(res, 413, errors_1.ErrorCode.VALIDATION_FAILED, 'Request body too large', {
829
+ hint: 'Maximum request body size is 10MB',
830
+ });
831
+ return;
832
+ }
833
+ // All other errors — never expose stack traces
834
+ const message = isProduction ? 'Internal server error' : (err.message || 'Internal server error');
835
+ (0, errors_1.sendError)(res, err.status || 500, errors_1.ErrorCode.INTERNAL_ERROR, message);
836
+ });
837
+ // Session cleanup job: purge expired sessions every 5 minutes
838
+ const sessionCleanupInterval = setInterval(() => {
839
+ try {
840
+ saasStores.sessionStore.deleteExpired();
841
+ }
842
+ catch {
843
+ // Ignore cleanup errors
844
+ }
845
+ }, 300000);
846
+ sessionCleanupInterval.unref();
847
+ // Forward proxy server (optional, started alongside Express)
848
+ let proxyServer;
849
+ if (config.proxy?.enabled) {
850
+ proxyServer = (0, proxy_1.createForwardProxy)(gateway, config);
851
+ }
852
+ return { app, gateway, metrics, tracer, healthChecks, saasStores, proxyServer };
853
+ }
854
+ //# sourceMappingURL=app.js.map