@useatlas/create 0.0.1 → 0.0.3

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 (298) 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 +11 -11
  7. package/templates/docker/bin/atlas.ts +120 -56
  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 +4 -41
  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 +38 -40
  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 +104 -1
  90. package/templates/docker/src/lib/plugins/registry.ts +14 -8
  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-sdk-compat.test.ts +1 -1
  101. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
  102. package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
  103. package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
  104. package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
  105. package/templates/docker/src/lib/tools/__tests__/python.test.ts +331 -0
  106. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
  107. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +38 -31
  108. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
  109. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
  110. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
  111. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +5 -308
  112. package/templates/docker/src/lib/tools/explore-nsjail.ts +17 -12
  113. package/templates/docker/src/lib/tools/explore-sidecar.ts +25 -0
  114. package/templates/docker/src/lib/tools/explore.ts +28 -32
  115. package/templates/docker/src/lib/tools/python-nsjail.ts +396 -0
  116. package/templates/docker/src/lib/tools/python-sandbox.ts +476 -0
  117. package/templates/docker/src/lib/tools/python-sidecar.ts +150 -0
  118. package/templates/docker/src/lib/tools/python.ts +367 -0
  119. package/templates/docker/src/lib/tools/registry.ts +49 -22
  120. package/templates/docker/src/lib/tools/sql.ts +88 -88
  121. package/templates/docker/src/types/vercel-sandbox.d.ts +7 -0
  122. package/templates/docker/src/ui/components/admin/admin-layout.tsx +77 -8
  123. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +25 -17
  124. package/templates/docker/src/ui/components/admin/change-password-dialog.tsx +128 -0
  125. package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -3
  126. package/templates/docker/src/ui/components/admin/semantic-file-tree.tsx +159 -0
  127. package/templates/docker/src/ui/components/atlas-chat.tsx +64 -12
  128. package/templates/docker/src/ui/components/chart/result-chart.tsx +25 -15
  129. package/templates/docker/src/ui/components/chat/markdown.tsx +88 -42
  130. package/templates/docker/src/ui/components/chat/python-result-card.tsx +244 -0
  131. package/templates/docker/src/ui/components/chat/sql-block.tsx +39 -15
  132. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +6 -1
  133. package/templates/docker/src/ui/components/chat/tool-part.tsx +12 -3
  134. package/templates/docker/src/ui/components/chat/typing-indicator.tsx +5 -2
  135. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +25 -20
  136. package/templates/docker/src/ui/context.tsx +1 -1
  137. package/templates/docker/src/ui/hooks/use-conversations.ts +3 -3
  138. package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -10
  139. package/templates/docker/tsconfig.json +2 -2
  140. package/templates/nextjs-standalone/.env.example +1 -1
  141. package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +11 -11
  142. package/templates/nextjs-standalone/bin/atlas.ts +120 -56
  143. package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +51 -27
  144. package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +95 -103
  145. package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +88 -152
  146. package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +82 -95
  147. package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +104 -8
  148. package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +62 -23
  149. package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +52 -78
  150. package/templates/nextjs-standalone/docs/deploy.md +4 -41
  151. package/templates/nextjs-standalone/package.json +11 -2
  152. package/templates/nextjs-standalone/scripts/migrate-auth.ts +25 -0
  153. package/templates/nextjs-standalone/scripts/seed-demo.ts +94 -0
  154. package/templates/nextjs-standalone/semantic/catalog.yml +62 -3
  155. package/templates/nextjs-standalone/semantic/entities/accounts.yml +162 -0
  156. package/templates/nextjs-standalone/semantic/entities/companies.yml +143 -0
  157. package/templates/nextjs-standalone/semantic/entities/people.yml +132 -0
  158. package/templates/nextjs-standalone/semantic/glossary.yml +116 -4
  159. package/templates/nextjs-standalone/semantic/metrics/accounts.yml +77 -0
  160. package/templates/nextjs-standalone/semantic/metrics/companies.yml +63 -0
  161. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +7 -7
  162. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +7 -0
  163. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +30 -8
  164. package/templates/nextjs-standalone/src/api/routes/admin.ts +549 -8
  165. package/templates/nextjs-standalone/src/api/routes/chat.ts +5 -20
  166. package/templates/nextjs-standalone/src/api/routes/health.ts +39 -27
  167. package/templates/nextjs-standalone/src/api/routes/openapi.ts +1329 -74
  168. package/templates/nextjs-standalone/src/api/routes/query.ts +2 -1
  169. package/templates/nextjs-standalone/src/api/server.ts +27 -0
  170. package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +2 -2
  171. package/templates/nextjs-standalone/src/app/globals.css +13 -12
  172. package/templates/nextjs-standalone/src/app/layout.tsx +9 -2
  173. package/templates/nextjs-standalone/src/components/ui/alert-dialog.tsx +196 -0
  174. package/templates/nextjs-standalone/src/components/ui/badge.tsx +48 -0
  175. package/templates/nextjs-standalone/src/components/ui/button.tsx +64 -0
  176. package/templates/nextjs-standalone/src/components/ui/card.tsx +92 -0
  177. package/templates/nextjs-standalone/src/components/ui/collapsible.tsx +33 -0
  178. package/templates/nextjs-standalone/src/components/ui/command.tsx +184 -0
  179. package/templates/nextjs-standalone/src/components/ui/dialog.tsx +158 -0
  180. package/templates/nextjs-standalone/src/components/ui/dropdown-menu.tsx +257 -0
  181. package/templates/nextjs-standalone/src/components/ui/input.tsx +21 -0
  182. package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +58 -0
  183. package/templates/nextjs-standalone/src/components/ui/select.tsx +190 -0
  184. package/templates/nextjs-standalone/src/components/ui/separator.tsx +28 -0
  185. package/templates/nextjs-standalone/src/components/ui/sheet.tsx +143 -0
  186. package/templates/nextjs-standalone/src/components/ui/sidebar.tsx +726 -0
  187. package/templates/nextjs-standalone/src/components/ui/skeleton.tsx +13 -0
  188. package/templates/nextjs-standalone/src/components/ui/table.tsx +116 -0
  189. package/templates/nextjs-standalone/src/components/ui/tabs.tsx +91 -0
  190. package/templates/nextjs-standalone/src/components/ui/toggle-group.tsx +83 -0
  191. package/templates/nextjs-standalone/src/components/ui/toggle.tsx +47 -0
  192. package/templates/nextjs-standalone/src/components/ui/tooltip.tsx +57 -0
  193. package/templates/nextjs-standalone/src/hooks/use-mobile.ts +19 -0
  194. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +2 -0
  195. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +17 -0
  196. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
  197. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +2 -0
  198. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +69 -19
  199. package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
  200. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +32 -1
  201. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +9 -0
  202. package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +429 -0
  203. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +5 -0
  204. package/templates/nextjs-standalone/src/lib/agent-query.ts +5 -23
  205. package/templates/nextjs-standalone/src/lib/agent.ts +32 -112
  206. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +5 -3
  207. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +30 -4
  208. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +97 -0
  209. package/templates/nextjs-standalone/src/lib/auth/server.ts +12 -1
  210. package/templates/nextjs-standalone/src/lib/config.ts +38 -40
  211. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +89 -14
  212. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +1 -18
  213. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
  214. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +11 -208
  215. package/templates/nextjs-standalone/src/lib/db/connection.ts +87 -265
  216. package/templates/nextjs-standalone/src/lib/db/internal.ts +6 -1
  217. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
  218. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +2 -2
  219. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +355 -1
  220. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +32 -5
  221. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +228 -14
  222. package/templates/nextjs-standalone/src/lib/plugins/index.ts +4 -1
  223. package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +104 -1
  224. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +14 -8
  225. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +113 -4
  226. package/templates/nextjs-standalone/src/lib/providers.ts +6 -1
  227. package/templates/nextjs-standalone/src/lib/security.ts +24 -0
  228. package/templates/nextjs-standalone/src/lib/semantic.ts +2 -0
  229. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +12 -1
  230. package/templates/nextjs-standalone/src/lib/startup.ts +71 -101
  231. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
  232. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
  233. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
  234. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +1 -1
  235. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
  236. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
  237. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
  238. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
  239. package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +331 -0
  240. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
  241. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +38 -31
  242. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
  243. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
  244. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
  245. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +5 -308
  246. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +17 -12
  247. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +25 -0
  248. package/templates/nextjs-standalone/src/lib/tools/explore.ts +28 -32
  249. package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +396 -0
  250. package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +476 -0
  251. package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +150 -0
  252. package/templates/nextjs-standalone/src/lib/tools/python.ts +367 -0
  253. package/templates/nextjs-standalone/src/lib/tools/registry.ts +49 -22
  254. package/templates/nextjs-standalone/src/lib/tools/sql.ts +88 -88
  255. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +77 -8
  256. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +25 -17
  257. package/templates/nextjs-standalone/src/ui/components/admin/change-password-dialog.tsx +128 -0
  258. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -3
  259. package/templates/nextjs-standalone/src/ui/components/admin/semantic-file-tree.tsx +159 -0
  260. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +64 -12
  261. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +25 -15
  262. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +88 -42
  263. package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +244 -0
  264. package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +39 -15
  265. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +6 -1
  266. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +12 -3
  267. package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +5 -2
  268. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +25 -20
  269. package/templates/nextjs-standalone/src/ui/context.tsx +1 -1
  270. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +3 -3
  271. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -10
  272. package/templates/nextjs-standalone/tsconfig.json +0 -1
  273. package/templates/nextjs-standalone/vercel.json +4 -1
  274. package/templates/docker/render.yaml +0 -34
  275. package/templates/docker/semantic/entities/.gitkeep +0 -0
  276. package/templates/docker/semantic/metrics/.gitkeep +0 -0
  277. package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +0 -141
  278. package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +0 -339
  279. package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +0 -217
  280. package/templates/docker/src/lib/db/duckdb.ts +0 -122
  281. package/templates/docker/src/lib/db/salesforce.ts +0 -342
  282. package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
  283. package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
  284. package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
  285. package/templates/docker/src/lib/tools/salesforce.ts +0 -138
  286. package/templates/docker/src/lib/tools/soql-validation.ts +0 -172
  287. package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
  288. package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
  289. package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +0 -141
  290. package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +0 -339
  291. package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +0 -217
  292. package/templates/nextjs-standalone/src/lib/db/duckdb.ts +0 -122
  293. package/templates/nextjs-standalone/src/lib/db/salesforce.ts +0 -342
  294. package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
  295. package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
  296. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
  297. package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +0 -138
  298. package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +0 -172
@@ -1,122 +0,0 @@
1
- /**
2
- * DuckDB adapter implementing the DBConnection interface.
3
- *
4
- * Uses @duckdb/node-api (the "Neo" API) for in-process analytical queries
5
- * on CSV, Parquet, and persistent DuckDB database files. DuckDB runs fully
6
- * in-process — no external server required.
7
- *
8
- * Connection URL formats:
9
- * - `duckdb://path/to/file.duckdb` — persistent database file
10
- * - `duckdb://:memory:` — in-memory database
11
- * - `duckdb://` — in-memory (shorthand)
12
- *
13
- * Read-only enforcement: the database is opened with `access_mode: 'READ_ONLY'`
14
- * when the file already exists. For newly-created databases (e.g. during CLI
15
- * ingestion), the caller is responsible for opening in read-write mode first,
16
- * then re-opening as read-only for runtime use.
17
- */
18
-
19
- import type { DBConnection, QueryResult } from "./connection";
20
- import { createLogger } from "@atlas/api/lib/logger";
21
-
22
- const log = createLogger("duckdb");
23
-
24
- export interface DuckDBConfig {
25
- /** Path to the .duckdb file, or ":memory:" for in-memory. */
26
- path: string;
27
- /** Open in read-only mode (default: true for runtime). */
28
- readOnly?: boolean;
29
- }
30
-
31
- /**
32
- * Parse a duckdb:// URL into a DuckDBConfig.
33
- *
34
- * - `duckdb://` or `duckdb://:memory:` → in-memory
35
- * - `duckdb:///absolute/path.duckdb` → absolute path
36
- * - `duckdb://relative/path.duckdb` → relative path
37
- */
38
- export function parseDuckDBUrl(url: string): DuckDBConfig {
39
- if (!url.startsWith("duckdb://")) {
40
- throw new Error(`Invalid DuckDB URL: expected duckdb:// scheme, got "${url.slice(0, 20)}..."`);
41
- }
42
-
43
- const rest = url.slice("duckdb://".length);
44
-
45
- if (!rest || rest === ":memory:") {
46
- return { path: ":memory:" };
47
- }
48
-
49
- // duckdb:///absolute/path → /absolute/path
50
- // duckdb://relative/path → relative/path
51
- return { path: rest };
52
- }
53
-
54
- export function createDuckDBConnection(config: DuckDBConfig): DBConnection {
55
- // Lazy-load to avoid requiring @duckdb/node-api when not needed
56
- let instancePromise: Promise<{ instance: unknown; connection: unknown }> | null = null;
57
-
58
- async function getConnection() {
59
- if (!instancePromise) {
60
- instancePromise = (async () => {
61
- // eslint-disable-next-line @typescript-eslint/no-require-imports
62
- const { DuckDBInstance } = require("@duckdb/node-api");
63
-
64
- const options: Record<string, string> = {};
65
- if (config.readOnly !== false && config.path !== ":memory:") {
66
- options.access_mode = "READ_ONLY";
67
- }
68
-
69
- const instance = await DuckDBInstance.create(config.path, options);
70
- const connection = await instance.connect();
71
- return { instance, connection };
72
- })();
73
- // Allow retry on transient failures — don't cache rejected promises
74
- instancePromise.catch(() => { instancePromise = null; });
75
- }
76
- return instancePromise;
77
- }
78
-
79
- return {
80
- async query(sql: string, timeoutMs?: number): Promise<QueryResult> {
81
- const { connection } = await getConnection();
82
-
83
- const runQuery = async () => {
84
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
- const reader = await (connection as any).runAndReadAll(sql);
86
- const columns: string[] = reader.columnNames();
87
- const rowObjects: Record<string, unknown>[] = reader.getRowObjects();
88
- return { columns, rows: rowObjects };
89
- };
90
-
91
- if (timeoutMs && timeoutMs > 0) {
92
- const timeout = new Promise<never>((_, reject) =>
93
- setTimeout(() => reject(new Error(
94
- `DuckDB query timed out after ${timeoutMs}ms`
95
- )), timeoutMs)
96
- );
97
- return Promise.race([runQuery(), timeout]);
98
- }
99
-
100
- return runQuery();
101
- },
102
-
103
- async close(): Promise<void> {
104
- if (instancePromise) {
105
- try {
106
- const { connection, instance } = await instancePromise;
107
- // DuckDB Neo API uses synchronous cleanup methods
108
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
- (connection as any).disconnectSync();
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
- (instance as any).closeSync();
112
- } catch (err) {
113
- log.warn(
114
- { err: err instanceof Error ? err.message : String(err) },
115
- "Failed to close DuckDB connection"
116
- );
117
- }
118
- instancePromise = null;
119
- }
120
- },
121
- };
122
- }
@@ -1,342 +0,0 @@
1
- /**
2
- * Salesforce DataSource adapter via jsforce.
3
- *
4
- * Salesforce uses SOQL, not SQL, so it cannot be a DBConnection (which has
5
- * `query(sql)`). Instead, it exposes a separate DataSource interface and
6
- * a separate registry.
7
- *
8
- * Connection URL format: salesforce://user:pass@login.salesforce.com?token=TOKEN
9
- */
10
-
11
- import type { QueryResult, ConnectionMetadata } from "@atlas/api/lib/db/connection";
12
- import { createLogger } from "@atlas/api/lib/logger";
13
-
14
- const log = createLogger("salesforce");
15
-
16
- // ---------------------------------------------------------------------------
17
- // Types
18
- // ---------------------------------------------------------------------------
19
-
20
- export interface SObjectInfo {
21
- name: string;
22
- label: string;
23
- queryable: boolean;
24
- }
25
-
26
- export interface SObjectField {
27
- name: string;
28
- type: string;
29
- label: string;
30
- picklistValues: { value: string; label: string; active: boolean }[];
31
- referenceTo: string[];
32
- nillable: boolean;
33
- length: number;
34
- }
35
-
36
- export interface SObjectDescribe {
37
- name: string;
38
- label: string;
39
- fields: SObjectField[];
40
- }
41
-
42
- export interface SalesforceConfig {
43
- loginUrl: string;
44
- username: string;
45
- password: string;
46
- securityToken?: string;
47
- clientId?: string;
48
- clientSecret?: string;
49
- }
50
-
51
- export interface SalesforceDataSource {
52
- query(soql: string, timeoutMs?: number): Promise<QueryResult>;
53
- describe(objectName: string): Promise<SObjectDescribe>;
54
- listObjects(): Promise<SObjectInfo[]>;
55
- close(): Promise<void>;
56
- }
57
-
58
- // ---------------------------------------------------------------------------
59
- // URL parser
60
- // ---------------------------------------------------------------------------
61
-
62
- /**
63
- * Parse a Salesforce connection URL into SalesforceConfig.
64
- *
65
- * Format: salesforce://user:pass@login.salesforce.com?token=TOKEN
66
- *
67
- * - hostname → loginUrl (default `login.salesforce.com`)
68
- * - URL username/password → credentials
69
- * - Query params: `token` (security token), `clientId`, `clientSecret`
70
- */
71
- export function parseSalesforceURL(url: string): SalesforceConfig {
72
- const parsed = new URL(url);
73
- if (parsed.protocol !== "salesforce:") {
74
- throw new Error(
75
- `Invalid Salesforce URL: expected salesforce:// scheme, got "${parsed.protocol}"`,
76
- );
77
- }
78
-
79
- const username = decodeURIComponent(parsed.username);
80
- if (!username) {
81
- throw new Error("Invalid Salesforce URL: missing username.");
82
- }
83
-
84
- const password = decodeURIComponent(parsed.password);
85
- if (!password) {
86
- throw new Error("Invalid Salesforce URL: missing password.");
87
- }
88
-
89
- const hostname = parsed.hostname || "login.salesforce.com";
90
- const loginUrl = `https://${hostname}`;
91
-
92
- const securityToken = parsed.searchParams.get("token") ?? undefined;
93
- const clientId = parsed.searchParams.get("clientId") ?? undefined;
94
- const clientSecret = parsed.searchParams.get("clientSecret") ?? undefined;
95
-
96
- return { loginUrl, username, password, securityToken, clientId, clientSecret };
97
- }
98
-
99
- // ---------------------------------------------------------------------------
100
- // DataSource factory
101
- // ---------------------------------------------------------------------------
102
-
103
- /**
104
- * Create a Salesforce DataSource backed by jsforce.
105
- */
106
- export function createSalesforceDataSource(
107
- config: SalesforceConfig,
108
- ): SalesforceDataSource {
109
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
- let jsforce: any;
111
- try {
112
- // eslint-disable-next-line @typescript-eslint/no-require-imports
113
- jsforce = require("jsforce");
114
- } catch {
115
- throw new Error(
116
- "Salesforce support requires the jsforce package. Install it with: bun add jsforce",
117
- );
118
- }
119
-
120
- const connOpts: Record<string, unknown> = {
121
- loginUrl: config.loginUrl,
122
- };
123
-
124
- if (config.clientId && config.clientSecret) {
125
- connOpts.oauth2 = {
126
- clientId: config.clientId,
127
- clientSecret: config.clientSecret,
128
- loginUrl: config.loginUrl,
129
- };
130
- }
131
-
132
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
- const conn = new jsforce.Connection(connOpts) as any;
134
-
135
- let loginPromise: Promise<void> | null = null;
136
-
137
- async function ensureLoggedIn(): Promise<void> {
138
- if (loginPromise) return loginPromise;
139
- loginPromise = (async () => {
140
- const loginPassword = config.securityToken
141
- ? config.password + config.securityToken
142
- : config.password;
143
- try {
144
- await conn.login(config.username, loginPassword);
145
- } catch (err) {
146
- loginPromise = null;
147
- log.error(
148
- {
149
- err: err instanceof Error ? err : new Error(String(err)),
150
- loginUrl: config.loginUrl,
151
- username: config.username,
152
- },
153
- "Salesforce login failed",
154
- );
155
- throw err;
156
- }
157
- log.info("Salesforce login successful");
158
- })();
159
- return loginPromise;
160
- }
161
-
162
- function isSessionExpiredError(err: unknown): boolean {
163
- if (!(err instanceof Error)) return false;
164
- const msg = err.message;
165
- return (
166
- msg.includes("INVALID_SESSION_ID") ||
167
- msg.includes("Session expired") ||
168
- msg.includes("session has expired")
169
- );
170
- }
171
-
172
- async function withSessionRetry<T>(fn: () => Promise<T>): Promise<T> {
173
- try {
174
- return await fn();
175
- } catch (err) {
176
- if (isSessionExpiredError(err)) {
177
- log.warn("Salesforce session expired — re-authenticating and retrying");
178
- loginPromise = null;
179
- await ensureLoggedIn();
180
- return await fn();
181
- }
182
- throw err;
183
- }
184
- }
185
-
186
- return {
187
- async query(soql: string, timeoutMs = 30000): Promise<QueryResult> {
188
- return withSessionRetry(async () => {
189
- await ensureLoggedIn();
190
-
191
- let timeoutId: ReturnType<typeof setTimeout>;
192
- const result = await Promise.race([
193
- conn.query(soql),
194
- new Promise<never>((_, reject) => {
195
- timeoutId = setTimeout(
196
- () => reject(new Error("Salesforce query timed out")),
197
- timeoutMs,
198
- );
199
- }),
200
- ]).finally(() => clearTimeout(timeoutId!));
201
-
202
- const records = (result.records ?? []) as Record<string, unknown>[];
203
-
204
- if (records.length === 0) {
205
- return { columns: [], rows: [] };
206
- }
207
-
208
- // Extract columns from first record, filtering out the `attributes` metadata key
209
- const columns = Object.keys(records[0]).filter(
210
- (k) => k !== "attributes",
211
- );
212
-
213
- const rows = records.map((record) => {
214
- const row: Record<string, unknown> = {};
215
- for (const col of columns) {
216
- row[col] = record[col];
217
- }
218
- return row;
219
- });
220
-
221
- return { columns, rows };
222
- });
223
- },
224
-
225
- async describe(objectName: string): Promise<SObjectDescribe> {
226
- return withSessionRetry(async () => {
227
- await ensureLoggedIn();
228
- const desc = await conn.describe(objectName);
229
- return {
230
- name: desc.name,
231
- label: desc.label,
232
- fields: (desc.fields ?? []).map(
233
- (f: Record<string, unknown>) => ({
234
- name: f.name as string,
235
- type: f.type as string,
236
- label: f.label as string,
237
- picklistValues: Array.isArray(f.picklistValues)
238
- ? f.picklistValues.map(
239
- (pv: Record<string, unknown>) => ({
240
- value: pv.value as string,
241
- label: pv.label as string,
242
- active: pv.active as boolean,
243
- }),
244
- )
245
- : [],
246
- referenceTo: Array.isArray(f.referenceTo)
247
- ? (f.referenceTo as string[])
248
- : [],
249
- nillable: f.nillable as boolean,
250
- length: (f.length as number) ?? 0,
251
- }),
252
- ),
253
- };
254
- });
255
- },
256
-
257
- async listObjects(): Promise<SObjectInfo[]> {
258
- return withSessionRetry(async () => {
259
- await ensureLoggedIn();
260
- const result = await conn.describeGlobal();
261
- return (result.sobjects ?? [])
262
- .filter((obj: Record<string, unknown>) => obj.queryable === true)
263
- .map((obj: Record<string, unknown>) => ({
264
- name: obj.name as string,
265
- label: obj.label as string,
266
- queryable: true,
267
- }));
268
- });
269
- },
270
-
271
- async close(): Promise<void> {
272
- if (loginPromise) {
273
- try {
274
- await loginPromise;
275
- await conn.logout();
276
- } catch (err) {
277
- log.warn(
278
- { err: err instanceof Error ? err.message : String(err) },
279
- "Failed to logout from Salesforce",
280
- );
281
- }
282
- loginPromise = null;
283
- }
284
- },
285
- };
286
- }
287
-
288
- // ---------------------------------------------------------------------------
289
- // Source registry (parallel to ConnectionRegistry)
290
- // ---------------------------------------------------------------------------
291
-
292
- const _sources = new Map<string, SalesforceDataSource>();
293
-
294
- export function registerSalesforceSource(
295
- id: string,
296
- config: SalesforceConfig,
297
- ): void {
298
- const existing = _sources.get(id);
299
- const newSource = createSalesforceDataSource(config);
300
- _sources.set(id, newSource);
301
- if (existing) {
302
- existing.close().catch((err) => {
303
- log.warn(
304
- { err: err instanceof Error ? err.message : String(err), sourceId: id },
305
- "Failed to close previous Salesforce source during re-registration",
306
- );
307
- });
308
- }
309
- }
310
-
311
- export function getSalesforceSource(id: string): SalesforceDataSource {
312
- const source = _sources.get(id);
313
- if (!source) {
314
- throw new Error(`Salesforce source "${id}" is not registered.`);
315
- }
316
- return source;
317
- }
318
-
319
- export function listSalesforceSources(): string[] {
320
- return Array.from(_sources.keys());
321
- }
322
-
323
- /** Return ConnectionMetadata for each registered Salesforce source. */
324
- export function describeSalesforceSources(): ConnectionMetadata[] {
325
- return Array.from(_sources.keys()).map((id) => ({
326
- id,
327
- dbType: "salesforce" as const,
328
- }));
329
- }
330
-
331
- /** Test helper — clears all registered sources. */
332
- export function _resetSalesforceSources(): void {
333
- for (const [id, source] of _sources.entries()) {
334
- source.close().catch((err) => {
335
- log.warn(
336
- { err: err instanceof Error ? err.message : String(err), sourceId: id },
337
- "Failed to close Salesforce source during registry reset",
338
- );
339
- });
340
- }
341
- _sources.clear();
342
- }
@@ -1,154 +0,0 @@
1
- /**
2
- * Tests for the querySalesforce agent tool.
3
- */
4
- import { describe, it, expect, beforeEach, mock } from "bun:test";
5
-
6
- // Mock Salesforce registry
7
- const mockQuery = mock(() =>
8
- Promise.resolve({
9
- columns: ["Id", "Name"],
10
- rows: [
11
- { Id: "001", Name: "Acme" },
12
- { Id: "002", Name: "Widget Co" },
13
- ],
14
- }),
15
- );
16
-
17
- const mockSources = new Map<string, { query: typeof mockQuery; close: () => Promise<void> }>();
18
-
19
- mock.module("@atlas/api/lib/db/salesforce", () => ({
20
- getSalesforceSource: (id: string) => {
21
- const source = mockSources.get(id);
22
- if (!source) throw new Error(`Salesforce source "${id}" is not registered.`);
23
- return source;
24
- },
25
- listSalesforceSources: () => Array.from(mockSources.keys()),
26
- }));
27
-
28
- // Mock semantic layer
29
- mock.module("@atlas/api/lib/semantic", () => ({
30
- getWhitelistedTables: () => new Set(["account", "contact", "opportunity"]),
31
- _resetWhitelists: () => {},
32
- }));
33
-
34
- // Mock audit log (no-op)
35
- mock.module("@atlas/api/lib/auth/audit", () => ({
36
- logQueryAudit: () => {},
37
- }));
38
-
39
- const { querySalesforce } = await import("@atlas/api/lib/tools/salesforce");
40
-
41
- // Helper to call the tool's execute function
42
- async function executeTool(params: {
43
- soql: string;
44
- explanation: string;
45
- connectionId?: string;
46
- }) {
47
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
- return (querySalesforce as any).execute(params);
49
- }
50
-
51
- describe("querySalesforce tool", () => {
52
- beforeEach(() => {
53
- mockQuery.mockClear();
54
- mockQuery.mockImplementation(() =>
55
- Promise.resolve({
56
- columns: ["Id", "Name"],
57
- rows: [
58
- { Id: "001", Name: "Acme" },
59
- { Id: "002", Name: "Widget Co" },
60
- ],
61
- }),
62
- );
63
- mockSources.clear();
64
- mockSources.set("default", {
65
- query: mockQuery,
66
- close: async () => {},
67
- });
68
- });
69
-
70
- it("executes a valid query and returns results", async () => {
71
- const result = await executeTool({
72
- soql: "SELECT Id, Name FROM Account",
73
- explanation: "Get all accounts",
74
- });
75
- expect(result.success).toBe(true);
76
- expect(result.columns).toEqual(["Id", "Name"]);
77
- expect(result.rows).toHaveLength(2);
78
- expect(result.row_count).toBe(2);
79
- });
80
-
81
- it("rejects invalid SOQL (mutation)", async () => {
82
- const result = await executeTool({
83
- soql: "DELETE FROM Account",
84
- explanation: "Delete accounts",
85
- });
86
- expect(result.success).toBe(false);
87
- expect(result.error).toContain("Forbidden");
88
- });
89
-
90
- it("rejects queries against non-whitelisted objects", async () => {
91
- const result = await executeTool({
92
- soql: "SELECT Id FROM CustomObject__c",
93
- explanation: "Get custom objects",
94
- });
95
- expect(result.success).toBe(false);
96
- expect(result.error).toContain("not in the allowed list");
97
- });
98
-
99
- it("returns error for unregistered connection", async () => {
100
- const result = await executeTool({
101
- soql: "SELECT Id FROM Account",
102
- explanation: "test",
103
- connectionId: "nonexistent",
104
- });
105
- expect(result.success).toBe(false);
106
- expect(result.error).toContain("not registered");
107
- });
108
-
109
- it("appends LIMIT when not present", async () => {
110
- await executeTool({
111
- soql: "SELECT Id FROM Account",
112
- explanation: "test",
113
- });
114
- // The mockQuery should have been called with a SOQL that includes LIMIT
115
- const calledWith = (mockQuery.mock.calls as unknown as string[][])[0][0];
116
- expect(calledWith).toContain("LIMIT");
117
- });
118
-
119
- it("does not double-append LIMIT", async () => {
120
- await executeTool({
121
- soql: "SELECT Id FROM Account LIMIT 5",
122
- explanation: "test",
123
- });
124
- const calledWith = (mockQuery.mock.calls as unknown as string[][])[0][0];
125
- // Should not have two LIMIT clauses
126
- const limitCount = (calledWith.match(/LIMIT/gi) ?? []).length;
127
- expect(limitCount).toBe(1);
128
- });
129
-
130
- it("scrubs sensitive error messages", async () => {
131
- mockQuery.mockImplementationOnce(() =>
132
- Promise.reject(new Error("INVALID_SESSION_ID: Session expired")),
133
- );
134
- const result = await executeTool({
135
- soql: "SELECT Id FROM Account",
136
- explanation: "test",
137
- });
138
- expect(result.success).toBe(false);
139
- expect(result.error).toContain("check server logs");
140
- expect(result.error).not.toContain("INVALID_SESSION_ID");
141
- });
142
-
143
- it("surfaces non-sensitive errors to the agent", async () => {
144
- mockQuery.mockImplementationOnce(() =>
145
- Promise.reject(new Error("INVALID_FIELD: No such column 'Foo'")),
146
- );
147
- const result = await executeTool({
148
- soql: "SELECT Foo FROM Account",
149
- explanation: "test",
150
- });
151
- expect(result.success).toBe(false);
152
- expect(result.error).toContain("INVALID_FIELD");
153
- });
154
- });