@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,111 @@
1
+ /**
2
+ * Better Auth table migration — runs once at server boot.
3
+ *
4
+ * Extracted from validateEnvironment() so the startup diagnostics
5
+ * function stays pure (diagnose-only, never mutate). The Hono server
6
+ * entry point (server.ts) calls migrateAuthTables() once on startup.
7
+ */
8
+
9
+ import { detectAuthMode } from "@atlas/api/lib/auth/detect";
10
+ import { hasInternalDB, internalQuery } from "@atlas/api/lib/db/internal";
11
+ import { createLogger } from "@atlas/api/lib/logger";
12
+
13
+ const log = createLogger("auth-migrate");
14
+
15
+ let _migrated = false;
16
+ let _migrationError: string | null = null;
17
+
18
+ /** Return the last migration error message, or null if migrations succeeded (or haven't run). */
19
+ export function getMigrationError(): string | null {
20
+ return _migrationError;
21
+ }
22
+
23
+ /**
24
+ * Run Better Auth table migrations (user, session, account, etc.)
25
+ * if managed auth mode is active and DATABASE_URL is configured.
26
+ *
27
+ * Safe to call multiple times — only runs once (idempotent guard).
28
+ * Also runs the internal DB migration (audit_log table).
29
+ */
30
+ export async function migrateAuthTables(): Promise<void> {
31
+ if (_migrated) return;
32
+
33
+ // Internal DB migration (audit_log) — runs regardless of auth mode
34
+ if (hasInternalDB()) {
35
+ try {
36
+ const { migrateInternalDB } = await import("@atlas/api/lib/db/internal");
37
+ await migrateInternalDB();
38
+ } catch (err) {
39
+ log.error({ err }, "Internal DB migration failed");
40
+ _migrationError = "Connected to the internal database but migration failed. Check database permissions (CREATE TABLE, CREATE INDEX).";
41
+ // Don't block server start — audit will fall back to pino-only
42
+ }
43
+ }
44
+
45
+ // Better Auth migration — only in managed mode
46
+ const authMode = detectAuthMode();
47
+ if (authMode !== "managed") {
48
+ _migrated = true;
49
+ return;
50
+ }
51
+
52
+ if (!hasInternalDB()) {
53
+ log.error(
54
+ "Managed auth mode requires DATABASE_URL for session storage. Skipping auth migration.",
55
+ );
56
+ _migrated = true;
57
+ return;
58
+ }
59
+
60
+ try {
61
+ const { getAuthInstance } = await import("@atlas/api/lib/auth/server");
62
+ const auth = getAuthInstance();
63
+ const ctx = await auth.$context;
64
+ await ctx.runMigrations();
65
+ log.info("Better Auth migration complete");
66
+ await bootstrapAdminUser();
67
+ } catch (err) {
68
+ log.error({ err }, "Better Auth migration failed — managed auth may not work");
69
+ _migrationError = "Connected to the internal database but Better Auth migration failed. Managed auth may not work. Check database permissions (CREATE TABLE).";
70
+ }
71
+
72
+ _migrated = true;
73
+ }
74
+
75
+ /**
76
+ * Promote an existing user to admin on upgrade if ATLAS_ADMIN_EMAIL is set
77
+ * and no admin exists yet. Handles the case where users were created before
78
+ * the admin plugin added the `role` column.
79
+ */
80
+ async function bootstrapAdminUser(): Promise<void> {
81
+ const adminEmail = process.env.ATLAS_ADMIN_EMAIL?.toLowerCase().trim();
82
+ if (!adminEmail) return;
83
+
84
+ try {
85
+ const existing = await internalQuery<{ count: string }>(
86
+ `SELECT COUNT(*) as count FROM "user" WHERE role = 'admin'`,
87
+ );
88
+ if (parseInt(String(existing[0]?.count ?? "0"), 10) > 0) {
89
+ log.debug("Bootstrap: admin user already exists — skipping promotion");
90
+ return;
91
+ }
92
+
93
+ const result = await internalQuery<{ id: string; email: string }>(
94
+ `UPDATE "user" SET role = 'admin' WHERE LOWER(email) = $1 RETURNING id, email`,
95
+ [adminEmail],
96
+ );
97
+ if (result.length > 0) {
98
+ log.info({ email: result[0].email, id: result[0].id }, "Bootstrap: existing user promoted to admin via ATLAS_ADMIN_EMAIL");
99
+ } else {
100
+ log.warn({ adminEmail }, "Bootstrap: ATLAS_ADMIN_EMAIL is set but no user with that email exists yet — role will be assigned on first signup");
101
+ }
102
+ } catch (err) {
103
+ log.error({ err }, "Bootstrap admin promotion failed — admin console may be inaccessible");
104
+ }
105
+ }
106
+
107
+ /** Reset migration state. For testing only. */
108
+ export function resetMigrationState(): void {
109
+ _migrated = false;
110
+ _migrationError = null;
111
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Role-based action permissions.
3
+ *
4
+ * Determines whether a user can approve an action based on their role
5
+ * and the action's approval mode. Roles are extracted from the authenticated
6
+ * user, with defaults that vary by auth mode.
7
+ *
8
+ * Role hierarchy: admin > analyst > viewer
9
+ *
10
+ * | Approval mode | viewer | analyst | admin |
11
+ * |---------------|--------|---------|-------|
12
+ * | auto | yes* | yes* | yes* |
13
+ * | manual | no | yes | yes |
14
+ * | admin-only | no | no | yes |
15
+ *
16
+ * * Auto-approved actions are executed immediately in handleAction and never
17
+ * reach the approval endpoint. canApprove returns true for any authenticated
18
+ * user when mode is "auto".
19
+ */
20
+
21
+ import type { AtlasUser, AtlasRole } from "@atlas/api/lib/auth/types";
22
+ import type { ActionApprovalMode } from "@atlas/api/lib/action-types";
23
+ import { ATLAS_ROLES } from "@atlas/api/lib/auth/types";
24
+ import { createLogger } from "@atlas/api/lib/logger";
25
+
26
+ const log = createLogger("auth:permissions");
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Role hierarchy
30
+ // ---------------------------------------------------------------------------
31
+
32
+ const ROLE_LEVEL: Record<AtlasRole, number> = {
33
+ viewer: 0,
34
+ analyst: 1,
35
+ admin: 2,
36
+ };
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Role extraction
40
+ // ---------------------------------------------------------------------------
41
+
42
+ /**
43
+ * Default role for each auth mode when the user object does not carry
44
+ * an explicit role.
45
+ * - simple-key: analyst (overridable via ATLAS_API_KEY_ROLE)
46
+ * - managed: viewer (role comes from Better Auth organization plugin)
47
+ * - byot: viewer (role comes from JWT claim)
48
+ */
49
+ const AUTH_MODE_DEFAULT_ROLE: Record<string, AtlasRole> = {
50
+ "simple-key": "analyst",
51
+ managed: "viewer",
52
+ byot: "viewer",
53
+ };
54
+
55
+ /**
56
+ * Get the effective role for a user. Falls back to auth-mode defaults
57
+ * when the user has no explicit role set.
58
+ */
59
+ export function getUserRole(user: AtlasUser): AtlasRole {
60
+ if (user.role) return user.role;
61
+ return AUTH_MODE_DEFAULT_ROLE[user.mode] ?? "viewer";
62
+ }
63
+
64
+ /**
65
+ * Parse and validate a role string. Returns the role if valid, undefined otherwise.
66
+ */
67
+ export function parseRole(value: string | undefined): AtlasRole | undefined {
68
+ if (!value) return undefined;
69
+ const lower = value.toLowerCase().trim();
70
+ if ((ATLAS_ROLES as readonly string[]).includes(lower)) {
71
+ return lower as AtlasRole;
72
+ }
73
+ return undefined;
74
+ }
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Permission check
78
+ // ---------------------------------------------------------------------------
79
+
80
+ /**
81
+ * Minimum role required for each approval mode.
82
+ * Auto-approved actions bypass this check entirely (no human approval needed).
83
+ */
84
+ const APPROVAL_MODE_MIN_ROLE: Record<ActionApprovalMode, AtlasRole> = {
85
+ auto: "viewer", // Not actually checked — auto actions don't need approval
86
+ manual: "analyst",
87
+ "admin-only": "admin",
88
+ };
89
+
90
+ /**
91
+ * Check whether a user can approve an action given its approval configuration.
92
+ *
93
+ * @param user - The authenticated user attempting to approve. If undefined
94
+ * (no-auth mode), approval is always denied.
95
+ * @param approvalMode - The action's effective approval mode (auto/manual/admin-only).
96
+ * @param requiredRole - Optional per-action role override from config. When set,
97
+ * this takes precedence over the approval mode's default role requirement.
98
+ * @returns true if the user has sufficient permissions to approve.
99
+ */
100
+ export function canApprove(
101
+ user: AtlasUser | undefined,
102
+ approvalMode: ActionApprovalMode,
103
+ requiredRole?: AtlasRole,
104
+ ): boolean {
105
+ // No user = no-auth mode. Actions require identity.
106
+ if (!user) {
107
+ log.debug("canApprove: denied — no authenticated user");
108
+ return false;
109
+ }
110
+
111
+ // Auto-approved actions don't need human approval
112
+ if (approvalMode === "auto") {
113
+ return true;
114
+ }
115
+
116
+ const userRole = getUserRole(user);
117
+ const userLevel = ROLE_LEVEL[userRole];
118
+
119
+ // If a per-action requiredRole is set, use it as the minimum
120
+ if (requiredRole) {
121
+ const requiredLevel = ROLE_LEVEL[requiredRole];
122
+ const modeMinRole = APPROVAL_MODE_MIN_ROLE[approvalMode];
123
+ const modeMinLevel = ROLE_LEVEL[modeMinRole];
124
+ if (requiredLevel < modeMinLevel) {
125
+ log.warn(
126
+ { approvalMode, requiredRole, modeMinRole },
127
+ "Per-action requiredRole (%s) is lower than approval mode default (%s) — this weakens the '%s' mode for this action",
128
+ requiredRole,
129
+ modeMinRole,
130
+ approvalMode,
131
+ );
132
+ }
133
+ const allowed = userLevel >= requiredLevel;
134
+ if (!allowed) {
135
+ log.debug(
136
+ { userId: user.id, userRole, requiredRole, approvalMode },
137
+ "canApprove: denied — user role below per-action requiredRole",
138
+ );
139
+ }
140
+ return allowed;
141
+ }
142
+
143
+ // Otherwise, use the approval mode's default minimum role
144
+ const minRole = APPROVAL_MODE_MIN_ROLE[approvalMode];
145
+ const minLevel = ROLE_LEVEL[minRole];
146
+ const allowed = userLevel >= minLevel;
147
+
148
+ if (!allowed) {
149
+ log.debug(
150
+ { userId: user.id, userRole, minRole, approvalMode },
151
+ "canApprove: denied — user role below approval mode minimum",
152
+ );
153
+ }
154
+
155
+ return allowed;
156
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Better Auth server instance — lazy singleton.
3
+ *
4
+ * The betterAuth() instance is created on first call to getAuthInstance(),
5
+ * so no Better Auth initialization (database connections, table migrations)
6
+ * happens unless managed mode is actively used. Although this module is
7
+ * loaded into the module graph via static imports (managed.ts → middleware.ts),
8
+ * the actual betterAuth() constructor is deferred until the first managed-mode
9
+ * request invokes getAuthInstance(). The catch-all route additionally uses
10
+ * dynamic import() for the better-auth/next-js adapter, keeping that
11
+ * subpackage out of the bundle for non-managed deployments.
12
+ */
13
+
14
+ import { betterAuth } from "better-auth";
15
+ import { bearer, admin } from "better-auth/plugins";
16
+ // @better-auth/api-key must match the better-auth core version.
17
+ // Both are pinned to ^1.5.1 in package.json — update together.
18
+ import { apiKey } from "@better-auth/api-key";
19
+ import { getInternalDB, hasInternalDB, internalQuery } from "@atlas/api/lib/db/internal";
20
+ import { createLogger } from "@atlas/api/lib/logger";
21
+
22
+ const log = createLogger("auth:server");
23
+
24
+ /**
25
+ * Intentionally typed as the base Auth type (without plugin extensions).
26
+ * The codebase only uses .handler, .api.getSession, and .$context — all of
27
+ * which exist on the base type. Plugin-specific API methods (e.g.
28
+ * createApiKey) are handled through Better Auth's HTTP handler, not called
29
+ * directly on this instance.
30
+ *
31
+ * The `as unknown as AuthInstance` cast below exists because
32
+ * @better-auth/api-key and the admin plugin return plugin types that make
33
+ * the concrete Auth<Options> nominally incompatible with
34
+ * Auth<BetterAuthOptions>. This is safe because the base type is a
35
+ * structural subset of the actual instance.
36
+ */
37
+ type AuthInstance = ReturnType<typeof betterAuth>;
38
+
39
+ let _instance: AuthInstance | null = null;
40
+
41
+ export function getAuthInstance(): AuthInstance {
42
+ if (_instance) return _instance;
43
+
44
+ const secret = process.env.BETTER_AUTH_SECRET;
45
+ if (!secret) {
46
+ throw new Error(
47
+ "BETTER_AUTH_SECRET is not set. Managed auth mode requires this environment variable.",
48
+ );
49
+ }
50
+ if (secret.length < 32) {
51
+ throw new Error(
52
+ `BETTER_AUTH_SECRET must be at least 32 characters (got ${secret.length}). Use a cryptographically random string.`,
53
+ );
54
+ }
55
+
56
+ const adminEmail = process.env.ATLAS_ADMIN_EMAIL?.toLowerCase().trim();
57
+
58
+ // Derive parent domain for cross-subdomain cookies (e.g. "useatlas.dev" from
59
+ // BETTER_AUTH_URL="https://api.useatlas.dev"). Only enabled when CORS origin
60
+ // is set (i.e. cross-origin deployment). Without this, cookies are scoped to
61
+ // the API subdomain and won't be sent from the frontend subdomain.
62
+ const corsOrigin = process.env.ATLAS_CORS_ORIGIN;
63
+ let cookieDomain: string | undefined;
64
+ if (corsOrigin && process.env.BETTER_AUTH_URL) {
65
+ try {
66
+ const host = new URL(process.env.BETTER_AUTH_URL).hostname;
67
+ const parts = host.split(".");
68
+ if (parts.length >= 2) {
69
+ cookieDomain = parts.slice(-2).join(".");
70
+ }
71
+ } catch { /* ignore malformed URL */ }
72
+ }
73
+
74
+ const instance = betterAuth({
75
+ // getInternalDB() returns a pg.Pool typed as InternalPool.
76
+ // Cast needed because Better Auth expects its own pool/adapter type.
77
+ database: getInternalDB() as unknown as Parameters<typeof betterAuth>[0]["database"],
78
+ secret,
79
+ baseURL: process.env.BETTER_AUTH_URL,
80
+ emailAndPassword: {
81
+ enabled: true,
82
+ requireEmailVerification: false,
83
+ autoSignIn: true,
84
+ },
85
+ session: {
86
+ expiresIn: 60 * 60 * 24 * 7,
87
+ updateAge: 60 * 60 * 24,
88
+ cookieCache: { enabled: true, maxAge: 5 * 60 },
89
+ },
90
+ plugins: [bearer(), apiKey(), admin({ defaultRole: "analyst", adminRoles: ["admin"] })],
91
+ trustedOrigins:
92
+ process.env.BETTER_AUTH_TRUSTED_ORIGINS?.split(",")
93
+ .map((s) => s.trim())
94
+ .filter(Boolean) || [],
95
+ advanced: cookieDomain ? {
96
+ defaultCookieAttributes: {
97
+ domain: `.${cookieDomain}`,
98
+ },
99
+ } : undefined,
100
+ databaseHooks: {
101
+ user: {
102
+ create: {
103
+ before: async (user) => {
104
+ try {
105
+ if (adminEmail && user.email?.toLowerCase().trim() === adminEmail) {
106
+ log.info({ email: user.email }, "Bootstrap: promoting signup to admin (ATLAS_ADMIN_EMAIL match)");
107
+ return { data: { ...user, role: "admin" } };
108
+ }
109
+
110
+ if (!adminEmail) {
111
+ if (!hasInternalDB()) return;
112
+ const rows = await internalQuery<{ id: string }>(
113
+ `SELECT id FROM "user" WHERE role = 'admin' LIMIT 1`,
114
+ );
115
+ if (rows.length === 0) {
116
+ log.info({ email: user.email }, "Bootstrap: no admin exists — promoting first signup to admin");
117
+ return { data: { ...user, role: "admin" } };
118
+ }
119
+ }
120
+ } catch (err) {
121
+ log.error({ err }, "Bootstrap admin check failed — defaulting to normal role assignment");
122
+ }
123
+ },
124
+ },
125
+ },
126
+ },
127
+ }) as unknown as AuthInstance;
128
+
129
+ _instance = instance;
130
+ return instance;
131
+ }
132
+
133
+ export function resetAuthInstance(): void {
134
+ _instance = null;
135
+ }
136
+
137
+ /** @internal — test-only. Inject a mock auth instance. */
138
+ export function _setAuthInstance(mock: AuthInstance | null): void {
139
+ _instance = mock;
140
+ }
141
+
142
+ export type Auth = AuthInstance;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Simple API key authentication.
3
+ *
4
+ * Validates requests against ATLAS_API_KEY using constant-time comparison.
5
+ * Extracts key from Authorization: Bearer <key> or X-API-Key: <key> header.
6
+ */
7
+
8
+ import { createHash, timingSafeEqual } from "crypto";
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 { createLogger } from "@atlas/api/lib/logger";
13
+
14
+ const log = createLogger("auth");
15
+
16
+ /** Extract API key from request headers. Authorization header takes precedence. */
17
+ function extractKey(req: Request): string | null {
18
+ const authHeader = req.headers.get("authorization");
19
+ if (authHeader) {
20
+ const match = authHeader.match(/^Bearer\s+(.+)$/i);
21
+ if (match) return match[1];
22
+ log.warn(
23
+ { scheme: authHeader.split(" ")[0] },
24
+ "Authorization header present but not in 'Bearer <key>' format",
25
+ );
26
+ }
27
+
28
+ const xApiKey = req.headers.get("x-api-key");
29
+ if (xApiKey) return xApiKey;
30
+
31
+ return null;
32
+ }
33
+
34
+ /** SHA-256 hash of a string, returned as hex. */
35
+ function sha256(input: string): string {
36
+ return createHash("sha256").update(input).digest("hex");
37
+ }
38
+
39
+ /** Validate a request against ATLAS_API_KEY. */
40
+ export function validateApiKey(req: Request): AuthResult {
41
+ const expected = process.env.ATLAS_API_KEY;
42
+ if (!expected) {
43
+ log.warn("ATLAS_API_KEY not configured but simple-key auth attempted");
44
+ return { authenticated: false, mode: "simple-key", status: 401, error: "API key not configured" };
45
+ }
46
+
47
+ const key = extractKey(req);
48
+ if (!key) {
49
+ return { authenticated: false, mode: "simple-key", status: 401, error: "API key required" };
50
+ }
51
+
52
+ // Hash both to fixed-length digests so timingSafeEqual never leaks key length
53
+ const keyHash = createHash("sha256").update(key).digest();
54
+ const expectedHash = createHash("sha256").update(expected).digest();
55
+
56
+ if (!timingSafeEqual(keyHash, expectedHash)) {
57
+ log.warn("API key validation failed");
58
+ return { authenticated: false, mode: "simple-key", status: 401, error: "Invalid API key" };
59
+ }
60
+
61
+ const id = `api-key-${sha256(key).slice(0, 8)}`;
62
+ const label = `api-key-${key.slice(0, 4)}`;
63
+
64
+ // Role override via ATLAS_API_KEY_ROLE (default: analyst — see permissions.ts)
65
+ const rawRole = process.env.ATLAS_API_KEY_ROLE;
66
+ const role = parseRole(rawRole);
67
+ if (rawRole && !role) {
68
+ log.warn({ value: rawRole, validRoles: ["viewer", "analyst", "admin"] }, "ATLAS_API_KEY_ROLE is set to an invalid value — defaulting to 'analyst'. Valid values: viewer, analyst, admin.");
69
+ }
70
+
71
+ // Parse optional claims from env var for RLS policy evaluation
72
+ let claims: Record<string, unknown> | undefined;
73
+ const rawClaims = process.env.ATLAS_RLS_CLAIMS;
74
+ if (rawClaims) {
75
+ try {
76
+ const parsed = JSON.parse(rawClaims);
77
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
78
+ claims = parsed;
79
+ } else {
80
+ log.warn("ATLAS_RLS_CLAIMS must be a JSON object — ignoring");
81
+ }
82
+ } catch {
83
+ log.warn({ value: rawClaims.slice(0, 50) }, "ATLAS_RLS_CLAIMS is not valid JSON — ignoring");
84
+ }
85
+ }
86
+
87
+ return {
88
+ authenticated: true,
89
+ mode: "simple-key",
90
+ user: createAtlasUser(id, "simple-key", label, role, claims),
91
+ };
92
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Auth types for Atlas.
3
+ *
4
+ * AuthMode determines how requests are authenticated.
5
+ * AtlasRole determines the user's permission level for action approval.
6
+ * AtlasUser represents a verified identity attached to a request.
7
+ * AuthResult is the return type from all auth validators.
8
+ */
9
+
10
+ export const AUTH_MODES = ["none", "simple-key", "managed", "byot"] as const;
11
+ export type AuthMode = (typeof AUTH_MODES)[number];
12
+
13
+ export const ATLAS_ROLES = ["viewer", "analyst", "admin"] as const;
14
+ export type AtlasRole = (typeof ATLAS_ROLES)[number];
15
+
16
+ export interface AtlasUser {
17
+ id: string;
18
+ mode: Exclude<AuthMode, "none">;
19
+ label: string;
20
+ /** Permission role for action approval. Defaults based on auth mode when not set. */
21
+ role?: AtlasRole;
22
+ /** Auth-source claims for RLS policy evaluation (JWT payload, session user, or env-derived). */
23
+ claims?: Readonly<Record<string, unknown>>;
24
+ }
25
+
26
+ export type AuthResult =
27
+ | { authenticated: true; mode: Exclude<AuthMode, "none">; user: AtlasUser }
28
+ | { authenticated: true; mode: "none"; user: undefined }
29
+ | { authenticated: false; mode: AuthMode; status: 401 | 500; error: string };
30
+
31
+ /** Create a frozen AtlasUser with non-empty id/label validation. */
32
+ export function createAtlasUser(
33
+ id: string,
34
+ mode: Exclude<AuthMode, "none">,
35
+ label: string,
36
+ role?: AtlasRole,
37
+ claims?: Record<string, unknown>,
38
+ ): AtlasUser {
39
+ if (!id) throw new Error("AtlasUser id must be non-empty");
40
+ if (!label) throw new Error("AtlasUser label must be non-empty");
41
+ const frozenClaims = claims ? Object.freeze({ ...claims }) : undefined;
42
+ return Object.freeze({
43
+ id,
44
+ mode,
45
+ label,
46
+ ...(role ? { role } : {}),
47
+ ...(frozenClaims ? { claims: frozenClaims } : {}),
48
+ });
49
+ }