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,698 @@
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
+ const supertest_1 = __importDefault(require("supertest"));
7
+ const express_1 = __importDefault(require("express"));
8
+ const admin_1 = require("../../src/admin");
9
+ const gateway_1 = require("../../src/server/gateway");
10
+ // ---------------------------------------------------------------------------
11
+ // Test config factory
12
+ // ---------------------------------------------------------------------------
13
+ function createTestConfig() {
14
+ return {
15
+ port: 0,
16
+ host: '127.0.0.1',
17
+ auth: {
18
+ enabled: false,
19
+ api_keys: {
20
+ 'test-key-001': { workspace_id: 'ws_test', description: 'Test key' },
21
+ 'test-key-002': { workspace_id: 'ws_other', description: 'Other key', revoked: true },
22
+ },
23
+ },
24
+ policy: {
25
+ pack_path: './policy-packs/dev_fast.yaml',
26
+ default_effect: 'DENY',
27
+ hot_reload: false,
28
+ },
29
+ dlp: {
30
+ enabled: true,
31
+ scan_args: true,
32
+ scan_output: true,
33
+ secrets_detection: true,
34
+ pii_detection: true,
35
+ default_redaction_method: 'mask',
36
+ },
37
+ budget: {
38
+ task_budget_usd: 2.0,
39
+ user_daily_budget_usd: 50.0,
40
+ user_monthly_budget_usd: 500.0,
41
+ workspace_daily_budget_usd: 200.0,
42
+ workspace_monthly_budget_usd: 5000.0,
43
+ max_steps_per_task: 50,
44
+ max_retries_per_call: 3,
45
+ max_wall_clock_ms: 300000,
46
+ },
47
+ audit: {
48
+ enabled: true,
49
+ log_dir: '',
50
+ console_output: false,
51
+ retention_days: 30,
52
+ },
53
+ executor: {
54
+ http: {
55
+ timeout_ms: 5000,
56
+ max_retries: 1,
57
+ backoff_base_ms: 100,
58
+ },
59
+ cache: {
60
+ enabled: true,
61
+ ttl_ms: 60000,
62
+ },
63
+ },
64
+ approval: {
65
+ enabled: true,
66
+ token_secret: 'test-admin-secret',
67
+ default_ttl_seconds: 3600,
68
+ },
69
+ rate_limit: {
70
+ enabled: false,
71
+ actor_max_per_window: 100,
72
+ workspace_max_per_window: 500,
73
+ window_ms: 60000,
74
+ },
75
+ };
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Setup: create an Express app with just the admin router
79
+ // ---------------------------------------------------------------------------
80
+ let app;
81
+ let gateway;
82
+ let config;
83
+ beforeEach(() => {
84
+ config = createTestConfig();
85
+ gateway = new gateway_1.Gateway(config);
86
+ app = (0, express_1.default)();
87
+ app.use(express_1.default.json());
88
+ const adminRouter = (0, admin_1.createAdminRouter)(gateway, config);
89
+ app.use('/admin', adminRouter);
90
+ });
91
+ afterEach(() => {
92
+ gateway.shutdown();
93
+ });
94
+ // ---------------------------------------------------------------------------
95
+ // Helper: create a pending approval via the Gateway
96
+ // ---------------------------------------------------------------------------
97
+ function createPendingApproval() {
98
+ const approvalManager = gateway.getApprovalManager();
99
+ const { approval } = approvalManager.createApproval({
100
+ tool_call_id: 'tc-test-001',
101
+ task_id: 'task-test-001',
102
+ workspace_id: 'ws_test',
103
+ actor: { id: 'user-1', type: 'user' },
104
+ tool: { name: 'http.request', version: '1.0', capability: 'write' },
105
+ args: { method: 'POST', url: 'https://api.example.com/data' },
106
+ source: { platform: 'test' },
107
+ timestamp: new Date().toISOString(),
108
+ }, 'admin', 'Requires admin approval');
109
+ return approval.approval_id;
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Helper: extract CSRF token from HTML response
113
+ // ---------------------------------------------------------------------------
114
+ function extractCSRFToken(html) {
115
+ const match = html.match(/name="_csrf"\s+value="([^"]+)"/);
116
+ return match ? match[1] : '';
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Helper: add some audit events via the Gateway
120
+ // ---------------------------------------------------------------------------
121
+ function seedAuditEvents() {
122
+ const auditLogger = gateway.getAuditLogger();
123
+ auditLogger.log({
124
+ event_type: 'TOOL_CALL_RECEIVED',
125
+ tool_call_id: 'tc-1',
126
+ task_id: 'task-1',
127
+ workspace_id: 'ws_test',
128
+ actor_id: 'user-1',
129
+ tool_name: 'http.request',
130
+ metadata: {},
131
+ });
132
+ auditLogger.log({
133
+ event_type: 'POLICY_DECIDED',
134
+ tool_call_id: 'tc-1',
135
+ task_id: 'task-1',
136
+ workspace_id: 'ws_test',
137
+ actor_id: 'user-1',
138
+ tool_name: 'http.request',
139
+ metadata: { decision: 'allow' },
140
+ });
141
+ auditLogger.log({
142
+ event_type: 'TOOL_EXECUTED',
143
+ tool_call_id: 'tc-1',
144
+ task_id: 'task-1',
145
+ workspace_id: 'ws_test',
146
+ actor_id: 'user-1',
147
+ tool_name: 'http.request',
148
+ metadata: { status: 'ok', duration_ms: 42 },
149
+ });
150
+ }
151
+ // ===========================================================================
152
+ // Dashboard
153
+ // ===========================================================================
154
+ describe('Admin Dashboard', () => {
155
+ test('GET /admin returns 200 with HTML containing "Palaryn Admin"', async () => {
156
+ const res = await (0, supertest_1.default)(app).get('/admin');
157
+ expect(res.status).toBe(200);
158
+ expect(res.text).toContain('Palaryn Admin');
159
+ expect(res.text).toContain('Dashboard');
160
+ expect(res.text).toContain('Total Requests');
161
+ });
162
+ test('GET /admin shows correct stats', async () => {
163
+ seedAuditEvents();
164
+ const res = await (0, supertest_1.default)(app).get('/admin');
165
+ expect(res.status).toBe(200);
166
+ expect(res.text).toContain('Total Requests');
167
+ // 1 TOOL_CALL_RECEIVED event
168
+ expect(res.text).toContain('>1<');
169
+ });
170
+ test('GET /admin shows recent events table when events exist', async () => {
171
+ seedAuditEvents();
172
+ const res = await (0, supertest_1.default)(app).get('/admin');
173
+ expect(res.status).toBe(200);
174
+ expect(res.text).toContain('Recent Events');
175
+ expect(res.text).toContain('TOOL_EXECUTED');
176
+ expect(res.text).toContain('http.request');
177
+ });
178
+ });
179
+ // ===========================================================================
180
+ // Approvals
181
+ // ===========================================================================
182
+ describe('Admin Approvals', () => {
183
+ test('GET /admin/approvals returns 200 with approvals table', async () => {
184
+ const res = await (0, supertest_1.default)(app).get('/admin/approvals');
185
+ expect(res.status).toBe(200);
186
+ expect(res.text).toContain('Palaryn Admin');
187
+ expect(res.text).toContain('Approvals');
188
+ });
189
+ test('GET /admin/approvals shows pending approvals', async () => {
190
+ createPendingApproval();
191
+ const res = await (0, supertest_1.default)(app).get('/admin/approvals');
192
+ expect(res.status).toBe(200);
193
+ expect(res.text).toContain('pending');
194
+ expect(res.text).toContain('http.request');
195
+ expect(res.text).toContain('Approve');
196
+ expect(res.text).toContain('Deny');
197
+ });
198
+ test('GET /admin/approvals shows "No pending approvals" when empty', async () => {
199
+ const res = await (0, supertest_1.default)(app).get('/admin/approvals');
200
+ expect(res.status).toBe(200);
201
+ expect(res.text).toContain('No pending approvals');
202
+ });
203
+ test('POST /admin/approvals/:id/approve returns redirect', async () => {
204
+ const approvalId = createPendingApproval();
205
+ const getRes = await (0, supertest_1.default)(app).get('/admin/approvals');
206
+ const csrfToken = extractCSRFToken(getRes.text);
207
+ const res = await (0, supertest_1.default)(app)
208
+ .post(`/admin/approvals/${approvalId}/approve`)
209
+ .type('form')
210
+ .send({ _csrf: csrfToken });
211
+ expect(res.status).toBe(302);
212
+ expect(res.headers.location).toBe('/admin/approvals');
213
+ });
214
+ test('POST /admin/approvals/:id/approve resolves the approval', async () => {
215
+ const approvalId = createPendingApproval();
216
+ const getRes = await (0, supertest_1.default)(app).get('/admin/approvals');
217
+ const csrfToken = extractCSRFToken(getRes.text);
218
+ await (0, supertest_1.default)(app)
219
+ .post(`/admin/approvals/${approvalId}/approve`)
220
+ .type('form')
221
+ .send({ _csrf: csrfToken });
222
+ const approval = gateway.getApprovalManager().getApproval(approvalId);
223
+ expect(approval).not.toBeNull();
224
+ expect(approval.status).toBe('approved');
225
+ });
226
+ test('POST /admin/approvals/:id/deny returns redirect', async () => {
227
+ const approvalId = createPendingApproval();
228
+ const getRes = await (0, supertest_1.default)(app).get('/admin/approvals');
229
+ const csrfToken = extractCSRFToken(getRes.text);
230
+ const res = await (0, supertest_1.default)(app)
231
+ .post(`/admin/approvals/${approvalId}/deny`)
232
+ .type('form')
233
+ .send({ _csrf: csrfToken });
234
+ expect(res.status).toBe(302);
235
+ expect(res.headers.location).toBe('/admin/approvals');
236
+ });
237
+ test('POST /admin/approvals/:id/deny resolves the approval as denied', async () => {
238
+ const approvalId = createPendingApproval();
239
+ const getRes = await (0, supertest_1.default)(app).get('/admin/approvals');
240
+ const csrfToken = extractCSRFToken(getRes.text);
241
+ await (0, supertest_1.default)(app)
242
+ .post(`/admin/approvals/${approvalId}/deny`)
243
+ .type('form')
244
+ .send({ _csrf: csrfToken });
245
+ const approval = gateway.getApprovalManager().getApproval(approvalId);
246
+ expect(approval).not.toBeNull();
247
+ expect(approval.status).toBe('denied');
248
+ });
249
+ test('POST /admin/approvals/:id/approve with invalid id still redirects', async () => {
250
+ // Create a real approval so the page renders forms with CSRF tokens
251
+ createPendingApproval();
252
+ const getRes = await (0, supertest_1.default)(app).get('/admin/approvals');
253
+ const csrfToken = extractCSRFToken(getRes.text);
254
+ const res = await (0, supertest_1.default)(app)
255
+ .post('/admin/approvals/nonexistent/approve')
256
+ .type('form')
257
+ .send({ _csrf: csrfToken });
258
+ expect(res.status).toBe(302);
259
+ expect(res.headers.location).toBe('/admin/approvals');
260
+ });
261
+ test('POST /admin/approvals/:id/approve without CSRF token returns 403', async () => {
262
+ const approvalId = createPendingApproval();
263
+ const res = await (0, supertest_1.default)(app)
264
+ .post(`/admin/approvals/${approvalId}/approve`)
265
+ .send('');
266
+ expect(res.status).toBe(403);
267
+ expect(res.text).toContain('Invalid or missing CSRF token');
268
+ });
269
+ });
270
+ // ===========================================================================
271
+ // Policies
272
+ // ===========================================================================
273
+ describe('Admin Policies', () => {
274
+ test('GET /admin/policies returns 200 with policy content', async () => {
275
+ const res = await (0, supertest_1.default)(app).get('/admin/policies');
276
+ expect(res.status).toBe(200);
277
+ expect(res.text).toContain('Palaryn Admin');
278
+ expect(res.text).toContain('Policies');
279
+ expect(res.text).toContain('Active Policy Pack');
280
+ expect(res.text).toContain('Validate Policy');
281
+ });
282
+ test('GET /admin/policies shows the loaded policy pack name', async () => {
283
+ const res = await (0, supertest_1.default)(app).get('/admin/policies');
284
+ expect(res.status).toBe(200);
285
+ // The dev_fast.yaml policy pack has a name field
286
+ const policy = gateway.getCurrentPolicy();
287
+ expect(res.text).toContain(policy.name);
288
+ });
289
+ test('POST /admin/policies/validate returns result for valid policy', async () => {
290
+ const getRes = await (0, supertest_1.default)(app).get('/admin/policies');
291
+ const csrfToken = extractCSRFToken(getRes.text);
292
+ const validPolicy = JSON.stringify({
293
+ name: 'test-policy',
294
+ version: '1.0',
295
+ rules: [
296
+ {
297
+ name: 'allow-all',
298
+ effect: 'ALLOW',
299
+ priority: 100,
300
+ conditions: {},
301
+ },
302
+ ],
303
+ });
304
+ const res = await (0, supertest_1.default)(app)
305
+ .post('/admin/policies/validate')
306
+ .type('form')
307
+ .send({ _csrf: csrfToken, policy_json: validPolicy });
308
+ expect(res.status).toBe(200);
309
+ expect(res.text).toContain('Policy is valid');
310
+ });
311
+ test('POST /admin/policies/validate returns errors for invalid policy', async () => {
312
+ const getRes = await (0, supertest_1.default)(app).get('/admin/policies');
313
+ const csrfToken = extractCSRFToken(getRes.text);
314
+ const invalidPolicy = JSON.stringify({
315
+ name: '',
316
+ version: '',
317
+ rules: [],
318
+ });
319
+ const res = await (0, supertest_1.default)(app)
320
+ .post('/admin/policies/validate')
321
+ .type('form')
322
+ .send({ _csrf: csrfToken, policy_json: invalidPolicy });
323
+ expect(res.status).toBe(200);
324
+ expect(res.text).toContain('Validation errors');
325
+ });
326
+ test('POST /admin/policies/validate handles malformed JSON', async () => {
327
+ const getRes = await (0, supertest_1.default)(app).get('/admin/policies');
328
+ const csrfToken = extractCSRFToken(getRes.text);
329
+ const res = await (0, supertest_1.default)(app)
330
+ .post('/admin/policies/validate')
331
+ .type('form')
332
+ .send({ _csrf: csrfToken, policy_json: '{not valid json' });
333
+ expect(res.status).toBe(200);
334
+ expect(res.text).toContain('Validation errors');
335
+ });
336
+ test('POST /admin/policies/validate without CSRF token returns 403', async () => {
337
+ const res = await (0, supertest_1.default)(app)
338
+ .post('/admin/policies/validate')
339
+ .type('form')
340
+ .send({ policy_json: '{}' });
341
+ expect(res.status).toBe(403);
342
+ expect(res.text).toContain('Invalid or missing CSRF token');
343
+ });
344
+ test('GET /admin/policies shows reload button', async () => {
345
+ const res = await (0, supertest_1.default)(app).get('/admin/policies');
346
+ expect(res.status).toBe(200);
347
+ expect(res.text).toContain('Reload from Disk');
348
+ expect(res.text).toContain('/admin/policies/reload');
349
+ });
350
+ test('POST /admin/policies/reload reloads policy and redirects', async () => {
351
+ const getRes = await (0, supertest_1.default)(app).get('/admin/policies');
352
+ const csrfToken = extractCSRFToken(getRes.text);
353
+ const res = await (0, supertest_1.default)(app)
354
+ .post('/admin/policies/reload')
355
+ .type('form')
356
+ .send({ _csrf: csrfToken });
357
+ expect(res.status).toBe(302);
358
+ expect(res.headers.location).toContain('/admin/policies?reloaded=1');
359
+ expect(res.headers.location).toContain('rules%20active');
360
+ });
361
+ test('POST /admin/policies/reload without CSRF token returns 403', async () => {
362
+ const res = await (0, supertest_1.default)(app)
363
+ .post('/admin/policies/reload')
364
+ .type('form')
365
+ .send({});
366
+ expect(res.status).toBe(403);
367
+ expect(res.text).toContain('Invalid or missing CSRF token');
368
+ });
369
+ });
370
+ // ===========================================================================
371
+ // Budgets
372
+ // ===========================================================================
373
+ describe('Admin Budgets', () => {
374
+ test('GET /admin/budgets returns 200', async () => {
375
+ const res = await (0, supertest_1.default)(app).get('/admin/budgets');
376
+ expect(res.status).toBe(200);
377
+ expect(res.text).toContain('Palaryn Admin');
378
+ expect(res.text).toContain('Budgets');
379
+ });
380
+ test('GET /admin/budgets shows configured budget limits', async () => {
381
+ const res = await (0, supertest_1.default)(app).get('/admin/budgets');
382
+ expect(res.status).toBe(200);
383
+ expect(res.text).toContain('Task Budget');
384
+ expect(res.text).toContain('User Daily Budget');
385
+ expect(res.text).toContain('User Monthly Budget');
386
+ expect(res.text).toContain('Workspace Daily Budget');
387
+ expect(res.text).toContain('Workspace Monthly Budget');
388
+ });
389
+ test('GET /admin/budgets shows budget limit values', async () => {
390
+ const res = await (0, supertest_1.default)(app).get('/admin/budgets');
391
+ expect(res.status).toBe(200);
392
+ expect(res.text).toContain('$2.00'); // task_budget_usd
393
+ expect(res.text).toContain('$50.00'); // user_daily_budget_usd
394
+ expect(res.text).toContain('$500.00'); // user_monthly_budget_usd
395
+ });
396
+ });
397
+ // ===========================================================================
398
+ // Traces
399
+ // ===========================================================================
400
+ describe('Admin Traces', () => {
401
+ test('GET /admin/traces returns 200', async () => {
402
+ const res = await (0, supertest_1.default)(app).get('/admin/traces');
403
+ expect(res.status).toBe(200);
404
+ expect(res.text).toContain('Palaryn Admin');
405
+ expect(res.text).toContain('Traces');
406
+ expect(res.text).toContain('Search Traces');
407
+ });
408
+ test('GET /admin/traces shows "No trace data" when empty', async () => {
409
+ const res = await (0, supertest_1.default)(app).get('/admin/traces');
410
+ expect(res.status).toBe(200);
411
+ expect(res.text).toContain('No trace data available');
412
+ });
413
+ test('GET /admin/traces shows task IDs when events exist', async () => {
414
+ seedAuditEvents();
415
+ const res = await (0, supertest_1.default)(app).get('/admin/traces');
416
+ expect(res.status).toBe(200);
417
+ expect(res.text).toContain('task-1');
418
+ });
419
+ test('GET /admin/traces with task_id query redirects to detail', async () => {
420
+ const res = await (0, supertest_1.default)(app).get('/admin/traces?task_id=task-1');
421
+ expect(res.status).toBe(302);
422
+ expect(res.headers.location).toBe('/admin/traces/task-1');
423
+ });
424
+ test('GET /admin/traces/:task_id returns trace detail', async () => {
425
+ seedAuditEvents();
426
+ const res = await (0, supertest_1.default)(app).get('/admin/traces/task-1');
427
+ expect(res.status).toBe(200);
428
+ expect(res.text).toContain('task-1');
429
+ expect(res.text).toContain('TOOL_CALL_RECEIVED');
430
+ expect(res.text).toContain('POLICY_DECIDED');
431
+ expect(res.text).toContain('TOOL_EXECUTED');
432
+ });
433
+ test('GET /admin/traces/:task_id shows "No events found" for unknown task', async () => {
434
+ const res = await (0, supertest_1.default)(app).get('/admin/traces/nonexistent');
435
+ expect(res.status).toBe(200);
436
+ expect(res.text).toContain('No events found for this task');
437
+ });
438
+ test('GET /admin/traces/:task_id includes back link', async () => {
439
+ const res = await (0, supertest_1.default)(app).get('/admin/traces/task-1');
440
+ expect(res.status).toBe(200);
441
+ expect(res.text).toContain('Back to Traces');
442
+ expect(res.text).toContain('/admin/traces');
443
+ });
444
+ });
445
+ // ===========================================================================
446
+ // API Keys
447
+ // ===========================================================================
448
+ describe('Admin API Keys', () => {
449
+ test('GET /admin/api-keys returns 200', async () => {
450
+ const res = await (0, supertest_1.default)(app).get('/admin/api-keys');
451
+ expect(res.status).toBe(200);
452
+ expect(res.text).toContain('Palaryn Admin');
453
+ expect(res.text).toContain('API Keys');
454
+ });
455
+ test('GET /admin/api-keys shows existing keys', async () => {
456
+ const res = await (0, supertest_1.default)(app).get('/admin/api-keys');
457
+ expect(res.status).toBe(200);
458
+ expect(res.text).toContain('ws_test');
459
+ expect(res.text).toContain('Test key');
460
+ expect(res.text).toContain('ws_other');
461
+ });
462
+ test('GET /admin/api-keys shows revoked status', async () => {
463
+ const res = await (0, supertest_1.default)(app).get('/admin/api-keys');
464
+ expect(res.status).toBe(200);
465
+ expect(res.text).toContain('revoked');
466
+ expect(res.text).toContain('active');
467
+ });
468
+ test('GET /admin/api-keys shows create form', async () => {
469
+ const res = await (0, supertest_1.default)(app).get('/admin/api-keys');
470
+ expect(res.status).toBe(200);
471
+ expect(res.text).toContain('Create New API Key');
472
+ expect(res.text).toContain('Workspace ID');
473
+ });
474
+ test('POST /admin/api-keys creates a key and redirects with created key', async () => {
475
+ const getRes = await (0, supertest_1.default)(app).get('/admin/api-keys');
476
+ const csrfToken = extractCSRFToken(getRes.text);
477
+ const keysBefore = Object.keys(config.auth.api_keys).length;
478
+ const res = await (0, supertest_1.default)(app)
479
+ .post('/admin/api-keys')
480
+ .type('form')
481
+ .send({ _csrf: csrfToken, workspace_id: 'ws_new', description: 'New test key' });
482
+ expect(res.status).toBe(302);
483
+ expect(res.headers.location).toMatch(/^\/admin\/api-keys\?created=key-/);
484
+ const keysAfter = Object.keys(config.auth.api_keys).length;
485
+ expect(keysAfter).toBe(keysBefore + 1);
486
+ // Find the new key
487
+ const newKey = Object.entries(config.auth.api_keys).find(([, v]) => v.workspace_id === 'ws_new' && v.description === 'New test key');
488
+ expect(newKey).toBeDefined();
489
+ expect(newKey[0]).toMatch(/^key-/);
490
+ expect(newKey[1].created_at).toBeDefined();
491
+ });
492
+ test('GET /admin/api-keys?created=key-xxx shows the new key alert', async () => {
493
+ const res = await (0, supertest_1.default)(app).get('/admin/api-keys?created=key-abc123test');
494
+ expect(res.status).toBe(200);
495
+ expect(res.text).toContain('key-abc123test');
496
+ expect(res.text).toContain('Copy this key now');
497
+ });
498
+ test('POST /admin/api-keys/:key/revoke revokes and redirects', async () => {
499
+ const getRes = await (0, supertest_1.default)(app).get('/admin/api-keys');
500
+ const csrfToken = extractCSRFToken(getRes.text);
501
+ const res = await (0, supertest_1.default)(app)
502
+ .post('/admin/api-keys/test-key-001/revoke')
503
+ .type('form')
504
+ .send({ _csrf: csrfToken });
505
+ expect(res.status).toBe(302);
506
+ expect(res.headers.location).toBe('/admin/api-keys');
507
+ expect(config.auth.api_keys['test-key-001'].revoked).toBe(true);
508
+ });
509
+ test('POST /admin/api-keys/:key/revoke with unknown key still redirects', async () => {
510
+ const getRes = await (0, supertest_1.default)(app).get('/admin/api-keys');
511
+ const csrfToken = extractCSRFToken(getRes.text);
512
+ const res = await (0, supertest_1.default)(app)
513
+ .post('/admin/api-keys/nonexistent-key/revoke')
514
+ .type('form')
515
+ .send({ _csrf: csrfToken });
516
+ expect(res.status).toBe(302);
517
+ expect(res.headers.location).toBe('/admin/api-keys');
518
+ });
519
+ test('POST /admin/api-keys without CSRF token returns 403', async () => {
520
+ const res = await (0, supertest_1.default)(app)
521
+ .post('/admin/api-keys')
522
+ .type('form')
523
+ .send({ workspace_id: 'ws_new', description: 'New test key' });
524
+ expect(res.status).toBe(403);
525
+ expect(res.text).toContain('Invalid or missing CSRF token');
526
+ });
527
+ });
528
+ // ===========================================================================
529
+ // Error Boundaries
530
+ // ===========================================================================
531
+ describe('Admin Error Boundaries', () => {
532
+ test('GET /admin returns 500 with error page when gateway throws', async () => {
533
+ // Force getAuditLogger to throw
534
+ const origMethod = gateway.getAuditLogger;
535
+ gateway.getAuditLogger = () => { throw new Error('Audit logger broken'); };
536
+ const res = await (0, supertest_1.default)(app).get('/admin');
537
+ expect(res.status).toBe(500);
538
+ expect(res.text).toContain('Failed to load dashboard');
539
+ expect(res.text).toContain('Audit logger broken');
540
+ // Restore
541
+ gateway.getAuditLogger = origMethod;
542
+ });
543
+ test('GET /admin/approvals returns 500 with error page when gateway throws', async () => {
544
+ const origMethod = gateway.getPendingApprovals;
545
+ gateway.getPendingApprovals = () => { throw new Error('Approvals broken'); };
546
+ const res = await (0, supertest_1.default)(app).get('/admin/approvals');
547
+ expect(res.status).toBe(500);
548
+ expect(res.text).toContain('Failed to load approvals');
549
+ expect(res.text).toContain('Approvals broken');
550
+ gateway.getPendingApprovals = origMethod;
551
+ });
552
+ test('GET /admin/policies returns 500 with error page when gateway throws', async () => {
553
+ const origMethod = gateway.getCurrentPolicy;
554
+ gateway.getCurrentPolicy = () => { throw new Error('Policy broken'); };
555
+ const res = await (0, supertest_1.default)(app).get('/admin/policies');
556
+ expect(res.status).toBe(500);
557
+ expect(res.text).toContain('Failed to load policies');
558
+ expect(res.text).toContain('Policy broken');
559
+ gateway.getCurrentPolicy = origMethod;
560
+ });
561
+ test('GET /admin/budgets returns 500 with error page when gateway throws', async () => {
562
+ const origMethod = gateway.getBudgetManager;
563
+ gateway.getBudgetManager = () => { throw new Error('Budget broken'); };
564
+ const res = await (0, supertest_1.default)(app).get('/admin/budgets');
565
+ expect(res.status).toBe(500);
566
+ expect(res.text).toContain('Failed to load budgets');
567
+ expect(res.text).toContain('Budget broken');
568
+ gateway.getBudgetManager = origMethod;
569
+ });
570
+ test('GET /admin/traces returns 500 with error page when gateway throws', async () => {
571
+ const origMethod = gateway.getAuditLogger;
572
+ gateway.getAuditLogger = () => { throw new Error('Traces broken'); };
573
+ const res = await (0, supertest_1.default)(app).get('/admin/traces');
574
+ expect(res.status).toBe(500);
575
+ expect(res.text).toContain('Failed to load traces');
576
+ expect(res.text).toContain('Traces broken');
577
+ gateway.getAuditLogger = origMethod;
578
+ });
579
+ test('GET /admin/traces/:task_id returns 500 with error page when gateway throws', async () => {
580
+ const origMethod = gateway.getTaskTrace;
581
+ gateway.getTaskTrace = () => { throw new Error('Trace detail broken'); };
582
+ const res = await (0, supertest_1.default)(app).get('/admin/traces/task-1');
583
+ expect(res.status).toBe(500);
584
+ expect(res.text).toContain('Failed to load trace detail');
585
+ expect(res.text).toContain('Trace detail broken');
586
+ gateway.getTaskTrace = origMethod;
587
+ });
588
+ });
589
+ // ===========================================================================
590
+ // Traces Pagination
591
+ // ===========================================================================
592
+ describe('Admin Traces Pagination', () => {
593
+ function seedManyTraces(count) {
594
+ const auditLogger = gateway.getAuditLogger();
595
+ for (let i = 0; i < count; i++) {
596
+ auditLogger.log({
597
+ event_type: 'TOOL_CALL_RECEIVED',
598
+ tool_call_id: `tc-page-${i}`,
599
+ task_id: `task-page-${i}`,
600
+ workspace_id: 'ws_test',
601
+ actor_id: 'user-1',
602
+ tool_name: 'http.request',
603
+ metadata: {},
604
+ });
605
+ }
606
+ }
607
+ test('GET /admin/traces with pagination params returns paginated results', async () => {
608
+ seedManyTraces(5);
609
+ const res = await (0, supertest_1.default)(app).get('/admin/traces?page=1&limit=2');
610
+ expect(res.status).toBe(200);
611
+ // Should show "Page 1 of 3"
612
+ expect(res.text).toContain('Page 1 of 3');
613
+ expect(res.text).toContain('(5 total)');
614
+ expect(res.text).toContain('Next');
615
+ });
616
+ test('GET /admin/traces page 2 shows next set of tasks', async () => {
617
+ seedManyTraces(5);
618
+ const res = await (0, supertest_1.default)(app).get('/admin/traces?page=2&limit=2');
619
+ expect(res.status).toBe(200);
620
+ expect(res.text).toContain('Page 2 of 3');
621
+ expect(res.text).toContain('Previous');
622
+ expect(res.text).toContain('Next');
623
+ });
624
+ test('GET /admin/traces last page disables Next button', async () => {
625
+ seedManyTraces(5);
626
+ const res = await (0, supertest_1.default)(app).get('/admin/traces?page=3&limit=2');
627
+ expect(res.status).toBe(200);
628
+ expect(res.text).toContain('Page 3 of 3');
629
+ expect(res.text).toContain('Previous');
630
+ });
631
+ test('GET /admin/traces defaults to page=1 limit=50', async () => {
632
+ seedManyTraces(3);
633
+ const res = await (0, supertest_1.default)(app).get('/admin/traces');
634
+ expect(res.status).toBe(200);
635
+ // Should show all 3 tasks on one page (no pagination controls since totalPages=1)
636
+ expect(res.text).toContain('task-page-0');
637
+ expect(res.text).toContain('task-page-1');
638
+ expect(res.text).toContain('task-page-2');
639
+ });
640
+ test('GET /admin/traces with no data shows no pagination', async () => {
641
+ const res = await (0, supertest_1.default)(app).get('/admin/traces?page=1&limit=2');
642
+ expect(res.status).toBe(200);
643
+ expect(res.text).toContain('No trace data available');
644
+ expect(res.text).not.toContain('Page 1 of');
645
+ });
646
+ });
647
+ // ===========================================================================
648
+ // Budget Spending
649
+ // ===========================================================================
650
+ describe('Admin Budget Spending', () => {
651
+ test('GET /admin/budgets shows actual spending after recording costs', async () => {
652
+ // Record some spending via the budget manager
653
+ const budgetManager = gateway.getBudgetManager();
654
+ const toolCall = {
655
+ tool_call_id: 'tc-spend-1',
656
+ task_id: 'task-spend-1',
657
+ workspace_id: 'ws_test',
658
+ actor: { id: 'user-spend', type: 'user' },
659
+ tool: { name: 'http.request', version: '1.0', capability: 'read' },
660
+ args: { method: 'GET', url: 'https://example.com' },
661
+ source: { platform: 'test' },
662
+ timestamp: new Date().toISOString(),
663
+ };
664
+ // Record a cost to populate spending
665
+ budgetManager.check(toolCall); // creates task state
666
+ budgetManager.record(toolCall, 0.05);
667
+ const res = await (0, supertest_1.default)(app).get('/admin/budgets');
668
+ expect(res.status).toBe(200);
669
+ // Should show the actual spent amount, not $0.0000
670
+ expect(res.text).toContain('$0.0500');
671
+ });
672
+ });
673
+ // ===========================================================================
674
+ // Navigation
675
+ // ===========================================================================
676
+ describe('Admin Navigation', () => {
677
+ test('All pages include sidebar navigation', async () => {
678
+ const pages = ['/admin', '/admin/approvals', '/admin/policies', '/admin/budgets', '/admin/traces', '/admin/api-keys'];
679
+ for (const page of pages) {
680
+ const res = await (0, supertest_1.default)(app).get(page);
681
+ expect(res.status).toBe(200);
682
+ expect(res.text).toContain('class="sidebar"');
683
+ expect(res.text).toContain('href="/admin"');
684
+ expect(res.text).toContain('href="/admin/approvals"');
685
+ expect(res.text).toContain('href="/admin/policies"');
686
+ expect(res.text).toContain('href="/admin/budgets"');
687
+ expect(res.text).toContain('href="/admin/traces"');
688
+ expect(res.text).toContain('href="/admin/api-keys"');
689
+ }
690
+ });
691
+ test('Active nav is highlighted on each page', async () => {
692
+ const res = await (0, supertest_1.default)(app).get('/admin');
693
+ expect(res.status).toBe(200);
694
+ // The Dashboard link should have the active class
695
+ expect(res.text).toContain('href="/admin" class="active"');
696
+ });
697
+ });
698
+ //# sourceMappingURL=admin.test.js.map