@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,704 @@
1
+ /**
2
+ * Declarative configuration for Atlas.
3
+ *
4
+ * Loads configuration from `atlas.config.ts` in the project root (if present),
5
+ * falling back to environment variables for backward compatibility. The config
6
+ * file is optional and additive — existing env-var-only deploys work without
7
+ * changes.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * // atlas.config.ts
12
+ * import { defineConfig } from "@atlas/api/lib/config";
13
+ *
14
+ * export default defineConfig({
15
+ * datasources: {
16
+ * default: { url: process.env.ATLAS_DATASOURCE_URL! },
17
+ * warehouse: { url: "postgresql://...", schema: "analytics", description: "Data warehouse" },
18
+ * },
19
+ * tools: ["explore", "executeSQL"],
20
+ * auth: "auto",
21
+ * semanticLayer: "./semantic",
22
+ * });
23
+ * ```
24
+ */
25
+
26
+ import { z } from "zod";
27
+ import * as fs from "fs";
28
+ import * as path from "path";
29
+ import { createLogger } from "./logger";
30
+ import type { ConnectionRegistry } from "./db/connection";
31
+ import type { ToolRegistry } from "./tools/registry";
32
+ import { ACTION_APPROVAL_MODES, type ActionApprovalMode } from "@atlas/api/lib/action-types";
33
+ import { ATLAS_ROLES } from "@atlas/api/lib/auth/types";
34
+
35
+ const log = createLogger("config");
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Zod schema
39
+ // ---------------------------------------------------------------------------
40
+
41
+ const RateLimitConfigSchema = z.object({
42
+ /** Max queries per minute for this datasource. */
43
+ queriesPerMinute: z.number().int().positive().default(60),
44
+ /** Max concurrent queries for this datasource. */
45
+ concurrency: z.number().int().positive().default(5),
46
+ });
47
+
48
+ export type RateLimitConfig = z.infer<typeof RateLimitConfigSchema>;
49
+
50
+ const DatasourceConfigSchema = z.object({
51
+ /** Database connection string (postgresql://, mysql://, clickhouse://, snowflake://, duckdb://, or salesforce://). */
52
+ url: z.string().min(1, "Datasource URL must not be empty"),
53
+ /** PostgreSQL schema name (sets search_path). Ignored for MySQL, ClickHouse, Snowflake, DuckDB, and Salesforce. */
54
+ schema: z.string().optional(),
55
+ /** Human-readable description shown in the agent system prompt. */
56
+ description: z.string().optional(),
57
+ /** Max connections in the pool for this datasource. */
58
+ maxConnections: z.number().int().positive().optional(),
59
+ /** Idle timeout in milliseconds before a connection is closed. */
60
+ idleTimeoutMs: z.number().int().positive().optional(),
61
+ /** Per-source rate limiting configuration. */
62
+ rateLimit: RateLimitConfigSchema.optional(),
63
+ });
64
+
65
+ export type DatasourceConfig = z.infer<typeof DatasourceConfigSchema>;
66
+
67
+ const AuthConfigSchema = z.union([
68
+ z.literal("auto"),
69
+ z.literal("none"),
70
+ z.literal("api-key"),
71
+ z.literal("managed"),
72
+ z.literal("byot"),
73
+ ]);
74
+
75
+ export type AuthConfig = z.infer<typeof AuthConfigSchema>;
76
+
77
+ const ActionApprovalSchema = z.enum(ACTION_APPROVAL_MODES);
78
+
79
+ const ActionDefaultsSchema = z.object({
80
+ approval: ActionApprovalSchema.optional(),
81
+ timeout: z.number().int().positive().optional(),
82
+ maxPerConversation: z.number().int().positive().optional(),
83
+ });
84
+
85
+ export type ActionDefaults = z.infer<typeof ActionDefaultsSchema>;
86
+
87
+ const AtlasRoleSchema = z.enum(ATLAS_ROLES);
88
+
89
+ const PerActionConfigSchema = z.object({
90
+ enabled: z.boolean().optional(),
91
+ approval: ActionApprovalSchema.optional(),
92
+ /** Minimum role required to approve this action. Overrides the approval mode's default role mapping. */
93
+ requiredRole: AtlasRoleSchema.optional(),
94
+ credentials: z.record(z.string(), z.object({ env: z.string() })).optional(),
95
+ rateLimit: z.number().int().positive().optional(),
96
+ }).passthrough();
97
+
98
+ export type PerActionConfig = z.infer<typeof PerActionConfigSchema>;
99
+
100
+ const ActionsConfigSchema = z.object({
101
+ defaults: ActionDefaultsSchema.optional(),
102
+ }).catchall(PerActionConfigSchema);
103
+
104
+ export type ActionsConfig = z.infer<typeof ActionsConfigSchema>;
105
+
106
+ const RLSPolicySchema = z.object({
107
+ /** Tables this policy applies to. Use ["*"] for all tables. */
108
+ tables: z.array(z.string().min(1)).min(1),
109
+ /** Column name to filter on in the matched tables. Must be a valid SQL identifier. */
110
+ column: z.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, "Must be a valid column name"),
111
+ /** Claim path to extract the filter value from the user's claims. Supports dot-delimited paths. */
112
+ claim: z.string().min(1),
113
+ });
114
+
115
+ export type RLSPolicy = z.infer<typeof RLSPolicySchema>;
116
+
117
+ const RLSConfigSchema = z.object({
118
+ /** Whether RLS is active. When true, policies are enforced on every query. */
119
+ enabled: z.boolean().default(false),
120
+ /** RLS policies. Each policy maps a claim to a column on one or more tables. */
121
+ policies: z.array(RLSPolicySchema).default([]),
122
+ }).refine(
123
+ (cfg) => !cfg.enabled || cfg.policies.length > 0,
124
+ { message: "RLS is enabled but no policies are defined", path: ["policies"] },
125
+ );
126
+
127
+ export type RLSConfig = z.infer<typeof RLSConfigSchema>;
128
+
129
+ const AtlasConfigSchema = z.object({
130
+ /**
131
+ * Named datasource connections. The "default" key is used when no
132
+ * connectionId is specified. At least one datasource should be defined.
133
+ */
134
+ datasources: z.record(z.string(), DatasourceConfigSchema).optional(),
135
+
136
+ /**
137
+ * Tool names to enable. When omitted, defaults to the two core tools
138
+ * (explore, executeSQL).
139
+ */
140
+ tools: z.array(z.string()).optional(),
141
+
142
+ /**
143
+ * Auth mode. "auto" (default) auto-detects from env vars — same as the
144
+ * current behavior. Other values pin the mode explicitly.
145
+ */
146
+ auth: AuthConfigSchema.optional().default("auto"),
147
+
148
+ /**
149
+ * Path to the semantic layer directory, relative to the project root.
150
+ * Defaults to "./semantic".
151
+ */
152
+ semanticLayer: z.string().optional().default("./semantic"),
153
+
154
+ /**
155
+ * Action framework configuration. Per-action overrides use the action
156
+ * type as the key (e.g. `"slack:send"`). The special `defaults` key
157
+ * sets fallback values for all actions.
158
+ */
159
+ actions: ActionsConfigSchema.optional(),
160
+
161
+ /**
162
+ * Maximum total pool slots across all datasource pools.
163
+ * When a new datasource registration would exceed this limit,
164
+ * the least-recently-used datasource is evicted.
165
+ */
166
+ maxTotalConnections: z.number().int().positive().default(100),
167
+
168
+ /**
169
+ * Plugin instances to register at boot. Each element should satisfy
170
+ * the `AtlasPlugin` interface from `@atlas/plugin-sdk`.
171
+ *
172
+ * Zod validates structural shape (id, type, version) at config load
173
+ * time. Plugin-level configSchema validation happens at factory call
174
+ * time via `createPlugin()`.
175
+ */
176
+ plugins: z.array(z.unknown()).optional(),
177
+
178
+ /**
179
+ * Scheduler configuration for recurring scheduled tasks.
180
+ * Requires ATLAS_SCHEDULER_ENABLED=true to activate.
181
+ */
182
+ scheduler: z.object({
183
+ /** Execution backend: "bun" runs an in-process tick loop, "webhook" relies on external cron hitting POST /:id/run, "vercel" uses Vercel Cron hitting POST /tick. */
184
+ backend: z.enum(["bun", "webhook", "vercel"]).default("bun"),
185
+ /** Maximum concurrent task executions per tick. */
186
+ maxConcurrentTasks: z.number().int().positive().default(5),
187
+ /** Per-task execution timeout in milliseconds. */
188
+ taskTimeout: z.number().int().positive().default(60_000),
189
+ /** Tick interval in seconds (how often the scheduler checks for due tasks). */
190
+ tickIntervalSeconds: z.number().int().positive().default(60),
191
+ }).optional(),
192
+
193
+ /**
194
+ * Row-Level Security configuration. When enabled, every SQL query gets
195
+ * automatic WHERE clause injection based on the authenticated user's claims.
196
+ */
197
+ rls: RLSConfigSchema.optional(),
198
+ });
199
+
200
+ /** The output type after Zod parsing (defaults applied, all fields present). */
201
+ export type AtlasConfig = z.infer<typeof AtlasConfigSchema>;
202
+
203
+ /** The input type for user-authored config (optional fields allowed). */
204
+ export type AtlasConfigInput = z.input<typeof AtlasConfigSchema>;
205
+
206
+ /** Expose schemas for external validation (e.g. tests). */
207
+ export { AtlasConfigSchema, RateLimitConfigSchema, RLSPolicySchema, RLSConfigSchema };
208
+
209
+ /**
210
+ * The resolved config after merging the config file with env var defaults.
211
+ * Guaranteed to have all fields populated.
212
+ */
213
+ export interface ResolvedConfig {
214
+ datasources: Record<string, DatasourceConfig>;
215
+ tools: string[];
216
+ auth: AuthConfig;
217
+ semanticLayer: string;
218
+ /** Action framework configuration (optional, only when actions are enabled). */
219
+ actions?: ActionsConfig;
220
+ /** Maximum total pool slots across all datasource pools. */
221
+ maxTotalConnections: number;
222
+ /** Plugin instances to register at boot. */
223
+ plugins?: unknown[];
224
+ /** Scheduler configuration (only when ATLAS_SCHEDULER_ENABLED=true). */
225
+ scheduler?: {
226
+ backend: "bun" | "webhook" | "vercel";
227
+ maxConcurrentTasks: number;
228
+ taskTimeout: number;
229
+ tickIntervalSeconds: number;
230
+ };
231
+ /** Row-Level Security configuration. */
232
+ rls?: RLSConfig;
233
+ /** Whether the config was loaded from a file or synthesized from env vars. */
234
+ source: "file" | "env";
235
+ }
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // defineConfig() — type-safe authoring helper
239
+ // ---------------------------------------------------------------------------
240
+
241
+ /**
242
+ * Type-safe helper for authoring `atlas.config.ts`. Validates the config
243
+ * at build time via TypeScript and at runtime via Zod.
244
+ *
245
+ * Accepts the input shape (optional fields allowed). Zod applies defaults
246
+ * during `loadConfig()`.
247
+ */
248
+ export function defineConfig(config: AtlasConfigInput): AtlasConfigInput {
249
+ return config;
250
+ }
251
+
252
+ // ---------------------------------------------------------------------------
253
+ // Config loading
254
+ // ---------------------------------------------------------------------------
255
+
256
+ /**
257
+ * Resolved config singleton — populated by {@link loadConfig}.
258
+ * Starts as null; after `loadConfig()` runs, this is always set.
259
+ */
260
+ let _resolved: ResolvedConfig | null = null;
261
+
262
+ /**
263
+ * Return the current resolved config, or null if {@link loadConfig} has
264
+ * not been called yet.
265
+ */
266
+ export function getConfig(): ResolvedConfig | null {
267
+ return _resolved;
268
+ }
269
+
270
+ /**
271
+ * Build a ResolvedConfig from environment variables alone.
272
+ * This is the fallback path when no `atlas.config.ts` exists.
273
+ */
274
+ export function configFromEnv(): ResolvedConfig {
275
+ const datasources: Record<string, DatasourceConfig> = {};
276
+
277
+ if (process.env.ATLAS_DATASOURCE_URL) {
278
+ datasources.default = {
279
+ url: process.env.ATLAS_DATASOURCE_URL,
280
+ ...(process.env.ATLAS_SCHEMA ? { schema: process.env.ATLAS_SCHEMA } : {}),
281
+ };
282
+ }
283
+
284
+ // Action framework config from env vars
285
+ let actions: ActionsConfig | undefined;
286
+ if (process.env.ATLAS_ACTIONS_ENABLED === "true") {
287
+ const defaults: ActionDefaults = {};
288
+ const approval = process.env.ATLAS_ACTION_APPROVAL;
289
+ if (approval) {
290
+ if ((ACTION_APPROVAL_MODES as readonly string[]).includes(approval)) {
291
+ defaults.approval = approval as ActionApprovalMode;
292
+ } else {
293
+ log.warn({ value: approval, valid: ACTION_APPROVAL_MODES }, "Invalid ATLAS_ACTION_APPROVAL — using default 'manual'");
294
+ }
295
+ }
296
+ const timeout = parseInt(process.env.ATLAS_ACTION_TIMEOUT ?? "", 10);
297
+ if (Number.isFinite(timeout) && timeout > 0) {
298
+ defaults.timeout = timeout;
299
+ }
300
+ const maxPerConv = parseInt(process.env.ATLAS_ACTION_MAX_PER_CONVERSATION ?? "", 10);
301
+ if (Number.isFinite(maxPerConv) && maxPerConv > 0) {
302
+ defaults.maxPerConversation = maxPerConv;
303
+ }
304
+ actions = { defaults };
305
+ }
306
+
307
+ // Scheduler config from env vars
308
+ let scheduler: ResolvedConfig["scheduler"];
309
+ if (process.env.ATLAS_SCHEDULER_ENABLED === "true") {
310
+ const rawBackend = process.env.ATLAS_SCHEDULER_BACKEND;
311
+ const backend = rawBackend === "webhook" ? "webhook" as const : rawBackend === "vercel" ? "vercel" as const : "bun" as const;
312
+ const maxConcurrent = parseInt(process.env.ATLAS_SCHEDULER_MAX_CONCURRENT ?? "", 10);
313
+ const timeout = parseInt(process.env.ATLAS_SCHEDULER_TIMEOUT ?? "", 10);
314
+ const tick = parseInt(process.env.ATLAS_SCHEDULER_TICK_INTERVAL ?? "", 10);
315
+ scheduler = {
316
+ backend,
317
+ maxConcurrentTasks: Number.isFinite(maxConcurrent) && maxConcurrent > 0 ? maxConcurrent : 5,
318
+ taskTimeout: Number.isFinite(timeout) && timeout > 0 ? timeout : 60_000,
319
+ tickIntervalSeconds: Number.isFinite(tick) && tick > 0 ? tick : 60,
320
+ };
321
+ }
322
+
323
+ // RLS config from env vars (single-policy shorthand)
324
+ let rls: RLSConfig | undefined;
325
+ if (process.env.ATLAS_RLS_ENABLED === "true") {
326
+ const column = process.env.ATLAS_RLS_COLUMN;
327
+ const claim = process.env.ATLAS_RLS_CLAIM;
328
+ if (!column || !claim) {
329
+ throw new Error(
330
+ `ATLAS_RLS_ENABLED=true requires both ATLAS_RLS_COLUMN and ATLAS_RLS_CLAIM to be set. ` +
331
+ `Got: ATLAS_RLS_COLUMN=${column ?? "(unset)"}, ATLAS_RLS_CLAIM=${claim ?? "(unset)"}`,
332
+ );
333
+ }
334
+ rls = { enabled: true, policies: [{ tables: ["*"], column, claim }] };
335
+ }
336
+
337
+ return {
338
+ datasources,
339
+ tools: ["explore", "executeSQL"],
340
+ auth: "auto",
341
+ semanticLayer: "./semantic",
342
+ ...(actions ? { actions } : {}),
343
+ maxTotalConnections: 100,
344
+ ...(scheduler ? { scheduler } : {}),
345
+ ...(rls ? { rls } : {}),
346
+ source: "env",
347
+ };
348
+ }
349
+
350
+ /**
351
+ * Attempt to find and dynamically import `atlas.config.ts` (or `.js`, `.mjs`)
352
+ * from the project root. Returns null when no config file is found.
353
+ *
354
+ * Uses a cache-busting query parameter on the import path so that the file
355
+ * is always re-evaluated (important when the server restarts with a changed
356
+ * config).
357
+ */
358
+ async function tryLoadConfigFile(
359
+ projectRoot: string,
360
+ ): Promise<AtlasConfig | null> {
361
+ const candidates = [
362
+ "atlas.config.ts",
363
+ "atlas.config.js",
364
+ "atlas.config.mjs",
365
+ ];
366
+
367
+ for (const filename of candidates) {
368
+ const filePath = path.resolve(projectRoot, filename);
369
+ if (!fs.existsSync(filePath)) continue;
370
+
371
+ try {
372
+ log.info({ file: filePath }, "Loading config file");
373
+ // Cache-bust: append timestamp so Bun re-evaluates the module
374
+ const mod = await import(`${filePath}?t=${Date.now()}`);
375
+ if (mod.default === undefined || mod.default === null) {
376
+ throw new Error(
377
+ `Config file "${filename}" does not have a default export. ` +
378
+ `Use \`export default defineConfig({ ... })\` or \`module.exports = { ... }\`.`,
379
+ );
380
+ }
381
+ const raw = mod.default;
382
+ return raw;
383
+ } catch (err) {
384
+ // If the file exists but fails to parse/import, that is a hard error
385
+ // that the user needs to fix — do not silently fall back to env vars.
386
+ const detail = err instanceof Error ? err.message : String(err);
387
+ throw new Error(
388
+ `Failed to load config file "${filename}": ${detail}`,
389
+ { cause: err },
390
+ );
391
+ }
392
+ }
393
+
394
+ return null;
395
+ }
396
+
397
+ // ---------------------------------------------------------------------------
398
+ // Plugin shape validation
399
+ // ---------------------------------------------------------------------------
400
+
401
+ const VALID_PLUGIN_TYPES = new Set(["datasource", "context", "interaction", "action"]);
402
+
403
+ /**
404
+ * Validate plugin array entries have the required structural shape:
405
+ * `id` (non-empty string), `type` (valid PluginType), `version` (non-empty string).
406
+ *
407
+ * Also detects duplicate plugin IDs.
408
+ *
409
+ * This is the sole structural validation point for plugins in the config
410
+ * pipeline — it must not be removed. Plugin-level configSchema validation
411
+ * is handled by `createPlugin()` at factory call time (typically before
412
+ * this runs, during config file evaluation).
413
+ *
414
+ * @throws {Error} When any plugin entry fails validation.
415
+ */
416
+ function validatePlugins(plugins: unknown[]): void {
417
+ const errors: string[] = [];
418
+ const seenIds = new Map<string, number>();
419
+
420
+ for (let i = 0; i < plugins.length; i++) {
421
+ const p = plugins[i];
422
+
423
+ if (p === null || p === undefined || typeof p !== "object") {
424
+ errors.push(`plugin at index ${i}: expected a plugin object, got ${p === null ? "null" : typeof p}`);
425
+ continue;
426
+ }
427
+
428
+ const obj = p as Record<string, unknown>;
429
+
430
+ // Build label upfront: use plugin id if available, otherwise index
431
+ const hasId = "id" in obj && typeof obj.id === "string" && obj.id.trim();
432
+ const label = hasId ? `plugin "${obj.id}" (index ${i})` : `plugin at index ${i}`;
433
+
434
+ // id
435
+ if (!("id" in obj) || typeof obj.id !== "string") {
436
+ errors.push(`${label} is missing "id" (string)`);
437
+ } else if (!obj.id.trim()) {
438
+ errors.push(`${label} has an empty "id"`);
439
+ } else {
440
+ // Duplicate id check
441
+ const prevIndex = seenIds.get(obj.id);
442
+ if (prevIndex !== undefined) {
443
+ errors.push(`${label} has duplicate id "${obj.id}" (first seen at index ${prevIndex})`);
444
+ } else {
445
+ seenIds.set(obj.id, i);
446
+ }
447
+ }
448
+
449
+ // type
450
+ if (!("type" in obj) || typeof obj.type !== "string") {
451
+ errors.push(`${label} is missing "type" (string)`);
452
+ } else if (!VALID_PLUGIN_TYPES.has(obj.type)) {
453
+ errors.push(`${label} has invalid type "${obj.type}" — must be one of: datasource, context, interaction, action`);
454
+ }
455
+
456
+ // version
457
+ if (!("version" in obj) || typeof obj.version !== "string") {
458
+ errors.push(`${label} is missing "version" (string)`);
459
+ } else if (!obj.version.trim()) {
460
+ errors.push(`${label} has an empty "version"`);
461
+ }
462
+ }
463
+
464
+ if (errors.length > 0) {
465
+ throw new Error(
466
+ `Invalid plugin configuration:\n${errors.map((e) => ` - ${e}`).join("\n")}`,
467
+ );
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Validate a raw config object against the Zod schema and return a
473
+ * ResolvedConfig. Throws on validation failure with human-readable errors.
474
+ */
475
+ export function validateAndResolve(raw: unknown): ResolvedConfig {
476
+ if (raw !== null && (typeof raw !== "object" || Array.isArray(raw))) {
477
+ throw new Error(
478
+ `atlas.config.ts must export a plain object. Got ${Array.isArray(raw) ? "array" : typeof raw}.`,
479
+ );
480
+ }
481
+
482
+ const parseResult = AtlasConfigSchema.safeParse(raw);
483
+ if (!parseResult.success) {
484
+ const formatted = parseResult.error.issues
485
+ .map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`)
486
+ .join("\n");
487
+ throw new Error(`Invalid atlas.config.ts:\n${formatted}`);
488
+ }
489
+
490
+ const config = parseResult.data;
491
+
492
+ // Structural validation of plugin entries (id, type, version)
493
+ if (config.plugins?.length) {
494
+ validatePlugins(config.plugins);
495
+ }
496
+
497
+ return {
498
+ datasources: config.datasources ?? {},
499
+ tools: config.tools ?? ["explore", "executeSQL"],
500
+ auth: config.auth ?? "auto",
501
+ semanticLayer: config.semanticLayer ?? "./semantic",
502
+ ...(config.actions ? { actions: config.actions } : {}),
503
+ maxTotalConnections: config.maxTotalConnections ?? 100,
504
+ ...(config.plugins?.length ? { plugins: config.plugins } : {}),
505
+ ...(config.scheduler ? { scheduler: config.scheduler } : {}),
506
+ ...(config.rls ? { rls: config.rls } : {}),
507
+ source: "file",
508
+ };
509
+ }
510
+
511
+ /**
512
+ * Load and validate Atlas configuration.
513
+ *
514
+ * Resolution order:
515
+ * 1. Look for `atlas.config.ts` (or .js/.mjs) in the project root.
516
+ * 2. If found, parse and validate with Zod.
517
+ * 3. If not found, build a config from environment variables.
518
+ *
519
+ * The resolved config is cached as a module-level singleton accessible
520
+ * via {@link getConfig}.
521
+ *
522
+ * @param projectRoot - The directory to search for config files. Defaults
523
+ * to `process.cwd()`.
524
+ * @throws {Error} When the config file exists but is invalid (Zod errors
525
+ * are formatted into a human-readable message).
526
+ */
527
+ export async function loadConfig(
528
+ projectRoot: string = process.cwd(),
529
+ ): Promise<ResolvedConfig> {
530
+ const raw = await tryLoadConfigFile(projectRoot);
531
+
532
+ if (raw === null) {
533
+ log.info("No atlas.config.ts found — using environment variables");
534
+ const resolved = configFromEnv();
535
+ _resolved = resolved;
536
+ return resolved;
537
+ }
538
+
539
+ const resolved = validateAndResolve(raw);
540
+
541
+ log.info(
542
+ {
543
+ datasources: Object.keys(resolved.datasources),
544
+ tools: resolved.tools,
545
+ auth: resolved.auth,
546
+ semanticLayer: resolved.semanticLayer,
547
+ },
548
+ "Config loaded from file",
549
+ );
550
+
551
+ _resolved = resolved;
552
+ return resolved;
553
+ }
554
+
555
+ // ---------------------------------------------------------------------------
556
+ // Wiring helpers — apply config to ConnectionRegistry and ToolRegistry
557
+ // ---------------------------------------------------------------------------
558
+
559
+ /**
560
+ * Register datasources from the resolved config into the ConnectionRegistry.
561
+ * Skips if no datasources are defined (the registry will lazy-init from
562
+ * ATLAS_DATASOURCE_URL on first access, preserving backward compat).
563
+ *
564
+ * @param config - The resolved configuration.
565
+ * @param registry - The ConnectionRegistry to register into. When omitted,
566
+ * uses the global singleton from `./db/connection`.
567
+ */
568
+ export async function applyDatasources(
569
+ config: ResolvedConfig,
570
+ registry?: ConnectionRegistry,
571
+ ): Promise<void> {
572
+ if (Object.keys(config.datasources).length === 0) {
573
+ log.debug("No datasources in config — ConnectionRegistry will use env var fallback");
574
+ return;
575
+ }
576
+
577
+ const connRegistry = registry ?? (await import("./db/connection")).connections;
578
+ const { detectDBType } = await import("./db/connection");
579
+
580
+ connRegistry.setMaxTotalConnections(config.maxTotalConnections);
581
+
582
+ for (const [id, ds] of Object.entries(config.datasources)) {
583
+ try {
584
+ const dbType = detectDBType(ds.url);
585
+ if (dbType === "salesforce") {
586
+ log.info({ connectionId: id }, "Registering Salesforce datasource from config");
587
+ const { parseSalesforceURL, registerSalesforceSource } = await import("./db/salesforce");
588
+ const sfConfig = parseSalesforceURL(ds.url);
589
+ registerSalesforceSource(id, sfConfig);
590
+ } else {
591
+ log.info({ connectionId: id }, "Registering datasource from config");
592
+ connRegistry.register(id, {
593
+ url: ds.url,
594
+ schema: ds.schema,
595
+ description: ds.description,
596
+ maxConnections: ds.maxConnections,
597
+ idleTimeoutMs: ds.idleTimeoutMs,
598
+ });
599
+
600
+ // Fire initial health check — logs on failure but does not block startup.
601
+ // A degraded connection is still usable (the DB may recover).
602
+ connRegistry.healthCheck(id).then((result) => {
603
+ if (result.status !== "healthy") {
604
+ log.warn(
605
+ { connectionId: id, status: result.status, message: result.message },
606
+ "Datasource registered but initial health check failed — connection may be misconfigured",
607
+ );
608
+ }
609
+ }).catch((healthErr) => {
610
+ log.warn(
611
+ { err: healthErr instanceof Error ? healthErr.message : String(healthErr), connectionId: id },
612
+ "Initial health check failed after registration",
613
+ );
614
+ });
615
+ }
616
+
617
+ if (ds.rateLimit) {
618
+ const { registerSourceRateLimit } = await import("./db/source-rate-limit");
619
+ registerSourceRateLimit(id, {
620
+ queriesPerMinute: ds.rateLimit.queriesPerMinute,
621
+ concurrency: ds.rateLimit.concurrency,
622
+ });
623
+ }
624
+ } catch (err) {
625
+ const detail = err instanceof Error ? err.message : String(err);
626
+ throw new Error(
627
+ `Failed to register datasource "${id}": ${detail}`,
628
+ { cause: err },
629
+ );
630
+ }
631
+ }
632
+ }
633
+
634
+ /**
635
+ * Validate that the tool names in the config match registered tools in the
636
+ * default registry. Throws if any tool names are unrecognized.
637
+ *
638
+ * @param config - The resolved configuration.
639
+ * @param registry - The ToolRegistry to validate against. When omitted,
640
+ * uses the default registry from `./tools/registry`.
641
+ * @throws {Error} When config references tool names not in the registry.
642
+ */
643
+ export async function validateToolConfig(
644
+ config: ResolvedConfig,
645
+ registry?: ToolRegistry,
646
+ ): Promise<void> {
647
+ const toolRegistry = registry ?? (await import("./tools/registry")).defaultRegistry;
648
+
649
+ const unknownTools: string[] = [];
650
+ for (const toolName of config.tools) {
651
+ if (!toolRegistry.get(toolName)) {
652
+ unknownTools.push(toolName);
653
+ }
654
+ }
655
+ if (unknownTools.length > 0) {
656
+ const available = Object.keys(toolRegistry.getAll());
657
+ throw new Error(
658
+ `Unknown tool(s) in config: ${unknownTools.join(", ")}. ` +
659
+ `Available: ${available.join(", ")}.`,
660
+ );
661
+ }
662
+ }
663
+
664
+ // ---------------------------------------------------------------------------
665
+ // Startup integration — single entry point for server boot
666
+ // ---------------------------------------------------------------------------
667
+
668
+ /**
669
+ * Load config, wire datasources, and validate tool names.
670
+ * Call this once during server startup (e.g. in server.ts).
671
+ *
672
+ * @param projectRoot - Directory to search for config files.
673
+ * @param opts - Optional dependency injection for registries (used in tests).
674
+ * @throws {Error} When the config file is present but invalid.
675
+ */
676
+ export async function initializeConfig(
677
+ projectRoot?: string,
678
+ opts?: {
679
+ connectionRegistry?: ConnectionRegistry;
680
+ toolRegistry?: ToolRegistry;
681
+ },
682
+ ): Promise<ResolvedConfig> {
683
+ const config = await loadConfig(projectRoot);
684
+ const connRegistry = opts?.connectionRegistry;
685
+ await applyDatasources(config, connRegistry);
686
+ await validateToolConfig(config, opts?.toolRegistry);
687
+
688
+ // Start periodic health checks when datasources are registered
689
+ const registry = connRegistry ?? (await import("./db/connection")).connections;
690
+ if (registry.list().length > 0) {
691
+ registry.startHealthChecks();
692
+ }
693
+
694
+ return config;
695
+ }
696
+
697
+ // ---------------------------------------------------------------------------
698
+ // Test helper
699
+ // ---------------------------------------------------------------------------
700
+
701
+ /** Reset the cached config. For testing only. */
702
+ export function _resetConfig(): void {
703
+ _resolved = null;
704
+ }