@useatlas/create 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/README.md +231 -0
  2. package/index.ts +829 -0
  3. package/package.json +38 -0
  4. package/templates/docker/.env.example +67 -0
  5. package/templates/docker/Dockerfile +52 -0
  6. package/templates/docker/bin/__tests__/benchmark.test.ts +598 -0
  7. package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +171 -0
  8. package/templates/docker/bin/__tests__/eval.test.ts +434 -0
  9. package/templates/docker/bin/__tests__/matview-partition.test.ts +615 -0
  10. package/templates/docker/bin/__tests__/multi-source.test.ts +113 -0
  11. package/templates/docker/bin/__tests__/plugin-cli.test.ts +322 -0
  12. package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +608 -0
  13. package/templates/docker/bin/__tests__/query.test.ts +240 -0
  14. package/templates/docker/bin/__tests__/schema-drift.test.ts +542 -0
  15. package/templates/docker/bin/__tests__/view-yaml-generation.test.ts +146 -0
  16. package/templates/docker/bin/atlas.ts +5044 -0
  17. package/templates/docker/bin/benchmark.ts +695 -0
  18. package/templates/docker/bin/enrich.ts +559 -0
  19. package/templates/docker/bin/eval.ts +770 -0
  20. package/templates/docker/bin/smoke.ts +438 -0
  21. package/templates/docker/data/.gitkeep +0 -0
  22. package/templates/docker/data/cybersec.sql +1961 -0
  23. package/templates/docker/data/demo-semantic/catalog.yml +40 -0
  24. package/templates/docker/data/demo-semantic/entities/accounts.yml +170 -0
  25. package/templates/docker/data/demo-semantic/entities/companies.yml +207 -0
  26. package/templates/docker/data/demo-semantic/entities/people.yml +145 -0
  27. package/templates/docker/data/demo-semantic/glossary.yml +22 -0
  28. package/templates/docker/data/demo-semantic/metrics/accounts.yml +38 -0
  29. package/templates/docker/data/demo-semantic/metrics/companies.yml +89 -0
  30. package/templates/docker/data/demo.sql +373 -0
  31. package/templates/docker/data/ecommerce.sql +1690 -0
  32. package/templates/docker/data/init-demo-db.sql +8 -0
  33. package/templates/docker/docker-compose.yml +34 -0
  34. package/templates/docker/docs/deploy.md +390 -0
  35. package/templates/docker/eslint.config.mjs +18 -0
  36. package/templates/docker/gitignore +5 -0
  37. package/templates/docker/next.config.ts +9 -0
  38. package/templates/docker/package.json +59 -0
  39. package/templates/docker/postcss.config.mjs +8 -0
  40. package/templates/docker/public/.gitkeep +0 -0
  41. package/templates/docker/public/favicon.svg +4 -0
  42. package/templates/docker/railway.json +13 -0
  43. package/templates/docker/render.yaml +34 -0
  44. package/templates/docker/semantic/catalog.yml +5 -0
  45. package/templates/docker/semantic/entities/.gitkeep +0 -0
  46. package/templates/docker/semantic/glossary.yml +6 -0
  47. package/templates/docker/semantic/metrics/.gitkeep +0 -0
  48. package/templates/docker/sidecar/Dockerfile +28 -0
  49. package/templates/docker/sidecar/railway.json +14 -0
  50. package/templates/docker/sidecar/server.ts +188 -0
  51. package/templates/docker/src/api/__tests__/actions.test.ts +683 -0
  52. package/templates/docker/src/api/__tests__/admin.test.ts +820 -0
  53. package/templates/docker/src/api/__tests__/auth.test.ts +165 -0
  54. package/templates/docker/src/api/__tests__/chat.test.ts +376 -0
  55. package/templates/docker/src/api/__tests__/conversations.test.ts +555 -0
  56. package/templates/docker/src/api/__tests__/cors.test.ts +135 -0
  57. package/templates/docker/src/api/__tests__/health-plugin.test.ts +169 -0
  58. package/templates/docker/src/api/__tests__/health.test.ts +261 -0
  59. package/templates/docker/src/api/__tests__/query.test.ts +891 -0
  60. package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +601 -0
  61. package/templates/docker/src/api/__tests__/slack.test.ts +847 -0
  62. package/templates/docker/src/api/index.ts +117 -0
  63. package/templates/docker/src/api/routes/actions.ts +274 -0
  64. package/templates/docker/src/api/routes/admin.ts +757 -0
  65. package/templates/docker/src/api/routes/auth.ts +48 -0
  66. package/templates/docker/src/api/routes/chat.ts +465 -0
  67. package/templates/docker/src/api/routes/conversations.ts +266 -0
  68. package/templates/docker/src/api/routes/health.ts +287 -0
  69. package/templates/docker/src/api/routes/openapi.ts +390 -0
  70. package/templates/docker/src/api/routes/query.ts +318 -0
  71. package/templates/docker/src/api/routes/scheduled-tasks.ts +467 -0
  72. package/templates/docker/src/api/routes/slack.ts +611 -0
  73. package/templates/docker/src/api/server.ts +226 -0
  74. package/templates/docker/src/app/api/[...route]/route.ts +33 -0
  75. package/templates/docker/src/app/error.tsx +24 -0
  76. package/templates/docker/src/app/globals.css +126 -0
  77. package/templates/docker/src/app/layout.tsx +19 -0
  78. package/templates/docker/src/app/page.tsx +14 -0
  79. package/templates/docker/src/global.d.ts +1 -0
  80. package/templates/docker/src/lib/__tests__/agent-cache.test.ts +437 -0
  81. package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +114 -0
  82. package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
  83. package/templates/docker/src/lib/__tests__/agent-integration.test.ts +514 -0
  84. package/templates/docker/src/lib/__tests__/config-actions.test.ts +166 -0
  85. package/templates/docker/src/lib/__tests__/config.test.ts +1063 -0
  86. package/templates/docker/src/lib/__tests__/conversations.test.ts +589 -0
  87. package/templates/docker/src/lib/__tests__/errors.test.ts +256 -0
  88. package/templates/docker/src/lib/__tests__/logger.test.ts +200 -0
  89. package/templates/docker/src/lib/__tests__/providers.test.ts +99 -0
  90. package/templates/docker/src/lib/__tests__/rls.test.ts +435 -0
  91. package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
  92. package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
  93. package/templates/docker/src/lib/__tests__/semantic-index.test.ts +547 -0
  94. package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +544 -0
  95. package/templates/docker/src/lib/__tests__/semantic.test.ts +363 -0
  96. package/templates/docker/src/lib/__tests__/startup-actions.test.ts +452 -0
  97. package/templates/docker/src/lib/__tests__/startup.test.ts +465 -0
  98. package/templates/docker/src/lib/__tests__/tracing.test.ts +28 -0
  99. package/templates/docker/src/lib/action-types.ts +95 -0
  100. package/templates/docker/src/lib/agent-query.ts +178 -0
  101. package/templates/docker/src/lib/agent.ts +505 -0
  102. package/templates/docker/src/lib/api-url.ts +2 -0
  103. package/templates/docker/src/lib/auth/__tests__/audit.test.ts +418 -0
  104. package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
  105. package/templates/docker/src/lib/auth/__tests__/byot.test.ts +366 -0
  106. package/templates/docker/src/lib/auth/__tests__/detect.test.ts +190 -0
  107. package/templates/docker/src/lib/auth/__tests__/managed.test.ts +173 -0
  108. package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +456 -0
  109. package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +201 -0
  110. package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +225 -0
  111. package/templates/docker/src/lib/auth/__tests__/server.test.ts +34 -0
  112. package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +176 -0
  113. package/templates/docker/src/lib/auth/__tests__/types.test.ts +44 -0
  114. package/templates/docker/src/lib/auth/audit.ts +89 -0
  115. package/templates/docker/src/lib/auth/byot.ts +158 -0
  116. package/templates/docker/src/lib/auth/client.ts +35 -0
  117. package/templates/docker/src/lib/auth/detect.ts +83 -0
  118. package/templates/docker/src/lib/auth/managed.ts +73 -0
  119. package/templates/docker/src/lib/auth/middleware.ts +208 -0
  120. package/templates/docker/src/lib/auth/migrate.ts +111 -0
  121. package/templates/docker/src/lib/auth/permissions.ts +156 -0
  122. package/templates/docker/src/lib/auth/server.ts +142 -0
  123. package/templates/docker/src/lib/auth/simple-key.ts +92 -0
  124. package/templates/docker/src/lib/auth/types.ts +49 -0
  125. package/templates/docker/src/lib/config.ts +704 -0
  126. package/templates/docker/src/lib/conversation-types.ts +29 -0
  127. package/templates/docker/src/lib/conversations.ts +270 -0
  128. package/templates/docker/src/lib/db/__tests__/connection.test.ts +69 -0
  129. package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +141 -0
  130. package/templates/docker/src/lib/db/__tests__/internal.test.ts +387 -0
  131. package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +207 -0
  132. package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
  133. package/templates/docker/src/lib/db/__tests__/registry.test.ts +595 -0
  134. package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +339 -0
  135. package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +217 -0
  136. package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
  137. package/templates/docker/src/lib/db/connection.ts +753 -0
  138. package/templates/docker/src/lib/db/duckdb.ts +122 -0
  139. package/templates/docker/src/lib/db/internal.ts +273 -0
  140. package/templates/docker/src/lib/db/salesforce.ts +342 -0
  141. package/templates/docker/src/lib/db/source-rate-limit.ts +191 -0
  142. package/templates/docker/src/lib/errors.ts +154 -0
  143. package/templates/docker/src/lib/logger.ts +98 -0
  144. package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
  145. package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +529 -0
  146. package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +521 -0
  147. package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +346 -0
  148. package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +49 -0
  149. package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +585 -0
  150. package/templates/docker/src/lib/plugins/hooks.ts +162 -0
  151. package/templates/docker/src/lib/plugins/index.ts +9 -0
  152. package/templates/docker/src/lib/plugins/migrate.ts +309 -0
  153. package/templates/docker/src/lib/plugins/registry.ts +231 -0
  154. package/templates/docker/src/lib/plugins/tools.ts +39 -0
  155. package/templates/docker/src/lib/plugins/wiring.ts +291 -0
  156. package/templates/docker/src/lib/providers.ts +102 -0
  157. package/templates/docker/src/lib/rls.ts +321 -0
  158. package/templates/docker/src/lib/scheduled-task-types.ts +132 -0
  159. package/templates/docker/src/lib/scheduled-tasks.ts +475 -0
  160. package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
  161. package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +248 -0
  162. package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
  163. package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
  164. package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
  165. package/templates/docker/src/lib/scheduler/delivery.ts +248 -0
  166. package/templates/docker/src/lib/scheduler/engine.ts +317 -0
  167. package/templates/docker/src/lib/scheduler/executor.ts +73 -0
  168. package/templates/docker/src/lib/scheduler/format-email.ts +109 -0
  169. package/templates/docker/src/lib/scheduler/format-slack.ts +35 -0
  170. package/templates/docker/src/lib/scheduler/format-webhook.ts +37 -0
  171. package/templates/docker/src/lib/scheduler/index.ts +7 -0
  172. package/templates/docker/src/lib/security.ts +11 -0
  173. package/templates/docker/src/lib/semantic-index.ts +503 -0
  174. package/templates/docker/src/lib/semantic.ts +387 -0
  175. package/templates/docker/src/lib/sidecar-types.ts +16 -0
  176. package/templates/docker/src/lib/slack/__tests__/api.test.ts +160 -0
  177. package/templates/docker/src/lib/slack/__tests__/format.test.ts +237 -0
  178. package/templates/docker/src/lib/slack/__tests__/store.test.ts +188 -0
  179. package/templates/docker/src/lib/slack/__tests__/threads.test.ts +112 -0
  180. package/templates/docker/src/lib/slack/__tests__/verify.test.ts +111 -0
  181. package/templates/docker/src/lib/slack/api.ts +102 -0
  182. package/templates/docker/src/lib/slack/format.ts +209 -0
  183. package/templates/docker/src/lib/slack/store.ts +107 -0
  184. package/templates/docker/src/lib/slack/threads.ts +64 -0
  185. package/templates/docker/src/lib/slack/verify.ts +71 -0
  186. package/templates/docker/src/lib/startup.ts +730 -0
  187. package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
  188. package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
  189. package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
  190. package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
  191. package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
  192. package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
  193. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
  194. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
  195. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +235 -0
  196. package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
  197. package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
  198. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
  199. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
  200. package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
  201. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
  202. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +1012 -0
  203. package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
  204. package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +378 -0
  205. package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
  206. package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
  207. package/templates/docker/src/lib/tools/actions/audit.ts +47 -0
  208. package/templates/docker/src/lib/tools/actions/email.ts +191 -0
  209. package/templates/docker/src/lib/tools/actions/handler.ts +591 -0
  210. package/templates/docker/src/lib/tools/actions/index.ts +23 -0
  211. package/templates/docker/src/lib/tools/actions/jira.ts +220 -0
  212. package/templates/docker/src/lib/tools/explore-nsjail.ts +343 -0
  213. package/templates/docker/src/lib/tools/explore-sandbox.ts +264 -0
  214. package/templates/docker/src/lib/tools/explore-sidecar.ts +163 -0
  215. package/templates/docker/src/lib/tools/explore.ts +379 -0
  216. package/templates/docker/src/lib/tools/registry.ts +221 -0
  217. package/templates/docker/src/lib/tools/salesforce.ts +138 -0
  218. package/templates/docker/src/lib/tools/soql-validation.ts +172 -0
  219. package/templates/docker/src/lib/tools/sql.ts +680 -0
  220. package/templates/docker/src/lib/tracing.ts +40 -0
  221. package/templates/docker/src/lib/utils.ts +6 -0
  222. package/templates/docker/src/test-setup.ts +38 -0
  223. package/templates/docker/src/types/vercel-sandbox.d.ts +54 -0
  224. package/templates/docker/src/ui/components/actions/action-approval-card.tsx +295 -0
  225. package/templates/docker/src/ui/components/actions/action-status-badge.tsx +50 -0
  226. package/templates/docker/src/ui/components/admin/admin-layout.tsx +26 -0
  227. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +96 -0
  228. package/templates/docker/src/ui/components/admin/empty-state.tsx +24 -0
  229. package/templates/docker/src/ui/components/admin/entity-detail.tsx +233 -0
  230. package/templates/docker/src/ui/components/admin/entity-list.tsx +96 -0
  231. package/templates/docker/src/ui/components/admin/error-banner.tsx +22 -0
  232. package/templates/docker/src/ui/components/admin/feature-disabled.tsx +44 -0
  233. package/templates/docker/src/ui/components/admin/health-badge.tsx +30 -0
  234. package/templates/docker/src/ui/components/admin/loading-state.tsx +14 -0
  235. package/templates/docker/src/ui/components/admin/stat-card.tsx +32 -0
  236. package/templates/docker/src/ui/components/atlas-chat.tsx +370 -0
  237. package/templates/docker/src/ui/components/chart/chart-detection.ts +261 -0
  238. package/templates/docker/src/ui/components/chart/result-chart.tsx +375 -0
  239. package/templates/docker/src/ui/components/chat/api-key-bar.tsx +66 -0
  240. package/templates/docker/src/ui/components/chat/copy-button.tsx +25 -0
  241. package/templates/docker/src/ui/components/chat/data-table.tsx +102 -0
  242. package/templates/docker/src/ui/components/chat/error-banner.tsx +32 -0
  243. package/templates/docker/src/ui/components/chat/explore-card.tsx +41 -0
  244. package/templates/docker/src/ui/components/chat/loading-card.tsx +10 -0
  245. package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +116 -0
  246. package/templates/docker/src/ui/components/chat/markdown.tsx +72 -0
  247. package/templates/docker/src/ui/components/chat/sql-block.tsx +30 -0
  248. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +144 -0
  249. package/templates/docker/src/ui/components/chat/starter-prompts.ts +6 -0
  250. package/templates/docker/src/ui/components/chat/tool-part.tsx +40 -0
  251. package/templates/docker/src/ui/components/chat/typing-indicator.tsx +19 -0
  252. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +120 -0
  253. package/templates/docker/src/ui/components/conversations/conversation-list.tsx +66 -0
  254. package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
  255. package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +27 -0
  256. package/templates/docker/src/ui/context.tsx +78 -0
  257. package/templates/docker/src/ui/hooks/use-admin-fetch.ts +104 -0
  258. package/templates/docker/src/ui/hooks/use-conversations.ts +184 -0
  259. package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -0
  260. package/templates/docker/src/ui/lib/action-types.ts +63 -0
  261. package/templates/docker/src/ui/lib/helpers.ts +104 -0
  262. package/templates/docker/src/ui/lib/types.ts +145 -0
  263. package/templates/docker/tsconfig.json +41 -0
  264. package/templates/docker/vercel.json +3 -0
  265. package/templates/nextjs-standalone/.env.example +68 -0
  266. package/templates/nextjs-standalone/bin/__tests__/benchmark.test.ts +598 -0
  267. package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +171 -0
  268. package/templates/nextjs-standalone/bin/__tests__/eval.test.ts +434 -0
  269. package/templates/nextjs-standalone/bin/__tests__/matview-partition.test.ts +615 -0
  270. package/templates/nextjs-standalone/bin/__tests__/multi-source.test.ts +113 -0
  271. package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +322 -0
  272. package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +608 -0
  273. package/templates/nextjs-standalone/bin/__tests__/query.test.ts +240 -0
  274. package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +542 -0
  275. package/templates/nextjs-standalone/bin/__tests__/view-yaml-generation.test.ts +146 -0
  276. package/templates/nextjs-standalone/bin/atlas.ts +5044 -0
  277. package/templates/nextjs-standalone/bin/benchmark.ts +695 -0
  278. package/templates/nextjs-standalone/bin/enrich.ts +559 -0
  279. package/templates/nextjs-standalone/bin/eval.ts +770 -0
  280. package/templates/nextjs-standalone/bin/smoke.ts +438 -0
  281. package/templates/nextjs-standalone/data/.gitkeep +0 -0
  282. package/templates/nextjs-standalone/data/cybersec.sql +1961 -0
  283. package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +40 -0
  284. package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +170 -0
  285. package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +207 -0
  286. package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +145 -0
  287. package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +22 -0
  288. package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +38 -0
  289. package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +89 -0
  290. package/templates/nextjs-standalone/data/demo.sql +373 -0
  291. package/templates/nextjs-standalone/data/ecommerce.sql +1690 -0
  292. package/templates/nextjs-standalone/data/init-demo-db.sql +8 -0
  293. package/templates/nextjs-standalone/docs/deploy.md +390 -0
  294. package/templates/nextjs-standalone/eslint.config.mjs +18 -0
  295. package/templates/nextjs-standalone/gitignore +5 -0
  296. package/templates/nextjs-standalone/next.config.ts +10 -0
  297. package/templates/nextjs-standalone/package.json +63 -0
  298. package/templates/nextjs-standalone/postcss.config.mjs +8 -0
  299. package/templates/nextjs-standalone/semantic/catalog.yml +5 -0
  300. package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
  301. package/templates/nextjs-standalone/semantic/glossary.yml +6 -0
  302. package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
  303. package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +683 -0
  304. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +820 -0
  305. package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +165 -0
  306. package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +376 -0
  307. package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +555 -0
  308. package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +135 -0
  309. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +169 -0
  310. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +261 -0
  311. package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +891 -0
  312. package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +601 -0
  313. package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +847 -0
  314. package/templates/nextjs-standalone/src/api/index.ts +117 -0
  315. package/templates/nextjs-standalone/src/api/routes/actions.ts +274 -0
  316. package/templates/nextjs-standalone/src/api/routes/admin.ts +757 -0
  317. package/templates/nextjs-standalone/src/api/routes/auth.ts +48 -0
  318. package/templates/nextjs-standalone/src/api/routes/chat.ts +465 -0
  319. package/templates/nextjs-standalone/src/api/routes/conversations.ts +266 -0
  320. package/templates/nextjs-standalone/src/api/routes/health.ts +287 -0
  321. package/templates/nextjs-standalone/src/api/routes/openapi.ts +390 -0
  322. package/templates/nextjs-standalone/src/api/routes/query.ts +318 -0
  323. package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +467 -0
  324. package/templates/nextjs-standalone/src/api/routes/slack.ts +611 -0
  325. package/templates/nextjs-standalone/src/api/server.ts +226 -0
  326. package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +33 -0
  327. package/templates/nextjs-standalone/src/app/error.tsx +24 -0
  328. package/templates/nextjs-standalone/src/app/global-error.tsx +68 -0
  329. package/templates/nextjs-standalone/src/app/globals.css +126 -0
  330. package/templates/nextjs-standalone/src/app/layout.tsx +19 -0
  331. package/templates/nextjs-standalone/src/app/page.tsx +14 -0
  332. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +437 -0
  333. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +114 -0
  334. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
  335. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +514 -0
  336. package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +166 -0
  337. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +1063 -0
  338. package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +589 -0
  339. package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +256 -0
  340. package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +200 -0
  341. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +99 -0
  342. package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +435 -0
  343. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
  344. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
  345. package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +547 -0
  346. package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +544 -0
  347. package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +363 -0
  348. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +452 -0
  349. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +465 -0
  350. package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +28 -0
  351. package/templates/nextjs-standalone/src/lib/action-types.ts +95 -0
  352. package/templates/nextjs-standalone/src/lib/agent-query.ts +178 -0
  353. package/templates/nextjs-standalone/src/lib/agent.ts +505 -0
  354. package/templates/nextjs-standalone/src/lib/api-url.ts +3 -0
  355. package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +418 -0
  356. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
  357. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +366 -0
  358. package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +190 -0
  359. package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +173 -0
  360. package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +456 -0
  361. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +201 -0
  362. package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +225 -0
  363. package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +34 -0
  364. package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +176 -0
  365. package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +44 -0
  366. package/templates/nextjs-standalone/src/lib/auth/audit.ts +89 -0
  367. package/templates/nextjs-standalone/src/lib/auth/byot.ts +158 -0
  368. package/templates/nextjs-standalone/src/lib/auth/client.ts +23 -0
  369. package/templates/nextjs-standalone/src/lib/auth/detect.ts +83 -0
  370. package/templates/nextjs-standalone/src/lib/auth/managed.ts +73 -0
  371. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +208 -0
  372. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +111 -0
  373. package/templates/nextjs-standalone/src/lib/auth/permissions.ts +156 -0
  374. package/templates/nextjs-standalone/src/lib/auth/server.ts +142 -0
  375. package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +92 -0
  376. package/templates/nextjs-standalone/src/lib/auth/types.ts +49 -0
  377. package/templates/nextjs-standalone/src/lib/config.ts +704 -0
  378. package/templates/nextjs-standalone/src/lib/conversation-types.ts +29 -0
  379. package/templates/nextjs-standalone/src/lib/conversations.ts +270 -0
  380. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +69 -0
  381. package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +141 -0
  382. package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +387 -0
  383. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +207 -0
  384. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
  385. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +595 -0
  386. package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +339 -0
  387. package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +217 -0
  388. package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
  389. package/templates/nextjs-standalone/src/lib/db/connection.ts +753 -0
  390. package/templates/nextjs-standalone/src/lib/db/duckdb.ts +122 -0
  391. package/templates/nextjs-standalone/src/lib/db/internal.ts +273 -0
  392. package/templates/nextjs-standalone/src/lib/db/salesforce.ts +342 -0
  393. package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +191 -0
  394. package/templates/nextjs-standalone/src/lib/errors.ts +154 -0
  395. package/templates/nextjs-standalone/src/lib/logger.ts +98 -0
  396. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
  397. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +529 -0
  398. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +521 -0
  399. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +346 -0
  400. package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +49 -0
  401. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +585 -0
  402. package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +162 -0
  403. package/templates/nextjs-standalone/src/lib/plugins/index.ts +9 -0
  404. package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +309 -0
  405. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +231 -0
  406. package/templates/nextjs-standalone/src/lib/plugins/tools.ts +39 -0
  407. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +291 -0
  408. package/templates/nextjs-standalone/src/lib/providers.ts +102 -0
  409. package/templates/nextjs-standalone/src/lib/rls.ts +321 -0
  410. package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +132 -0
  411. package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +475 -0
  412. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
  413. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +248 -0
  414. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
  415. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
  416. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
  417. package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -0
  418. package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +317 -0
  419. package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +73 -0
  420. package/templates/nextjs-standalone/src/lib/scheduler/format-email.ts +109 -0
  421. package/templates/nextjs-standalone/src/lib/scheduler/format-slack.ts +35 -0
  422. package/templates/nextjs-standalone/src/lib/scheduler/format-webhook.ts +37 -0
  423. package/templates/nextjs-standalone/src/lib/scheduler/index.ts +7 -0
  424. package/templates/nextjs-standalone/src/lib/security.ts +11 -0
  425. package/templates/nextjs-standalone/src/lib/semantic-index.ts +503 -0
  426. package/templates/nextjs-standalone/src/lib/semantic.ts +387 -0
  427. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +16 -0
  428. package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +160 -0
  429. package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +237 -0
  430. package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +188 -0
  431. package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +112 -0
  432. package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +111 -0
  433. package/templates/nextjs-standalone/src/lib/slack/api.ts +102 -0
  434. package/templates/nextjs-standalone/src/lib/slack/format.ts +209 -0
  435. package/templates/nextjs-standalone/src/lib/slack/store.ts +107 -0
  436. package/templates/nextjs-standalone/src/lib/slack/threads.ts +64 -0
  437. package/templates/nextjs-standalone/src/lib/slack/verify.ts +71 -0
  438. package/templates/nextjs-standalone/src/lib/startup.ts +730 -0
  439. package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
  440. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
  441. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
  442. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
  443. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
  444. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
  445. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
  446. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
  447. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +235 -0
  448. package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
  449. package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
  450. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
  451. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
  452. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
  453. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
  454. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +1012 -0
  455. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
  456. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +378 -0
  457. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
  458. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
  459. package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +47 -0
  460. package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +191 -0
  461. package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +591 -0
  462. package/templates/nextjs-standalone/src/lib/tools/actions/index.ts +23 -0
  463. package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +220 -0
  464. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +343 -0
  465. package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +264 -0
  466. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +163 -0
  467. package/templates/nextjs-standalone/src/lib/tools/explore.ts +379 -0
  468. package/templates/nextjs-standalone/src/lib/tools/registry.ts +221 -0
  469. package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +138 -0
  470. package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +172 -0
  471. package/templates/nextjs-standalone/src/lib/tools/sql.ts +680 -0
  472. package/templates/nextjs-standalone/src/lib/tracing.ts +40 -0
  473. package/templates/nextjs-standalone/src/lib/utils.ts +6 -0
  474. package/templates/nextjs-standalone/src/test-setup.ts +38 -0
  475. package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +295 -0
  476. package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +50 -0
  477. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +26 -0
  478. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +96 -0
  479. package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +24 -0
  480. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +233 -0
  481. package/templates/nextjs-standalone/src/ui/components/admin/entity-list.tsx +96 -0
  482. package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +22 -0
  483. package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +44 -0
  484. package/templates/nextjs-standalone/src/ui/components/admin/health-badge.tsx +30 -0
  485. package/templates/nextjs-standalone/src/ui/components/admin/loading-state.tsx +14 -0
  486. package/templates/nextjs-standalone/src/ui/components/admin/stat-card.tsx +32 -0
  487. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +370 -0
  488. package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +261 -0
  489. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +375 -0
  490. package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +66 -0
  491. package/templates/nextjs-standalone/src/ui/components/chat/copy-button.tsx +25 -0
  492. package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +102 -0
  493. package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +32 -0
  494. package/templates/nextjs-standalone/src/ui/components/chat/explore-card.tsx +41 -0
  495. package/templates/nextjs-standalone/src/ui/components/chat/loading-card.tsx +10 -0
  496. package/templates/nextjs-standalone/src/ui/components/chat/managed-auth-card.tsx +116 -0
  497. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +72 -0
  498. package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +30 -0
  499. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +144 -0
  500. package/templates/nextjs-standalone/src/ui/components/chat/starter-prompts.ts +6 -0
  501. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +40 -0
  502. package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +19 -0
  503. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +120 -0
  504. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +66 -0
  505. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
  506. package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +27 -0
  507. package/templates/nextjs-standalone/src/ui/context.tsx +78 -0
  508. package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +104 -0
  509. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +184 -0
  510. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -0
  511. package/templates/nextjs-standalone/src/ui/lib/action-types.ts +63 -0
  512. package/templates/nextjs-standalone/src/ui/lib/helpers.ts +104 -0
  513. package/templates/nextjs-standalone/src/ui/lib/types.ts +145 -0
  514. package/templates/nextjs-standalone/tsconfig.json +32 -0
  515. package/templates/nextjs-standalone/vercel.json +4 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Query audit logger.
3
+ *
4
+ * Logs every SQL query to pino (always) and to the internal Postgres
5
+ * audit_log table (when DATABASE_URL is set). Fire-and-forget DB writes —
6
+ * two layers of protection: internalExecute() returns a promise whose
7
+ * rejections are swallowed (async errors), and the surrounding try/catch
8
+ * in logQueryAudit covers synchronous throws from getInternalDB() (e.g.
9
+ * pool not initialized). Either way, audit failures never propagate to
10
+ * the caller.
11
+ *
12
+ * Dual-truncation strategy:
13
+ * - Pino log entries truncate SQL to 500 chars (PINO_SQL_LIMIT) to keep
14
+ * structured log output concise and avoid overwhelming log aggregators.
15
+ * - DB audit_log inserts truncate SQL to 2000 chars (DB_SQL_LIMIT) to
16
+ * preserve enough context for forensic review while respecting column limits.
17
+ */
18
+
19
+ import { createLogger, getRequestContext } from "@atlas/api/lib/logger";
20
+ import { hasInternalDB, internalExecute } from "@atlas/api/lib/db/internal";
21
+ import { SENSITIVE_PATTERNS } from "@atlas/api/lib/security";
22
+ import type { DBType } from "@atlas/api/lib/db/connection";
23
+
24
+ const log = createLogger("audit");
25
+
26
+ const PINO_SQL_LIMIT = 500;
27
+ const DB_SQL_LIMIT = 2000;
28
+
29
+ export type AuditEntry =
30
+ | { sql: string; durationMs: number; rowCount: number; success: true; sourceId?: string; sourceType?: DBType; targetHost?: string }
31
+ | { sql: string; durationMs: number; rowCount: null; success: false; error?: string; sourceId?: string; sourceType?: DBType; targetHost?: string };
32
+
33
+ function scrubError(error: string | undefined): string | undefined {
34
+ if (!error) return undefined;
35
+ if (SENSITIVE_PATTERNS.test(error)) return "[scrubbed]";
36
+ return error;
37
+ }
38
+
39
+ export function logQueryAudit(entry: AuditEntry): void {
40
+ const ctx = getRequestContext();
41
+ const userId = ctx?.user?.id ?? null;
42
+ const userLabel = ctx?.user?.label ?? null;
43
+ const authMode = ctx?.user?.mode ?? "none";
44
+ const scrubbedError = scrubError(entry.success ? undefined : entry.error);
45
+
46
+ // Always log to pino (SQL truncated to 500 chars)
47
+ const logFn = entry.success ? log.info.bind(log) : log.warn.bind(log);
48
+ logFn(
49
+ {
50
+ sql: entry.sql.slice(0, PINO_SQL_LIMIT),
51
+ durationMs: entry.durationMs,
52
+ rowCount: entry.rowCount,
53
+ success: entry.success,
54
+ ...(scrubbedError && { error: scrubbedError }),
55
+ userId,
56
+ userLabel,
57
+ authMode,
58
+ ...(entry.sourceId && { sourceId: entry.sourceId }),
59
+ ...(entry.sourceType && { sourceType: entry.sourceType }),
60
+ ...(entry.targetHost && { targetHost: entry.targetHost }),
61
+ },
62
+ entry.success ? "query_success" : "query_failure",
63
+ );
64
+
65
+ // Insert into audit_log when internal DB is available (SQL truncated to 2000 chars)
66
+ if (hasInternalDB()) {
67
+ try {
68
+ internalExecute(
69
+ `INSERT INTO audit_log (user_id, user_label, auth_mode, sql, duration_ms, row_count, success, error, source_id, source_type, target_host)
70
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
71
+ [
72
+ userId,
73
+ userLabel,
74
+ authMode,
75
+ entry.sql.slice(0, DB_SQL_LIMIT),
76
+ entry.durationMs,
77
+ entry.rowCount,
78
+ entry.success,
79
+ scrubbedError ?? null,
80
+ entry.sourceId ?? null,
81
+ entry.sourceType ?? null,
82
+ entry.targetHost ?? null,
83
+ ],
84
+ );
85
+ } catch (err) {
86
+ log.warn({ err }, "audit_log insert failed");
87
+ }
88
+ }
89
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * BYOT auth — JWT/JWKS validation for external identity providers.
3
+ *
4
+ * Validates JWTs signed by external IdPs (Auth0, Clerk, Supabase Auth, etc.)
5
+ * using a remote JWKS endpoint. Requires ATLAS_AUTH_JWKS_URL + ATLAS_AUTH_ISSUER.
6
+ */
7
+
8
+ import { createRemoteJWKSet, jwtVerify, errors } from "jose";
9
+ import type { AuthResult } from "@atlas/api/lib/auth/types";
10
+ import { createAtlasUser } from "@atlas/api/lib/auth/types";
11
+ import { parseRole } from "@atlas/api/lib/auth/permissions";
12
+ import { resolveClaimPath } from "@atlas/api/lib/rls";
13
+ import { createLogger } from "@atlas/api/lib/logger";
14
+
15
+ const log = createLogger("auth:byot");
16
+
17
+ let _jwks: ReturnType<typeof createRemoteJWKSet> | null = null;
18
+
19
+ function getJWKS() {
20
+ if (!_jwks) {
21
+ const url = process.env.ATLAS_AUTH_JWKS_URL;
22
+ if (!url) throw new Error("ATLAS_AUTH_JWKS_URL is required for BYOT mode");
23
+ log.info({ jwksUrl: url }, "Initializing JWKS client");
24
+ _jwks = createRemoteJWKSet(new URL(url));
25
+ }
26
+ return _jwks;
27
+ }
28
+
29
+ /** Reset the cached JWKS instance — for test isolation. */
30
+ export function resetJWKSCache(): void {
31
+ _jwks = null;
32
+ }
33
+
34
+ /** @internal — test-only. Inject a JWKS key-set getter (bypasses createRemoteJWKSet). */
35
+ export function _setJWKS(jwks: ReturnType<typeof createRemoteJWKSet>): void {
36
+ _jwks = jwks;
37
+ }
38
+
39
+ /**
40
+ * Extract an Atlas role from the JWT payload using the configured claim path.
41
+ * ATLAS_AUTH_ROLE_CLAIM can be a dot-delimited path (e.g. "app_metadata.role").
42
+ * Defaults to checking "role" then "atlas_role" when not configured.
43
+ */
44
+ function extractRoleFromPayload(payload: Record<string, unknown>) {
45
+ const claimPath = process.env.ATLAS_AUTH_ROLE_CLAIM;
46
+
47
+ if (claimPath) {
48
+ // Single configured claim path — may be nested (e.g. "app_metadata.role")
49
+ const value = resolveClaimPath(payload, claimPath);
50
+ if (typeof value === "string") {
51
+ const role = parseRole(value);
52
+ if (role) return role;
53
+ log.warn({ claimPath, value, validRoles: ["viewer", "analyst", "admin"] }, "JWT role claim value is not a valid Atlas role — ignoring");
54
+ } else if (value !== undefined && value !== null) {
55
+ log.warn({ claimPath, type: typeof value }, "JWT role claim is not a string — ignoring");
56
+ }
57
+ return undefined;
58
+ }
59
+
60
+ // Default: check "role" then "atlas_role" at the top level
61
+ for (const claim of ["role", "atlas_role"]) {
62
+ const value = payload[claim];
63
+ if (typeof value === "string") {
64
+ const role = parseRole(value);
65
+ if (role) return role;
66
+ log.warn({ claim, value, validRoles: ["viewer", "analyst", "admin"] }, "JWT role claim value is not a valid Atlas role — ignoring");
67
+ } else if (value !== undefined && value !== null) {
68
+ log.warn({ claim, type: typeof value }, "JWT role claim is not a string — ignoring");
69
+ }
70
+ }
71
+
72
+ return undefined;
73
+ }
74
+
75
+ export async function validateBYOT(req: Request): Promise<AuthResult> {
76
+ const authHeader = req.headers.get("authorization");
77
+ if (!authHeader?.startsWith("Bearer ")) {
78
+ return {
79
+ authenticated: false,
80
+ mode: "byot",
81
+ status: 401,
82
+ error: "Missing or malformed Authorization header",
83
+ };
84
+ }
85
+
86
+ const token = authHeader.slice(7);
87
+ const jwks = getJWKS();
88
+
89
+ const issuer = process.env.ATLAS_AUTH_ISSUER;
90
+ if (!issuer)
91
+ throw new Error("ATLAS_AUTH_ISSUER is required for BYOT mode");
92
+
93
+ const rawAudience = process.env.ATLAS_AUTH_AUDIENCE;
94
+ if (rawAudience === "") {
95
+ log.warn("ATLAS_AUTH_AUDIENCE is set to empty string — audience check will be skipped");
96
+ }
97
+ const audience = rawAudience || undefined;
98
+
99
+ try {
100
+ const { payload } = await jwtVerify(token, jwks, { issuer, audience });
101
+ const sub = payload.sub;
102
+ if (!sub) {
103
+ return {
104
+ authenticated: false,
105
+ mode: "byot",
106
+ status: 401,
107
+ error: "JWT missing sub claim",
108
+ };
109
+ }
110
+ const email =
111
+ typeof payload.email === "string" ? payload.email : undefined;
112
+
113
+ // Extract role from JWT claim. Configurable via ATLAS_AUTH_ROLE_CLAIM
114
+ // (default: check "role", then "atlas_role").
115
+ const role = extractRoleFromPayload(payload);
116
+
117
+ return {
118
+ authenticated: true,
119
+ mode: "byot",
120
+ user: createAtlasUser(sub, "byot", email || sub, role, payload as Record<string, unknown>),
121
+ };
122
+ } catch (err) {
123
+ // Infrastructure errors — JWKS endpoint issues are not client auth failures.
124
+ // Re-throw so middleware.ts catches them as 500.
125
+ if (
126
+ err instanceof errors.JWKSTimeout ||
127
+ err instanceof errors.JWKSInvalid ||
128
+ err instanceof errors.JOSENotSupported
129
+ ) {
130
+ log.error(
131
+ { err, code: err.code },
132
+ "JWKS infrastructure error during BYOT validation",
133
+ );
134
+ throw err;
135
+ }
136
+
137
+ // Token validation errors — legitimate 401s
138
+ if (err instanceof errors.JOSEError) {
139
+ log.debug(
140
+ { code: err.code, message: err.message },
141
+ "BYOT token rejected",
142
+ );
143
+ return {
144
+ authenticated: false,
145
+ mode: "byot",
146
+ status: 401,
147
+ error: "Invalid or expired token",
148
+ };
149
+ }
150
+
151
+ // Unexpected non-JOSE errors (e.g., network fetch failure) — re-throw
152
+ log.error(
153
+ { err: err instanceof Error ? err : new Error(String(err)) },
154
+ "Unexpected BYOT validation error",
155
+ );
156
+ throw err;
157
+ }
158
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Better Auth React client.
3
+ *
4
+ * Imported from page.tsx. The useSession() hook runs in all auth modes
5
+ * but session data is only meaningful when authMode === "managed".
6
+ * Bearer tokens are handled server-side by the bearer plugin;
7
+ * browser sessions use cookies automatically.
8
+ *
9
+ * Base URL resolution priority:
10
+ * 1. NEXT_PUBLIC_ATLAS_API_URL — cross-origin API (set when frontend and API are separate)
11
+ * 2. window.location.origin — same-origin fallback (browser)
12
+ * 3. http://localhost:3000 — SSR / prerender fallback
13
+ *
14
+ * A full URL is required for SSR compatibility — relative "/api/auth" fails
15
+ * during Next.js static prerendering because there's no host to resolve against.
16
+ */
17
+
18
+ import { createAuthClient } from "better-auth/react";
19
+ import { apiKeyClient } from "@better-auth/api-key/client";
20
+ import { adminClient } from "better-auth/client/plugins";
21
+ import { API_URL } from "@/lib/api-url";
22
+
23
+ function getBaseURL(): string {
24
+ if (API_URL) return API_URL + "/api/auth";
25
+ if (typeof window !== "undefined") return window.location.origin + "/api/auth";
26
+ return "http://localhost:3000/api/auth";
27
+ }
28
+
29
+ export const authClient = createAuthClient({
30
+ baseURL: getBaseURL(),
31
+ plugins: [apiKeyClient(), adminClient()],
32
+ // Cross-origin deployments (app.useatlas.dev → api.useatlas.dev) require
33
+ // credentials: "include" so the browser stores and sends session cookies.
34
+ fetchOptions: API_URL ? { credentials: "include" as RequestCredentials } : {},
35
+ });
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Auth mode detection.
3
+ *
4
+ * Resolves the active auth mode from environment variables.
5
+ *
6
+ * When `ATLAS_AUTH_MODE` is set, it is used directly (explicit mode).
7
+ * When unset, auto-detection reads env vars with priority:
8
+ * JWKS (byot) > Better Auth (managed) > API key (simple-key) > none.
9
+ *
10
+ * Result is cached — call resetAuthModeCache() in tests.
11
+ */
12
+
13
+ import type { AuthMode } from "@atlas/api/lib/auth/types";
14
+ import { createLogger } from "@atlas/api/lib/logger";
15
+
16
+ const log = createLogger("auth");
17
+
18
+ /** User-friendly aliases accepted by ATLAS_AUTH_MODE. */
19
+ const MODE_ALIASES: Record<string, AuthMode> = {
20
+ "none": "none",
21
+ "api-key": "simple-key",
22
+ "simple-key": "simple-key",
23
+ "managed": "managed",
24
+ "byot": "byot",
25
+ };
26
+
27
+ export type AuthModeSource = "explicit" | "auto-detected";
28
+
29
+ let _cached: AuthMode | null = null;
30
+ let _source: AuthModeSource | null = null;
31
+
32
+ /**
33
+ * Detect auth mode from ATLAS_AUTH_MODE (explicit) or env var presence (auto).
34
+ * Cached after first call.
35
+ */
36
+ export function detectAuthMode(): AuthMode {
37
+ if (_cached !== null) return _cached;
38
+
39
+ const explicit = process.env.ATLAS_AUTH_MODE?.trim();
40
+ if (explicit) {
41
+ const resolved = MODE_ALIASES[explicit.toLowerCase()];
42
+ if (resolved) {
43
+ _cached = resolved;
44
+ _source = "explicit";
45
+ log.info({ mode: _cached }, "Auth mode: %s (explicit)", _cached);
46
+ return _cached;
47
+ }
48
+ const valid = Object.keys(MODE_ALIASES).join(", ");
49
+ throw new Error(
50
+ `Invalid ATLAS_AUTH_MODE '${explicit}'. Valid values: ${valid}. ` +
51
+ `Remove ATLAS_AUTH_MODE to use auto-detection, or set it to a valid value.`,
52
+ );
53
+ }
54
+
55
+ // Auto-detection fallback
56
+ if (process.env.ATLAS_AUTH_JWKS_URL) {
57
+ _cached = "byot";
58
+ } else if (process.env.BETTER_AUTH_SECRET) {
59
+ _cached = "managed";
60
+ } else if (process.env.ATLAS_API_KEY) {
61
+ _cached = "simple-key";
62
+ } else {
63
+ _cached = "none";
64
+ }
65
+
66
+ _source = "auto-detected";
67
+ log.info({ mode: _cached }, "Auth mode: %s (auto-detected)", _cached);
68
+ return _cached;
69
+ }
70
+
71
+ /**
72
+ * Return how the auth mode was resolved: "explicit" or "auto-detected".
73
+ * Returns null if detectAuthMode() has not been called yet.
74
+ */
75
+ export function getAuthModeSource(): AuthModeSource | null {
76
+ return _source;
77
+ }
78
+
79
+ /** Reset cached auth mode. For testing only. */
80
+ export function resetAuthModeCache(): void {
81
+ _cached = null;
82
+ _source = null;
83
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Managed auth (Better Auth) — session validation.
3
+ *
4
+ * Checks cookies and bearer tokens via auth.api.getSession().
5
+ * Returns AuthResult on success or missing session (never throws for
6
+ * "no session" — returns { authenticated: false } instead).
7
+ * Throws on infrastructure errors (DB unavailable, etc.);
8
+ * callers (middleware.ts) are expected to catch.
9
+ */
10
+
11
+ import type { AuthResult } from "@atlas/api/lib/auth/types";
12
+ import { createAtlasUser } from "@atlas/api/lib/auth/types";
13
+ import { parseRole } from "@atlas/api/lib/auth/permissions";
14
+ import { getAuthInstance } from "@atlas/api/lib/auth/server";
15
+ import { createLogger } from "@atlas/api/lib/logger";
16
+
17
+ const log = createLogger("auth:managed");
18
+
19
+ export async function validateManaged(req: Request): Promise<AuthResult> {
20
+ const auth = getAuthInstance();
21
+
22
+ // Debug: log whether cookies are present in the request
23
+ const cookieHeader = req.headers.get("cookie");
24
+ const hasSessionToken = cookieHeader?.includes("session_token") ?? false;
25
+ const hasAuthorization = !!req.headers.get("authorization");
26
+ if (!hasSessionToken && !hasAuthorization) {
27
+ log.info({ url: req.url }, "No session_token cookie or Authorization header in request");
28
+ } else {
29
+ log.info({ hasSessionToken, hasAuthorization, url: req.url }, "Auth headers present");
30
+ }
31
+
32
+ const session = await auth.api.getSession({ headers: req.headers });
33
+
34
+ if (!session) {
35
+ log.info({ hasSessionToken, hasAuthorization }, "getSession returned null");
36
+ return { authenticated: false, mode: "managed", status: 401, error: "Not signed in" };
37
+ }
38
+
39
+ const userId = session.user?.id;
40
+ const email = session.user?.email;
41
+ if (!userId) {
42
+ log.error({ sessionExists: true }, "Session found but user.id is missing");
43
+ return { authenticated: false, mode: "managed", status: 500, error: "Session data is incomplete" };
44
+ }
45
+
46
+ // Extract role from session user (set by Better Auth admin plugin, stored in the `role` column).
47
+ // Falls back to default (viewer) when not present — see permissions.ts.
48
+ const sessionUser = session.user as Record<string, unknown>;
49
+ // Better Auth can store multiple roles as comma-separated strings; Atlas uses only the first.
50
+ const rawRoleField = sessionUser?.role;
51
+ const rawRole = typeof rawRoleField === "string" ? rawRoleField.split(",")[0].trim() : rawRoleField;
52
+ let role: ReturnType<typeof parseRole>;
53
+ if (typeof rawRole === "string") {
54
+ role = parseRole(rawRole);
55
+ if (rawRole && !role) {
56
+ log.warn({ value: rawRole, validRoles: ["viewer", "analyst", "admin"] }, "Session user role is not a valid Atlas role — defaulting to 'viewer'");
57
+ }
58
+ } else {
59
+ role = undefined;
60
+ if (rawRole !== undefined && rawRole !== null) {
61
+ log.warn({ type: typeof rawRole }, "Session user role is not a string — ignoring");
62
+ }
63
+ }
64
+
65
+ // Carry session user fields as claims for RLS policy evaluation
66
+ const claims: Record<string, unknown> = { ...sessionUser, sub: userId };
67
+
68
+ return {
69
+ authenticated: true,
70
+ mode: "managed",
71
+ user: createAtlasUser(userId, "managed", email || userId, role, claims),
72
+ };
73
+ }
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Auth middleware — central dispatcher + rate limiting.
3
+ *
4
+ * Calls detectAuthMode() and routes to the appropriate validator.
5
+ * Exports in-memory sliding-window rate limiting (checkRateLimit, getClientIP).
6
+ *
7
+ * A background setInterval timer evicts stale rate-limit entries every 60s.
8
+ * Call _stopCleanup() in tests to prevent the timer from keeping the process alive.
9
+ */
10
+
11
+ import type { AuthResult } from "@atlas/api/lib/auth/types";
12
+ import { detectAuthMode } from "@atlas/api/lib/auth/detect";
13
+ import { validateApiKey } from "@atlas/api/lib/auth/simple-key";
14
+ import { validateManaged } from "@atlas/api/lib/auth/managed";
15
+ import { validateBYOT } from "@atlas/api/lib/auth/byot";
16
+ import { createLogger } from "@atlas/api/lib/logger";
17
+
18
+ const log = createLogger("auth");
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Rate limiting — in-memory sliding window
22
+ // ---------------------------------------------------------------------------
23
+
24
+ const WINDOW_MS = 60_000; // 60 seconds
25
+
26
+ /** Map of rate-limit key → array of request timestamps (ms). */
27
+ const windows = new Map<string, number[]>();
28
+
29
+ let warnedInvalidRpm = false;
30
+
31
+ function getRpmLimit(): number {
32
+ const raw = process.env.ATLAS_RATE_LIMIT_RPM;
33
+ if (raw === undefined || raw === "") return 0; // disabled
34
+ const n = Number(raw);
35
+ if (!Number.isFinite(n) || n < 0) {
36
+ if (!warnedInvalidRpm) {
37
+ log.warn({ value: raw }, "Invalid ATLAS_RATE_LIMIT_RPM; rate limiting disabled");
38
+ warnedInvalidRpm = true;
39
+ }
40
+ return 0;
41
+ }
42
+ return Math.floor(n);
43
+ }
44
+
45
+ /**
46
+ * Extract client IP from request headers.
47
+ *
48
+ * Both `X-Forwarded-For` and `X-Real-IP` are only trusted when
49
+ * `ATLAS_TRUST_PROXY` is `"true"` or `"1"`. Without this, an attacker
50
+ * can spoof these headers to bypass per-IP rate limits.
51
+ */
52
+ export function getClientIP(req: Request): string | null {
53
+ const trustProxy = process.env.ATLAS_TRUST_PROXY;
54
+ if (trustProxy === "true" || trustProxy === "1") {
55
+ const xff = req.headers.get("x-forwarded-for");
56
+ if (xff) {
57
+ const first = xff.split(",")[0].trim();
58
+ if (first) return first;
59
+ }
60
+ const realIp = req.headers.get("x-real-ip");
61
+ if (realIp) return realIp.trim();
62
+ }
63
+ return null;
64
+ }
65
+
66
+ /**
67
+ * Sliding-window rate limit check.
68
+ *
69
+ * Returns `{ allowed: true }` when the request should proceed, or
70
+ * `{ allowed: false, retryAfterMs }` when the caller should back off.
71
+ * Always allows when ATLAS_RATE_LIMIT_RPM is unset or "0".
72
+ *
73
+ * Note: this limits API *requests*, not agent steps. A single chat request
74
+ * may run up to 25 agent steps internally, so effective LLM call volume
75
+ * can be higher than the RPM value implies.
76
+ */
77
+ export function checkRateLimit(key: string): {
78
+ allowed: boolean;
79
+ retryAfterMs?: number;
80
+ } {
81
+ const limit = getRpmLimit();
82
+ if (limit === 0) return { allowed: true };
83
+
84
+ const now = Date.now();
85
+ const cutoff = now - WINDOW_MS;
86
+
87
+ let timestamps = windows.get(key);
88
+ if (!timestamps) {
89
+ timestamps = [];
90
+ windows.set(key, timestamps);
91
+ }
92
+
93
+ // Evict stale entries
94
+ const firstValid = timestamps.findIndex((t) => t > cutoff);
95
+ if (firstValid > 0) timestamps.splice(0, firstValid);
96
+ else if (firstValid === -1) timestamps.length = 0;
97
+
98
+ if (timestamps.length < limit) {
99
+ timestamps.push(now);
100
+ return { allowed: true };
101
+ }
102
+
103
+ // Blocked — oldest entry determines when a slot opens
104
+ const retryAfterMs = Math.max(1, timestamps[0] + WINDOW_MS - now);
105
+ return { allowed: false, retryAfterMs };
106
+ }
107
+
108
+ /** Clear all rate limit state. For tests. */
109
+ export function resetRateLimits(): void {
110
+ windows.clear();
111
+ warnedInvalidRpm = false;
112
+ }
113
+
114
+ /** Periodic cleanup — evict keys with no recent timestamps. */
115
+ const cleanupInterval = setInterval(() => {
116
+ try {
117
+ const cutoff = Date.now() - WINDOW_MS;
118
+ for (const [key, timestamps] of windows) {
119
+ if (timestamps.length === 0 || timestamps[timestamps.length - 1] <= cutoff) {
120
+ windows.delete(key);
121
+ }
122
+ }
123
+ } catch (err) {
124
+ log.error(
125
+ { err: err instanceof Error ? err : new Error(String(err)) },
126
+ "Rate limit cleanup failed",
127
+ );
128
+ }
129
+ }, WINDOW_MS);
130
+
131
+ // Don't prevent Node/bun from exiting
132
+ cleanupInterval.unref();
133
+
134
+ /** Stop the periodic cleanup timer. For tests. */
135
+ export function _stopCleanup(): void {
136
+ clearInterval(cleanupInterval);
137
+ }
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Auth dispatcher
141
+ // ---------------------------------------------------------------------------
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Test-only validator overrides
145
+ // ---------------------------------------------------------------------------
146
+
147
+ let _managedOverride: ((req: Request) => Promise<AuthResult>) | null = null;
148
+ let _byotOverride: ((req: Request) => Promise<AuthResult>) | null = null;
149
+
150
+ /** @internal — test-only. Override validateManaged/validateBYOT for isolation. */
151
+ export function _setValidatorOverrides(overrides: {
152
+ managed?: ((req: Request) => Promise<AuthResult>) | null;
153
+ byot?: ((req: Request) => Promise<AuthResult>) | null;
154
+ }): void {
155
+ _managedOverride = overrides.managed ?? null;
156
+ _byotOverride = overrides.byot ?? null;
157
+ }
158
+
159
+ /** Authenticate an incoming request based on the detected auth mode. */
160
+ export async function authenticateRequest(req: Request): Promise<AuthResult> {
161
+ const mode = detectAuthMode();
162
+
163
+ switch (mode) {
164
+ case "none":
165
+ return { authenticated: true, user: undefined, mode: "none" };
166
+
167
+ case "simple-key":
168
+ return validateApiKey(req);
169
+
170
+ case "managed":
171
+ try {
172
+ return await (_managedOverride ?? validateManaged)(req);
173
+ } catch (err) {
174
+ log.error(
175
+ { err: err instanceof Error ? err : new Error(String(err)), mode },
176
+ "Managed auth error",
177
+ );
178
+ if (err instanceof TypeError || err instanceof ReferenceError || err instanceof SyntaxError) {
179
+ log.error({ err, mode }, "BUG: Unexpected programming error in auth validator");
180
+ }
181
+ return {
182
+ authenticated: false,
183
+ mode,
184
+ status: 500,
185
+ error: "Authentication service error",
186
+ };
187
+ }
188
+
189
+ case "byot":
190
+ try {
191
+ return await (_byotOverride ?? validateBYOT)(req);
192
+ } catch (err) {
193
+ log.error(
194
+ { err: err instanceof Error ? err : new Error(String(err)), mode },
195
+ "BYOT auth error",
196
+ );
197
+ if (err instanceof TypeError || err instanceof ReferenceError || err instanceof SyntaxError) {
198
+ log.error({ err, mode }, "BUG: Unexpected programming error in auth validator");
199
+ }
200
+ return {
201
+ authenticated: false,
202
+ mode,
203
+ status: 500,
204
+ error: "Authentication service error",
205
+ };
206
+ }
207
+ }
208
+ }