@useatlas/create 0.0.6 → 0.0.7

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 (952) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -1
  3. package/index.ts +253 -36
  4. package/package.json +4 -4
  5. package/templates/docker/Dockerfile +1 -1
  6. package/templates/docker/Dockerfile.sidecar +1 -1
  7. package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +17 -14
  8. package/templates/docker/bin/__tests__/failure-threshold.test.ts +148 -0
  9. package/templates/docker/bin/__tests__/fatal-error-propagation.test.ts +267 -0
  10. package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +5 -5
  11. package/templates/docker/bin/__tests__/schema-drift.test.ts +39 -0
  12. package/templates/docker/bin/atlas.ts +981 -1819
  13. package/templates/docker/bin/benchmark.ts +14 -16
  14. package/templates/docker/bin/enrich.ts +7 -2
  15. package/templates/docker/brand.css +13 -0
  16. package/templates/docker/data/cybersec-semantic/catalog.yml +222 -0
  17. package/templates/docker/data/cybersec-semantic/entities/alerts.yml +195 -0
  18. package/templates/docker/data/cybersec-semantic/entities/assets.yml +191 -0
  19. package/templates/docker/data/cybersec-semantic/entities/compliance_assessments.yml +170 -0
  20. package/templates/docker/data/cybersec-semantic/entities/incidents.yml +219 -0
  21. package/templates/docker/data/cybersec-semantic/entities/organizations.yml +136 -0
  22. package/templates/docker/data/cybersec-semantic/entities/plans.yml +114 -0
  23. package/templates/docker/data/cybersec-semantic/entities/remediation_actions.yml +212 -0
  24. package/templates/docker/data/cybersec-semantic/entities/scan_results.yml +215 -0
  25. package/templates/docker/data/cybersec-semantic/entities/scans.yml +180 -0
  26. package/templates/docker/data/cybersec-semantic/entities/subscriptions.yml +184 -0
  27. package/templates/docker/data/cybersec-semantic/entities/users.yml +140 -0
  28. package/templates/docker/data/cybersec-semantic/entities/vulnerabilities.yml +154 -0
  29. package/templates/docker/data/cybersec-semantic/glossary.yml +207 -0
  30. package/templates/docker/data/cybersec-semantic/metrics/business.yml +148 -0
  31. package/templates/docker/data/cybersec-semantic/metrics/compliance.yml +138 -0
  32. package/templates/docker/data/cybersec-semantic/metrics/security.yml +181 -0
  33. package/templates/docker/data/cybersec.sql +8 -8
  34. package/templates/docker/data/demo.sql +3 -0
  35. package/templates/docker/data/ecommerce-semantic/catalog.yml +221 -0
  36. package/templates/docker/data/ecommerce-semantic/entities/categories.yml +91 -0
  37. package/templates/docker/data/ecommerce-semantic/entities/customers.yml +133 -0
  38. package/templates/docker/data/ecommerce-semantic/entities/email_campaigns.yml +119 -0
  39. package/templates/docker/data/ecommerce-semantic/entities/inventory_levels.yml +153 -0
  40. package/templates/docker/data/ecommerce-semantic/entities/order_items.yml +159 -0
  41. package/templates/docker/data/ecommerce-semantic/entities/orders.yml +199 -0
  42. package/templates/docker/data/ecommerce-semantic/entities/payments.yml +140 -0
  43. package/templates/docker/data/ecommerce-semantic/entities/product_reviews.yml +155 -0
  44. package/templates/docker/data/ecommerce-semantic/entities/products.yml +178 -0
  45. package/templates/docker/data/ecommerce-semantic/entities/promotions.yml +171 -0
  46. package/templates/docker/data/ecommerce-semantic/entities/returns.yml +144 -0
  47. package/templates/docker/data/ecommerce-semantic/entities/sellers.yml +124 -0
  48. package/templates/docker/data/ecommerce-semantic/entities/shipments.yml +159 -0
  49. package/templates/docker/data/ecommerce-semantic/glossary.yml +193 -0
  50. package/templates/docker/data/ecommerce-semantic/metrics/customers.yml +116 -0
  51. package/templates/docker/data/ecommerce-semantic/metrics/operations.yml +131 -0
  52. package/templates/docker/data/ecommerce-semantic/metrics/revenue.yml +120 -0
  53. package/templates/docker/docs/deploy.md +2 -1
  54. package/templates/docker/ee/src/__mocks__/internal.ts +170 -0
  55. package/templates/docker/ee/src/audit/purge-scheduler.ts +113 -0
  56. package/templates/docker/ee/src/audit/retention.ts +467 -0
  57. package/templates/docker/ee/src/auth/ip-allowlist.ts +367 -0
  58. package/templates/docker/ee/src/auth/roles.ts +562 -0
  59. package/templates/docker/ee/src/auth/scim.ts +343 -0
  60. package/templates/docker/ee/src/auth/sso.ts +538 -0
  61. package/templates/docker/ee/src/backups/engine.ts +355 -0
  62. package/templates/docker/ee/src/backups/index.ts +26 -0
  63. package/templates/docker/ee/src/backups/restore.ts +169 -0
  64. package/templates/docker/ee/src/backups/scheduler.ts +153 -0
  65. package/templates/docker/ee/src/backups/verify.ts +124 -0
  66. package/templates/docker/ee/src/branding/white-label.ts +228 -0
  67. package/templates/docker/ee/src/compliance/masking.ts +477 -0
  68. package/templates/docker/ee/src/compliance/patterns.ts +16 -0
  69. package/templates/docker/ee/src/compliance/pii-detection.ts +217 -0
  70. package/templates/docker/ee/src/compliance/reports.ts +402 -0
  71. package/templates/docker/ee/src/deploy-mode.ts +37 -0
  72. package/templates/docker/ee/src/governance/approval.ts +699 -0
  73. package/templates/docker/ee/src/index.ts +74 -0
  74. package/templates/docker/ee/src/platform/domains.ts +562 -0
  75. package/templates/docker/ee/src/platform/model-routing.ts +382 -0
  76. package/templates/docker/ee/src/platform/residency.ts +265 -0
  77. package/templates/docker/ee/src/sla/alerting.ts +382 -0
  78. package/templates/docker/ee/src/sla/index.ts +12 -0
  79. package/templates/docker/ee/src/sla/metrics.ts +275 -0
  80. package/templates/docker/ee/src/test-setup.ts +1 -0
  81. package/templates/docker/next.config.ts +4 -1
  82. package/templates/docker/package.json +49 -29
  83. package/templates/docker/sidecar/Dockerfile +1 -1
  84. package/templates/docker/src/api/index.ts +336 -24
  85. package/templates/docker/src/api/routes/actions.ts +443 -176
  86. package/templates/docker/src/api/routes/admin-abuse.ts +219 -0
  87. package/templates/docker/src/api/routes/admin-approval.ts +418 -0
  88. package/templates/docker/src/api/routes/admin-audit-retention.ts +405 -0
  89. package/templates/docker/src/api/routes/admin-auth.ts +122 -0
  90. package/templates/docker/src/api/routes/admin-branding.ts +252 -0
  91. package/templates/docker/src/api/routes/admin-compliance.ts +352 -0
  92. package/templates/docker/src/api/routes/admin-domains.ts +334 -0
  93. package/templates/docker/src/api/routes/admin-integrations.ts +2667 -0
  94. package/templates/docker/src/api/routes/admin-ip-allowlist.ts +261 -0
  95. package/templates/docker/src/api/routes/admin-learned-patterns.ts +525 -0
  96. package/templates/docker/src/api/routes/admin-model-config.ts +252 -0
  97. package/templates/docker/src/api/routes/admin-onboarding-emails.ts +145 -0
  98. package/templates/docker/src/api/routes/admin-orgs.ts +710 -0
  99. package/templates/docker/src/api/routes/admin-prompts.ts +694 -0
  100. package/templates/docker/src/api/routes/admin-residency.ts +570 -0
  101. package/templates/docker/src/api/routes/admin-roles.ts +296 -0
  102. package/templates/docker/src/api/routes/admin-router.ts +120 -0
  103. package/templates/docker/src/api/routes/admin-sandbox.ts +417 -0
  104. package/templates/docker/src/api/routes/admin-scim.ts +262 -0
  105. package/templates/docker/src/api/routes/admin-sso.ts +545 -0
  106. package/templates/docker/src/api/routes/admin-suggestions.ts +176 -0
  107. package/templates/docker/src/api/routes/admin-usage.ts +310 -0
  108. package/templates/docker/src/api/routes/admin.ts +4156 -898
  109. package/templates/docker/src/api/routes/auth-preamble.ts +105 -0
  110. package/templates/docker/src/api/routes/billing.ts +397 -0
  111. package/templates/docker/src/api/routes/chat.ts +597 -334
  112. package/templates/docker/src/api/routes/conversations.ts +987 -132
  113. package/templates/docker/src/api/routes/demo.ts +673 -0
  114. package/templates/docker/src/api/routes/discord.ts +274 -0
  115. package/templates/docker/src/api/routes/ee-error-handler.ts +32 -0
  116. package/templates/docker/src/api/routes/health.ts +129 -14
  117. package/templates/docker/src/api/routes/middleware.ts +244 -0
  118. package/templates/docker/src/api/routes/onboarding-emails.ts +134 -0
  119. package/templates/docker/src/api/routes/onboarding.ts +1109 -0
  120. package/templates/docker/src/api/routes/openapi.ts +184 -1597
  121. package/templates/docker/src/api/routes/platform-admin.ts +760 -0
  122. package/templates/docker/src/api/routes/platform-backups.ts +436 -0
  123. package/templates/docker/src/api/routes/platform-domains.ts +235 -0
  124. package/templates/docker/src/api/routes/platform-residency.ts +257 -0
  125. package/templates/docker/src/api/routes/platform-sla.ts +379 -0
  126. package/templates/docker/src/api/routes/prompts.ts +221 -0
  127. package/templates/docker/src/api/routes/public-branding.ts +106 -0
  128. package/templates/docker/src/api/routes/query.ts +330 -219
  129. package/templates/docker/src/api/routes/scheduled-tasks.ts +393 -297
  130. package/templates/docker/src/api/routes/semantic.ts +179 -0
  131. package/templates/docker/src/api/routes/sessions.ts +210 -0
  132. package/templates/docker/src/api/routes/shared-domains.ts +98 -0
  133. package/templates/docker/src/api/routes/shared-schemas.ts +139 -0
  134. package/templates/docker/src/api/routes/slack.ts +209 -52
  135. package/templates/docker/src/api/routes/suggestions.ts +233 -0
  136. package/templates/docker/src/api/routes/tables.ts +67 -0
  137. package/templates/docker/src/api/routes/teams.ts +222 -0
  138. package/templates/docker/src/api/routes/validate-sql.ts +188 -0
  139. package/templates/docker/src/api/routes/validation-hook.ts +62 -0
  140. package/templates/docker/src/api/routes/widget-loader.ts +356 -0
  141. package/templates/docker/src/api/routes/widget.ts +428 -0
  142. package/templates/docker/src/api/routes/wizard.ts +852 -0
  143. package/templates/docker/src/api/server.ts +187 -69
  144. package/templates/docker/src/app/error.tsx +5 -2
  145. package/templates/docker/src/app/globals.css +1 -1
  146. package/templates/docker/src/app/layout.tsx +7 -2
  147. package/templates/docker/src/app/page.tsx +39 -5
  148. package/templates/docker/src/components/data-table/data-table-column-header.tsx +99 -0
  149. package/templates/docker/src/components/data-table/data-table-date-filter.tsx +225 -0
  150. package/templates/docker/src/components/data-table/data-table-expandable.tsx +125 -0
  151. package/templates/docker/src/components/data-table/data-table-faceted-filter.tsx +189 -0
  152. package/templates/docker/src/components/data-table/data-table-pagination.tsx +112 -0
  153. package/templates/docker/src/components/data-table/data-table-range-filter.tsx +122 -0
  154. package/templates/docker/src/components/data-table/data-table-slider-filter.tsx +256 -0
  155. package/templates/docker/src/components/data-table/data-table-sort-list.tsx +407 -0
  156. package/templates/docker/src/components/data-table/data-table-toolbar.tsx +149 -0
  157. package/templates/docker/src/components/data-table/data-table-view-options.tsx +89 -0
  158. package/templates/docker/src/components/data-table/data-table.tsx +105 -0
  159. package/templates/docker/src/components/form-dialog.tsx +135 -0
  160. package/templates/docker/src/components/ui/accordion.tsx +66 -0
  161. package/templates/docker/src/components/ui/calendar.tsx +220 -0
  162. package/templates/docker/src/components/ui/checkbox.tsx +32 -0
  163. package/templates/docker/src/components/ui/faceted.tsx +283 -0
  164. package/templates/docker/src/components/ui/form.tsx +167 -0
  165. package/templates/docker/src/components/ui/label.tsx +24 -0
  166. package/templates/docker/src/components/ui/popover.tsx +89 -0
  167. package/templates/docker/src/components/ui/progress.tsx +31 -0
  168. package/templates/docker/src/components/ui/scroll-area.tsx +6 -2
  169. package/templates/docker/src/components/ui/slider.tsx +63 -0
  170. package/templates/docker/src/components/ui/sortable.tsx +581 -0
  171. package/templates/docker/src/components/ui/switch.tsx +35 -0
  172. package/templates/docker/src/components/ui/textarea.tsx +18 -0
  173. package/templates/docker/src/config/data-table.ts +82 -0
  174. package/templates/docker/src/env-check.ts +74 -0
  175. package/templates/docker/src/hooks/use-callback-ref.ts +27 -0
  176. package/templates/docker/src/hooks/use-data-table.ts +316 -0
  177. package/templates/docker/src/hooks/use-debounced-callback.ts +28 -0
  178. package/templates/docker/src/lib/action-types.ts +7 -41
  179. package/templates/docker/src/lib/agent-query.ts +4 -2
  180. package/templates/docker/src/lib/agent.ts +363 -31
  181. package/templates/docker/src/lib/auth/admin-permissions.ts +38 -0
  182. package/templates/docker/src/lib/auth/audit.ts +19 -4
  183. package/templates/docker/src/lib/auth/byot.ts +3 -3
  184. package/templates/docker/src/lib/auth/client.ts +33 -3
  185. package/templates/docker/src/lib/auth/detect.ts +29 -8
  186. package/templates/docker/src/lib/auth/managed.ts +104 -14
  187. package/templates/docker/src/lib/auth/middleware.ts +53 -6
  188. package/templates/docker/src/lib/auth/migrate.ts +140 -15
  189. package/templates/docker/src/lib/auth/oauth-state.ts +123 -0
  190. package/templates/docker/src/lib/auth/org-permissions.ts +55 -0
  191. package/templates/docker/src/lib/auth/permissions.ts +26 -19
  192. package/templates/docker/src/lib/auth/server.ts +355 -9
  193. package/templates/docker/src/lib/auth/simple-key.ts +3 -3
  194. package/templates/docker/src/lib/auth/types.ts +15 -21
  195. package/templates/docker/src/lib/billing/enforcement.ts +368 -0
  196. package/templates/docker/src/lib/billing/plans.ts +155 -0
  197. package/templates/docker/src/lib/cache/index.ts +92 -0
  198. package/templates/docker/src/lib/cache/keys.ts +30 -0
  199. package/templates/docker/src/lib/cache/lru.ts +79 -0
  200. package/templates/docker/src/lib/cache/types.ts +31 -0
  201. package/templates/docker/src/lib/compose-refs.ts +62 -0
  202. package/templates/docker/src/lib/config.ts +563 -11
  203. package/templates/docker/src/lib/connection-types.ts +9 -0
  204. package/templates/docker/src/lib/conversation-types.ts +1 -25
  205. package/templates/docker/src/lib/conversations.ts +345 -14
  206. package/templates/docker/src/lib/data-table.ts +61 -0
  207. package/templates/docker/src/lib/db/connection.ts +793 -39
  208. package/templates/docker/src/lib/db/internal.ts +985 -139
  209. package/templates/docker/src/lib/db/migrate.ts +295 -0
  210. package/templates/docker/src/lib/db/migrations/0000_baseline.sql +703 -0
  211. package/templates/docker/src/lib/db/migrations/0001_teams_installations.sql +14 -0
  212. package/templates/docker/src/lib/db/migrations/0002_discord_installations.sql +14 -0
  213. package/templates/docker/src/lib/db/migrations/0003_telegram_installations.sql +15 -0
  214. package/templates/docker/src/lib/db/migrations/0004_sandbox_credentials.sql +18 -0
  215. package/templates/docker/src/lib/db/migrations/0005_oauth_state.sql +16 -0
  216. package/templates/docker/src/lib/db/migrations/0006_byot_credentials.sql +14 -0
  217. package/templates/docker/src/lib/db/migrations/0007_gchat_installations.sql +15 -0
  218. package/templates/docker/src/lib/db/migrations/0008_github_installations.sql +14 -0
  219. package/templates/docker/src/lib/db/migrations/0009_linear_installations.sql +15 -0
  220. package/templates/docker/src/lib/db/migrations/0010_whatsapp_installations.sql +14 -0
  221. package/templates/docker/src/lib/db/migrations/0011_email_installations.sql +16 -0
  222. package/templates/docker/src/lib/db/migrations/0012_region_migrations.sql +25 -0
  223. package/templates/docker/src/lib/db/schema.ts +1120 -0
  224. package/templates/docker/src/lib/db/source-rate-limit.ts +89 -139
  225. package/templates/docker/src/lib/demo.ts +308 -0
  226. package/templates/docker/src/lib/discord/store.ts +225 -0
  227. package/templates/docker/src/lib/effect/ai.ts +243 -0
  228. package/templates/docker/src/lib/effect/errors.ts +234 -0
  229. package/templates/docker/src/lib/effect/hono.ts +454 -0
  230. package/templates/docker/src/lib/effect/index.ts +137 -0
  231. package/templates/docker/src/lib/effect/layers.ts +496 -0
  232. package/templates/docker/src/lib/effect/services.ts +776 -0
  233. package/templates/docker/src/lib/effect/sql.ts +178 -0
  234. package/templates/docker/src/lib/effect/toolkit.ts +123 -0
  235. package/templates/docker/src/lib/email/delivery.ts +232 -0
  236. package/templates/docker/src/lib/email/engine.ts +349 -0
  237. package/templates/docker/src/lib/email/hooks.ts +107 -0
  238. package/templates/docker/src/lib/email/index.ts +16 -0
  239. package/templates/docker/src/lib/email/scheduler.ts +72 -0
  240. package/templates/docker/src/lib/email/sequence.ts +73 -0
  241. package/templates/docker/src/lib/email/store.ts +163 -0
  242. package/templates/docker/src/lib/email/templates.ts +215 -0
  243. package/templates/docker/src/lib/format.ts +67 -0
  244. package/templates/docker/src/lib/gchat/store.ts +202 -0
  245. package/templates/docker/src/lib/github/store.ts +197 -0
  246. package/templates/docker/src/lib/id.ts +29 -0
  247. package/templates/docker/src/lib/integrations/types.ts +166 -0
  248. package/templates/docker/src/lib/learn/pattern-analyzer.ts +224 -0
  249. package/templates/docker/src/lib/learn/pattern-cache.ts +229 -0
  250. package/templates/docker/src/lib/learn/pattern-proposer.ts +87 -0
  251. package/templates/docker/src/lib/learn/suggestion-helpers.ts +34 -0
  252. package/templates/docker/src/lib/learn/suggestions.ts +139 -0
  253. package/templates/docker/src/lib/linear/store.ts +200 -0
  254. package/templates/docker/src/lib/logger.ts +35 -3
  255. package/templates/docker/src/lib/metering.ts +272 -0
  256. package/templates/docker/src/lib/parsers.ts +99 -0
  257. package/templates/docker/src/lib/plugins/hooks.ts +13 -11
  258. package/templates/docker/src/lib/plugins/index.ts +3 -1
  259. package/templates/docker/src/lib/plugins/registry.ts +58 -6
  260. package/templates/docker/src/lib/plugins/settings.ts +147 -0
  261. package/templates/docker/src/lib/plugins/wiring.ts +6 -9
  262. package/templates/docker/src/lib/profiler.ts +1665 -0
  263. package/templates/docker/src/lib/providers.ts +188 -13
  264. package/templates/docker/src/lib/rls.ts +172 -60
  265. package/templates/docker/src/lib/sandbox/credentials.ts +206 -0
  266. package/templates/docker/src/lib/sandbox/validate.ts +179 -0
  267. package/templates/docker/src/lib/scheduled-task-types.ts +26 -94
  268. package/templates/docker/src/lib/scheduled-tasks.ts +174 -34
  269. package/templates/docker/src/lib/scheduler/delivery.ts +248 -150
  270. package/templates/docker/src/lib/scheduler/engine.ts +190 -154
  271. package/templates/docker/src/lib/scheduler/executor.ts +74 -23
  272. package/templates/docker/src/lib/scheduler/preview.ts +72 -0
  273. package/templates/docker/src/lib/security/abuse.ts +463 -0
  274. package/templates/docker/src/lib/semantic/diff.ts +267 -0
  275. package/templates/docker/src/lib/semantic/entities.ts +167 -0
  276. package/templates/docker/src/lib/semantic/files.ts +283 -0
  277. package/templates/docker/src/lib/semantic/index.ts +27 -0
  278. package/templates/docker/src/lib/{semantic-index.ts → semantic/search.ts} +80 -9
  279. package/templates/docker/src/lib/semantic/sync.ts +581 -0
  280. package/templates/docker/src/lib/{semantic.ts → semantic/whitelist.ts} +189 -3
  281. package/templates/docker/src/lib/settings.ts +817 -0
  282. package/templates/docker/src/lib/sidecar-types.ts +13 -0
  283. package/templates/docker/src/lib/slack/store.ts +134 -25
  284. package/templates/docker/src/lib/startup.ts +528 -362
  285. package/templates/docker/src/lib/teams/store.ts +216 -0
  286. package/templates/docker/src/lib/telegram/store.ts +202 -0
  287. package/templates/docker/src/lib/telemetry.ts +40 -0
  288. package/templates/docker/src/lib/tools/actions/audit.ts +8 -5
  289. package/templates/docker/src/lib/tools/actions/email.ts +3 -1
  290. package/templates/docker/src/lib/tools/actions/handler.ts +276 -93
  291. package/templates/docker/src/lib/tools/actions/jira.ts +2 -2
  292. package/templates/docker/src/lib/tools/backends/detect.ts +16 -0
  293. package/templates/docker/src/lib/tools/backends/index.ts +11 -0
  294. package/templates/docker/src/lib/tools/backends/nsjail.ts +213 -0
  295. package/templates/docker/src/lib/tools/backends/shared.ts +103 -0
  296. package/templates/docker/src/lib/tools/backends/types.ts +26 -0
  297. package/templates/docker/src/lib/tools/explore-nsjail.ts +7 -228
  298. package/templates/docker/src/lib/tools/explore-sandbox.ts +4 -29
  299. package/templates/docker/src/lib/tools/explore-sidecar.ts +18 -2
  300. package/templates/docker/src/lib/tools/explore.ts +246 -54
  301. package/templates/docker/src/lib/tools/index.ts +17 -0
  302. package/templates/docker/src/lib/tools/python-nsjail.ts +11 -139
  303. package/templates/docker/src/lib/tools/python-sandbox.ts +9 -132
  304. package/templates/docker/src/lib/tools/python-sidecar.ts +184 -3
  305. package/templates/docker/src/lib/tools/python-stream.ts +33 -0
  306. package/templates/docker/src/lib/tools/python-wrapper.ts +129 -0
  307. package/templates/docker/src/lib/tools/python.ts +115 -15
  308. package/templates/docker/src/lib/tools/registry.ts +14 -2
  309. package/templates/docker/src/lib/tools/sql.ts +778 -362
  310. package/templates/docker/src/lib/tracing.ts +16 -0
  311. package/templates/docker/src/lib/whatsapp/store.ts +198 -0
  312. package/templates/docker/src/lib/workspace.ts +89 -0
  313. package/templates/docker/src/progress.ts +121 -0
  314. package/templates/docker/src/types/data-table.ts +48 -0
  315. package/templates/docker/src/ui/atlas-chat-reexport.ts +3 -0
  316. package/templates/docker/src/ui/components/actions/action-approval-card.tsx +26 -19
  317. package/templates/docker/src/ui/components/actions/action-status-badge.tsx +3 -3
  318. package/templates/docker/src/ui/components/admin/admin-layout.tsx +57 -39
  319. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +213 -35
  320. package/templates/docker/src/ui/components/admin/delivery-status-badge.tsx +53 -0
  321. package/templates/docker/src/ui/components/admin/empty-state.tsx +27 -6
  322. package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -52
  323. package/templates/docker/src/ui/components/admin/error-banner.tsx +2 -2
  324. package/templates/docker/src/ui/components/admin/feature-disabled.tsx +28 -5
  325. package/templates/docker/src/ui/components/admin-content-wrapper.tsx +87 -0
  326. package/templates/docker/src/ui/components/atlas-chat.tsx +449 -166
  327. package/templates/docker/src/ui/components/branding-head.tsx +41 -0
  328. package/templates/docker/src/ui/components/chart/chart-detection.ts +62 -5
  329. package/templates/docker/src/ui/components/chart/result-chart.tsx +316 -125
  330. package/templates/docker/src/ui/components/chat/api-key-bar.tsx +4 -4
  331. package/templates/docker/src/ui/components/chat/data-table.tsx +45 -4
  332. package/templates/docker/src/ui/components/chat/error-banner.tsx +86 -5
  333. package/templates/docker/src/ui/components/chat/follow-up-chips.tsx +29 -0
  334. package/templates/docker/src/ui/components/chat/markdown.tsx +24 -0
  335. package/templates/docker/src/ui/components/chat/prompt-library.tsx +206 -0
  336. package/templates/docker/src/ui/components/chat/python-result-card.tsx +106 -78
  337. package/templates/docker/src/ui/components/chat/result-card-base.tsx +101 -0
  338. package/templates/docker/src/ui/components/chat/share-dialog.tsx +377 -0
  339. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +94 -73
  340. package/templates/docker/src/ui/components/chat/suggestion-chips.tsx +46 -0
  341. package/templates/docker/src/ui/components/chat/tool-part.tsx +16 -4
  342. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +48 -17
  343. package/templates/docker/src/ui/components/conversations/conversation-list.tsx +38 -24
  344. package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +66 -7
  345. package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +9 -2
  346. package/templates/docker/src/ui/components/error-boundary.tsx +66 -0
  347. package/templates/docker/src/ui/components/notebook/delete-cell-dialog.tsx +48 -0
  348. package/templates/docker/src/ui/components/notebook/fork-branch-selector.tsx +68 -0
  349. package/templates/docker/src/ui/components/notebook/notebook-cell-input.tsx +76 -0
  350. package/templates/docker/src/ui/components/notebook/notebook-cell-output.tsx +58 -0
  351. package/templates/docker/src/ui/components/notebook/notebook-cell-toolbar.tsx +91 -0
  352. package/templates/docker/src/ui/components/notebook/notebook-cell.tsx +119 -0
  353. package/templates/docker/src/ui/components/notebook/notebook-empty-state.tsx +19 -0
  354. package/templates/docker/src/ui/components/notebook/notebook-export.ts +287 -0
  355. package/templates/docker/src/ui/components/notebook/notebook-input-bar.tsx +49 -0
  356. package/templates/docker/src/ui/components/notebook/notebook-shell.tsx +266 -0
  357. package/templates/docker/src/ui/components/notebook/notebook-text-cell.tsx +152 -0
  358. package/templates/docker/src/ui/components/notebook/types.ts +39 -0
  359. package/templates/docker/src/ui/components/notebook/use-keyboard-nav.ts +109 -0
  360. package/templates/docker/src/ui/components/notebook/use-notebook.ts +684 -0
  361. package/templates/docker/src/ui/components/org-switcher.tsx +111 -0
  362. package/templates/docker/src/ui/components/region-picker.tsx +103 -0
  363. package/templates/docker/src/ui/components/schema-explorer/schema-explorer.tsx +522 -0
  364. package/templates/docker/src/ui/components/social-icons.tsx +26 -0
  365. package/templates/docker/src/ui/components/tour/guided-tour.tsx +81 -0
  366. package/templates/docker/src/ui/components/tour/index.ts +5 -0
  367. package/templates/docker/src/ui/components/tour/nav-bar.tsx +100 -0
  368. package/templates/docker/src/ui/components/tour/tour-overlay.tsx +298 -0
  369. package/templates/docker/src/ui/components/tour/tour-steps.ts +43 -0
  370. package/templates/docker/src/ui/components/tour/types.ts +21 -0
  371. package/templates/docker/src/ui/components/tour/use-tour.ts +193 -0
  372. package/templates/docker/src/ui/context-reexport.ts +3 -0
  373. package/templates/docker/src/ui/hooks/theme-init-script.ts +17 -0
  374. package/templates/docker/src/ui/hooks/use-admin-fetch.ts +38 -30
  375. package/templates/docker/src/ui/hooks/use-admin-mutation.ts +188 -0
  376. package/templates/docker/src/ui/hooks/use-atlas-transport.ts +225 -0
  377. package/templates/docker/src/ui/hooks/use-branding.ts +68 -0
  378. package/templates/docker/src/ui/hooks/use-conversations.ts +106 -83
  379. package/templates/docker/src/ui/hooks/use-dark-mode.ts +134 -10
  380. package/templates/docker/src/ui/hooks/use-deploy-mode.ts +36 -0
  381. package/templates/docker/src/ui/hooks/use-platform-admin-guard.ts +49 -0
  382. package/templates/docker/src/ui/lib/action-types.ts +11 -63
  383. package/templates/docker/src/ui/lib/admin-schemas.ts +744 -0
  384. package/templates/docker/src/ui/lib/fetch-client.ts +84 -0
  385. package/templates/docker/src/ui/lib/fetch-error.ts +54 -0
  386. package/templates/docker/src/ui/lib/helpers.ts +94 -1
  387. package/templates/docker/src/ui/lib/types.ts +149 -140
  388. package/templates/docker/tsconfig.json +4 -2
  389. package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +17 -14
  390. package/templates/nextjs-standalone/bin/__tests__/failure-threshold.test.ts +148 -0
  391. package/templates/nextjs-standalone/bin/__tests__/fatal-error-propagation.test.ts +267 -0
  392. package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +5 -5
  393. package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +39 -0
  394. package/templates/nextjs-standalone/bin/atlas.ts +981 -1819
  395. package/templates/nextjs-standalone/bin/benchmark.ts +14 -16
  396. package/templates/nextjs-standalone/bin/enrich.ts +7 -2
  397. package/templates/nextjs-standalone/brand.css +13 -0
  398. package/templates/nextjs-standalone/data/cybersec-semantic/catalog.yml +222 -0
  399. package/templates/nextjs-standalone/data/cybersec-semantic/entities/alerts.yml +195 -0
  400. package/templates/nextjs-standalone/data/cybersec-semantic/entities/assets.yml +191 -0
  401. package/templates/nextjs-standalone/data/cybersec-semantic/entities/compliance_assessments.yml +170 -0
  402. package/templates/nextjs-standalone/data/cybersec-semantic/entities/incidents.yml +219 -0
  403. package/templates/nextjs-standalone/data/cybersec-semantic/entities/organizations.yml +136 -0
  404. package/templates/nextjs-standalone/data/cybersec-semantic/entities/plans.yml +114 -0
  405. package/templates/nextjs-standalone/data/cybersec-semantic/entities/remediation_actions.yml +212 -0
  406. package/templates/nextjs-standalone/data/cybersec-semantic/entities/scan_results.yml +215 -0
  407. package/templates/nextjs-standalone/data/cybersec-semantic/entities/scans.yml +180 -0
  408. package/templates/nextjs-standalone/data/cybersec-semantic/entities/subscriptions.yml +184 -0
  409. package/templates/nextjs-standalone/data/cybersec-semantic/entities/users.yml +140 -0
  410. package/templates/nextjs-standalone/data/cybersec-semantic/entities/vulnerabilities.yml +154 -0
  411. package/templates/nextjs-standalone/data/cybersec-semantic/glossary.yml +207 -0
  412. package/templates/nextjs-standalone/data/cybersec-semantic/metrics/business.yml +148 -0
  413. package/templates/nextjs-standalone/data/cybersec-semantic/metrics/compliance.yml +138 -0
  414. package/templates/nextjs-standalone/data/cybersec-semantic/metrics/security.yml +181 -0
  415. package/templates/nextjs-standalone/data/cybersec.sql +8 -8
  416. package/templates/nextjs-standalone/data/demo.sql +3 -0
  417. package/templates/nextjs-standalone/data/ecommerce-semantic/catalog.yml +221 -0
  418. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/categories.yml +91 -0
  419. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/customers.yml +133 -0
  420. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/email_campaigns.yml +119 -0
  421. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/inventory_levels.yml +153 -0
  422. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/order_items.yml +159 -0
  423. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/orders.yml +199 -0
  424. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/payments.yml +140 -0
  425. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/product_reviews.yml +155 -0
  426. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/products.yml +178 -0
  427. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/promotions.yml +171 -0
  428. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/returns.yml +144 -0
  429. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/sellers.yml +124 -0
  430. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/shipments.yml +159 -0
  431. package/templates/nextjs-standalone/data/ecommerce-semantic/glossary.yml +193 -0
  432. package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/customers.yml +116 -0
  433. package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/operations.yml +131 -0
  434. package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/revenue.yml +120 -0
  435. package/templates/nextjs-standalone/docs/deploy.md +2 -1
  436. package/templates/nextjs-standalone/ee/src/__mocks__/internal.ts +170 -0
  437. package/templates/nextjs-standalone/ee/src/audit/purge-scheduler.ts +113 -0
  438. package/templates/nextjs-standalone/ee/src/audit/retention.ts +467 -0
  439. package/templates/nextjs-standalone/ee/src/auth/ip-allowlist.ts +367 -0
  440. package/templates/nextjs-standalone/ee/src/auth/roles.ts +562 -0
  441. package/templates/nextjs-standalone/ee/src/auth/scim.ts +343 -0
  442. package/templates/nextjs-standalone/ee/src/auth/sso.ts +538 -0
  443. package/templates/nextjs-standalone/ee/src/backups/engine.ts +355 -0
  444. package/templates/nextjs-standalone/ee/src/backups/index.ts +26 -0
  445. package/templates/nextjs-standalone/ee/src/backups/restore.ts +169 -0
  446. package/templates/nextjs-standalone/ee/src/backups/scheduler.ts +153 -0
  447. package/templates/nextjs-standalone/ee/src/backups/verify.ts +124 -0
  448. package/templates/nextjs-standalone/ee/src/branding/white-label.ts +228 -0
  449. package/templates/nextjs-standalone/ee/src/compliance/masking.ts +477 -0
  450. package/templates/nextjs-standalone/ee/src/compliance/patterns.ts +16 -0
  451. package/templates/nextjs-standalone/ee/src/compliance/pii-detection.ts +217 -0
  452. package/templates/nextjs-standalone/ee/src/compliance/reports.ts +402 -0
  453. package/templates/nextjs-standalone/ee/src/deploy-mode.ts +37 -0
  454. package/templates/nextjs-standalone/ee/src/governance/approval.ts +699 -0
  455. package/templates/nextjs-standalone/ee/src/index.ts +74 -0
  456. package/templates/nextjs-standalone/ee/src/platform/domains.ts +562 -0
  457. package/templates/nextjs-standalone/ee/src/platform/model-routing.ts +382 -0
  458. package/templates/nextjs-standalone/ee/src/platform/residency.ts +265 -0
  459. package/templates/nextjs-standalone/ee/src/sla/alerting.ts +382 -0
  460. package/templates/nextjs-standalone/ee/src/sla/index.ts +12 -0
  461. package/templates/nextjs-standalone/ee/src/sla/metrics.ts +275 -0
  462. package/templates/nextjs-standalone/ee/src/test-setup.ts +1 -0
  463. package/templates/nextjs-standalone/next.config.ts +1 -1
  464. package/templates/nextjs-standalone/package.json +50 -30
  465. package/templates/nextjs-standalone/src/api/index.ts +336 -24
  466. package/templates/nextjs-standalone/src/api/routes/actions.ts +443 -176
  467. package/templates/nextjs-standalone/src/api/routes/admin-abuse.ts +219 -0
  468. package/templates/nextjs-standalone/src/api/routes/admin-approval.ts +418 -0
  469. package/templates/nextjs-standalone/src/api/routes/admin-audit-retention.ts +405 -0
  470. package/templates/nextjs-standalone/src/api/routes/admin-auth.ts +122 -0
  471. package/templates/nextjs-standalone/src/api/routes/admin-branding.ts +252 -0
  472. package/templates/nextjs-standalone/src/api/routes/admin-compliance.ts +352 -0
  473. package/templates/nextjs-standalone/src/api/routes/admin-domains.ts +334 -0
  474. package/templates/nextjs-standalone/src/api/routes/admin-integrations.ts +2667 -0
  475. package/templates/nextjs-standalone/src/api/routes/admin-ip-allowlist.ts +261 -0
  476. package/templates/nextjs-standalone/src/api/routes/admin-learned-patterns.ts +525 -0
  477. package/templates/nextjs-standalone/src/api/routes/admin-model-config.ts +252 -0
  478. package/templates/nextjs-standalone/src/api/routes/admin-onboarding-emails.ts +145 -0
  479. package/templates/nextjs-standalone/src/api/routes/admin-orgs.ts +710 -0
  480. package/templates/nextjs-standalone/src/api/routes/admin-prompts.ts +694 -0
  481. package/templates/nextjs-standalone/src/api/routes/admin-residency.ts +570 -0
  482. package/templates/nextjs-standalone/src/api/routes/admin-roles.ts +296 -0
  483. package/templates/nextjs-standalone/src/api/routes/admin-router.ts +120 -0
  484. package/templates/nextjs-standalone/src/api/routes/admin-sandbox.ts +417 -0
  485. package/templates/nextjs-standalone/src/api/routes/admin-scim.ts +262 -0
  486. package/templates/nextjs-standalone/src/api/routes/admin-sso.ts +545 -0
  487. package/templates/nextjs-standalone/src/api/routes/admin-suggestions.ts +176 -0
  488. package/templates/nextjs-standalone/src/api/routes/admin-usage.ts +310 -0
  489. package/templates/nextjs-standalone/src/api/routes/admin.ts +4156 -898
  490. package/templates/nextjs-standalone/src/api/routes/auth-preamble.ts +105 -0
  491. package/templates/nextjs-standalone/src/api/routes/billing.ts +397 -0
  492. package/templates/nextjs-standalone/src/api/routes/chat.ts +597 -334
  493. package/templates/nextjs-standalone/src/api/routes/conversations.ts +987 -132
  494. package/templates/nextjs-standalone/src/api/routes/demo.ts +673 -0
  495. package/templates/nextjs-standalone/src/api/routes/discord.ts +274 -0
  496. package/templates/nextjs-standalone/src/api/routes/ee-error-handler.ts +32 -0
  497. package/templates/nextjs-standalone/src/api/routes/health.ts +129 -14
  498. package/templates/nextjs-standalone/src/api/routes/middleware.ts +244 -0
  499. package/templates/nextjs-standalone/src/api/routes/onboarding-emails.ts +134 -0
  500. package/templates/nextjs-standalone/src/api/routes/onboarding.ts +1109 -0
  501. package/templates/nextjs-standalone/src/api/routes/openapi.ts +184 -1597
  502. package/templates/nextjs-standalone/src/api/routes/platform-admin.ts +760 -0
  503. package/templates/nextjs-standalone/src/api/routes/platform-backups.ts +436 -0
  504. package/templates/nextjs-standalone/src/api/routes/platform-domains.ts +235 -0
  505. package/templates/nextjs-standalone/src/api/routes/platform-residency.ts +257 -0
  506. package/templates/nextjs-standalone/src/api/routes/platform-sla.ts +379 -0
  507. package/templates/nextjs-standalone/src/api/routes/prompts.ts +221 -0
  508. package/templates/nextjs-standalone/src/api/routes/public-branding.ts +106 -0
  509. package/templates/nextjs-standalone/src/api/routes/query.ts +330 -219
  510. package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +393 -297
  511. package/templates/nextjs-standalone/src/api/routes/semantic.ts +179 -0
  512. package/templates/nextjs-standalone/src/api/routes/sessions.ts +210 -0
  513. package/templates/nextjs-standalone/src/api/routes/shared-domains.ts +98 -0
  514. package/templates/nextjs-standalone/src/api/routes/shared-schemas.ts +139 -0
  515. package/templates/nextjs-standalone/src/api/routes/slack.ts +209 -52
  516. package/templates/nextjs-standalone/src/api/routes/suggestions.ts +233 -0
  517. package/templates/nextjs-standalone/src/api/routes/tables.ts +67 -0
  518. package/templates/nextjs-standalone/src/api/routes/teams.ts +222 -0
  519. package/templates/nextjs-standalone/src/api/routes/validate-sql.ts +188 -0
  520. package/templates/nextjs-standalone/src/api/routes/validation-hook.ts +62 -0
  521. package/templates/nextjs-standalone/src/api/routes/widget-loader.ts +356 -0
  522. package/templates/nextjs-standalone/src/api/routes/widget.ts +428 -0
  523. package/templates/nextjs-standalone/src/api/routes/wizard.ts +852 -0
  524. package/templates/nextjs-standalone/src/api/server.ts +187 -69
  525. package/templates/nextjs-standalone/src/app/error.tsx +5 -2
  526. package/templates/nextjs-standalone/src/app/globals.css +1 -1
  527. package/templates/nextjs-standalone/src/app/layout.tsx +7 -2
  528. package/templates/nextjs-standalone/src/app/page.tsx +39 -5
  529. package/templates/nextjs-standalone/src/components/data-table/data-table-column-header.tsx +99 -0
  530. package/templates/nextjs-standalone/src/components/data-table/data-table-date-filter.tsx +225 -0
  531. package/templates/nextjs-standalone/src/components/data-table/data-table-expandable.tsx +125 -0
  532. package/templates/nextjs-standalone/src/components/data-table/data-table-faceted-filter.tsx +189 -0
  533. package/templates/nextjs-standalone/src/components/data-table/data-table-pagination.tsx +112 -0
  534. package/templates/nextjs-standalone/src/components/data-table/data-table-range-filter.tsx +122 -0
  535. package/templates/nextjs-standalone/src/components/data-table/data-table-slider-filter.tsx +256 -0
  536. package/templates/nextjs-standalone/src/components/data-table/data-table-sort-list.tsx +407 -0
  537. package/templates/nextjs-standalone/src/components/data-table/data-table-toolbar.tsx +149 -0
  538. package/templates/nextjs-standalone/src/components/data-table/data-table-view-options.tsx +89 -0
  539. package/templates/nextjs-standalone/src/components/data-table/data-table.tsx +105 -0
  540. package/templates/nextjs-standalone/src/components/form-dialog.tsx +135 -0
  541. package/templates/nextjs-standalone/src/components/ui/accordion.tsx +66 -0
  542. package/templates/nextjs-standalone/src/components/ui/calendar.tsx +220 -0
  543. package/templates/nextjs-standalone/src/components/ui/checkbox.tsx +32 -0
  544. package/templates/nextjs-standalone/src/components/ui/faceted.tsx +283 -0
  545. package/templates/nextjs-standalone/src/components/ui/form.tsx +167 -0
  546. package/templates/nextjs-standalone/src/components/ui/label.tsx +24 -0
  547. package/templates/nextjs-standalone/src/components/ui/popover.tsx +89 -0
  548. package/templates/nextjs-standalone/src/components/ui/progress.tsx +31 -0
  549. package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +6 -2
  550. package/templates/nextjs-standalone/src/components/ui/slider.tsx +63 -0
  551. package/templates/nextjs-standalone/src/components/ui/sortable.tsx +581 -0
  552. package/templates/nextjs-standalone/src/components/ui/switch.tsx +35 -0
  553. package/templates/nextjs-standalone/src/components/ui/textarea.tsx +18 -0
  554. package/templates/nextjs-standalone/src/config/data-table.ts +82 -0
  555. package/templates/nextjs-standalone/src/env-check.ts +74 -0
  556. package/templates/nextjs-standalone/src/hooks/use-callback-ref.ts +27 -0
  557. package/templates/nextjs-standalone/src/hooks/use-data-table.ts +316 -0
  558. package/templates/nextjs-standalone/src/hooks/use-debounced-callback.ts +28 -0
  559. package/templates/nextjs-standalone/src/lib/action-types.ts +7 -41
  560. package/templates/nextjs-standalone/src/lib/agent-query.ts +4 -2
  561. package/templates/nextjs-standalone/src/lib/agent.ts +363 -31
  562. package/templates/nextjs-standalone/src/lib/api-url.ts +2 -3
  563. package/templates/nextjs-standalone/src/lib/auth/admin-permissions.ts +38 -0
  564. package/templates/nextjs-standalone/src/lib/auth/audit.ts +19 -4
  565. package/templates/nextjs-standalone/src/lib/auth/byot.ts +3 -3
  566. package/templates/nextjs-standalone/src/lib/auth/detect.ts +29 -8
  567. package/templates/nextjs-standalone/src/lib/auth/managed.ts +104 -14
  568. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +53 -6
  569. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +140 -15
  570. package/templates/nextjs-standalone/src/lib/auth/oauth-state.ts +123 -0
  571. package/templates/nextjs-standalone/src/lib/auth/org-permissions.ts +55 -0
  572. package/templates/nextjs-standalone/src/lib/auth/permissions.ts +26 -19
  573. package/templates/nextjs-standalone/src/lib/auth/server.ts +355 -9
  574. package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +3 -3
  575. package/templates/nextjs-standalone/src/lib/auth/types.ts +15 -21
  576. package/templates/nextjs-standalone/src/lib/billing/enforcement.ts +368 -0
  577. package/templates/nextjs-standalone/src/lib/billing/plans.ts +155 -0
  578. package/templates/nextjs-standalone/src/lib/cache/index.ts +92 -0
  579. package/templates/nextjs-standalone/src/lib/cache/keys.ts +30 -0
  580. package/templates/nextjs-standalone/src/lib/cache/lru.ts +79 -0
  581. package/templates/nextjs-standalone/src/lib/cache/types.ts +31 -0
  582. package/templates/nextjs-standalone/src/lib/compose-refs.ts +62 -0
  583. package/templates/nextjs-standalone/src/lib/config.ts +563 -11
  584. package/templates/nextjs-standalone/src/lib/connection-types.ts +9 -0
  585. package/templates/nextjs-standalone/src/lib/conversation-types.ts +1 -25
  586. package/templates/nextjs-standalone/src/lib/conversations.ts +345 -14
  587. package/templates/nextjs-standalone/src/lib/data-table.ts +61 -0
  588. package/templates/nextjs-standalone/src/lib/db/connection.ts +793 -39
  589. package/templates/nextjs-standalone/src/lib/db/internal.ts +985 -139
  590. package/templates/nextjs-standalone/src/lib/db/migrate.ts +295 -0
  591. package/templates/nextjs-standalone/src/lib/db/migrations/0000_baseline.sql +703 -0
  592. package/templates/nextjs-standalone/src/lib/db/migrations/0001_teams_installations.sql +14 -0
  593. package/templates/nextjs-standalone/src/lib/db/migrations/0002_discord_installations.sql +14 -0
  594. package/templates/nextjs-standalone/src/lib/db/migrations/0003_telegram_installations.sql +15 -0
  595. package/templates/nextjs-standalone/src/lib/db/migrations/0004_sandbox_credentials.sql +18 -0
  596. package/templates/nextjs-standalone/src/lib/db/migrations/0005_oauth_state.sql +16 -0
  597. package/templates/nextjs-standalone/src/lib/db/migrations/0006_byot_credentials.sql +14 -0
  598. package/templates/nextjs-standalone/src/lib/db/migrations/0007_gchat_installations.sql +15 -0
  599. package/templates/nextjs-standalone/src/lib/db/migrations/0008_github_installations.sql +14 -0
  600. package/templates/nextjs-standalone/src/lib/db/migrations/0009_linear_installations.sql +15 -0
  601. package/templates/nextjs-standalone/src/lib/db/migrations/0010_whatsapp_installations.sql +14 -0
  602. package/templates/nextjs-standalone/src/lib/db/migrations/0011_email_installations.sql +16 -0
  603. package/templates/nextjs-standalone/src/lib/db/migrations/0012_region_migrations.sql +25 -0
  604. package/templates/nextjs-standalone/src/lib/db/schema.ts +1120 -0
  605. package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +89 -139
  606. package/templates/nextjs-standalone/src/lib/demo.ts +308 -0
  607. package/templates/nextjs-standalone/src/lib/discord/store.ts +225 -0
  608. package/templates/nextjs-standalone/src/lib/effect/ai.ts +243 -0
  609. package/templates/nextjs-standalone/src/lib/effect/errors.ts +234 -0
  610. package/templates/nextjs-standalone/src/lib/effect/hono.ts +454 -0
  611. package/templates/nextjs-standalone/src/lib/effect/index.ts +137 -0
  612. package/templates/nextjs-standalone/src/lib/effect/layers.ts +496 -0
  613. package/templates/nextjs-standalone/src/lib/effect/services.ts +776 -0
  614. package/templates/nextjs-standalone/src/lib/effect/sql.ts +178 -0
  615. package/templates/nextjs-standalone/src/lib/effect/toolkit.ts +123 -0
  616. package/templates/nextjs-standalone/src/lib/email/delivery.ts +232 -0
  617. package/templates/nextjs-standalone/src/lib/email/engine.ts +349 -0
  618. package/templates/nextjs-standalone/src/lib/email/hooks.ts +107 -0
  619. package/templates/nextjs-standalone/src/lib/email/index.ts +16 -0
  620. package/templates/nextjs-standalone/src/lib/email/scheduler.ts +72 -0
  621. package/templates/nextjs-standalone/src/lib/email/sequence.ts +73 -0
  622. package/templates/nextjs-standalone/src/lib/email/store.ts +163 -0
  623. package/templates/nextjs-standalone/src/lib/email/templates.ts +215 -0
  624. package/templates/nextjs-standalone/src/lib/format.test.ts +117 -0
  625. package/templates/nextjs-standalone/src/lib/format.ts +67 -0
  626. package/templates/nextjs-standalone/src/lib/gchat/store.ts +202 -0
  627. package/templates/nextjs-standalone/src/lib/github/store.ts +197 -0
  628. package/templates/nextjs-standalone/src/lib/id.ts +29 -0
  629. package/templates/nextjs-standalone/src/lib/integrations/types.ts +166 -0
  630. package/templates/nextjs-standalone/src/lib/learn/pattern-analyzer.ts +224 -0
  631. package/templates/nextjs-standalone/src/lib/learn/pattern-cache.ts +229 -0
  632. package/templates/nextjs-standalone/src/lib/learn/pattern-proposer.ts +87 -0
  633. package/templates/nextjs-standalone/src/lib/learn/suggestion-helpers.ts +34 -0
  634. package/templates/nextjs-standalone/src/lib/learn/suggestions.ts +139 -0
  635. package/templates/nextjs-standalone/src/lib/linear/store.ts +200 -0
  636. package/templates/nextjs-standalone/src/lib/logger.ts +35 -3
  637. package/templates/nextjs-standalone/src/lib/metering.ts +272 -0
  638. package/templates/nextjs-standalone/src/lib/parsers.ts +99 -0
  639. package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +13 -11
  640. package/templates/nextjs-standalone/src/lib/plugins/index.ts +3 -1
  641. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +58 -6
  642. package/templates/nextjs-standalone/src/lib/plugins/settings.ts +147 -0
  643. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +6 -9
  644. package/templates/nextjs-standalone/src/lib/profiler.ts +1665 -0
  645. package/templates/nextjs-standalone/src/lib/providers.ts +188 -13
  646. package/templates/nextjs-standalone/src/lib/rls.ts +172 -60
  647. package/templates/nextjs-standalone/src/lib/sandbox/credentials.ts +206 -0
  648. package/templates/nextjs-standalone/src/lib/sandbox/validate.ts +179 -0
  649. package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +26 -94
  650. package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +174 -34
  651. package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -150
  652. package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +190 -154
  653. package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +74 -23
  654. package/templates/nextjs-standalone/src/lib/scheduler/preview.ts +72 -0
  655. package/templates/nextjs-standalone/src/lib/security/abuse.ts +463 -0
  656. package/templates/nextjs-standalone/src/lib/semantic/diff.ts +267 -0
  657. package/templates/nextjs-standalone/src/lib/semantic/entities.ts +167 -0
  658. package/templates/nextjs-standalone/src/lib/semantic/files.ts +283 -0
  659. package/templates/nextjs-standalone/src/lib/semantic/index.ts +27 -0
  660. package/templates/nextjs-standalone/src/lib/{semantic-index.ts → semantic/search.ts} +80 -9
  661. package/templates/nextjs-standalone/src/lib/semantic/sync.ts +581 -0
  662. package/templates/nextjs-standalone/src/lib/{semantic.ts → semantic/whitelist.ts} +189 -3
  663. package/templates/nextjs-standalone/src/lib/settings.ts +817 -0
  664. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +13 -0
  665. package/templates/nextjs-standalone/src/lib/slack/store.ts +134 -25
  666. package/templates/nextjs-standalone/src/lib/startup.ts +528 -362
  667. package/templates/nextjs-standalone/src/lib/teams/store.ts +216 -0
  668. package/templates/nextjs-standalone/src/lib/telegram/store.ts +202 -0
  669. package/templates/nextjs-standalone/src/lib/telemetry.ts +40 -0
  670. package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +8 -5
  671. package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +3 -1
  672. package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +276 -93
  673. package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +2 -2
  674. package/templates/nextjs-standalone/src/lib/tools/backends/detect.ts +16 -0
  675. package/templates/nextjs-standalone/src/lib/tools/backends/index.ts +11 -0
  676. package/templates/nextjs-standalone/src/lib/tools/backends/nsjail.ts +213 -0
  677. package/templates/nextjs-standalone/src/lib/tools/backends/shared.ts +103 -0
  678. package/templates/nextjs-standalone/src/lib/tools/backends/types.ts +26 -0
  679. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +7 -228
  680. package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +4 -29
  681. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +18 -2
  682. package/templates/nextjs-standalone/src/lib/tools/explore.ts +246 -54
  683. package/templates/nextjs-standalone/src/lib/tools/index.ts +17 -0
  684. package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +11 -139
  685. package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +9 -132
  686. package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +184 -3
  687. package/templates/nextjs-standalone/src/lib/tools/python-stream.ts +33 -0
  688. package/templates/nextjs-standalone/src/lib/tools/python-wrapper.ts +129 -0
  689. package/templates/nextjs-standalone/src/lib/tools/python.ts +115 -15
  690. package/templates/nextjs-standalone/src/lib/tools/registry.ts +14 -2
  691. package/templates/nextjs-standalone/src/lib/tools/sql.ts +778 -362
  692. package/templates/nextjs-standalone/src/lib/tracing.ts +16 -0
  693. package/templates/nextjs-standalone/src/lib/whatsapp/store.ts +198 -0
  694. package/templates/nextjs-standalone/src/lib/workspace.ts +89 -0
  695. package/templates/nextjs-standalone/src/progress.ts +121 -0
  696. package/templates/nextjs-standalone/src/types/data-table.ts +48 -0
  697. package/templates/nextjs-standalone/src/ui/atlas-chat-reexport.ts +3 -0
  698. package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +26 -19
  699. package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +3 -3
  700. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +57 -39
  701. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +213 -35
  702. package/templates/nextjs-standalone/src/ui/components/admin/delivery-status-badge.tsx +53 -0
  703. package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +27 -6
  704. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -52
  705. package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +2 -2
  706. package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +28 -5
  707. package/templates/nextjs-standalone/src/ui/components/admin-content-wrapper.tsx +87 -0
  708. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +449 -166
  709. package/templates/nextjs-standalone/src/ui/components/branding-head.tsx +41 -0
  710. package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +62 -5
  711. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +316 -125
  712. package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +4 -4
  713. package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +45 -4
  714. package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +86 -5
  715. package/templates/nextjs-standalone/src/ui/components/chat/follow-up-chips.tsx +29 -0
  716. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +24 -0
  717. package/templates/nextjs-standalone/src/ui/components/chat/prompt-library.tsx +206 -0
  718. package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +106 -78
  719. package/templates/nextjs-standalone/src/ui/components/chat/result-card-base.tsx +101 -0
  720. package/templates/nextjs-standalone/src/ui/components/chat/share-dialog.tsx +377 -0
  721. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +94 -73
  722. package/templates/nextjs-standalone/src/ui/components/chat/suggestion-chips.tsx +46 -0
  723. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +16 -4
  724. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +48 -17
  725. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +38 -24
  726. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +66 -7
  727. package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +9 -2
  728. package/templates/nextjs-standalone/src/ui/components/error-boundary.tsx +66 -0
  729. package/templates/nextjs-standalone/src/ui/components/notebook/delete-cell-dialog.tsx +48 -0
  730. package/templates/nextjs-standalone/src/ui/components/notebook/fork-branch-selector.tsx +68 -0
  731. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-input.tsx +76 -0
  732. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-output.tsx +58 -0
  733. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-toolbar.tsx +91 -0
  734. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell.tsx +119 -0
  735. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-empty-state.tsx +19 -0
  736. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-export.ts +287 -0
  737. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-input-bar.tsx +49 -0
  738. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-shell.tsx +266 -0
  739. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-text-cell.tsx +152 -0
  740. package/templates/nextjs-standalone/src/ui/components/notebook/types.ts +39 -0
  741. package/templates/nextjs-standalone/src/ui/components/notebook/use-keyboard-nav.ts +109 -0
  742. package/templates/nextjs-standalone/src/ui/components/notebook/use-notebook.ts +684 -0
  743. package/templates/nextjs-standalone/src/ui/components/org-switcher.tsx +111 -0
  744. package/templates/nextjs-standalone/src/ui/components/region-picker.tsx +103 -0
  745. package/templates/nextjs-standalone/src/ui/components/schema-explorer/schema-explorer.tsx +522 -0
  746. package/templates/nextjs-standalone/src/ui/components/social-icons.tsx +26 -0
  747. package/templates/nextjs-standalone/src/ui/components/tour/guided-tour.tsx +81 -0
  748. package/templates/nextjs-standalone/src/ui/components/tour/index.ts +5 -0
  749. package/templates/nextjs-standalone/src/ui/components/tour/nav-bar.tsx +100 -0
  750. package/templates/nextjs-standalone/src/ui/components/tour/tour-overlay.tsx +298 -0
  751. package/templates/nextjs-standalone/src/ui/components/tour/tour-steps.ts +43 -0
  752. package/templates/nextjs-standalone/src/ui/components/tour/types.ts +21 -0
  753. package/templates/nextjs-standalone/src/ui/components/tour/use-tour.ts +193 -0
  754. package/templates/nextjs-standalone/src/ui/context-reexport.ts +3 -0
  755. package/templates/nextjs-standalone/src/ui/hooks/theme-init-script.ts +17 -0
  756. package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +38 -30
  757. package/templates/nextjs-standalone/src/ui/hooks/use-admin-mutation.ts +188 -0
  758. package/templates/nextjs-standalone/src/ui/hooks/use-atlas-transport.ts +225 -0
  759. package/templates/nextjs-standalone/src/ui/hooks/use-branding.ts +68 -0
  760. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +106 -83
  761. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +134 -10
  762. package/templates/nextjs-standalone/src/ui/hooks/use-deploy-mode.ts +36 -0
  763. package/templates/nextjs-standalone/src/ui/hooks/use-platform-admin-guard.ts +49 -0
  764. package/templates/nextjs-standalone/src/ui/lib/action-types.ts +11 -63
  765. package/templates/nextjs-standalone/src/ui/lib/admin-schemas.ts +744 -0
  766. package/templates/nextjs-standalone/src/ui/lib/fetch-client.ts +84 -0
  767. package/templates/nextjs-standalone/src/ui/lib/fetch-error.ts +54 -0
  768. package/templates/nextjs-standalone/src/ui/lib/helpers.ts +94 -1
  769. package/templates/nextjs-standalone/src/ui/lib/types.ts +149 -140
  770. package/templates/nextjs-standalone/tsconfig.json +3 -2
  771. package/templates/docker/src/api/__tests__/actions.test.ts +0 -683
  772. package/templates/docker/src/api/__tests__/admin.test.ts +0 -820
  773. package/templates/docker/src/api/__tests__/auth.test.ts +0 -165
  774. package/templates/docker/src/api/__tests__/chat.test.ts +0 -376
  775. package/templates/docker/src/api/__tests__/conversations.test.ts +0 -555
  776. package/templates/docker/src/api/__tests__/cors.test.ts +0 -135
  777. package/templates/docker/src/api/__tests__/health-plugin.test.ts +0 -176
  778. package/templates/docker/src/api/__tests__/health.test.ts +0 -283
  779. package/templates/docker/src/api/__tests__/query.test.ts +0 -891
  780. package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +0 -601
  781. package/templates/docker/src/api/__tests__/slack.test.ts +0 -847
  782. package/templates/docker/src/lib/__tests__/agent-cache.test.ts +0 -439
  783. package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +0 -131
  784. package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +0 -166
  785. package/templates/docker/src/lib/__tests__/agent-integration.test.ts +0 -516
  786. package/templates/docker/src/lib/__tests__/config-actions.test.ts +0 -166
  787. package/templates/docker/src/lib/__tests__/config.test.ts +0 -1113
  788. package/templates/docker/src/lib/__tests__/conversations.test.ts +0 -589
  789. package/templates/docker/src/lib/__tests__/errors.test.ts +0 -256
  790. package/templates/docker/src/lib/__tests__/logger.test.ts +0 -200
  791. package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +0 -321
  792. package/templates/docker/src/lib/__tests__/providers.test.ts +0 -130
  793. package/templates/docker/src/lib/__tests__/rls.test.ts +0 -435
  794. package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +0 -124
  795. package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +0 -550
  796. package/templates/docker/src/lib/__tests__/semantic-index.test.ts +0 -547
  797. package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +0 -544
  798. package/templates/docker/src/lib/__tests__/semantic.test.ts +0 -363
  799. package/templates/docker/src/lib/__tests__/startup-actions.test.ts +0 -461
  800. package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +0 -429
  801. package/templates/docker/src/lib/__tests__/startup.test.ts +0 -470
  802. package/templates/docker/src/lib/__tests__/tracing.test.ts +0 -28
  803. package/templates/docker/src/lib/auth/__tests__/audit.test.ts +0 -418
  804. package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +0 -222
  805. package/templates/docker/src/lib/auth/__tests__/byot.test.ts +0 -366
  806. package/templates/docker/src/lib/auth/__tests__/detect.test.ts +0 -190
  807. package/templates/docker/src/lib/auth/__tests__/managed.test.ts +0 -173
  808. package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +0 -456
  809. package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +0 -203
  810. package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +0 -225
  811. package/templates/docker/src/lib/auth/__tests__/server.test.ts +0 -34
  812. package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +0 -176
  813. package/templates/docker/src/lib/auth/__tests__/types.test.ts +0 -44
  814. package/templates/docker/src/lib/db/__tests__/connection.test.ts +0 -144
  815. package/templates/docker/src/lib/db/__tests__/internal.test.ts +0 -387
  816. package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +0 -190
  817. package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -137
  818. package/templates/docker/src/lib/db/__tests__/registry.test.ts +0 -398
  819. package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +0 -130
  820. package/templates/docker/src/lib/errors.ts +0 -154
  821. package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +0 -204
  822. package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +0 -529
  823. package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +0 -875
  824. package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +0 -373
  825. package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +0 -49
  826. package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +0 -799
  827. package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +0 -192
  828. package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +0 -248
  829. package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +0 -96
  830. package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +0 -78
  831. package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +0 -78
  832. package/templates/docker/src/lib/scheduler/index.ts +0 -7
  833. package/templates/docker/src/lib/slack/__tests__/api.test.ts +0 -160
  834. package/templates/docker/src/lib/slack/__tests__/format.test.ts +0 -237
  835. package/templates/docker/src/lib/slack/__tests__/store.test.ts +0 -188
  836. package/templates/docker/src/lib/slack/__tests__/threads.test.ts +0 -112
  837. package/templates/docker/src/lib/slack/__tests__/verify.test.ts +0 -111
  838. package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +0 -594
  839. package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +0 -240
  840. package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +0 -267
  841. package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +0 -506
  842. package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +0 -374
  843. package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +0 -82
  844. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +0 -210
  845. package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +0 -515
  846. package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +0 -397
  847. package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +0 -365
  848. package/templates/docker/src/lib/tools/__tests__/python.test.ts +0 -331
  849. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +0 -132
  850. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +0 -242
  851. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +0 -227
  852. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +0 -100
  853. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +0 -227
  854. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +0 -709
  855. package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +0 -211
  856. package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +0 -378
  857. package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +0 -681
  858. package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +0 -427
  859. package/templates/docker/src/test-setup.ts +0 -38
  860. package/templates/docker/src/types/vercel-sandbox.d.ts +0 -61
  861. package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +0 -116
  862. package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +0 -683
  863. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +0 -820
  864. package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +0 -165
  865. package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +0 -376
  866. package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +0 -555
  867. package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +0 -135
  868. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +0 -176
  869. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +0 -283
  870. package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +0 -891
  871. package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +0 -601
  872. package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +0 -847
  873. package/templates/nextjs-standalone/src/app/global-error.tsx +0 -68
  874. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +0 -439
  875. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +0 -131
  876. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +0 -166
  877. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +0 -516
  878. package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +0 -166
  879. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +0 -1113
  880. package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +0 -589
  881. package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +0 -256
  882. package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +0 -200
  883. package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +0 -321
  884. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +0 -130
  885. package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +0 -435
  886. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +0 -124
  887. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +0 -550
  888. package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +0 -547
  889. package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +0 -544
  890. package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +0 -363
  891. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +0 -461
  892. package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +0 -429
  893. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +0 -470
  894. package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +0 -28
  895. package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +0 -418
  896. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +0 -222
  897. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +0 -366
  898. package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +0 -190
  899. package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +0 -173
  900. package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +0 -456
  901. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +0 -203
  902. package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +0 -225
  903. package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +0 -34
  904. package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +0 -176
  905. package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +0 -44
  906. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +0 -144
  907. package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +0 -387
  908. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +0 -190
  909. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -137
  910. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +0 -398
  911. package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +0 -130
  912. package/templates/nextjs-standalone/src/lib/errors.ts +0 -154
  913. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +0 -204
  914. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +0 -529
  915. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +0 -875
  916. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +0 -373
  917. package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +0 -49
  918. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +0 -799
  919. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +0 -192
  920. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +0 -248
  921. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +0 -96
  922. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +0 -78
  923. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +0 -78
  924. package/templates/nextjs-standalone/src/lib/scheduler/index.ts +0 -7
  925. package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +0 -160
  926. package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +0 -237
  927. package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +0 -188
  928. package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +0 -112
  929. package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +0 -111
  930. package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +0 -594
  931. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +0 -240
  932. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +0 -267
  933. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +0 -506
  934. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +0 -374
  935. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +0 -82
  936. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +0 -210
  937. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +0 -515
  938. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +0 -397
  939. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +0 -365
  940. package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +0 -331
  941. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +0 -132
  942. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +0 -242
  943. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +0 -227
  944. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +0 -100
  945. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +0 -227
  946. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +0 -709
  947. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +0 -211
  948. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +0 -378
  949. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +0 -681
  950. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +0 -427
  951. package/templates/nextjs-standalone/src/test-setup.ts +0 -38
  952. package/templates/nextjs-standalone/src/ui/components/chat/managed-auth-card.tsx +0 -116
@@ -4,19 +4,232 @@
4
4
  * Read-write Postgres connection for Atlas's own state (auth, audit, settings).
5
5
  * Completely separate from the analytics datasource in connection.ts.
6
6
  * Configured via DATABASE_URL.
7
+ *
8
+ * Effect migration (P11b):
9
+ * The pool singleton is replaced by an Effect-managed PgClient Layer.
10
+ * Pool lifecycle is automatic via Effect scope — closeInternalDB() is
11
+ * no longer needed (kept for backward compat but delegates to PgClient).
12
+ * The existing internalQuery/internalExecute API is unchanged.
7
13
  */
8
14
 
15
+ import * as crypto from "crypto";
16
+ import { Context, Effect, Layer, Schedule, Duration, Fiber } from "effect";
9
17
  import { createLogger } from "@atlas/api/lib/logger";
10
18
 
11
19
  const log = createLogger("internal-db");
12
20
 
21
+ // ---------------------------------------------------------------------------
22
+ // Connection URL encryption (AES-256-GCM)
23
+ // ---------------------------------------------------------------------------
24
+
25
+ const ENCRYPTION_ALGORITHM = "aes-256-gcm";
26
+ const IV_LENGTH = 12; // 96-bit IV recommended for GCM
27
+ const AUTH_TAG_LENGTH = 16;
28
+
29
+ let _cachedKey: { raw: string; key: Buffer } | null = null;
30
+
31
+ /**
32
+ * Returns the 32-byte encryption key derived via SHA-256 from
33
+ * ATLAS_ENCRYPTION_KEY (takes precedence) or BETTER_AUTH_SECRET.
34
+ * Returns null if neither is set. Result is cached.
35
+ */
36
+ export function getEncryptionKey(): Buffer | null {
37
+ const raw = process.env.ATLAS_ENCRYPTION_KEY ?? process.env.BETTER_AUTH_SECRET;
38
+ if (!raw) return null;
39
+ if (_cachedKey && _cachedKey.raw === raw) return _cachedKey.key;
40
+ // Derive a fixed 32-byte key via SHA-256 so any-length secret works
41
+ const key = crypto.createHash("sha256").update(raw).digest();
42
+ _cachedKey = { raw, key };
43
+ return key;
44
+ }
45
+
46
+ /** @internal Reset cached encryption key — for testing only. */
47
+ export function _resetEncryptionKeyCache(): void {
48
+ _cachedKey = null;
49
+ }
50
+
51
+ /**
52
+ * Encrypts a connection URL using AES-256-GCM.
53
+ * Returns `iv:authTag:ciphertext` (all base64). Returns the plaintext
54
+ * unchanged if no encryption key is available.
55
+ */
56
+ export function encryptUrl(plaintext: string): string {
57
+ const key = getEncryptionKey();
58
+ if (!key) return plaintext;
59
+
60
+ const iv = crypto.randomBytes(IV_LENGTH);
61
+ const cipher = crypto.createCipheriv(ENCRYPTION_ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });
62
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
63
+ const authTag = cipher.getAuthTag();
64
+
65
+ // `:` is safe as delimiter — base64 alphabet is A-Za-z0-9+/= (no colon)
66
+ return `${iv.toString("base64")}:${authTag.toString("base64")}:${encrypted.toString("base64")}`;
67
+ }
68
+
69
+ /**
70
+ * Decrypts a connection URL encrypted by `encryptUrl()`.
71
+ * Plaintext detection (two checks):
72
+ * 1. Starts with a URL scheme (`postgresql://`, `mysql://`, etc.) → plaintext
73
+ * 2. Not exactly 3 colon-separated parts (`iv:authTag:ciphertext`) → plaintext
74
+ * Returns plaintext values as-is for backward compatibility with pre-encryption data.
75
+ */
76
+ export function decryptUrl(stored: string): string {
77
+ if (isPlaintextUrl(stored)) return stored;
78
+
79
+ const key = getEncryptionKey();
80
+ if (!key) {
81
+ log.error("Encrypted connection URL found but no encryption key is available — set ATLAS_ENCRYPTION_KEY or BETTER_AUTH_SECRET");
82
+ throw new Error("Cannot decrypt connection URL: no encryption key available");
83
+ }
84
+
85
+ const parts = stored.split(":");
86
+ if (parts.length !== 3) {
87
+ log.error({ partCount: parts.length }, "Stored connection URL is not plaintext and does not match encrypted format (expected 3 colon-separated parts)");
88
+ throw new Error("Failed to decrypt connection URL: unrecognized format");
89
+ }
90
+
91
+ try {
92
+ const iv = Buffer.from(parts[0], "base64");
93
+ const authTag = Buffer.from(parts[1], "base64");
94
+ const ciphertext = Buffer.from(parts[2], "base64");
95
+
96
+ const decipher = crypto.createDecipheriv(ENCRYPTION_ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH });
97
+ decipher.setAuthTag(authTag);
98
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
99
+ return decrypted.toString("utf8");
100
+ } catch (err) {
101
+ log.error(
102
+ { err: err instanceof Error ? err.message : String(err) },
103
+ "Failed to decrypt connection URL — data may be corrupted or key may have changed",
104
+ );
105
+ throw new Error("Failed to decrypt connection URL", { cause: err });
106
+ }
107
+ }
108
+
109
+ /** Returns true if the stored value looks like a plaintext URL (any URI scheme, not just database schemes). */
110
+ export function isPlaintextUrl(value: string): boolean {
111
+ return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value);
112
+ }
113
+
13
114
  /** Typed interface for the internal pg.Pool — avoids importing pg at module level. */
115
+ export interface InternalPoolClient {
116
+ query(sql: string, params?: unknown[]): Promise<{ rows: Record<string, unknown>[] }>;
117
+ release(): void;
118
+ }
119
+
14
120
  export interface InternalPool {
15
121
  query(sql: string, params?: unknown[]): Promise<{ rows: Record<string, unknown>[] }>;
122
+ connect(): Promise<InternalPoolClient>;
16
123
  end(): Promise<void>;
17
124
  on(event: "error", listener: (err: Error) => void): void;
18
125
  }
19
126
 
127
+ // ── Effect Service: InternalDB (P11b) ───────────────────────────────
128
+
129
+ /**
130
+ * InternalDB Effect service — provides access to the internal Postgres pool.
131
+ *
132
+ * Effect-managed lifecycle: pool is created during Layer construction and
133
+ * closed automatically when the Layer scope ends. Replaces the manual
134
+ * _pool singleton + closeInternalDB() pattern.
135
+ */
136
+ export interface InternalDBShape {
137
+ /** Execute a parameterized query returning typed rows. */
138
+ query<T extends Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
139
+ /**
140
+ * Fire-and-forget write (uses circuit breaker internally).
141
+ * Intentionally void (not Effect/Promise) — called from onFinish callbacks
142
+ * in the agent loop where back-pressure would block stream finalization.
143
+ */
144
+ execute(sql: string, params?: unknown[]): void;
145
+ /** Whether the internal DB is available. */
146
+ readonly available: boolean;
147
+ /** The underlying pool (for advanced usage like migrations). Null when DATABASE_URL is not set. */
148
+ readonly pool: InternalPool | null;
149
+ }
150
+
151
+ export class InternalDB extends Context.Tag("InternalDB")<
152
+ InternalDB,
153
+ InternalDBShape
154
+ >() {}
155
+
156
+ /**
157
+ * Create the Live Layer for InternalDB.
158
+ *
159
+ * Bridge layer: creates the pool via the existing getInternalDB() singleton
160
+ * to preserve production-tested pg.Pool configuration (sslmode normalization,
161
+ * max connections, idle timeout). Pool cleanup is managed by an Effect
162
+ * finalizer that delegates to closeInternalDB().
163
+ *
164
+ * Future: replace getInternalDB() with PgClient.make() for native @effect/sql.
165
+ */
166
+ export function makeInternalDBLive(): Layer.Layer<InternalDB> {
167
+ return Layer.scoped(
168
+ InternalDB,
169
+ Effect.gen(function* () {
170
+ const databaseUrl = process.env.DATABASE_URL;
171
+ if (!databaseUrl) {
172
+ return {
173
+ query: async () => { throw new Error("DATABASE_URL is not set"); },
174
+ execute: () => { log.debug("internalExecute called but DATABASE_URL is not set — no-op"); },
175
+ available: false,
176
+ pool: null,
177
+ } satisfies InternalDBShape;
178
+ }
179
+
180
+ // Create pool via the existing getInternalDB() for now.
181
+ // This preserves the pg.Pool configuration (sslmode normalization,
182
+ // max connections, idle timeout) that has been production-tested.
183
+ // Future: replace with PgClient.make({ url: Redacted.make(databaseUrl) })
184
+ const pool = getInternalDB();
185
+
186
+ yield* Effect.addFinalizer(() =>
187
+ Effect.tryPromise({
188
+ try: () => closeInternalDB(),
189
+ catch: (err) => (err instanceof Error ? err.message : String(err)),
190
+ }).pipe(
191
+ Effect.catchAll((errMsg) => {
192
+ log.warn({ err: errMsg }, "Error closing internal DB pool via Effect finalizer");
193
+ return Effect.void;
194
+ }),
195
+ ),
196
+ );
197
+
198
+ return {
199
+ query: async <T extends Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]> => {
200
+ const result = await pool.query(sql, params);
201
+ return result.rows as T[];
202
+ },
203
+ execute: (sql: string, params?: unknown[]) => internalExecute(sql, params),
204
+ available: true,
205
+ pool,
206
+ } satisfies InternalDBShape;
207
+ }),
208
+ );
209
+ }
210
+
211
+ /** Create a test Layer for InternalDB. */
212
+ export function createInternalDBTestLayer(
213
+ partial: Partial<InternalDBShape> = {},
214
+ ): Layer.Layer<InternalDB> {
215
+ const mockPool: InternalPool = {
216
+ query: async () => ({ rows: [] }),
217
+ async connect() {
218
+ return { query: async () => ({ rows: [] }), release() {} };
219
+ },
220
+ end: async () => {},
221
+ on: () => {},
222
+ };
223
+ return Layer.succeed(InternalDB, {
224
+ query: partial.query ?? (async () => []),
225
+ execute: partial.execute ?? (() => {}),
226
+ available: partial.available ?? true,
227
+ pool: partial.pool ?? mockPool,
228
+ });
229
+ }
230
+
231
+ // ── Legacy singleton (backward compat) ──────────────────────────────
232
+
20
233
  let _pool: InternalPool | null = null;
21
234
 
22
235
  /** Returns true if DATABASE_URL is configured. */
@@ -27,7 +240,8 @@ export function hasInternalDB(): boolean {
27
240
  /** Returns the singleton pg.Pool for the internal database. Throws if DATABASE_URL is not set. */
28
241
  export function getInternalDB(): InternalPool {
29
242
  if (!_pool) {
30
- if (!process.env.DATABASE_URL) {
243
+ const databaseUrl = process.env.DATABASE_URL;
244
+ if (!databaseUrl) {
31
245
  throw new Error(
32
246
  "DATABASE_URL is not set. Atlas internal database requires a PostgreSQL connection string."
33
247
  );
@@ -35,7 +249,7 @@ export function getInternalDB(): InternalPool {
35
249
  // eslint-disable-next-line @typescript-eslint/no-require-imports
36
250
  const { Pool } = require("pg");
37
251
  // Normalize sslmode: pg v8 treats 'require' as 'verify-full' but warns.
38
- const connString = process.env.DATABASE_URL!.replace(
252
+ const connString = databaseUrl.replace(
39
253
  /([?&])sslmode=require(?=&|$)/,
40
254
  "$1sslmode=verify-full",
41
255
  );
@@ -92,14 +306,81 @@ let _consecutiveFailures = 0;
92
306
  const MAX_CONSECUTIVE_FAILURES = 5;
93
307
  let _circuitOpen = false;
94
308
  let _droppedCount = 0;
309
+ /** Recovery fiber — when set, a background fiber is attempting exponential backoff recovery. */
310
+ let _recoveryFiber: Fiber.RuntimeFiber<void, never> | null = null;
311
+
312
+ /**
313
+ * Exponential backoff recovery schedule for the circuit breaker.
314
+ * Starts at 30s, doubles each attempt, caps at 5 minutes.
315
+ * Retries up to 5 times with increasing delays (30s, 60s, 120s, 240s, 300s).
316
+ * If all retries fail, circuit remains open and recovery re-triggers on next write.
317
+ */
318
+ const RECOVERY_SCHEDULE = Schedule.exponential(Duration.seconds(30)).pipe(
319
+ Schedule.union(Schedule.spaced(Duration.minutes(5))),
320
+ // Cap at 5 retries (30s → 60s → 120s → 240s → 300s)
321
+ Schedule.intersect(Schedule.recurs(5)),
322
+ Schedule.map(([duration]) => duration),
323
+ );
324
+
325
+ /**
326
+ * Start an exponential-backoff recovery probe. On success, re-opens the circuit.
327
+ * On exhaustion of retries, the circuit remains open and the recovery fiber clears
328
+ * itself so the next internalExecute call re-triggers recovery.
329
+ *
330
+ * After an initial 30s delay, makes the first probe attempt. On failure, retries
331
+ * up to 5 times with exponential backoff (30s, 60s, 120s, 240s, 300s).
332
+ * Worst-case recovery takes ~13 minutes from circuit trip to retry exhaustion.
333
+ */
334
+ function _startRecovery(): void {
335
+ if (_recoveryFiber) return;
336
+
337
+ const probe = Effect.gen(function* () {
338
+ const pool = getInternalDB();
339
+ yield* Effect.tryPromise({
340
+ try: () => pool.query("SELECT 1"),
341
+ catch: (err) => (err instanceof Error ? err : new Error(String(err))),
342
+ });
343
+ });
344
+
345
+ const recovery = Effect.sleep(Duration.seconds(30)).pipe(
346
+ Effect.andThen(
347
+ probe.pipe(Effect.retry(RECOVERY_SCHEDULE)),
348
+ ),
349
+ Effect.andThen(
350
+ Effect.sync(() => {
351
+ const dropped = _droppedCount;
352
+ _circuitOpen = false;
353
+ _consecutiveFailures = 0;
354
+ _droppedCount = 0;
355
+ _recoveryFiber = null;
356
+ log.info({ droppedCount: dropped }, "Internal DB circuit breaker recovered — fire-and-forget writes resumed");
357
+ }),
358
+ ),
359
+ Effect.catchAll((err) => {
360
+ // All retries exhausted — keep circuit open, clear fiber so next write re-triggers recovery
361
+ _recoveryFiber = null;
362
+ log.error(
363
+ { err: err instanceof Error ? err.message : String(err), droppedCount: _droppedCount },
364
+ "Internal DB circuit breaker recovery exhausted — circuit remains open, will re-attempt on next write",
365
+ );
366
+ return Effect.void;
367
+ }),
368
+ );
369
+
370
+ _recoveryFiber = Effect.runFork(recovery);
371
+ }
95
372
 
96
373
  /** Fire-and-forget query — async errors are logged, never thrown.
97
- * After 5 consecutive failures, a circuit breaker trips and silently
98
- * drops all calls for 60s before retrying. Throws synchronously if
99
- * DATABASE_URL is not set (callers should check hasInternalDB() first). */
374
+ * After 5 consecutive failures, a circuit breaker trips and drops
375
+ * all calls until recovery succeeds. Recovery uses exponential backoff
376
+ * (30s 60s 120s 240s → 300s) via Effect.retry. Throws
377
+ * synchronously if DATABASE_URL is not set (callers should check
378
+ * hasInternalDB() first). */
100
379
  export function internalExecute(sql: string, params?: unknown[]): void {
101
380
  if (_circuitOpen) {
102
381
  _droppedCount++;
382
+ // Re-trigger recovery if previous attempt exhausted retries
383
+ if (!_recoveryFiber) _startRecovery();
103
384
  return;
104
385
  }
105
386
  const pool = getInternalDB();
@@ -110,14 +391,7 @@ export function internalExecute(sql: string, params?: unknown[]): void {
110
391
  if (_consecutiveFailures >= MAX_CONSECUTIVE_FAILURES && !_circuitOpen) {
111
392
  _circuitOpen = true;
112
393
  log.error("Internal DB circuit breaker open — fire-and-forget writes disabled until recovery");
113
- // Try to recover every 60s
114
- setTimeout(() => {
115
- const dropped = _droppedCount;
116
- _circuitOpen = false;
117
- _consecutiveFailures = 0;
118
- _droppedCount = 0;
119
- log.info({ droppedCount: dropped }, "Internal DB circuit breaker recovered — fire-and-forget writes resumed");
120
- }, 60_000).unref();
394
+ _startRecovery();
121
395
  }
122
396
  if (!_circuitOpen) {
123
397
  log.error(
@@ -137,142 +411,714 @@ export function _resetCircuitBreaker(): void {
137
411
  _consecutiveFailures = 0;
138
412
  _circuitOpen = false;
139
413
  _droppedCount = 0;
414
+ if (_recoveryFiber) {
415
+ Effect.runFork(Fiber.interrupt(_recoveryFiber));
416
+ _recoveryFiber = null;
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Log a warning when DATABASE_URL and ATLAS_DATASOURCE_URL resolve to the
422
+ * same Postgres database. Internal tables (auth, audit, settings) will
423
+ * share the public schema with analytics data.
424
+ *
425
+ * This is intentional in single-DB deployments (e.g. Railway with one
426
+ * Postgres addon) but can confuse the seed script or the agent — call
427
+ * this once at migration time to surface the situation.
428
+ */
429
+ function warnIfSharedDatabase(): void {
430
+ const databaseUrl = process.env.DATABASE_URL;
431
+ const datasourceUrl = process.env.ATLAS_DATASOURCE_URL;
432
+ if (!databaseUrl || !datasourceUrl) return;
433
+
434
+ try {
435
+ const internalParsed = new URL(databaseUrl);
436
+ const datasourceParsed = new URL(datasourceUrl);
437
+
438
+ // Compare host + port + pathname (database name) to detect shared DB
439
+ const sameHost = internalParsed.hostname === datasourceParsed.hostname;
440
+ const samePort = (internalParsed.port || "5432") === (datasourceParsed.port || "5432");
441
+ const sameDB = internalParsed.pathname === datasourceParsed.pathname;
442
+
443
+ if (sameHost && samePort && sameDB) {
444
+ log.warn(
445
+ "DATABASE_URL and ATLAS_DATASOURCE_URL point to the same database — " +
446
+ "Atlas internal tables will share the schema with analytics data. " +
447
+ "Consider using a separate database for ATLAS_DATASOURCE_URL to isolate analytics data.",
448
+ );
449
+ }
450
+ } catch {
451
+ // URL parsing failed — not critical, skip the warning
452
+ log.debug("Could not parse DATABASE_URL or ATLAS_DATASOURCE_URL for shared-DB detection");
453
+ }
140
454
  }
141
455
 
142
- /** Idempotent migration: creates audit_log, conversations, and messages tables. */
456
+ /**
457
+ * Idempotent migration: runs versioned SQL migrations from `migrations/`
458
+ * directory, then applies data seeds.
459
+ *
460
+ * Replaces the old imperative DDL approach (152 individual pool.query calls)
461
+ * with a file-based migration runner tracked in `__atlas_migrations`. See #978.
462
+ */
143
463
  export async function migrateInternalDB(): Promise<void> {
464
+ // Warn when DATABASE_URL and ATLAS_DATASOURCE_URL resolve to the same
465
+ // database — internal tables will share the schema with analytics data.
466
+ // This is intentional in single-DB deployments but can surprise operators
467
+ // who expect isolation. (#962)
468
+ warnIfSharedDatabase();
469
+
144
470
  const pool = getInternalDB();
145
- await pool.query(`
146
- CREATE TABLE IF NOT EXISTS audit_log (
147
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
148
- timestamp TIMESTAMPTZ NOT NULL DEFAULT now(),
149
- user_id TEXT,
150
- user_label TEXT,
151
- auth_mode TEXT NOT NULL,
152
- sql TEXT NOT NULL,
153
- duration_ms INTEGER NOT NULL,
154
- row_count INTEGER,
155
- success BOOLEAN NOT NULL,
156
- error TEXT
157
- );
158
- `);
159
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_audit_log_timestamp ON audit_log(timestamp);`);
160
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_audit_log_user_id ON audit_log(user_id);`);
161
- await pool.query(`
162
- CREATE TABLE IF NOT EXISTS conversations (
163
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
164
- user_id TEXT,
165
- title TEXT,
166
- surface TEXT DEFAULT 'web',
167
- connection_id TEXT,
168
- created_at TIMESTAMPTZ DEFAULT now(),
169
- updated_at TIMESTAMPTZ DEFAULT now()
471
+
472
+ const { runMigrations, runSeeds } = await import("@atlas/api/lib/db/migrate");
473
+ await runMigrations(pool);
474
+ await runSeeds(pool);
475
+
476
+ log.info("Internal DB migration complete");
477
+ }
478
+
479
+ // Old imperative DDL removed — see migrations/0000_baseline.sql (#978)
480
+
481
+ // seedPromptLibrary moved to migrate.ts → runSeeds() (#978)
482
+
483
+ /**
484
+ * Load admin-managed connections from the internal DB and register them
485
+ * in the ConnectionRegistry. Idempotent safe to call at startup.
486
+ * Silently skips if no internal DB or the connections table doesn't exist yet.
487
+ */
488
+ export async function loadSavedConnections(): Promise<number> {
489
+ if (!hasInternalDB()) return 0;
490
+
491
+ // Lazy-import to avoid circular dependency at module level
492
+ const { connections } = await import("@atlas/api/lib/db/connection");
493
+
494
+ try {
495
+ const rows = await internalQuery<{
496
+ id: string;
497
+ url: string;
498
+ type: string;
499
+ description: string | null;
500
+ schema_name: string | null;
501
+ }>("SELECT id, url, type, description, schema_name FROM connections");
502
+
503
+ let registered = 0;
504
+ for (const row of rows) {
505
+ try {
506
+ const url = decryptUrl(row.url);
507
+ connections.register(row.id, {
508
+ url,
509
+ description: row.description ?? undefined,
510
+ schema: row.schema_name ?? undefined,
511
+ });
512
+ registered++;
513
+ } catch (err) {
514
+ log.warn(
515
+ { connectionId: row.id, err: err instanceof Error ? err.message : String(err) },
516
+ "Failed to register saved connection — skipping",
517
+ );
518
+ }
519
+ }
520
+
521
+ if (registered > 0) {
522
+ log.info({ count: registered }, "Loaded saved connections from internal DB");
523
+ }
524
+ return registered;
525
+ } catch (err) {
526
+ // Table may not exist yet (pre-migration) — that's expected on first boot
527
+ log.warn(
528
+ { err: err instanceof Error ? err.message : String(err) },
529
+ "Could not load saved connections (table may not exist yet)",
170
530
  );
171
- `);
172
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_conversations_user ON conversations(user_id);`);
173
- await pool.query(`
174
- CREATE TABLE IF NOT EXISTS messages (
175
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
176
- conversation_id UUID REFERENCES conversations(id) ON DELETE CASCADE,
177
- role TEXT NOT NULL,
178
- content JSONB NOT NULL,
179
- created_at TIMESTAMPTZ DEFAULT now()
531
+ return 0;
532
+ }
533
+ }
534
+
535
+ // ── Learned pattern helpers ─────────────────────────────────────────
536
+
537
+ /**
538
+ * Find a learned pattern by exact normalized SQL match for the given org.
539
+ * Returns the pattern's id, confidence, and repetition count, or null if not found.
540
+ */
541
+ export async function findPatternBySQL(
542
+ orgId: string | null | undefined,
543
+ patternSql: string,
544
+ ): Promise<{ id: string; confidence: number; repetitionCount: number } | null> {
545
+ const pool = getInternalDB();
546
+ const params: unknown[] = [patternSql];
547
+ let orgClause: string;
548
+ if (orgId) {
549
+ params.push(orgId);
550
+ orgClause = `org_id = $2`;
551
+ } else {
552
+ orgClause = `org_id IS NULL`;
553
+ }
554
+
555
+ const result = await pool.query(
556
+ `SELECT id, confidence, repetition_count FROM learned_patterns WHERE pattern_sql = $1 AND ${orgClause} LIMIT 1`,
557
+ params,
558
+ );
559
+
560
+ if (result.rows.length === 0) return null;
561
+ const row = result.rows[0];
562
+ return {
563
+ id: row.id as string,
564
+ confidence: row.confidence as number,
565
+ repetitionCount: row.repetition_count as number,
566
+ };
567
+ }
568
+
569
+ /**
570
+ * Insert a new learned pattern. Fire-and-forget — errors are logged, never thrown.
571
+ */
572
+ export function insertLearnedPattern(pattern: {
573
+ orgId: string | null | undefined;
574
+ patternSql: string;
575
+ description: string;
576
+ sourceEntity: string;
577
+ sourceQueries: string[];
578
+ proposedBy: string;
579
+ }): void {
580
+ internalExecute(
581
+ `INSERT INTO learned_patterns (org_id, pattern_sql, description, source_entity, source_queries, confidence, repetition_count, status, proposed_by)
582
+ VALUES ($1, $2, $3, $4, $5, 0.1, 1, 'pending', $6)`,
583
+ [
584
+ pattern.orgId ?? null,
585
+ pattern.patternSql,
586
+ pattern.description,
587
+ pattern.sourceEntity,
588
+ JSON.stringify(pattern.sourceQueries),
589
+ pattern.proposedBy,
590
+ ],
591
+ );
592
+ }
593
+
594
+ /**
595
+ * Increment repetition_count by 1 and increase confidence by 0.1 (capped at 1.0).
596
+ * When sourceFingerprint is provided, appends it to source_queries (capped at 100 entries).
597
+ * Fire-and-forget — errors are logged, never thrown.
598
+ */
599
+ export function incrementPatternCount(id: string, sourceFingerprint?: string): void {
600
+ if (sourceFingerprint) {
601
+ const newEntry = JSON.stringify([sourceFingerprint]);
602
+ internalExecute(
603
+ `UPDATE learned_patterns SET
604
+ repetition_count = repetition_count + 1,
605
+ confidence = LEAST(1.0, confidence + 0.1),
606
+ source_queries = CASE
607
+ WHEN source_queries IS NULL THEN $2::jsonb
608
+ WHEN jsonb_array_length(source_queries) >= 100 THEN source_queries
609
+ ELSE source_queries || $2::jsonb
610
+ END,
611
+ updated_at = now()
612
+ WHERE id = $1`,
613
+ [id, newEntry],
180
614
  );
181
- `);
182
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id);`);
183
-
184
- // Slack integration tables
185
- await pool.query(`
186
- CREATE TABLE IF NOT EXISTS slack_installations (
187
- team_id TEXT PRIMARY KEY,
188
- bot_token TEXT NOT NULL,
189
- installed_at TIMESTAMPTZ DEFAULT now()
615
+ } else {
616
+ internalExecute(
617
+ `UPDATE learned_patterns SET
618
+ repetition_count = repetition_count + 1,
619
+ confidence = LEAST(1.0, confidence + 0.1),
620
+ updated_at = now()
621
+ WHERE id = $1`,
622
+ [id],
190
623
  );
191
- `);
192
- await pool.query(`
193
- CREATE TABLE IF NOT EXISTS slack_threads (
194
- thread_ts TEXT NOT NULL,
195
- channel_id TEXT NOT NULL,
196
- conversation_id UUID NOT NULL,
197
- PRIMARY KEY (thread_ts, channel_id)
624
+ }
625
+ }
626
+
627
+ /** Row shape returned by getApprovedPatterns. */
628
+ export interface ApprovedPatternRow {
629
+ id: string;
630
+ org_id: string | null;
631
+ pattern_sql: string;
632
+ description: string | null;
633
+ source_entity: string | null;
634
+ /** Confidence score between 0.0 and 1.0. */
635
+ confidence: number;
636
+ [key: string]: unknown;
637
+ }
638
+
639
+ /** Row shape for query_suggestions table. */
640
+ export interface QuerySuggestionRow {
641
+ id: string;
642
+ org_id: string | null;
643
+ description: string;
644
+ pattern_sql: string;
645
+ normalized_hash: string;
646
+ tables_involved: string; // JSONB string, parse to string[]
647
+ primary_table: string | null;
648
+ frequency: number;
649
+ clicked_count: number;
650
+ score: number;
651
+ last_seen_at: string;
652
+ created_at: string;
653
+ updated_at: string;
654
+ [key: string]: unknown;
655
+ }
656
+
657
+ /**
658
+ * Fetch approved learned patterns, scoped to an org (or global when orgId is null).
659
+ * Ordered by confidence DESC, capped at 100 rows.
660
+ */
661
+ export async function getApprovedPatterns(orgId: string | null): Promise<ApprovedPatternRow[]> {
662
+ if (!hasInternalDB()) return [];
663
+
664
+ return internalQuery<ApprovedPatternRow>(
665
+ orgId
666
+ ? `SELECT id, org_id, pattern_sql, description, source_entity, confidence
667
+ FROM learned_patterns
668
+ WHERE status = 'approved' AND (org_id = $1 OR org_id IS NULL)
669
+ ORDER BY confidence DESC
670
+ LIMIT 100`
671
+ : `SELECT id, org_id, pattern_sql, description, source_entity, confidence
672
+ FROM learned_patterns
673
+ WHERE status = 'approved' AND org_id IS NULL
674
+ ORDER BY confidence DESC
675
+ LIMIT 100`,
676
+ orgId ? [orgId] : [],
677
+ );
678
+ }
679
+
680
+ export async function upsertSuggestion(suggestion: {
681
+ orgId: string | null;
682
+ description: string;
683
+ patternSql: string;
684
+ normalizedHash: string;
685
+ tablesInvolved: string[];
686
+ primaryTable: string | null;
687
+ frequency: number;
688
+ score: number;
689
+ lastSeenAt: Date;
690
+ }): Promise<"created" | "updated" | "skipped"> {
691
+ if (!hasInternalDB()) return "skipped";
692
+ try {
693
+ const rows = await internalQuery<{ id: string; created: boolean }>(
694
+ `INSERT INTO query_suggestions (org_id, description, pattern_sql, normalized_hash, tables_involved, primary_table, frequency, score, last_seen_at)
695
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
696
+ ON CONFLICT ON CONSTRAINT uq_query_suggestions_org_hash DO UPDATE SET
697
+ frequency = EXCLUDED.frequency,
698
+ score = EXCLUDED.score,
699
+ last_seen_at = EXCLUDED.last_seen_at,
700
+ updated_at = NOW()
701
+ RETURNING id, (xmax = 0) AS created`,
702
+ [
703
+ suggestion.orgId,
704
+ suggestion.description,
705
+ suggestion.patternSql,
706
+ suggestion.normalizedHash,
707
+ JSON.stringify(suggestion.tablesInvolved),
708
+ suggestion.primaryTable,
709
+ suggestion.frequency,
710
+ suggestion.score,
711
+ suggestion.lastSeenAt.toISOString(),
712
+ ]
198
713
  );
199
- `);
200
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_slack_threads_conversation ON slack_threads(conversation_id);`);
201
-
202
- // Action framework tables
203
- await pool.query(`
204
- CREATE TABLE IF NOT EXISTS action_log (
205
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
206
- requested_at TIMESTAMPTZ NOT NULL DEFAULT now(),
207
- resolved_at TIMESTAMPTZ,
208
- executed_at TIMESTAMPTZ,
209
- requested_by TEXT,
210
- approved_by TEXT,
211
- auth_mode TEXT NOT NULL,
212
- action_type TEXT NOT NULL,
213
- target TEXT NOT NULL,
214
- summary TEXT NOT NULL,
215
- payload JSONB NOT NULL,
216
- status TEXT NOT NULL DEFAULT 'pending',
217
- result JSONB,
218
- error TEXT,
219
- rollback_info JSONB,
220
- conversation_id UUID,
221
- request_id TEXT
714
+ return rows[0]?.created ? "created" : "updated";
715
+ } catch (err) {
716
+ log.warn({ err: err instanceof Error ? err.message : String(err) }, "Failed to upsert suggestion");
717
+ return "skipped";
718
+ }
719
+ }
720
+
721
+ export async function getSuggestionsByTables(
722
+ orgId: string | null,
723
+ tables: string[],
724
+ limit: number = 10
725
+ ): Promise<QuerySuggestionRow[]> {
726
+ if (!hasInternalDB()) return [];
727
+ try {
728
+ const orgClause = orgId != null ? "org_id = $1" : "org_id IS NULL";
729
+ const params: unknown[] = orgId != null ? [orgId] : [];
730
+ const nextIdx = params.length + 1;
731
+
732
+ let tableClause: string;
733
+ if (tables.length === 1) {
734
+ tableClause = `primary_table = $${nextIdx}`;
735
+ params.push(tables[0]);
736
+ } else {
737
+ tableClause = `tables_involved ?| $${nextIdx}::text[]`;
738
+ params.push(tables);
739
+ }
740
+
741
+ params.push(limit);
742
+ const limitIdx = params.length;
743
+
744
+ return await internalQuery<QuerySuggestionRow>(
745
+ `SELECT * FROM query_suggestions WHERE ${orgClause} AND ${tableClause} ORDER BY score DESC LIMIT $${limitIdx}`,
746
+ params
222
747
  );
223
- `);
224
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_action_log_requested_by ON action_log(requested_by);`);
225
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_action_log_status ON action_log(status);`);
226
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_action_log_action_type ON action_log(action_type);`);
227
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_action_log_conversation ON action_log(conversation_id);`);
228
-
229
- // Multi-database production hardening: add source tracking columns to audit_log
230
- await pool.query(`ALTER TABLE audit_log ADD COLUMN IF NOT EXISTS source_id TEXT, ADD COLUMN IF NOT EXISTS source_type TEXT, ADD COLUMN IF NOT EXISTS target_host TEXT;`);
231
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_audit_log_source_id ON audit_log(source_id);`);
232
-
233
- // Saved/starred conversations
234
- await pool.query(`ALTER TABLE conversations ADD COLUMN IF NOT EXISTS starred BOOLEAN NOT NULL DEFAULT false;`);
235
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_conversations_starred ON conversations(user_id, starred) WHERE starred = true;`);
236
-
237
- // Scheduled tasks
238
- await pool.query(`
239
- CREATE TABLE IF NOT EXISTS scheduled_tasks (
240
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
241
- owner_id TEXT NOT NULL,
242
- name TEXT NOT NULL,
243
- question TEXT NOT NULL,
244
- cron_expression TEXT NOT NULL,
245
- delivery_channel TEXT NOT NULL DEFAULT 'webhook',
246
- recipients JSONB NOT NULL DEFAULT '[]',
247
- connection_id TEXT,
248
- approval_mode TEXT NOT NULL DEFAULT 'auto',
249
- enabled BOOLEAN NOT NULL DEFAULT true,
250
- last_run_at TIMESTAMPTZ,
251
- next_run_at TIMESTAMPTZ,
252
- created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
253
- updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
748
+ } catch (err) {
749
+ log.warn({ err: err instanceof Error ? err.message : String(err) }, "Failed to get suggestions by tables");
750
+ return [];
751
+ }
752
+ }
753
+
754
+ export async function getPopularSuggestions(
755
+ orgId: string | null,
756
+ limit: number = 10
757
+ ): Promise<QuerySuggestionRow[]> {
758
+ if (!hasInternalDB()) return [];
759
+ try {
760
+ const orgClause = orgId != null ? "org_id = $1" : "org_id IS NULL";
761
+ const params: unknown[] = orgId != null ? [orgId, limit] : [limit];
762
+ const limitIdx = params.length;
763
+
764
+ return await internalQuery<QuerySuggestionRow>(
765
+ `SELECT * FROM query_suggestions WHERE ${orgClause} ORDER BY score DESC LIMIT $${limitIdx}`,
766
+ params
254
767
  );
255
- `);
256
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_owner ON scheduled_tasks(owner_id);`);
257
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_enabled ON scheduled_tasks(enabled) WHERE enabled = true;`);
258
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_next_run ON scheduled_tasks(next_run_at) WHERE enabled = true;`);
259
-
260
- await pool.query(`
261
- CREATE TABLE IF NOT EXISTS scheduled_task_runs (
262
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
263
- task_id UUID NOT NULL REFERENCES scheduled_tasks(id) ON DELETE CASCADE,
264
- started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
265
- completed_at TIMESTAMPTZ,
266
- status TEXT NOT NULL DEFAULT 'running',
267
- conversation_id UUID,
268
- action_id UUID,
269
- error TEXT,
270
- tokens_used INTEGER,
271
- created_at TIMESTAMPTZ NOT NULL DEFAULT now()
768
+ } catch (err) {
769
+ log.warn({ err: err instanceof Error ? err.message : String(err) }, "Failed to get popular suggestions");
770
+ return [];
771
+ }
772
+ }
773
+
774
+ export function incrementSuggestionClick(
775
+ id: string,
776
+ orgId: string | null
777
+ ): void {
778
+ if (!hasInternalDB()) return;
779
+ const orgClause = orgId != null ? "org_id = $1" : "org_id IS NULL";
780
+ const params: unknown[] = orgId != null ? [orgId, id] : [id];
781
+ const idIdx = params.length;
782
+
783
+ internalExecute(
784
+ `UPDATE query_suggestions SET clicked_count = clicked_count + 1 WHERE ${orgClause} AND id = $${idIdx}`,
785
+ params
786
+ );
787
+ }
788
+
789
+ export async function deleteSuggestion(
790
+ id: string,
791
+ orgId: string | null
792
+ ): Promise<boolean> {
793
+ if (!hasInternalDB()) return false;
794
+ const orgClause = orgId != null ? "org_id = $1" : "org_id IS NULL";
795
+ const params: unknown[] = orgId != null ? [orgId, id] : [id];
796
+ const idIdx = params.length;
797
+
798
+ const rows = await internalQuery<{ id: string }>(
799
+ `DELETE FROM query_suggestions WHERE ${orgClause} AND id = $${idIdx} RETURNING id`,
800
+ params
801
+ );
802
+ return rows.length > 0;
803
+ }
804
+
805
+ export async function getAuditLogQueries(
806
+ orgId: string | null,
807
+ limit: number = 5000
808
+ ): Promise<Array<{ sql: string; tables_accessed: string | null; timestamp: string }>> {
809
+ if (!hasInternalDB()) return [];
810
+ try {
811
+ const orgClause = orgId != null ? "org_id = $1" : "org_id IS NULL";
812
+ const params: unknown[] = orgId != null ? [orgId, limit] : [limit];
813
+ const limitIdx = params.length;
814
+
815
+ return await internalQuery<{ sql: string; tables_accessed: string | null; timestamp: string }>(
816
+ `SELECT sql, tables_accessed, timestamp FROM audit_log WHERE ${orgClause} AND success = true AND sql IS NOT NULL ORDER BY timestamp DESC LIMIT $${limitIdx}`,
817
+ params
272
818
  );
273
- `);
274
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_scheduled_task_runs_task ON scheduled_task_runs(task_id);`);
275
- await pool.query(`CREATE INDEX IF NOT EXISTS idx_scheduled_task_runs_status ON scheduled_task_runs(status);`);
819
+ } catch (err) {
820
+ log.warn({ err: err instanceof Error ? err.message : String(err) }, "Failed to get audit log queries");
821
+ return [];
822
+ }
823
+ }
824
+
825
+ // ── Workspace lifecycle helpers (0.9.0) ─────────────────────────────
826
+
827
+ export type WorkspaceStatus = "active" | "suspended" | "deleted";
828
+ export type PlanTier = "free" | "trial" | "team" | "enterprise";
829
+
830
+ export interface WorkspaceRow {
831
+ id: string;
832
+ name: string;
833
+ slug: string;
834
+ workspace_status: WorkspaceStatus;
835
+ plan_tier: PlanTier;
836
+ byot: boolean;
837
+ stripe_customer_id: string | null;
838
+ trial_ends_at: string | null;
839
+ suspended_at: string | null;
840
+ deleted_at: string | null;
841
+ region: string | null;
842
+ region_assigned_at: string | null;
843
+ createdAt: string;
844
+ [key: string]: unknown;
845
+ }
846
+
847
+ /**
848
+ * Get the workspace status for an organization.
849
+ * Returns null if the org doesn't exist or internal DB is unavailable.
850
+ * Throws on database errors — callers must handle failures explicitly.
851
+ */
852
+ export async function getWorkspaceStatus(orgId: string): Promise<WorkspaceStatus | null> {
853
+ if (!hasInternalDB()) return null;
854
+ const rows = await internalQuery<{ workspace_status: WorkspaceStatus }>(
855
+ `SELECT workspace_status FROM organization WHERE id = $1`,
856
+ [orgId],
857
+ );
858
+ return rows[0]?.workspace_status ?? null;
859
+ }
860
+
861
+ /**
862
+ * Get full workspace details for an organization.
863
+ */
864
+ export async function getWorkspaceDetails(orgId: string): Promise<WorkspaceRow | null> {
865
+ if (!hasInternalDB()) return null;
866
+ const rows = await internalQuery<WorkspaceRow>(
867
+ `SELECT id, name, slug, workspace_status, plan_tier, byot, stripe_customer_id, trial_ends_at, suspended_at, deleted_at, region, region_assigned_at, "createdAt"
868
+ FROM organization WHERE id = $1`,
869
+ [orgId],
870
+ );
871
+ return rows[0] ?? null;
872
+ }
873
+
874
+ /**
875
+ * Update workspace status. Returns true if the org was found and updated,
876
+ * false if no row matched the given orgId.
877
+ */
878
+ export async function updateWorkspaceStatus(
879
+ orgId: string,
880
+ status: WorkspaceStatus,
881
+ ): Promise<boolean> {
882
+ const pool = getInternalDB();
883
+ const timestampCol = status === "suspended" ? "suspended_at" : status === "deleted" ? "deleted_at" : null;
884
+
885
+ let sql: string;
886
+ if (timestampCol) {
887
+ sql = `UPDATE organization SET workspace_status = $1, ${timestampCol} = now() WHERE id = $2 RETURNING id`;
888
+ } else {
889
+ // Activating: clear both timestamps
890
+ sql = `UPDATE organization SET workspace_status = $1, suspended_at = NULL, deleted_at = NULL WHERE id = $2 RETURNING id`;
891
+ }
892
+
893
+ const result = await pool.query(sql, [status, orgId]);
894
+ return result.rows.length > 0;
895
+ }
896
+
897
+ /**
898
+ * Update workspace plan tier. Returns true if the org was found and updated,
899
+ * false if no row matched the given orgId.
900
+ */
901
+ export async function updateWorkspacePlanTier(
902
+ orgId: string,
903
+ planTier: PlanTier,
904
+ ): Promise<boolean> {
905
+ const pool = getInternalDB();
906
+ const result = await pool.query(
907
+ `UPDATE organization SET plan_tier = $1 WHERE id = $2 RETURNING id`,
908
+ [planTier, orgId],
909
+ );
910
+ return result.rows.length > 0;
911
+ }
912
+
913
+ /**
914
+ * Get the region assigned to a workspace. Returns null if no region is assigned
915
+ * or the workspace doesn't exist.
916
+ */
917
+ export async function getWorkspaceRegion(orgId: string): Promise<string | null> {
918
+ if (!hasInternalDB()) return null;
919
+ const rows = await internalQuery<{ region: string | null }>(
920
+ `SELECT region FROM organization WHERE id = $1`,
921
+ [orgId],
922
+ );
923
+ return rows[0]?.region ?? null;
924
+ }
925
+
926
+ /**
927
+ * Assign a region to a workspace. Region is immutable — once set, returns
928
+ * `{ assigned: false, existing: <current region> }` without updating.
929
+ * On first assignment, returns `{ assigned: true }`. If the workspace
930
+ * does not exist, returns `{ assigned: false }` without an `existing` field.
931
+ */
932
+ export async function setWorkspaceRegion(
933
+ orgId: string,
934
+ region: string,
935
+ ): Promise<{ assigned: boolean; existing?: string }> {
936
+ const pool = getInternalDB();
937
+ // Only assign if region is currently NULL (immutable after first assignment)
938
+ const result = await pool.query(
939
+ `UPDATE organization SET region = $1, region_assigned_at = now()
940
+ WHERE id = $2 AND region IS NULL RETURNING id`,
941
+ [region, orgId],
942
+ );
943
+ if (result.rows.length > 0) {
944
+ return { assigned: true };
945
+ }
946
+ // Check if workspace exists with a different region already set
947
+ const existing = await internalQuery<{ region: string | null }>(
948
+ `SELECT region FROM organization WHERE id = $1`,
949
+ [orgId],
950
+ );
951
+ if (existing.length === 0) {
952
+ return { assigned: false };
953
+ }
954
+ return { assigned: false, existing: existing[0].region ?? undefined };
955
+ }
956
+
957
+ /**
958
+ * Cascading soft-delete cleanup for a workspace (transactional):
959
+ * - Soft-deletes conversations (sets deleted_at)
960
+ * - Hard-deletes org-scoped semantic entities, learned patterns, and query suggestions
961
+ * - Hard-deletes org-scoped settings
962
+ * - Disables scheduled tasks
963
+ *
964
+ * All operations run inside a single transaction — either all succeed or
965
+ * none take effect, so retries are always safe.
966
+ */
967
+ export async function cascadeWorkspaceDelete(orgId: string): Promise<{
968
+ conversations: number;
969
+ semanticEntities: number;
970
+ learnedPatterns: number;
971
+ suggestions: number;
972
+ scheduledTasks: number;
973
+ settings: number;
974
+ }> {
975
+ const pool = getInternalDB();
976
+ const client = await pool.connect();
276
977
 
277
- log.info("Internal DB migration complete (audit_log, conversations, messages, slack, action_log, scheduled_tasks)");
978
+ try {
979
+ await client.query("BEGIN");
980
+
981
+ const [convResult, seResult, lpResult, qsResult, stResult, settingsResult] = await Promise.all([
982
+ client.query(
983
+ `UPDATE conversations SET deleted_at = now(), updated_at = now() WHERE org_id = $1 AND deleted_at IS NULL RETURNING id`,
984
+ [orgId],
985
+ ),
986
+ client.query(
987
+ `DELETE FROM semantic_entities WHERE org_id = $1 RETURNING id`,
988
+ [orgId],
989
+ ),
990
+ client.query(
991
+ `DELETE FROM learned_patterns WHERE org_id = $1 RETURNING id`,
992
+ [orgId],
993
+ ),
994
+ client.query(
995
+ `DELETE FROM query_suggestions WHERE org_id = $1 RETURNING id`,
996
+ [orgId],
997
+ ),
998
+ client.query(
999
+ `UPDATE scheduled_tasks SET enabled = false, updated_at = now() WHERE org_id = $1 RETURNING id`,
1000
+ [orgId],
1001
+ ),
1002
+ client.query(
1003
+ `DELETE FROM settings WHERE org_id = $1 RETURNING key`,
1004
+ [orgId],
1005
+ ),
1006
+ ]);
1007
+
1008
+ await client.query("COMMIT");
1009
+
1010
+ return {
1011
+ conversations: convResult.rows.length,
1012
+ semanticEntities: seResult.rows.length,
1013
+ learnedPatterns: lpResult.rows.length,
1014
+ suggestions: qsResult.rows.length,
1015
+ scheduledTasks: stResult.rows.length,
1016
+ settings: settingsResult.rows.length,
1017
+ };
1018
+ } catch (err) {
1019
+ await client.query("ROLLBACK").catch(() => {
1020
+ // intentionally ignored: ROLLBACK failure after a failed transaction is non-actionable
1021
+ });
1022
+ throw err;
1023
+ } finally {
1024
+ client.release();
1025
+ }
1026
+ }
1027
+
1028
+ /**
1029
+ * Get a workspace health summary: member count, conversation count,
1030
+ * query count (last 24h), connection count, and scheduled task count.
1031
+ */
1032
+ export async function getWorkspaceHealthSummary(orgId: string): Promise<{
1033
+ workspace: WorkspaceRow;
1034
+ members: number;
1035
+ conversations: number;
1036
+ queriesLast24h: number;
1037
+ connections: number;
1038
+ scheduledTasks: number;
1039
+ } | null> {
1040
+ if (!hasInternalDB()) return null;
1041
+
1042
+ const workspace = await getWorkspaceDetails(orgId);
1043
+ if (!workspace) return null;
1044
+
1045
+ const [memberRows, convRows, queryRows, connRows, taskRows] = await Promise.all([
1046
+ internalQuery<{ count: number }>(
1047
+ `SELECT COUNT(*)::int as count FROM member WHERE "organizationId" = $1`,
1048
+ [orgId],
1049
+ ),
1050
+ internalQuery<{ count: number }>(
1051
+ `SELECT COUNT(*)::int as count FROM conversations WHERE org_id = $1`,
1052
+ [orgId],
1053
+ ),
1054
+ internalQuery<{ count: number }>(
1055
+ `SELECT COUNT(*)::int as count FROM audit_log WHERE org_id = $1 AND timestamp > now() - interval '24 hours'`,
1056
+ [orgId],
1057
+ ),
1058
+ internalQuery<{ count: number }>(
1059
+ `SELECT COUNT(*)::int as count FROM connections WHERE org_id = $1`,
1060
+ [orgId],
1061
+ ),
1062
+ internalQuery<{ count: number }>(
1063
+ `SELECT COUNT(*)::int as count FROM scheduled_tasks WHERE org_id = $1 AND enabled = true`,
1064
+ [orgId],
1065
+ ),
1066
+ ]);
1067
+
1068
+ return {
1069
+ workspace,
1070
+ members: memberRows[0]?.count ?? 0,
1071
+ conversations: convRows[0]?.count ?? 0,
1072
+ queriesLast24h: queryRows[0]?.count ?? 0,
1073
+ connections: connRows[0]?.count ?? 0,
1074
+ scheduledTasks: taskRows[0]?.count ?? 0,
1075
+ };
1076
+ }
1077
+
1078
+ // ── Billing helpers (0.9.0 — Stripe billing) ────────────────────────
1079
+
1080
+ /**
1081
+ * Update the BYOT (Bring Your Own Token) flag for a workspace.
1082
+ * Returns true if the org was found and updated.
1083
+ */
1084
+ export async function updateWorkspaceByot(
1085
+ orgId: string,
1086
+ byot: boolean,
1087
+ ): Promise<boolean> {
1088
+ const pool = getInternalDB();
1089
+ const result = await pool.query(
1090
+ `UPDATE organization SET byot = $1 WHERE id = $2 RETURNING id`,
1091
+ [byot, orgId],
1092
+ );
1093
+ return result.rows.length > 0;
1094
+ }
1095
+
1096
+ /**
1097
+ * Set the Stripe customer ID for a workspace.
1098
+ */
1099
+ export async function setWorkspaceStripeCustomerId(
1100
+ orgId: string,
1101
+ stripeCustomerId: string,
1102
+ ): Promise<boolean> {
1103
+ const pool = getInternalDB();
1104
+ const result = await pool.query(
1105
+ `UPDATE organization SET stripe_customer_id = $1 WHERE id = $2 RETURNING id`,
1106
+ [stripeCustomerId, orgId],
1107
+ );
1108
+ return result.rows.length > 0;
1109
+ }
1110
+
1111
+ /**
1112
+ * Set the trial end date for a workspace.
1113
+ */
1114
+ export async function setWorkspaceTrialEndsAt(
1115
+ orgId: string,
1116
+ trialEndsAt: Date,
1117
+ ): Promise<boolean> {
1118
+ const pool = getInternalDB();
1119
+ const result = await pool.query(
1120
+ `UPDATE organization SET trial_ends_at = $1 WHERE id = $2 RETURNING id`,
1121
+ [trialEndsAt.toISOString(), orgId],
1122
+ );
1123
+ return result.rows.length > 0;
278
1124
  }