@useatlas/create 0.0.2 → 0.0.4

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 (296) hide show
  1. package/README.md +4 -18
  2. package/index.ts +191 -31
  3. package/package.json +1 -1
  4. package/templates/docker/.env.example +3 -3
  5. package/templates/docker/Dockerfile.sidecar +28 -0
  6. package/templates/docker/bin/__tests__/plugin-cli.test.ts +9 -9
  7. package/templates/docker/bin/atlas.ts +108 -44
  8. package/templates/docker/data/demo-semantic/catalog.yml +51 -27
  9. package/templates/docker/data/demo-semantic/entities/accounts.yml +95 -103
  10. package/templates/docker/data/demo-semantic/entities/companies.yml +88 -152
  11. package/templates/docker/data/demo-semantic/entities/people.yml +82 -95
  12. package/templates/docker/data/demo-semantic/glossary.yml +104 -8
  13. package/templates/docker/data/demo-semantic/metrics/accounts.yml +62 -23
  14. package/templates/docker/data/demo-semantic/metrics/companies.yml +52 -78
  15. package/templates/docker/docker-compose.yml +1 -1
  16. package/templates/docker/docs/deploy.md +2 -39
  17. package/templates/docker/package.json +17 -1
  18. package/templates/docker/semantic/catalog.yml +62 -3
  19. package/templates/docker/semantic/entities/accounts.yml +162 -0
  20. package/templates/docker/semantic/entities/companies.yml +143 -0
  21. package/templates/docker/semantic/entities/people.yml +132 -0
  22. package/templates/docker/semantic/glossary.yml +116 -4
  23. package/templates/docker/semantic/metrics/accounts.yml +77 -0
  24. package/templates/docker/semantic/metrics/companies.yml +63 -0
  25. package/templates/docker/sidecar/Dockerfile +5 -6
  26. package/templates/docker/sidecar/railway.json +1 -2
  27. package/templates/docker/src/api/__tests__/admin.test.ts +7 -7
  28. package/templates/docker/src/api/__tests__/health-plugin.test.ts +7 -0
  29. package/templates/docker/src/api/__tests__/health.test.ts +30 -8
  30. package/templates/docker/src/api/routes/admin.ts +549 -8
  31. package/templates/docker/src/api/routes/chat.ts +5 -20
  32. package/templates/docker/src/api/routes/health.ts +39 -27
  33. package/templates/docker/src/api/routes/openapi.ts +1329 -74
  34. package/templates/docker/src/api/routes/query.ts +2 -1
  35. package/templates/docker/src/api/server.ts +27 -0
  36. package/templates/docker/src/app/api/[...route]/route.ts +2 -2
  37. package/templates/docker/src/app/globals.css +13 -12
  38. package/templates/docker/src/app/layout.tsx +9 -2
  39. package/templates/docker/src/components/ui/alert-dialog.tsx +196 -0
  40. package/templates/docker/src/components/ui/badge.tsx +48 -0
  41. package/templates/docker/src/components/ui/button.tsx +64 -0
  42. package/templates/docker/src/components/ui/card.tsx +92 -0
  43. package/templates/docker/src/components/ui/collapsible.tsx +33 -0
  44. package/templates/docker/src/components/ui/command.tsx +184 -0
  45. package/templates/docker/src/components/ui/dialog.tsx +158 -0
  46. package/templates/docker/src/components/ui/dropdown-menu.tsx +257 -0
  47. package/templates/docker/src/components/ui/input.tsx +21 -0
  48. package/templates/docker/src/components/ui/scroll-area.tsx +58 -0
  49. package/templates/docker/src/components/ui/select.tsx +190 -0
  50. package/templates/docker/src/components/ui/separator.tsx +28 -0
  51. package/templates/docker/src/components/ui/sheet.tsx +143 -0
  52. package/templates/docker/src/components/ui/sidebar.tsx +726 -0
  53. package/templates/docker/src/components/ui/skeleton.tsx +13 -0
  54. package/templates/docker/src/components/ui/table.tsx +116 -0
  55. package/templates/docker/src/components/ui/tabs.tsx +91 -0
  56. package/templates/docker/src/components/ui/toggle-group.tsx +83 -0
  57. package/templates/docker/src/components/ui/toggle.tsx +47 -0
  58. package/templates/docker/src/components/ui/tooltip.tsx +57 -0
  59. package/templates/docker/src/hooks/use-mobile.ts +19 -0
  60. package/templates/docker/src/lib/__tests__/agent-cache.test.ts +2 -0
  61. package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +17 -0
  62. package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
  63. package/templates/docker/src/lib/__tests__/agent-integration.test.ts +2 -0
  64. package/templates/docker/src/lib/__tests__/config.test.ts +69 -19
  65. package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
  66. package/templates/docker/src/lib/__tests__/providers.test.ts +32 -1
  67. package/templates/docker/src/lib/__tests__/startup-actions.test.ts +9 -0
  68. package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +429 -0
  69. package/templates/docker/src/lib/__tests__/startup.test.ts +5 -0
  70. package/templates/docker/src/lib/agent-query.ts +5 -23
  71. package/templates/docker/src/lib/agent.ts +32 -112
  72. package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +5 -3
  73. package/templates/docker/src/lib/auth/middleware.ts +30 -4
  74. package/templates/docker/src/lib/auth/migrate.ts +97 -0
  75. package/templates/docker/src/lib/auth/server.ts +12 -1
  76. package/templates/docker/src/lib/config.ts +37 -39
  77. package/templates/docker/src/lib/db/__tests__/connection.test.ts +89 -14
  78. package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +1 -18
  79. package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
  80. package/templates/docker/src/lib/db/__tests__/registry.test.ts +11 -208
  81. package/templates/docker/src/lib/db/connection.ts +87 -265
  82. package/templates/docker/src/lib/db/internal.ts +6 -1
  83. package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
  84. package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +2 -2
  85. package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +355 -1
  86. package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +32 -5
  87. package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +228 -14
  88. package/templates/docker/src/lib/plugins/index.ts +4 -1
  89. package/templates/docker/src/lib/plugins/migrate.ts +103 -0
  90. package/templates/docker/src/lib/plugins/registry.ts +12 -6
  91. package/templates/docker/src/lib/plugins/wiring.ts +113 -4
  92. package/templates/docker/src/lib/providers.ts +6 -1
  93. package/templates/docker/src/lib/security.ts +24 -0
  94. package/templates/docker/src/lib/semantic.ts +2 -0
  95. package/templates/docker/src/lib/sidecar-types.ts +12 -1
  96. package/templates/docker/src/lib/startup.ts +71 -101
  97. package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
  98. package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
  99. package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
  100. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
  101. package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
  102. package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
  103. package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
  104. package/templates/docker/src/lib/tools/__tests__/python.test.ts +331 -0
  105. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
  106. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +38 -31
  107. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
  108. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
  109. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
  110. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +5 -308
  111. package/templates/docker/src/lib/tools/explore-nsjail.ts +17 -12
  112. package/templates/docker/src/lib/tools/explore-sidecar.ts +25 -0
  113. package/templates/docker/src/lib/tools/explore.ts +28 -32
  114. package/templates/docker/src/lib/tools/python-nsjail.ts +396 -0
  115. package/templates/docker/src/lib/tools/python-sandbox.ts +476 -0
  116. package/templates/docker/src/lib/tools/python-sidecar.ts +150 -0
  117. package/templates/docker/src/lib/tools/python.ts +367 -0
  118. package/templates/docker/src/lib/tools/registry.ts +49 -22
  119. package/templates/docker/src/lib/tools/sql.ts +88 -88
  120. package/templates/docker/src/types/vercel-sandbox.d.ts +7 -0
  121. package/templates/docker/src/ui/components/admin/admin-layout.tsx +77 -8
  122. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +25 -17
  123. package/templates/docker/src/ui/components/admin/change-password-dialog.tsx +128 -0
  124. package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -3
  125. package/templates/docker/src/ui/components/admin/semantic-file-tree.tsx +159 -0
  126. package/templates/docker/src/ui/components/atlas-chat.tsx +64 -12
  127. package/templates/docker/src/ui/components/chart/result-chart.tsx +25 -15
  128. package/templates/docker/src/ui/components/chat/markdown.tsx +88 -42
  129. package/templates/docker/src/ui/components/chat/python-result-card.tsx +244 -0
  130. package/templates/docker/src/ui/components/chat/sql-block.tsx +39 -15
  131. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +6 -1
  132. package/templates/docker/src/ui/components/chat/tool-part.tsx +12 -3
  133. package/templates/docker/src/ui/components/chat/typing-indicator.tsx +5 -2
  134. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +25 -20
  135. package/templates/docker/src/ui/context.tsx +1 -1
  136. package/templates/docker/src/ui/hooks/use-conversations.ts +3 -3
  137. package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -10
  138. package/templates/docker/tsconfig.json +2 -2
  139. package/templates/nextjs-standalone/.env.example +1 -1
  140. package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +9 -9
  141. package/templates/nextjs-standalone/bin/atlas.ts +108 -44
  142. package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +51 -27
  143. package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +95 -103
  144. package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +88 -152
  145. package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +82 -95
  146. package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +104 -8
  147. package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +62 -23
  148. package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +52 -78
  149. package/templates/nextjs-standalone/docs/deploy.md +2 -39
  150. package/templates/nextjs-standalone/package.json +11 -2
  151. package/templates/nextjs-standalone/scripts/migrate-auth.ts +25 -0
  152. package/templates/nextjs-standalone/scripts/seed-demo.ts +94 -0
  153. package/templates/nextjs-standalone/semantic/catalog.yml +62 -3
  154. package/templates/nextjs-standalone/semantic/entities/accounts.yml +162 -0
  155. package/templates/nextjs-standalone/semantic/entities/companies.yml +143 -0
  156. package/templates/nextjs-standalone/semantic/entities/people.yml +132 -0
  157. package/templates/nextjs-standalone/semantic/glossary.yml +116 -4
  158. package/templates/nextjs-standalone/semantic/metrics/accounts.yml +77 -0
  159. package/templates/nextjs-standalone/semantic/metrics/companies.yml +63 -0
  160. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +7 -7
  161. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +7 -0
  162. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +30 -8
  163. package/templates/nextjs-standalone/src/api/routes/admin.ts +549 -8
  164. package/templates/nextjs-standalone/src/api/routes/chat.ts +5 -20
  165. package/templates/nextjs-standalone/src/api/routes/health.ts +39 -27
  166. package/templates/nextjs-standalone/src/api/routes/openapi.ts +1329 -74
  167. package/templates/nextjs-standalone/src/api/routes/query.ts +2 -1
  168. package/templates/nextjs-standalone/src/api/server.ts +27 -0
  169. package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +2 -2
  170. package/templates/nextjs-standalone/src/app/globals.css +13 -12
  171. package/templates/nextjs-standalone/src/app/layout.tsx +9 -2
  172. package/templates/nextjs-standalone/src/components/ui/alert-dialog.tsx +196 -0
  173. package/templates/nextjs-standalone/src/components/ui/badge.tsx +48 -0
  174. package/templates/nextjs-standalone/src/components/ui/button.tsx +64 -0
  175. package/templates/nextjs-standalone/src/components/ui/card.tsx +92 -0
  176. package/templates/nextjs-standalone/src/components/ui/collapsible.tsx +33 -0
  177. package/templates/nextjs-standalone/src/components/ui/command.tsx +184 -0
  178. package/templates/nextjs-standalone/src/components/ui/dialog.tsx +158 -0
  179. package/templates/nextjs-standalone/src/components/ui/dropdown-menu.tsx +257 -0
  180. package/templates/nextjs-standalone/src/components/ui/input.tsx +21 -0
  181. package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +58 -0
  182. package/templates/nextjs-standalone/src/components/ui/select.tsx +190 -0
  183. package/templates/nextjs-standalone/src/components/ui/separator.tsx +28 -0
  184. package/templates/nextjs-standalone/src/components/ui/sheet.tsx +143 -0
  185. package/templates/nextjs-standalone/src/components/ui/sidebar.tsx +726 -0
  186. package/templates/nextjs-standalone/src/components/ui/skeleton.tsx +13 -0
  187. package/templates/nextjs-standalone/src/components/ui/table.tsx +116 -0
  188. package/templates/nextjs-standalone/src/components/ui/tabs.tsx +91 -0
  189. package/templates/nextjs-standalone/src/components/ui/toggle-group.tsx +83 -0
  190. package/templates/nextjs-standalone/src/components/ui/toggle.tsx +47 -0
  191. package/templates/nextjs-standalone/src/components/ui/tooltip.tsx +57 -0
  192. package/templates/nextjs-standalone/src/hooks/use-mobile.ts +19 -0
  193. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +2 -0
  194. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +17 -0
  195. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
  196. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +2 -0
  197. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +69 -19
  198. package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
  199. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +32 -1
  200. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +9 -0
  201. package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +429 -0
  202. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +5 -0
  203. package/templates/nextjs-standalone/src/lib/agent-query.ts +5 -23
  204. package/templates/nextjs-standalone/src/lib/agent.ts +32 -112
  205. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +5 -3
  206. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +30 -4
  207. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +97 -0
  208. package/templates/nextjs-standalone/src/lib/auth/server.ts +12 -1
  209. package/templates/nextjs-standalone/src/lib/config.ts +37 -39
  210. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +89 -14
  211. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +1 -18
  212. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
  213. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +11 -208
  214. package/templates/nextjs-standalone/src/lib/db/connection.ts +87 -265
  215. package/templates/nextjs-standalone/src/lib/db/internal.ts +6 -1
  216. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
  217. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +2 -2
  218. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +355 -1
  219. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +32 -5
  220. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +228 -14
  221. package/templates/nextjs-standalone/src/lib/plugins/index.ts +4 -1
  222. package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +103 -0
  223. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +12 -6
  224. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +113 -4
  225. package/templates/nextjs-standalone/src/lib/providers.ts +6 -1
  226. package/templates/nextjs-standalone/src/lib/security.ts +24 -0
  227. package/templates/nextjs-standalone/src/lib/semantic.ts +2 -0
  228. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +12 -1
  229. package/templates/nextjs-standalone/src/lib/startup.ts +71 -101
  230. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
  231. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
  232. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
  233. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
  234. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
  235. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
  236. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
  237. package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +331 -0
  238. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
  239. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +38 -31
  240. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
  241. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
  242. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
  243. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +5 -308
  244. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +17 -12
  245. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +25 -0
  246. package/templates/nextjs-standalone/src/lib/tools/explore.ts +28 -32
  247. package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +396 -0
  248. package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +476 -0
  249. package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +150 -0
  250. package/templates/nextjs-standalone/src/lib/tools/python.ts +367 -0
  251. package/templates/nextjs-standalone/src/lib/tools/registry.ts +49 -22
  252. package/templates/nextjs-standalone/src/lib/tools/sql.ts +88 -88
  253. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +77 -8
  254. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +25 -17
  255. package/templates/nextjs-standalone/src/ui/components/admin/change-password-dialog.tsx +128 -0
  256. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -3
  257. package/templates/nextjs-standalone/src/ui/components/admin/semantic-file-tree.tsx +159 -0
  258. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +64 -12
  259. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +25 -15
  260. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +88 -42
  261. package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +244 -0
  262. package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +39 -15
  263. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +6 -1
  264. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +12 -3
  265. package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +5 -2
  266. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +25 -20
  267. package/templates/nextjs-standalone/src/ui/context.tsx +1 -1
  268. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +3 -3
  269. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -10
  270. package/templates/nextjs-standalone/tsconfig.json +0 -1
  271. package/templates/nextjs-standalone/vercel.json +4 -1
  272. package/templates/docker/render.yaml +0 -34
  273. package/templates/docker/semantic/entities/.gitkeep +0 -0
  274. package/templates/docker/semantic/metrics/.gitkeep +0 -0
  275. package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +0 -141
  276. package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +0 -339
  277. package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +0 -217
  278. package/templates/docker/src/lib/db/duckdb.ts +0 -122
  279. package/templates/docker/src/lib/db/salesforce.ts +0 -342
  280. package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
  281. package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
  282. package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
  283. package/templates/docker/src/lib/tools/salesforce.ts +0 -138
  284. package/templates/docker/src/lib/tools/soql-validation.ts +0 -172
  285. package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
  286. package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
  287. package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +0 -141
  288. package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +0 -339
  289. package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +0 -217
  290. package/templates/nextjs-standalone/src/lib/db/duckdb.ts +0 -122
  291. package/templates/nextjs-standalone/src/lib/db/salesforce.ts +0 -342
  292. package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
  293. package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
  294. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
  295. package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +0 -138
  296. package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +0 -172
@@ -84,84 +84,15 @@ This database uses MySQL. Key differences from PostgreSQL:
84
84
  - \`LIMIT offset, count\` or \`LIMIT count OFFSET offset\` — both forms work
85
85
  - \`COALESCE\`, \`CASE\`, \`NULLIF\`, \`COUNT\`, \`SUM\`, \`AVG\`, \`MIN\`, \`MAX\` work identically`;
86
86
 
87
- const CLICKHOUSE_DIALECT_GUIDE = `
88
-
89
- ## SQL Dialect: ClickHouse
90
- This database uses ClickHouse. Key differences from PostgreSQL:
91
- - Use \`toYear(col)\` and \`toMonth(col)\` instead of \`EXTRACT(YEAR FROM col)\`
92
- - Use \`formatDateTime(col, '%Y-%m')\` instead of \`TO_CHAR(col, 'YYYY-MM')\`
93
- - Use \`toString(col)\` for explicit string casts — no \`::text\` casting
94
- - Use \`toInt32(col)\`, \`toFloat64(col)\` for numeric casts — no \`::integer\` casting
95
- - Use \`ifNull(col, default)\` or \`COALESCE(col, default)\` — both work
96
- - \`arrayJoin(arr)\` unnests array columns into rows
97
- - No \`UPDATE\` or \`DELETE\` — ClickHouse is append-only OLAP
98
- - No foreign keys — joins are supported but there are no referential integrity constraints
99
- - \`count()\` instead of \`COUNT(*)\` — both work but \`count()\` is idiomatic
100
- - Use single quotes for strings, double quotes or backticks for identifiers
101
- - \`argMax(col, ordering_col)\` / \`argMin\` for "latest value" queries
102
- - Date arithmetic: \`today()\`, \`yesterday()\`, \`now()\`, \`toStartOfMonth(col)\`, \`toStartOfWeek(col)\`
103
- - \`COALESCE\`, \`CASE\`, \`COUNT\`, \`SUM\`, \`AVG\`, \`MIN\`, \`MAX\` work identically
104
- - Note: Atlas SQL validation uses PostgreSQL-compatible parsing. Avoid ClickHouse-only syntax (PREWHERE, LIMIT BY, WITH TOTALS) — use standard SQL equivalents instead
105
- - \`EXPLAIN\`, \`SHOW\`, and \`DESCRIBE\` are not available — use the explore tool to read entity schema files instead`;
106
-
107
- const SOQL_DIALECT_GUIDE = `
108
-
109
- ## Query Language: Salesforce SOQL
110
- This datasource uses Salesforce Object Query Language (SOQL). Key differences from SQL:
111
- - **No JOINs** — use relationship queries instead:
112
- - Child-to-parent: \`SELECT Account.Name FROM Contact\`
113
- - Parent-to-child: \`SELECT Name, (SELECT LastName FROM Contacts) FROM Account\`
114
- - **Object names** instead of table names (e.g. \`Account\`, \`Contact\`, \`Opportunity\`)
115
- - **Field API names** — always use the API name (e.g. \`FirstName\`), not the label
116
- - **No wildcards** — \`SELECT *\` is not supported; list fields explicitly
117
- - **Date literals**: \`TODAY\`, \`YESTERDAY\`, \`LAST_WEEK\`, \`THIS_MONTH\`, \`LAST_N_DAYS:30\`, \`NEXT_N_DAYS:7\`
118
- - **Date functions**: \`CALENDAR_YEAR(CloseDate)\`, \`CALENDAR_MONTH(CloseDate)\`, \`DAY_IN_MONTH(CloseDate)\`
119
- - **Aggregate functions**: \`COUNT()\`, \`COUNT(Id)\`, \`SUM(Amount)\`, \`AVG(Amount)\`, \`MIN(Amount)\`, \`MAX(Amount)\`
120
- - **GROUP BY** works similarly to SQL: \`SELECT StageName, COUNT(Id) FROM Opportunity GROUP BY StageName\`
121
- - **HAVING** clause is supported for filtering aggregates
122
- - **LIMIT** and \`OFFSET\` are supported
123
- - **No subqueries in FROM** — subqueries only in WHERE (semi-joins): \`WHERE AccountId IN (SELECT Id FROM Account WHERE ...)\`
124
- - **No UNION, INTERSECT, EXCEPT**
125
- - Use the \`querySalesforce\` tool (not \`executeSQL\`) for all Salesforce queries`;
126
-
127
- const SNOWFLAKE_DIALECT_GUIDE = `
128
-
129
- ## SQL Dialect: Snowflake
130
- This database uses Snowflake. Key differences from PostgreSQL:
131
- - \`ILIKE\` works for case-insensitive matching (same as PostgreSQL)
132
- - \`::type\` casting works (e.g. \`col::VARCHAR\`), plus \`TRY_CAST(col AS type)\` for safe casting that returns NULL on failure
133
- - Identifiers are case-insensitive by default; double-quoted identifiers are case-sensitive
134
- - Use \`FLATTEN()\` / \`LATERAL FLATTEN(input => col)\` to unnest semi-structured (VARIANT/ARRAY/OBJECT) data
135
- - \`QUALIFY\` clause filters window function results directly (e.g. \`QUALIFY ROW_NUMBER() OVER (...) = 1\`)
136
- - \`DATE_TRUNC('month', col)\` syntax (same as PostgreSQL)
137
- - No \`LIMIT offset, count\` — use \`LIMIT count OFFSET offset\`
138
- - Use \`LISTAGG(col, ', ')\` instead of \`STRING_AGG(col, ', ')\` or \`ARRAY_AGG(col)\` for aggregation
139
- - \`COALESCE\`, \`CASE\`, \`NULLIF\`, \`COUNT\`, \`SUM\`, \`AVG\`, \`MIN\`, \`MAX\` work identically`;
140
-
141
- const DUCKDB_DIALECT_GUIDE = `
142
-
143
- ## SQL Dialect: DuckDB
144
- This database uses DuckDB, an in-process analytical engine. DuckDB's SQL is PostgreSQL-compatible with extensions:
145
- - \`::type\` casting works (e.g. \`col::INTEGER\`, \`col::VARCHAR\`) — same as PostgreSQL
146
- - \`ILIKE\` works for case-insensitive matching (same as PostgreSQL)
147
- - \`EXTRACT(YEAR FROM col)\`, \`DATE_TRUNC('month', col)\`, \`TO_CHAR(col, 'YYYY-MM')\` — same as PostgreSQL
148
- - \`STRING_AGG(col, ', ')\` works (same as PostgreSQL)
149
- - \`PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY col)\` works for percentile calculations
150
- - \`LIST_AGG\`, \`ARRAY_AGG\`, \`LIST()\` for array aggregation
151
- - \`UNNEST(list_col)\` to expand list/array columns into rows
152
- - Note: Atlas SQL validation uses PostgreSQL-compatible parsing. Avoid DuckDB-only syntax (QUALIFY, EXCLUDE, REPLACE, STRUCT_PACK, COLUMNS(*), COLUMNS(regex), STRUCT literals with curly braces) — use standard SQL equivalents instead
153
- - \`COALESCE\`, \`CASE\`, \`NULLIF\`, \`COUNT\`, \`SUM\`, \`AVG\`, \`MIN\`, \`MAX\` work identically`;
87
+ // Display names for core DB types. Plugin-registered types fall through
88
+ // to the capitalize fallback intentionally.
89
+ const DIALECT_DISPLAY_NAMES: Record<string, string> = {
90
+ postgres: "PostgreSQL",
91
+ mysql: "MySQL",
92
+ };
154
93
 
155
94
  function dialectName(dbType: DBType): string {
156
- switch (dbType) {
157
- case "postgres": return "PostgreSQL";
158
- case "mysql": return "MySQL";
159
- case "clickhouse": return "ClickHouse";
160
- case "snowflake": return "Snowflake";
161
- case "duckdb": return "DuckDB";
162
- case "salesforce": return "Salesforce (SOQL)";
163
- default: { const _: never = dbType; return _; }
164
- }
95
+ return DIALECT_DISPLAY_NAMES[dbType] ?? dbType.charAt(0).toUpperCase() + dbType.slice(1);
165
96
  }
166
97
 
167
98
  function buildMultiSourceSection(
@@ -214,34 +145,30 @@ ${lines.join("\n")}
214
145
  return section;
215
146
  }
216
147
 
217
- /**
218
- * Collect Salesforce source metadata via dynamic import to avoid a hard
219
- * dependency on the salesforce module (it may not be installed).
220
- */
221
- function getSfSourceMeta(): ConnectionMetadata[] {
222
- try {
223
- // eslint-disable-next-line @typescript-eslint/no-require-imports
224
- const { describeSalesforceSources } = require("./db/salesforce") as {
225
- describeSalesforceSources: () => ConnectionMetadata[];
226
- };
227
- return describeSalesforceSources();
228
- } catch (err) {
229
- // jsforce not installed — expected, don't log
230
- if (err instanceof Error && err.message.includes("Cannot find module")) return [];
231
- log.warn({ err: err instanceof Error ? err.message : String(err) }, "Failed to describe Salesforce sources");
232
- return [];
233
- }
234
- }
235
-
236
148
  function appendDialectHints(prompt: string): string {
237
149
  const hints = getDialectHints();
238
150
  if (hints.length === 0) return prompt;
239
151
  return prompt + "\n\n## Additional SQL Dialect Notes\n\n" + hints.map((h) => h.dialect).join("\n\n");
240
152
  }
241
153
 
154
+ const PYTHON_GUIDANCE = `
155
+ ## SQL vs Python
156
+
157
+ **Use SQL for:** filtering, aggregation, joins, window functions, GROUP BY, HAVING — anything the database handles natively.
158
+ **Use Python for:** statistical analysis (correlations, regressions, hypothesis tests), complex reshaping (pivots, multi-index), time series decomposition, clustering, and advanced visualizations (heatmaps, scatter matrices, violin plots).
159
+
160
+ **Anti-patterns to avoid:** SELECT * then aggregate in pandas, re-implementing GROUP BY or window functions in Python, using Python for simple counts/sums.
161
+
162
+ **Chart guidance:** prefer \`_atlas_chart\` (interactive Recharts) for bar/line/pie charts. Use \`chart_path()\` only for advanced matplotlib visualizations that Recharts cannot render.`;
163
+
242
164
  function buildSystemPrompt(registry: ToolRegistry): string {
243
165
  let base = SYSTEM_PROMPT_PREFIX + "\n\n" + registry.describe() + "\n\n" + SYSTEM_PROMPT_SUFFIX;
244
166
 
167
+ // Add Python guidance only when the tool is available
168
+ if (registry.get("executePython")) {
169
+ base += "\n" + PYTHON_GUIDANCE;
170
+ }
171
+
245
172
  // Append the pre-indexed semantic layer summary
246
173
  const semanticIndex = getSemanticIndex();
247
174
  if (semanticIndex) {
@@ -253,9 +180,7 @@ function buildSystemPrompt(registry: ToolRegistry): string {
253
180
  if (fragments.length > 0) {
254
181
  base += "\n\n" + fragments.join("\n\n");
255
182
  }
256
- const connectionMeta = connections.describe();
257
- const sfMeta = getSfSourceMeta();
258
- const meta = [...connectionMeta, ...sfMeta];
183
+ const meta = connections.describe();
259
184
 
260
185
  // Single-connection: identical to pre-v0.7 behavior
261
186
  if (meta.length <= 1) {
@@ -268,26 +193,21 @@ function buildSystemPrompt(registry: ToolRegistry): string {
268
193
  log.debug({ err: err instanceof Error ? err.message : String(err) }, "Could not detect DB type — omitting dialect guide");
269
194
  return appendDialectHints(base);
270
195
  }
271
- switch (dbType) {
272
- case "postgres": return appendDialectHints(base);
273
- case "mysql": return appendDialectHints(base + MYSQL_DIALECT_GUIDE);
274
- case "clickhouse": return appendDialectHints(base + CLICKHOUSE_DIALECT_GUIDE);
275
- case "snowflake": return appendDialectHints(base + SNOWFLAKE_DIALECT_GUIDE);
276
- case "duckdb": return appendDialectHints(base + DUCKDB_DIALECT_GUIDE);
277
- case "salesforce": return appendDialectHints(base + SOQL_DIALECT_GUIDE);
278
- default: { const _exhaustive: never = dbType; throw new Error(`Unknown: ${_exhaustive}`); }
196
+ // Core adapters get their dialect guide inline; everything else is
197
+ // handled by plugin dialect hints via appendDialectHints().
198
+ if (dbType === "mysql") {
199
+ return appendDialectHints(base + MYSQL_DIALECT_GUIDE);
279
200
  }
201
+ return appendDialectHints(base);
280
202
  }
281
203
 
282
- // Multi-connection: list sources + include all relevant dialect guides
204
+ // Multi-connection: list sources + include core dialect guides
283
205
  let prompt = base + "\n\n" + buildMultiSourceSection(meta);
284
206
 
285
207
  const dbTypes = new Set(meta.map((m) => m.dbType));
286
208
  if (dbTypes.has("mysql")) prompt += MYSQL_DIALECT_GUIDE;
287
- if (dbTypes.has("clickhouse")) prompt += CLICKHOUSE_DIALECT_GUIDE;
288
- if (dbTypes.has("snowflake")) prompt += SNOWFLAKE_DIALECT_GUIDE;
289
- if (dbTypes.has("duckdb")) prompt += DUCKDB_DIALECT_GUIDE;
290
- if (dbTypes.has("salesforce")) prompt += SOQL_DIALECT_GUIDE;
209
+ // Non-core dialects (clickhouse, snowflake, duckdb, salesforce, etc.)
210
+ // are provided by plugins via appendDialectHints().
291
211
 
292
212
  return appendDialectHints(prompt);
293
213
  }
@@ -438,7 +358,7 @@ export async function runAgent({
438
358
  maxOutputTokens: 4096,
439
359
  stopWhen: stepCountIs(25),
440
360
  // totalMs: 180s for self-hosted (full agent loop budget).
441
- // On Vercel, maxDuration caps the serverless function at 60s.
361
+ // On Vercel, maxDuration caps the serverless function at 300s (Pro plan).
442
362
  timeout: { totalMs: 180_000, stepMs: 30_000, chunkMs: 5_000 },
443
363
 
444
364
  onError: ({ error }) => {
@@ -56,6 +56,7 @@ const MANAGED_VARS = [
56
56
  "BETTER_AUTH_SECRET",
57
57
  "ATLAS_AUTH_JWKS_URL",
58
58
  "ATLAS_API_KEY",
59
+ "ATLAS_ADMIN_EMAIL",
59
60
  ] as const;
60
61
 
61
62
  const saved: Record<string, string | undefined> = {};
@@ -74,6 +75,7 @@ beforeEach(() => {
74
75
  delete process.env.BETTER_AUTH_SECRET;
75
76
  delete process.env.ATLAS_AUTH_JWKS_URL;
76
77
  delete process.env.ATLAS_API_KEY;
78
+ delete process.env.ATLAS_ADMIN_EMAIL;
77
79
  });
78
80
 
79
81
  afterEach(() => {
@@ -99,7 +101,7 @@ describe("migrateAuthTables", () => {
99
101
 
100
102
  await migrateAuthTables();
101
103
 
102
- // migrateInternalDB: 3 audit_log + 4 conversations/messages + 2 starred column + 3 slack + 5 action_log + 2 source tracking = 19 queries
104
+ // migrateInternalDB: 3 audit_log + 4 conversations/messages + 2 starred column + 3 slack + 5 action_log + 2 source tracking + 7 scheduled_tasks = 26 queries
103
105
  expect(queries.length).toBe(26);
104
106
  expect(queries[0]).toContain("CREATE TABLE IF NOT EXISTS audit_log");
105
107
  });
@@ -141,8 +143,8 @@ describe("migrateAuthTables", () => {
141
143
  await migrateAuthTables();
142
144
  await migrateAuthTables();
143
145
 
144
- // Internal DB migration runs once (26 queries: audit_log + conversations/messages + starred column + slack + action_log + source tracking + scheduled_tasks/runs)
145
- expect(queries.length).toBe(26);
146
+ // Internal DB migration runs once (26 queries) + 1 ALTER TABLE for password_change_required (managed mode)
147
+ expect(queries.length).toBe(27);
146
148
  // Better Auth migration runs once
147
149
  expect(getMigrationCount()).toBe(1);
148
150
  });
@@ -156,6 +156,28 @@ export function _setValidatorOverrides(overrides: {
156
156
  _byotOverride = overrides.byot ?? null;
157
157
  }
158
158
 
159
+ /**
160
+ * Categorize an auth error for diagnostic logging.
161
+ * Helps operators quickly identify whether a failure is a database issue,
162
+ * network problem, configuration error, or a programming bug.
163
+ */
164
+ function categorizeAuthError(err: unknown): string {
165
+ if (err instanceof TypeError || err instanceof ReferenceError || err instanceof SyntaxError) {
166
+ return "programming-error";
167
+ }
168
+ const msg = err instanceof Error ? err.message : String(err);
169
+ if (/ECONNREFUSED|ENOTFOUND|ETIMEDOUT|ECONNRESET|fetch failed/i.test(msg)) {
170
+ return "network-error";
171
+ }
172
+ if (/relation.*does not exist|database|SQLITE|pg_|connect|pool/i.test(msg)) {
173
+ return "database-error";
174
+ }
175
+ if (/secret|config|env|missing|undefined|not set/i.test(msg)) {
176
+ return "config-error";
177
+ }
178
+ return "unknown";
179
+ }
180
+
159
181
  /** Authenticate an incoming request based on the detected auth mode. */
160
182
  export async function authenticateRequest(req: Request): Promise<AuthResult> {
161
183
  const mode = detectAuthMode();
@@ -171,9 +193,11 @@ export async function authenticateRequest(req: Request): Promise<AuthResult> {
171
193
  try {
172
194
  return await (_managedOverride ?? validateManaged)(req);
173
195
  } catch (err) {
196
+ const category = categorizeAuthError(err);
174
197
  log.error(
175
- { err: err instanceof Error ? err : new Error(String(err)), mode },
176
- "Managed auth error",
198
+ { err: err instanceof Error ? err : new Error(String(err)), mode, category },
199
+ "Managed auth error (%s)",
200
+ category,
177
201
  );
178
202
  if (err instanceof TypeError || err instanceof ReferenceError || err instanceof SyntaxError) {
179
203
  log.error({ err, mode }, "BUG: Unexpected programming error in auth validator");
@@ -190,9 +214,11 @@ export async function authenticateRequest(req: Request): Promise<AuthResult> {
190
214
  try {
191
215
  return await (_byotOverride ?? validateBYOT)(req);
192
216
  } catch (err) {
217
+ const category = categorizeAuthError(err);
193
218
  log.error(
194
- { err: err instanceof Error ? err : new Error(String(err)), mode },
195
- "BYOT auth error",
219
+ { err: err instanceof Error ? err : new Error(String(err)), mode, category },
220
+ "BYOT auth error (%s)",
221
+ category,
196
222
  );
197
223
  if (err instanceof TypeError || err instanceof ReferenceError || err instanceof SyntaxError) {
198
224
  log.error({ err, mode }, "BUG: Unexpected programming error in auth validator");
@@ -63,7 +63,20 @@ export async function migrateAuthTables(): Promise<void> {
63
63
  const ctx = await auth.$context;
64
64
  await ctx.runMigrations();
65
65
  log.info("Better Auth migration complete");
66
+
67
+ // Add password_change_required column to Better Auth's user table.
68
+ // Must run AFTER Better Auth migrations (which create the "user" table).
69
+ try {
70
+ await internalQuery(
71
+ `ALTER TABLE "user" ADD COLUMN IF NOT EXISTS password_change_required BOOLEAN NOT NULL DEFAULT false`,
72
+ );
73
+ } catch {
74
+ log.warn("Could not add password_change_required column — password change enforcement will be skipped");
75
+ }
76
+
66
77
  await bootstrapAdminUser();
78
+ await seedDevUser(auth);
79
+ await backfillPasswordChangeFlag();
67
80
  } catch (err) {
68
81
  log.error({ err }, "Better Auth migration failed — managed auth may not work");
69
82
  _migrationError = "Connected to the internal database but Better Auth migration failed. Managed auth may not work. Check database permissions (CREATE TABLE).";
@@ -104,6 +117,90 @@ async function bootstrapAdminUser(): Promise<void> {
104
117
  }
105
118
  }
106
119
 
120
+ /**
121
+ * Seed a default dev admin account when no users exist.
122
+ * Only runs when ATLAS_ADMIN_EMAIL is set — uses that email with a
123
+ * well-known password ("atlas-dev"). The databaseHook in server.ts
124
+ * promotes this user to admin on creation.
125
+ *
126
+ * Skips silently if any users already exist (idempotent).
127
+ */
128
+ async function seedDevUser(auth: { api: Record<string, unknown> }): Promise<void> {
129
+ const adminEmail = process.env.ATLAS_ADMIN_EMAIL?.toLowerCase().trim();
130
+ if (!adminEmail) return;
131
+
132
+ try {
133
+ const userCount = await internalQuery<{ count: string }>(
134
+ `SELECT COUNT(*) as count FROM "user"`,
135
+ );
136
+ if (parseInt(String(userCount[0]?.count ?? "0"), 10) > 0) return;
137
+
138
+ // Use Better Auth's createUser API (from the admin plugin)
139
+ const createUser = auth.api.createUser as (opts: {
140
+ body: { email: string; password: string; name: string; role: string };
141
+ }) => Promise<unknown>;
142
+
143
+ await createUser({
144
+ body: {
145
+ email: adminEmail,
146
+ password: "atlas-dev",
147
+ name: "Atlas Admin",
148
+ role: "admin",
149
+ },
150
+ });
151
+
152
+ // Mark the seeded user as requiring a password change
153
+ await internalQuery(
154
+ `UPDATE "user" SET password_change_required = true WHERE LOWER(email) = $1`,
155
+ [adminEmail],
156
+ );
157
+
158
+ log.info({ email: adminEmail }, "Dev admin account seeded (password: atlas-dev)");
159
+ } catch (err) {
160
+ // User might already exist from a previous partial boot — not fatal
161
+ log.debug({ err }, "Dev user seed skipped or failed");
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Backfill: if the dev admin user exists with the default password and
167
+ * password_change_required is false, set the flag. Handles upgrades where
168
+ * the column was added after the user was already seeded.
169
+ */
170
+ async function backfillPasswordChangeFlag(): Promise<void> {
171
+ const adminEmail = process.env.ATLAS_ADMIN_EMAIL?.toLowerCase().trim();
172
+ if (!adminEmail) return;
173
+
174
+ try {
175
+ // Only backfill if the user exists and doesn't already have the flag set
176
+ const rows = await internalQuery<{ id: string; password_change_required: boolean }>(
177
+ `SELECT u.id, u.password_change_required FROM "user" u
178
+ JOIN "account" a ON a."userId" = u.id AND a."providerId" = 'credential'
179
+ WHERE LOWER(u.email) = $1`,
180
+ [adminEmail],
181
+ );
182
+ if (rows.length === 0 || rows[0].password_change_required) return;
183
+
184
+ // Check if the password is still the default "atlas-dev"
185
+ const account = await internalQuery<{ password: string }>(
186
+ `SELECT password FROM "account" WHERE "userId" = $1 AND "providerId" = 'credential'`,
187
+ [rows[0].id],
188
+ );
189
+ if (account.length === 0 || !account[0].password) return;
190
+
191
+ const isDefault = await Bun.password.verify("atlas-dev", account[0].password);
192
+ if (!isDefault) return;
193
+
194
+ await internalQuery(
195
+ `UPDATE "user" SET password_change_required = true WHERE id = $1`,
196
+ [rows[0].id],
197
+ );
198
+ log.info({ email: adminEmail }, "Backfill: flagged dev admin for password change");
199
+ } catch (err) {
200
+ log.debug({ err }, "Backfill password change flag skipped");
201
+ }
202
+ }
203
+
107
204
  /** Reset migration state. For testing only. */
108
205
  export function resetMigrationState(): void {
109
206
  _migrated = false;
@@ -71,12 +71,23 @@ export function getAuthInstance(): AuthInstance {
71
71
  } catch { /* ignore malformed URL */ }
72
72
  }
73
73
 
74
+ // Resolve base URL: explicit env var > Vercel auto-detect > undefined (Better Auth auto-detect).
75
+ // On Vercel, VERCEL_PROJECT_PRODUCTION_URL or VERCEL_URL are always set.
76
+ // Without a baseURL, Better Auth logs a noisy warning on every cold start.
77
+ const baseURL =
78
+ process.env.BETTER_AUTH_URL ||
79
+ (process.env.VERCEL_PROJECT_PRODUCTION_URL
80
+ ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
81
+ : process.env.VERCEL_URL
82
+ ? `https://${process.env.VERCEL_URL}`
83
+ : undefined);
84
+
74
85
  const instance = betterAuth({
75
86
  // getInternalDB() returns a pg.Pool typed as InternalPool.
76
87
  // Cast needed because Better Auth expects its own pool/adapter type.
77
88
  database: getInternalDB() as unknown as Parameters<typeof betterAuth>[0]["database"],
78
89
  secret,
79
- baseURL: process.env.BETTER_AUTH_URL,
90
+ baseURL,
80
91
  emailAndPassword: {
81
92
  enabled: true,
82
93
  requireEmailVerification: false,
@@ -48,9 +48,9 @@ const RateLimitConfigSchema = z.object({
48
48
  export type RateLimitConfig = z.infer<typeof RateLimitConfigSchema>;
49
49
 
50
50
  const DatasourceConfigSchema = z.object({
51
- /** Database connection string (postgresql://, mysql://, clickhouse://, snowflake://, duckdb://, or salesforce://). */
51
+ /** Database connection string (postgresql:// or mysql:// for core; other schemes via plugins). */
52
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. */
53
+ /** PostgreSQL schema name (sets search_path). Ignored for MySQL and plugin-managed connections. */
54
54
  schema: z.string().optional(),
55
55
  /** Human-readable description shown in the agent system prompt. */
56
56
  description: z.string().optional(),
@@ -379,6 +379,7 @@ async function tryLoadConfigFile(
379
379
  );
380
380
  }
381
381
  const raw = mod.default;
382
+ log.debug({ file: filePath }, "Config file loaded successfully");
382
383
  return raw;
383
384
  } catch (err) {
384
385
  // If the file exists but fails to parse/import, that is a hard error
@@ -398,7 +399,7 @@ async function tryLoadConfigFile(
398
399
  // Plugin shape validation
399
400
  // ---------------------------------------------------------------------------
400
401
 
401
- const VALID_PLUGIN_TYPES = new Set(["datasource", "context", "interaction", "action"]);
402
+ const VALID_PLUGIN_TYPES = new Set(["datasource", "context", "interaction", "action", "sandbox"]);
402
403
 
403
404
  /**
404
405
  * Validate plugin array entries have the required structural shape:
@@ -446,11 +447,17 @@ function validatePlugins(plugins: unknown[]): void {
446
447
  }
447
448
  }
448
449
 
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`);
450
+ // types
451
+ if (!("types" in obj) || !Array.isArray(obj.types)) {
452
+ errors.push(`${label} is missing "types" (array of plugin types)`);
453
+ } else if (obj.types.length === 0) {
454
+ errors.push(`${label} has an empty "types" array — must contain at least one plugin type`);
455
+ } else {
456
+ for (const t of obj.types) {
457
+ if (typeof t !== "string" || !VALID_PLUGIN_TYPES.has(t)) {
458
+ errors.push(`${label} has invalid type "${t}" in "types" — must be one of: ${[...VALID_PLUGIN_TYPES].join(", ")}`);
459
+ }
460
+ }
454
461
  }
455
462
 
456
463
  // version
@@ -575,44 +582,35 @@ export async function applyDatasources(
575
582
  }
576
583
 
577
584
  const connRegistry = registry ?? (await import("./db/connection")).connections;
578
- const { detectDBType } = await import("./db/connection");
579
585
 
580
586
  connRegistry.setMaxTotalConnections(config.maxTotalConnections);
581
587
 
582
588
  for (const [id, ds] of Object.entries(config.datasources)) {
583
589
  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) => {
590
+ log.info({ connectionId: id }, "Registering datasource from config");
591
+ connRegistry.register(id, {
592
+ url: ds.url,
593
+ schema: ds.schema,
594
+ description: ds.description,
595
+ maxConnections: ds.maxConnections,
596
+ idleTimeoutMs: ds.idleTimeoutMs,
597
+ });
598
+
599
+ // Fire initial health check — logs on failure but does not block startup.
600
+ // A degraded connection is still usable (the DB may recover).
601
+ connRegistry.healthCheck(id).then((result) => {
602
+ if (result.status !== "healthy") {
610
603
  log.warn(
611
- { err: healthErr instanceof Error ? healthErr.message : String(healthErr), connectionId: id },
612
- "Initial health check failed after registration",
604
+ { connectionId: id, status: result.status, message: result.message },
605
+ "Datasource registered but initial health check failed connection may be misconfigured",
613
606
  );
614
- });
615
- }
607
+ }
608
+ }).catch((healthErr) => {
609
+ log.warn(
610
+ { err: healthErr instanceof Error ? healthErr.message : String(healthErr), connectionId: id },
611
+ "Initial health check failed after registration",
612
+ );
613
+ });
616
614
 
617
615
  if (ds.rateLimit) {
618
616
  const { registerSourceRateLimit } = await import("./db/source-rate-limit");