@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.
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/index.ts +253 -36
- package/package.json +4 -4
- package/templates/docker/Dockerfile +1 -1
- package/templates/docker/Dockerfile.sidecar +1 -1
- package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +17 -14
- package/templates/docker/bin/__tests__/failure-threshold.test.ts +148 -0
- package/templates/docker/bin/__tests__/fatal-error-propagation.test.ts +267 -0
- package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +5 -5
- package/templates/docker/bin/__tests__/schema-drift.test.ts +39 -0
- package/templates/docker/bin/atlas.ts +981 -1819
- package/templates/docker/bin/benchmark.ts +14 -16
- package/templates/docker/bin/enrich.ts +7 -2
- package/templates/docker/brand.css +13 -0
- package/templates/docker/data/cybersec-semantic/catalog.yml +222 -0
- package/templates/docker/data/cybersec-semantic/entities/alerts.yml +195 -0
- package/templates/docker/data/cybersec-semantic/entities/assets.yml +191 -0
- package/templates/docker/data/cybersec-semantic/entities/compliance_assessments.yml +170 -0
- package/templates/docker/data/cybersec-semantic/entities/incidents.yml +219 -0
- package/templates/docker/data/cybersec-semantic/entities/organizations.yml +136 -0
- package/templates/docker/data/cybersec-semantic/entities/plans.yml +114 -0
- package/templates/docker/data/cybersec-semantic/entities/remediation_actions.yml +212 -0
- package/templates/docker/data/cybersec-semantic/entities/scan_results.yml +215 -0
- package/templates/docker/data/cybersec-semantic/entities/scans.yml +180 -0
- package/templates/docker/data/cybersec-semantic/entities/subscriptions.yml +184 -0
- package/templates/docker/data/cybersec-semantic/entities/users.yml +140 -0
- package/templates/docker/data/cybersec-semantic/entities/vulnerabilities.yml +154 -0
- package/templates/docker/data/cybersec-semantic/glossary.yml +207 -0
- package/templates/docker/data/cybersec-semantic/metrics/business.yml +148 -0
- package/templates/docker/data/cybersec-semantic/metrics/compliance.yml +138 -0
- package/templates/docker/data/cybersec-semantic/metrics/security.yml +181 -0
- package/templates/docker/data/cybersec.sql +8 -8
- package/templates/docker/data/demo.sql +3 -0
- package/templates/docker/data/ecommerce-semantic/catalog.yml +221 -0
- package/templates/docker/data/ecommerce-semantic/entities/categories.yml +91 -0
- package/templates/docker/data/ecommerce-semantic/entities/customers.yml +133 -0
- package/templates/docker/data/ecommerce-semantic/entities/email_campaigns.yml +119 -0
- package/templates/docker/data/ecommerce-semantic/entities/inventory_levels.yml +153 -0
- package/templates/docker/data/ecommerce-semantic/entities/order_items.yml +159 -0
- package/templates/docker/data/ecommerce-semantic/entities/orders.yml +199 -0
- package/templates/docker/data/ecommerce-semantic/entities/payments.yml +140 -0
- package/templates/docker/data/ecommerce-semantic/entities/product_reviews.yml +155 -0
- package/templates/docker/data/ecommerce-semantic/entities/products.yml +178 -0
- package/templates/docker/data/ecommerce-semantic/entities/promotions.yml +171 -0
- package/templates/docker/data/ecommerce-semantic/entities/returns.yml +144 -0
- package/templates/docker/data/ecommerce-semantic/entities/sellers.yml +124 -0
- package/templates/docker/data/ecommerce-semantic/entities/shipments.yml +159 -0
- package/templates/docker/data/ecommerce-semantic/glossary.yml +193 -0
- package/templates/docker/data/ecommerce-semantic/metrics/customers.yml +116 -0
- package/templates/docker/data/ecommerce-semantic/metrics/operations.yml +131 -0
- package/templates/docker/data/ecommerce-semantic/metrics/revenue.yml +120 -0
- package/templates/docker/docs/deploy.md +2 -1
- package/templates/docker/ee/src/__mocks__/internal.ts +170 -0
- package/templates/docker/ee/src/audit/purge-scheduler.ts +113 -0
- package/templates/docker/ee/src/audit/retention.ts +467 -0
- package/templates/docker/ee/src/auth/ip-allowlist.ts +367 -0
- package/templates/docker/ee/src/auth/roles.ts +562 -0
- package/templates/docker/ee/src/auth/scim.ts +343 -0
- package/templates/docker/ee/src/auth/sso.ts +538 -0
- package/templates/docker/ee/src/backups/engine.ts +355 -0
- package/templates/docker/ee/src/backups/index.ts +26 -0
- package/templates/docker/ee/src/backups/restore.ts +169 -0
- package/templates/docker/ee/src/backups/scheduler.ts +153 -0
- package/templates/docker/ee/src/backups/verify.ts +124 -0
- package/templates/docker/ee/src/branding/white-label.ts +228 -0
- package/templates/docker/ee/src/compliance/masking.ts +477 -0
- package/templates/docker/ee/src/compliance/patterns.ts +16 -0
- package/templates/docker/ee/src/compliance/pii-detection.ts +217 -0
- package/templates/docker/ee/src/compliance/reports.ts +402 -0
- package/templates/docker/ee/src/deploy-mode.ts +37 -0
- package/templates/docker/ee/src/governance/approval.ts +699 -0
- package/templates/docker/ee/src/index.ts +74 -0
- package/templates/docker/ee/src/platform/domains.ts +562 -0
- package/templates/docker/ee/src/platform/model-routing.ts +382 -0
- package/templates/docker/ee/src/platform/residency.ts +265 -0
- package/templates/docker/ee/src/sla/alerting.ts +382 -0
- package/templates/docker/ee/src/sla/index.ts +12 -0
- package/templates/docker/ee/src/sla/metrics.ts +275 -0
- package/templates/docker/ee/src/test-setup.ts +1 -0
- package/templates/docker/next.config.ts +4 -1
- package/templates/docker/package.json +49 -29
- package/templates/docker/sidecar/Dockerfile +1 -1
- package/templates/docker/src/api/index.ts +336 -24
- package/templates/docker/src/api/routes/actions.ts +443 -176
- package/templates/docker/src/api/routes/admin-abuse.ts +219 -0
- package/templates/docker/src/api/routes/admin-approval.ts +418 -0
- package/templates/docker/src/api/routes/admin-audit-retention.ts +405 -0
- package/templates/docker/src/api/routes/admin-auth.ts +122 -0
- package/templates/docker/src/api/routes/admin-branding.ts +252 -0
- package/templates/docker/src/api/routes/admin-compliance.ts +352 -0
- package/templates/docker/src/api/routes/admin-domains.ts +334 -0
- package/templates/docker/src/api/routes/admin-integrations.ts +2667 -0
- package/templates/docker/src/api/routes/admin-ip-allowlist.ts +261 -0
- package/templates/docker/src/api/routes/admin-learned-patterns.ts +525 -0
- package/templates/docker/src/api/routes/admin-model-config.ts +252 -0
- package/templates/docker/src/api/routes/admin-onboarding-emails.ts +145 -0
- package/templates/docker/src/api/routes/admin-orgs.ts +710 -0
- package/templates/docker/src/api/routes/admin-prompts.ts +694 -0
- package/templates/docker/src/api/routes/admin-residency.ts +570 -0
- package/templates/docker/src/api/routes/admin-roles.ts +296 -0
- package/templates/docker/src/api/routes/admin-router.ts +120 -0
- package/templates/docker/src/api/routes/admin-sandbox.ts +417 -0
- package/templates/docker/src/api/routes/admin-scim.ts +262 -0
- package/templates/docker/src/api/routes/admin-sso.ts +545 -0
- package/templates/docker/src/api/routes/admin-suggestions.ts +176 -0
- package/templates/docker/src/api/routes/admin-usage.ts +310 -0
- package/templates/docker/src/api/routes/admin.ts +4156 -898
- package/templates/docker/src/api/routes/auth-preamble.ts +105 -0
- package/templates/docker/src/api/routes/billing.ts +397 -0
- package/templates/docker/src/api/routes/chat.ts +597 -334
- package/templates/docker/src/api/routes/conversations.ts +987 -132
- package/templates/docker/src/api/routes/demo.ts +673 -0
- package/templates/docker/src/api/routes/discord.ts +274 -0
- package/templates/docker/src/api/routes/ee-error-handler.ts +32 -0
- package/templates/docker/src/api/routes/health.ts +129 -14
- package/templates/docker/src/api/routes/middleware.ts +244 -0
- package/templates/docker/src/api/routes/onboarding-emails.ts +134 -0
- package/templates/docker/src/api/routes/onboarding.ts +1109 -0
- package/templates/docker/src/api/routes/openapi.ts +184 -1597
- package/templates/docker/src/api/routes/platform-admin.ts +760 -0
- package/templates/docker/src/api/routes/platform-backups.ts +436 -0
- package/templates/docker/src/api/routes/platform-domains.ts +235 -0
- package/templates/docker/src/api/routes/platform-residency.ts +257 -0
- package/templates/docker/src/api/routes/platform-sla.ts +379 -0
- package/templates/docker/src/api/routes/prompts.ts +221 -0
- package/templates/docker/src/api/routes/public-branding.ts +106 -0
- package/templates/docker/src/api/routes/query.ts +330 -219
- package/templates/docker/src/api/routes/scheduled-tasks.ts +393 -297
- package/templates/docker/src/api/routes/semantic.ts +179 -0
- package/templates/docker/src/api/routes/sessions.ts +210 -0
- package/templates/docker/src/api/routes/shared-domains.ts +98 -0
- package/templates/docker/src/api/routes/shared-schemas.ts +139 -0
- package/templates/docker/src/api/routes/slack.ts +209 -52
- package/templates/docker/src/api/routes/suggestions.ts +233 -0
- package/templates/docker/src/api/routes/tables.ts +67 -0
- package/templates/docker/src/api/routes/teams.ts +222 -0
- package/templates/docker/src/api/routes/validate-sql.ts +188 -0
- package/templates/docker/src/api/routes/validation-hook.ts +62 -0
- package/templates/docker/src/api/routes/widget-loader.ts +356 -0
- package/templates/docker/src/api/routes/widget.ts +428 -0
- package/templates/docker/src/api/routes/wizard.ts +852 -0
- package/templates/docker/src/api/server.ts +187 -69
- package/templates/docker/src/app/error.tsx +5 -2
- package/templates/docker/src/app/globals.css +1 -1
- package/templates/docker/src/app/layout.tsx +7 -2
- package/templates/docker/src/app/page.tsx +39 -5
- package/templates/docker/src/components/data-table/data-table-column-header.tsx +99 -0
- package/templates/docker/src/components/data-table/data-table-date-filter.tsx +225 -0
- package/templates/docker/src/components/data-table/data-table-expandable.tsx +125 -0
- package/templates/docker/src/components/data-table/data-table-faceted-filter.tsx +189 -0
- package/templates/docker/src/components/data-table/data-table-pagination.tsx +112 -0
- package/templates/docker/src/components/data-table/data-table-range-filter.tsx +122 -0
- package/templates/docker/src/components/data-table/data-table-slider-filter.tsx +256 -0
- package/templates/docker/src/components/data-table/data-table-sort-list.tsx +407 -0
- package/templates/docker/src/components/data-table/data-table-toolbar.tsx +149 -0
- package/templates/docker/src/components/data-table/data-table-view-options.tsx +89 -0
- package/templates/docker/src/components/data-table/data-table.tsx +105 -0
- package/templates/docker/src/components/form-dialog.tsx +135 -0
- package/templates/docker/src/components/ui/accordion.tsx +66 -0
- package/templates/docker/src/components/ui/calendar.tsx +220 -0
- package/templates/docker/src/components/ui/checkbox.tsx +32 -0
- package/templates/docker/src/components/ui/faceted.tsx +283 -0
- package/templates/docker/src/components/ui/form.tsx +167 -0
- package/templates/docker/src/components/ui/label.tsx +24 -0
- package/templates/docker/src/components/ui/popover.tsx +89 -0
- package/templates/docker/src/components/ui/progress.tsx +31 -0
- package/templates/docker/src/components/ui/scroll-area.tsx +6 -2
- package/templates/docker/src/components/ui/slider.tsx +63 -0
- package/templates/docker/src/components/ui/sortable.tsx +581 -0
- package/templates/docker/src/components/ui/switch.tsx +35 -0
- package/templates/docker/src/components/ui/textarea.tsx +18 -0
- package/templates/docker/src/config/data-table.ts +82 -0
- package/templates/docker/src/env-check.ts +74 -0
- package/templates/docker/src/hooks/use-callback-ref.ts +27 -0
- package/templates/docker/src/hooks/use-data-table.ts +316 -0
- package/templates/docker/src/hooks/use-debounced-callback.ts +28 -0
- package/templates/docker/src/lib/action-types.ts +7 -41
- package/templates/docker/src/lib/agent-query.ts +4 -2
- package/templates/docker/src/lib/agent.ts +363 -31
- package/templates/docker/src/lib/auth/admin-permissions.ts +38 -0
- package/templates/docker/src/lib/auth/audit.ts +19 -4
- package/templates/docker/src/lib/auth/byot.ts +3 -3
- package/templates/docker/src/lib/auth/client.ts +33 -3
- package/templates/docker/src/lib/auth/detect.ts +29 -8
- package/templates/docker/src/lib/auth/managed.ts +104 -14
- package/templates/docker/src/lib/auth/middleware.ts +53 -6
- package/templates/docker/src/lib/auth/migrate.ts +140 -15
- package/templates/docker/src/lib/auth/oauth-state.ts +123 -0
- package/templates/docker/src/lib/auth/org-permissions.ts +55 -0
- package/templates/docker/src/lib/auth/permissions.ts +26 -19
- package/templates/docker/src/lib/auth/server.ts +355 -9
- package/templates/docker/src/lib/auth/simple-key.ts +3 -3
- package/templates/docker/src/lib/auth/types.ts +15 -21
- package/templates/docker/src/lib/billing/enforcement.ts +368 -0
- package/templates/docker/src/lib/billing/plans.ts +155 -0
- package/templates/docker/src/lib/cache/index.ts +92 -0
- package/templates/docker/src/lib/cache/keys.ts +30 -0
- package/templates/docker/src/lib/cache/lru.ts +79 -0
- package/templates/docker/src/lib/cache/types.ts +31 -0
- package/templates/docker/src/lib/compose-refs.ts +62 -0
- package/templates/docker/src/lib/config.ts +563 -11
- package/templates/docker/src/lib/connection-types.ts +9 -0
- package/templates/docker/src/lib/conversation-types.ts +1 -25
- package/templates/docker/src/lib/conversations.ts +345 -14
- package/templates/docker/src/lib/data-table.ts +61 -0
- package/templates/docker/src/lib/db/connection.ts +793 -39
- package/templates/docker/src/lib/db/internal.ts +985 -139
- package/templates/docker/src/lib/db/migrate.ts +295 -0
- package/templates/docker/src/lib/db/migrations/0000_baseline.sql +703 -0
- package/templates/docker/src/lib/db/migrations/0001_teams_installations.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0002_discord_installations.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0003_telegram_installations.sql +15 -0
- package/templates/docker/src/lib/db/migrations/0004_sandbox_credentials.sql +18 -0
- package/templates/docker/src/lib/db/migrations/0005_oauth_state.sql +16 -0
- package/templates/docker/src/lib/db/migrations/0006_byot_credentials.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0007_gchat_installations.sql +15 -0
- package/templates/docker/src/lib/db/migrations/0008_github_installations.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0009_linear_installations.sql +15 -0
- package/templates/docker/src/lib/db/migrations/0010_whatsapp_installations.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0011_email_installations.sql +16 -0
- package/templates/docker/src/lib/db/migrations/0012_region_migrations.sql +25 -0
- package/templates/docker/src/lib/db/schema.ts +1120 -0
- package/templates/docker/src/lib/db/source-rate-limit.ts +89 -139
- package/templates/docker/src/lib/demo.ts +308 -0
- package/templates/docker/src/lib/discord/store.ts +225 -0
- package/templates/docker/src/lib/effect/ai.ts +243 -0
- package/templates/docker/src/lib/effect/errors.ts +234 -0
- package/templates/docker/src/lib/effect/hono.ts +454 -0
- package/templates/docker/src/lib/effect/index.ts +137 -0
- package/templates/docker/src/lib/effect/layers.ts +496 -0
- package/templates/docker/src/lib/effect/services.ts +776 -0
- package/templates/docker/src/lib/effect/sql.ts +178 -0
- package/templates/docker/src/lib/effect/toolkit.ts +123 -0
- package/templates/docker/src/lib/email/delivery.ts +232 -0
- package/templates/docker/src/lib/email/engine.ts +349 -0
- package/templates/docker/src/lib/email/hooks.ts +107 -0
- package/templates/docker/src/lib/email/index.ts +16 -0
- package/templates/docker/src/lib/email/scheduler.ts +72 -0
- package/templates/docker/src/lib/email/sequence.ts +73 -0
- package/templates/docker/src/lib/email/store.ts +163 -0
- package/templates/docker/src/lib/email/templates.ts +215 -0
- package/templates/docker/src/lib/format.ts +67 -0
- package/templates/docker/src/lib/gchat/store.ts +202 -0
- package/templates/docker/src/lib/github/store.ts +197 -0
- package/templates/docker/src/lib/id.ts +29 -0
- package/templates/docker/src/lib/integrations/types.ts +166 -0
- package/templates/docker/src/lib/learn/pattern-analyzer.ts +224 -0
- package/templates/docker/src/lib/learn/pattern-cache.ts +229 -0
- package/templates/docker/src/lib/learn/pattern-proposer.ts +87 -0
- package/templates/docker/src/lib/learn/suggestion-helpers.ts +34 -0
- package/templates/docker/src/lib/learn/suggestions.ts +139 -0
- package/templates/docker/src/lib/linear/store.ts +200 -0
- package/templates/docker/src/lib/logger.ts +35 -3
- package/templates/docker/src/lib/metering.ts +272 -0
- package/templates/docker/src/lib/parsers.ts +99 -0
- package/templates/docker/src/lib/plugins/hooks.ts +13 -11
- package/templates/docker/src/lib/plugins/index.ts +3 -1
- package/templates/docker/src/lib/plugins/registry.ts +58 -6
- package/templates/docker/src/lib/plugins/settings.ts +147 -0
- package/templates/docker/src/lib/plugins/wiring.ts +6 -9
- package/templates/docker/src/lib/profiler.ts +1665 -0
- package/templates/docker/src/lib/providers.ts +188 -13
- package/templates/docker/src/lib/rls.ts +172 -60
- package/templates/docker/src/lib/sandbox/credentials.ts +206 -0
- package/templates/docker/src/lib/sandbox/validate.ts +179 -0
- package/templates/docker/src/lib/scheduled-task-types.ts +26 -94
- package/templates/docker/src/lib/scheduled-tasks.ts +174 -34
- package/templates/docker/src/lib/scheduler/delivery.ts +248 -150
- package/templates/docker/src/lib/scheduler/engine.ts +190 -154
- package/templates/docker/src/lib/scheduler/executor.ts +74 -23
- package/templates/docker/src/lib/scheduler/preview.ts +72 -0
- package/templates/docker/src/lib/security/abuse.ts +463 -0
- package/templates/docker/src/lib/semantic/diff.ts +267 -0
- package/templates/docker/src/lib/semantic/entities.ts +167 -0
- package/templates/docker/src/lib/semantic/files.ts +283 -0
- package/templates/docker/src/lib/semantic/index.ts +27 -0
- package/templates/docker/src/lib/{semantic-index.ts → semantic/search.ts} +80 -9
- package/templates/docker/src/lib/semantic/sync.ts +581 -0
- package/templates/docker/src/lib/{semantic.ts → semantic/whitelist.ts} +189 -3
- package/templates/docker/src/lib/settings.ts +817 -0
- package/templates/docker/src/lib/sidecar-types.ts +13 -0
- package/templates/docker/src/lib/slack/store.ts +134 -25
- package/templates/docker/src/lib/startup.ts +528 -362
- package/templates/docker/src/lib/teams/store.ts +216 -0
- package/templates/docker/src/lib/telegram/store.ts +202 -0
- package/templates/docker/src/lib/telemetry.ts +40 -0
- package/templates/docker/src/lib/tools/actions/audit.ts +8 -5
- package/templates/docker/src/lib/tools/actions/email.ts +3 -1
- package/templates/docker/src/lib/tools/actions/handler.ts +276 -93
- package/templates/docker/src/lib/tools/actions/jira.ts +2 -2
- package/templates/docker/src/lib/tools/backends/detect.ts +16 -0
- package/templates/docker/src/lib/tools/backends/index.ts +11 -0
- package/templates/docker/src/lib/tools/backends/nsjail.ts +213 -0
- package/templates/docker/src/lib/tools/backends/shared.ts +103 -0
- package/templates/docker/src/lib/tools/backends/types.ts +26 -0
- package/templates/docker/src/lib/tools/explore-nsjail.ts +7 -228
- package/templates/docker/src/lib/tools/explore-sandbox.ts +4 -29
- package/templates/docker/src/lib/tools/explore-sidecar.ts +18 -2
- package/templates/docker/src/lib/tools/explore.ts +246 -54
- package/templates/docker/src/lib/tools/index.ts +17 -0
- package/templates/docker/src/lib/tools/python-nsjail.ts +11 -139
- package/templates/docker/src/lib/tools/python-sandbox.ts +9 -132
- package/templates/docker/src/lib/tools/python-sidecar.ts +184 -3
- package/templates/docker/src/lib/tools/python-stream.ts +33 -0
- package/templates/docker/src/lib/tools/python-wrapper.ts +129 -0
- package/templates/docker/src/lib/tools/python.ts +115 -15
- package/templates/docker/src/lib/tools/registry.ts +14 -2
- package/templates/docker/src/lib/tools/sql.ts +778 -362
- package/templates/docker/src/lib/tracing.ts +16 -0
- package/templates/docker/src/lib/whatsapp/store.ts +198 -0
- package/templates/docker/src/lib/workspace.ts +89 -0
- package/templates/docker/src/progress.ts +121 -0
- package/templates/docker/src/types/data-table.ts +48 -0
- package/templates/docker/src/ui/atlas-chat-reexport.ts +3 -0
- package/templates/docker/src/ui/components/actions/action-approval-card.tsx +26 -19
- package/templates/docker/src/ui/components/actions/action-status-badge.tsx +3 -3
- package/templates/docker/src/ui/components/admin/admin-layout.tsx +57 -39
- package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +213 -35
- package/templates/docker/src/ui/components/admin/delivery-status-badge.tsx +53 -0
- package/templates/docker/src/ui/components/admin/empty-state.tsx +27 -6
- package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -52
- package/templates/docker/src/ui/components/admin/error-banner.tsx +2 -2
- package/templates/docker/src/ui/components/admin/feature-disabled.tsx +28 -5
- package/templates/docker/src/ui/components/admin-content-wrapper.tsx +87 -0
- package/templates/docker/src/ui/components/atlas-chat.tsx +449 -166
- package/templates/docker/src/ui/components/branding-head.tsx +41 -0
- package/templates/docker/src/ui/components/chart/chart-detection.ts +62 -5
- package/templates/docker/src/ui/components/chart/result-chart.tsx +316 -125
- package/templates/docker/src/ui/components/chat/api-key-bar.tsx +4 -4
- package/templates/docker/src/ui/components/chat/data-table.tsx +45 -4
- package/templates/docker/src/ui/components/chat/error-banner.tsx +86 -5
- package/templates/docker/src/ui/components/chat/follow-up-chips.tsx +29 -0
- package/templates/docker/src/ui/components/chat/markdown.tsx +24 -0
- package/templates/docker/src/ui/components/chat/prompt-library.tsx +206 -0
- package/templates/docker/src/ui/components/chat/python-result-card.tsx +106 -78
- package/templates/docker/src/ui/components/chat/result-card-base.tsx +101 -0
- package/templates/docker/src/ui/components/chat/share-dialog.tsx +377 -0
- package/templates/docker/src/ui/components/chat/sql-result-card.tsx +94 -73
- package/templates/docker/src/ui/components/chat/suggestion-chips.tsx +46 -0
- package/templates/docker/src/ui/components/chat/tool-part.tsx +16 -4
- package/templates/docker/src/ui/components/conversations/conversation-item.tsx +48 -17
- package/templates/docker/src/ui/components/conversations/conversation-list.tsx +38 -24
- package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +66 -7
- package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +9 -2
- package/templates/docker/src/ui/components/error-boundary.tsx +66 -0
- package/templates/docker/src/ui/components/notebook/delete-cell-dialog.tsx +48 -0
- package/templates/docker/src/ui/components/notebook/fork-branch-selector.tsx +68 -0
- package/templates/docker/src/ui/components/notebook/notebook-cell-input.tsx +76 -0
- package/templates/docker/src/ui/components/notebook/notebook-cell-output.tsx +58 -0
- package/templates/docker/src/ui/components/notebook/notebook-cell-toolbar.tsx +91 -0
- package/templates/docker/src/ui/components/notebook/notebook-cell.tsx +119 -0
- package/templates/docker/src/ui/components/notebook/notebook-empty-state.tsx +19 -0
- package/templates/docker/src/ui/components/notebook/notebook-export.ts +287 -0
- package/templates/docker/src/ui/components/notebook/notebook-input-bar.tsx +49 -0
- package/templates/docker/src/ui/components/notebook/notebook-shell.tsx +266 -0
- package/templates/docker/src/ui/components/notebook/notebook-text-cell.tsx +152 -0
- package/templates/docker/src/ui/components/notebook/types.ts +39 -0
- package/templates/docker/src/ui/components/notebook/use-keyboard-nav.ts +109 -0
- package/templates/docker/src/ui/components/notebook/use-notebook.ts +684 -0
- package/templates/docker/src/ui/components/org-switcher.tsx +111 -0
- package/templates/docker/src/ui/components/region-picker.tsx +103 -0
- package/templates/docker/src/ui/components/schema-explorer/schema-explorer.tsx +522 -0
- package/templates/docker/src/ui/components/social-icons.tsx +26 -0
- package/templates/docker/src/ui/components/tour/guided-tour.tsx +81 -0
- package/templates/docker/src/ui/components/tour/index.ts +5 -0
- package/templates/docker/src/ui/components/tour/nav-bar.tsx +100 -0
- package/templates/docker/src/ui/components/tour/tour-overlay.tsx +298 -0
- package/templates/docker/src/ui/components/tour/tour-steps.ts +43 -0
- package/templates/docker/src/ui/components/tour/types.ts +21 -0
- package/templates/docker/src/ui/components/tour/use-tour.ts +193 -0
- package/templates/docker/src/ui/context-reexport.ts +3 -0
- package/templates/docker/src/ui/hooks/theme-init-script.ts +17 -0
- package/templates/docker/src/ui/hooks/use-admin-fetch.ts +38 -30
- package/templates/docker/src/ui/hooks/use-admin-mutation.ts +188 -0
- package/templates/docker/src/ui/hooks/use-atlas-transport.ts +225 -0
- package/templates/docker/src/ui/hooks/use-branding.ts +68 -0
- package/templates/docker/src/ui/hooks/use-conversations.ts +106 -83
- package/templates/docker/src/ui/hooks/use-dark-mode.ts +134 -10
- package/templates/docker/src/ui/hooks/use-deploy-mode.ts +36 -0
- package/templates/docker/src/ui/hooks/use-platform-admin-guard.ts +49 -0
- package/templates/docker/src/ui/lib/action-types.ts +11 -63
- package/templates/docker/src/ui/lib/admin-schemas.ts +744 -0
- package/templates/docker/src/ui/lib/fetch-client.ts +84 -0
- package/templates/docker/src/ui/lib/fetch-error.ts +54 -0
- package/templates/docker/src/ui/lib/helpers.ts +94 -1
- package/templates/docker/src/ui/lib/types.ts +149 -140
- package/templates/docker/tsconfig.json +4 -2
- package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +17 -14
- package/templates/nextjs-standalone/bin/__tests__/failure-threshold.test.ts +148 -0
- package/templates/nextjs-standalone/bin/__tests__/fatal-error-propagation.test.ts +267 -0
- package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +5 -5
- package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +39 -0
- package/templates/nextjs-standalone/bin/atlas.ts +981 -1819
- package/templates/nextjs-standalone/bin/benchmark.ts +14 -16
- package/templates/nextjs-standalone/bin/enrich.ts +7 -2
- package/templates/nextjs-standalone/brand.css +13 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/catalog.yml +222 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/alerts.yml +195 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/assets.yml +191 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/compliance_assessments.yml +170 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/incidents.yml +219 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/organizations.yml +136 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/plans.yml +114 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/remediation_actions.yml +212 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/scan_results.yml +215 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/scans.yml +180 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/subscriptions.yml +184 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/users.yml +140 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/vulnerabilities.yml +154 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/glossary.yml +207 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/metrics/business.yml +148 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/metrics/compliance.yml +138 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/metrics/security.yml +181 -0
- package/templates/nextjs-standalone/data/cybersec.sql +8 -8
- package/templates/nextjs-standalone/data/demo.sql +3 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/catalog.yml +221 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/categories.yml +91 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/customers.yml +133 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/email_campaigns.yml +119 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/inventory_levels.yml +153 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/order_items.yml +159 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/orders.yml +199 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/payments.yml +140 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/product_reviews.yml +155 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/products.yml +178 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/promotions.yml +171 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/returns.yml +144 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/sellers.yml +124 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/shipments.yml +159 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/glossary.yml +193 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/customers.yml +116 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/operations.yml +131 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/revenue.yml +120 -0
- package/templates/nextjs-standalone/docs/deploy.md +2 -1
- package/templates/nextjs-standalone/ee/src/__mocks__/internal.ts +170 -0
- package/templates/nextjs-standalone/ee/src/audit/purge-scheduler.ts +113 -0
- package/templates/nextjs-standalone/ee/src/audit/retention.ts +467 -0
- package/templates/nextjs-standalone/ee/src/auth/ip-allowlist.ts +367 -0
- package/templates/nextjs-standalone/ee/src/auth/roles.ts +562 -0
- package/templates/nextjs-standalone/ee/src/auth/scim.ts +343 -0
- package/templates/nextjs-standalone/ee/src/auth/sso.ts +538 -0
- package/templates/nextjs-standalone/ee/src/backups/engine.ts +355 -0
- package/templates/nextjs-standalone/ee/src/backups/index.ts +26 -0
- package/templates/nextjs-standalone/ee/src/backups/restore.ts +169 -0
- package/templates/nextjs-standalone/ee/src/backups/scheduler.ts +153 -0
- package/templates/nextjs-standalone/ee/src/backups/verify.ts +124 -0
- package/templates/nextjs-standalone/ee/src/branding/white-label.ts +228 -0
- package/templates/nextjs-standalone/ee/src/compliance/masking.ts +477 -0
- package/templates/nextjs-standalone/ee/src/compliance/patterns.ts +16 -0
- package/templates/nextjs-standalone/ee/src/compliance/pii-detection.ts +217 -0
- package/templates/nextjs-standalone/ee/src/compliance/reports.ts +402 -0
- package/templates/nextjs-standalone/ee/src/deploy-mode.ts +37 -0
- package/templates/nextjs-standalone/ee/src/governance/approval.ts +699 -0
- package/templates/nextjs-standalone/ee/src/index.ts +74 -0
- package/templates/nextjs-standalone/ee/src/platform/domains.ts +562 -0
- package/templates/nextjs-standalone/ee/src/platform/model-routing.ts +382 -0
- package/templates/nextjs-standalone/ee/src/platform/residency.ts +265 -0
- package/templates/nextjs-standalone/ee/src/sla/alerting.ts +382 -0
- package/templates/nextjs-standalone/ee/src/sla/index.ts +12 -0
- package/templates/nextjs-standalone/ee/src/sla/metrics.ts +275 -0
- package/templates/nextjs-standalone/ee/src/test-setup.ts +1 -0
- package/templates/nextjs-standalone/next.config.ts +1 -1
- package/templates/nextjs-standalone/package.json +50 -30
- package/templates/nextjs-standalone/src/api/index.ts +336 -24
- package/templates/nextjs-standalone/src/api/routes/actions.ts +443 -176
- package/templates/nextjs-standalone/src/api/routes/admin-abuse.ts +219 -0
- package/templates/nextjs-standalone/src/api/routes/admin-approval.ts +418 -0
- package/templates/nextjs-standalone/src/api/routes/admin-audit-retention.ts +405 -0
- package/templates/nextjs-standalone/src/api/routes/admin-auth.ts +122 -0
- package/templates/nextjs-standalone/src/api/routes/admin-branding.ts +252 -0
- package/templates/nextjs-standalone/src/api/routes/admin-compliance.ts +352 -0
- package/templates/nextjs-standalone/src/api/routes/admin-domains.ts +334 -0
- package/templates/nextjs-standalone/src/api/routes/admin-integrations.ts +2667 -0
- package/templates/nextjs-standalone/src/api/routes/admin-ip-allowlist.ts +261 -0
- package/templates/nextjs-standalone/src/api/routes/admin-learned-patterns.ts +525 -0
- package/templates/nextjs-standalone/src/api/routes/admin-model-config.ts +252 -0
- package/templates/nextjs-standalone/src/api/routes/admin-onboarding-emails.ts +145 -0
- package/templates/nextjs-standalone/src/api/routes/admin-orgs.ts +710 -0
- package/templates/nextjs-standalone/src/api/routes/admin-prompts.ts +694 -0
- package/templates/nextjs-standalone/src/api/routes/admin-residency.ts +570 -0
- package/templates/nextjs-standalone/src/api/routes/admin-roles.ts +296 -0
- package/templates/nextjs-standalone/src/api/routes/admin-router.ts +120 -0
- package/templates/nextjs-standalone/src/api/routes/admin-sandbox.ts +417 -0
- package/templates/nextjs-standalone/src/api/routes/admin-scim.ts +262 -0
- package/templates/nextjs-standalone/src/api/routes/admin-sso.ts +545 -0
- package/templates/nextjs-standalone/src/api/routes/admin-suggestions.ts +176 -0
- package/templates/nextjs-standalone/src/api/routes/admin-usage.ts +310 -0
- package/templates/nextjs-standalone/src/api/routes/admin.ts +4156 -898
- package/templates/nextjs-standalone/src/api/routes/auth-preamble.ts +105 -0
- package/templates/nextjs-standalone/src/api/routes/billing.ts +397 -0
- package/templates/nextjs-standalone/src/api/routes/chat.ts +597 -334
- package/templates/nextjs-standalone/src/api/routes/conversations.ts +987 -132
- package/templates/nextjs-standalone/src/api/routes/demo.ts +673 -0
- package/templates/nextjs-standalone/src/api/routes/discord.ts +274 -0
- package/templates/nextjs-standalone/src/api/routes/ee-error-handler.ts +32 -0
- package/templates/nextjs-standalone/src/api/routes/health.ts +129 -14
- package/templates/nextjs-standalone/src/api/routes/middleware.ts +244 -0
- package/templates/nextjs-standalone/src/api/routes/onboarding-emails.ts +134 -0
- package/templates/nextjs-standalone/src/api/routes/onboarding.ts +1109 -0
- package/templates/nextjs-standalone/src/api/routes/openapi.ts +184 -1597
- package/templates/nextjs-standalone/src/api/routes/platform-admin.ts +760 -0
- package/templates/nextjs-standalone/src/api/routes/platform-backups.ts +436 -0
- package/templates/nextjs-standalone/src/api/routes/platform-domains.ts +235 -0
- package/templates/nextjs-standalone/src/api/routes/platform-residency.ts +257 -0
- package/templates/nextjs-standalone/src/api/routes/platform-sla.ts +379 -0
- package/templates/nextjs-standalone/src/api/routes/prompts.ts +221 -0
- package/templates/nextjs-standalone/src/api/routes/public-branding.ts +106 -0
- package/templates/nextjs-standalone/src/api/routes/query.ts +330 -219
- package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +393 -297
- package/templates/nextjs-standalone/src/api/routes/semantic.ts +179 -0
- package/templates/nextjs-standalone/src/api/routes/sessions.ts +210 -0
- package/templates/nextjs-standalone/src/api/routes/shared-domains.ts +98 -0
- package/templates/nextjs-standalone/src/api/routes/shared-schemas.ts +139 -0
- package/templates/nextjs-standalone/src/api/routes/slack.ts +209 -52
- package/templates/nextjs-standalone/src/api/routes/suggestions.ts +233 -0
- package/templates/nextjs-standalone/src/api/routes/tables.ts +67 -0
- package/templates/nextjs-standalone/src/api/routes/teams.ts +222 -0
- package/templates/nextjs-standalone/src/api/routes/validate-sql.ts +188 -0
- package/templates/nextjs-standalone/src/api/routes/validation-hook.ts +62 -0
- package/templates/nextjs-standalone/src/api/routes/widget-loader.ts +356 -0
- package/templates/nextjs-standalone/src/api/routes/widget.ts +428 -0
- package/templates/nextjs-standalone/src/api/routes/wizard.ts +852 -0
- package/templates/nextjs-standalone/src/api/server.ts +187 -69
- package/templates/nextjs-standalone/src/app/error.tsx +5 -2
- package/templates/nextjs-standalone/src/app/globals.css +1 -1
- package/templates/nextjs-standalone/src/app/layout.tsx +7 -2
- package/templates/nextjs-standalone/src/app/page.tsx +39 -5
- package/templates/nextjs-standalone/src/components/data-table/data-table-column-header.tsx +99 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-date-filter.tsx +225 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-expandable.tsx +125 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-faceted-filter.tsx +189 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-pagination.tsx +112 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-range-filter.tsx +122 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-slider-filter.tsx +256 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-sort-list.tsx +407 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-toolbar.tsx +149 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-view-options.tsx +89 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table.tsx +105 -0
- package/templates/nextjs-standalone/src/components/form-dialog.tsx +135 -0
- package/templates/nextjs-standalone/src/components/ui/accordion.tsx +66 -0
- package/templates/nextjs-standalone/src/components/ui/calendar.tsx +220 -0
- package/templates/nextjs-standalone/src/components/ui/checkbox.tsx +32 -0
- package/templates/nextjs-standalone/src/components/ui/faceted.tsx +283 -0
- package/templates/nextjs-standalone/src/components/ui/form.tsx +167 -0
- package/templates/nextjs-standalone/src/components/ui/label.tsx +24 -0
- package/templates/nextjs-standalone/src/components/ui/popover.tsx +89 -0
- package/templates/nextjs-standalone/src/components/ui/progress.tsx +31 -0
- package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +6 -2
- package/templates/nextjs-standalone/src/components/ui/slider.tsx +63 -0
- package/templates/nextjs-standalone/src/components/ui/sortable.tsx +581 -0
- package/templates/nextjs-standalone/src/components/ui/switch.tsx +35 -0
- package/templates/nextjs-standalone/src/components/ui/textarea.tsx +18 -0
- package/templates/nextjs-standalone/src/config/data-table.ts +82 -0
- package/templates/nextjs-standalone/src/env-check.ts +74 -0
- package/templates/nextjs-standalone/src/hooks/use-callback-ref.ts +27 -0
- package/templates/nextjs-standalone/src/hooks/use-data-table.ts +316 -0
- package/templates/nextjs-standalone/src/hooks/use-debounced-callback.ts +28 -0
- package/templates/nextjs-standalone/src/lib/action-types.ts +7 -41
- package/templates/nextjs-standalone/src/lib/agent-query.ts +4 -2
- package/templates/nextjs-standalone/src/lib/agent.ts +363 -31
- package/templates/nextjs-standalone/src/lib/api-url.ts +2 -3
- package/templates/nextjs-standalone/src/lib/auth/admin-permissions.ts +38 -0
- package/templates/nextjs-standalone/src/lib/auth/audit.ts +19 -4
- package/templates/nextjs-standalone/src/lib/auth/byot.ts +3 -3
- package/templates/nextjs-standalone/src/lib/auth/detect.ts +29 -8
- package/templates/nextjs-standalone/src/lib/auth/managed.ts +104 -14
- package/templates/nextjs-standalone/src/lib/auth/middleware.ts +53 -6
- package/templates/nextjs-standalone/src/lib/auth/migrate.ts +140 -15
- package/templates/nextjs-standalone/src/lib/auth/oauth-state.ts +123 -0
- package/templates/nextjs-standalone/src/lib/auth/org-permissions.ts +55 -0
- package/templates/nextjs-standalone/src/lib/auth/permissions.ts +26 -19
- package/templates/nextjs-standalone/src/lib/auth/server.ts +355 -9
- package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +3 -3
- package/templates/nextjs-standalone/src/lib/auth/types.ts +15 -21
- package/templates/nextjs-standalone/src/lib/billing/enforcement.ts +368 -0
- package/templates/nextjs-standalone/src/lib/billing/plans.ts +155 -0
- package/templates/nextjs-standalone/src/lib/cache/index.ts +92 -0
- package/templates/nextjs-standalone/src/lib/cache/keys.ts +30 -0
- package/templates/nextjs-standalone/src/lib/cache/lru.ts +79 -0
- package/templates/nextjs-standalone/src/lib/cache/types.ts +31 -0
- package/templates/nextjs-standalone/src/lib/compose-refs.ts +62 -0
- package/templates/nextjs-standalone/src/lib/config.ts +563 -11
- package/templates/nextjs-standalone/src/lib/connection-types.ts +9 -0
- package/templates/nextjs-standalone/src/lib/conversation-types.ts +1 -25
- package/templates/nextjs-standalone/src/lib/conversations.ts +345 -14
- package/templates/nextjs-standalone/src/lib/data-table.ts +61 -0
- package/templates/nextjs-standalone/src/lib/db/connection.ts +793 -39
- package/templates/nextjs-standalone/src/lib/db/internal.ts +985 -139
- package/templates/nextjs-standalone/src/lib/db/migrate.ts +295 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0000_baseline.sql +703 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0001_teams_installations.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0002_discord_installations.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0003_telegram_installations.sql +15 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0004_sandbox_credentials.sql +18 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0005_oauth_state.sql +16 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0006_byot_credentials.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0007_gchat_installations.sql +15 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0008_github_installations.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0009_linear_installations.sql +15 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0010_whatsapp_installations.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0011_email_installations.sql +16 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0012_region_migrations.sql +25 -0
- package/templates/nextjs-standalone/src/lib/db/schema.ts +1120 -0
- package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +89 -139
- package/templates/nextjs-standalone/src/lib/demo.ts +308 -0
- package/templates/nextjs-standalone/src/lib/discord/store.ts +225 -0
- package/templates/nextjs-standalone/src/lib/effect/ai.ts +243 -0
- package/templates/nextjs-standalone/src/lib/effect/errors.ts +234 -0
- package/templates/nextjs-standalone/src/lib/effect/hono.ts +454 -0
- package/templates/nextjs-standalone/src/lib/effect/index.ts +137 -0
- package/templates/nextjs-standalone/src/lib/effect/layers.ts +496 -0
- package/templates/nextjs-standalone/src/lib/effect/services.ts +776 -0
- package/templates/nextjs-standalone/src/lib/effect/sql.ts +178 -0
- package/templates/nextjs-standalone/src/lib/effect/toolkit.ts +123 -0
- package/templates/nextjs-standalone/src/lib/email/delivery.ts +232 -0
- package/templates/nextjs-standalone/src/lib/email/engine.ts +349 -0
- package/templates/nextjs-standalone/src/lib/email/hooks.ts +107 -0
- package/templates/nextjs-standalone/src/lib/email/index.ts +16 -0
- package/templates/nextjs-standalone/src/lib/email/scheduler.ts +72 -0
- package/templates/nextjs-standalone/src/lib/email/sequence.ts +73 -0
- package/templates/nextjs-standalone/src/lib/email/store.ts +163 -0
- package/templates/nextjs-standalone/src/lib/email/templates.ts +215 -0
- package/templates/nextjs-standalone/src/lib/format.test.ts +117 -0
- package/templates/nextjs-standalone/src/lib/format.ts +67 -0
- package/templates/nextjs-standalone/src/lib/gchat/store.ts +202 -0
- package/templates/nextjs-standalone/src/lib/github/store.ts +197 -0
- package/templates/nextjs-standalone/src/lib/id.ts +29 -0
- package/templates/nextjs-standalone/src/lib/integrations/types.ts +166 -0
- package/templates/nextjs-standalone/src/lib/learn/pattern-analyzer.ts +224 -0
- package/templates/nextjs-standalone/src/lib/learn/pattern-cache.ts +229 -0
- package/templates/nextjs-standalone/src/lib/learn/pattern-proposer.ts +87 -0
- package/templates/nextjs-standalone/src/lib/learn/suggestion-helpers.ts +34 -0
- package/templates/nextjs-standalone/src/lib/learn/suggestions.ts +139 -0
- package/templates/nextjs-standalone/src/lib/linear/store.ts +200 -0
- package/templates/nextjs-standalone/src/lib/logger.ts +35 -3
- package/templates/nextjs-standalone/src/lib/metering.ts +272 -0
- package/templates/nextjs-standalone/src/lib/parsers.ts +99 -0
- package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +13 -11
- package/templates/nextjs-standalone/src/lib/plugins/index.ts +3 -1
- package/templates/nextjs-standalone/src/lib/plugins/registry.ts +58 -6
- package/templates/nextjs-standalone/src/lib/plugins/settings.ts +147 -0
- package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +6 -9
- package/templates/nextjs-standalone/src/lib/profiler.ts +1665 -0
- package/templates/nextjs-standalone/src/lib/providers.ts +188 -13
- package/templates/nextjs-standalone/src/lib/rls.ts +172 -60
- package/templates/nextjs-standalone/src/lib/sandbox/credentials.ts +206 -0
- package/templates/nextjs-standalone/src/lib/sandbox/validate.ts +179 -0
- package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +26 -94
- package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +174 -34
- package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -150
- package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +190 -154
- package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +74 -23
- package/templates/nextjs-standalone/src/lib/scheduler/preview.ts +72 -0
- package/templates/nextjs-standalone/src/lib/security/abuse.ts +463 -0
- package/templates/nextjs-standalone/src/lib/semantic/diff.ts +267 -0
- package/templates/nextjs-standalone/src/lib/semantic/entities.ts +167 -0
- package/templates/nextjs-standalone/src/lib/semantic/files.ts +283 -0
- package/templates/nextjs-standalone/src/lib/semantic/index.ts +27 -0
- package/templates/nextjs-standalone/src/lib/{semantic-index.ts → semantic/search.ts} +80 -9
- package/templates/nextjs-standalone/src/lib/semantic/sync.ts +581 -0
- package/templates/nextjs-standalone/src/lib/{semantic.ts → semantic/whitelist.ts} +189 -3
- package/templates/nextjs-standalone/src/lib/settings.ts +817 -0
- package/templates/nextjs-standalone/src/lib/sidecar-types.ts +13 -0
- package/templates/nextjs-standalone/src/lib/slack/store.ts +134 -25
- package/templates/nextjs-standalone/src/lib/startup.ts +528 -362
- package/templates/nextjs-standalone/src/lib/teams/store.ts +216 -0
- package/templates/nextjs-standalone/src/lib/telegram/store.ts +202 -0
- package/templates/nextjs-standalone/src/lib/telemetry.ts +40 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +8 -5
- package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +3 -1
- package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +276 -93
- package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +2 -2
- package/templates/nextjs-standalone/src/lib/tools/backends/detect.ts +16 -0
- package/templates/nextjs-standalone/src/lib/tools/backends/index.ts +11 -0
- package/templates/nextjs-standalone/src/lib/tools/backends/nsjail.ts +213 -0
- package/templates/nextjs-standalone/src/lib/tools/backends/shared.ts +103 -0
- package/templates/nextjs-standalone/src/lib/tools/backends/types.ts +26 -0
- package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +7 -228
- package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +4 -29
- package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +18 -2
- package/templates/nextjs-standalone/src/lib/tools/explore.ts +246 -54
- package/templates/nextjs-standalone/src/lib/tools/index.ts +17 -0
- package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +11 -139
- package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +9 -132
- package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +184 -3
- package/templates/nextjs-standalone/src/lib/tools/python-stream.ts +33 -0
- package/templates/nextjs-standalone/src/lib/tools/python-wrapper.ts +129 -0
- package/templates/nextjs-standalone/src/lib/tools/python.ts +115 -15
- package/templates/nextjs-standalone/src/lib/tools/registry.ts +14 -2
- package/templates/nextjs-standalone/src/lib/tools/sql.ts +778 -362
- package/templates/nextjs-standalone/src/lib/tracing.ts +16 -0
- package/templates/nextjs-standalone/src/lib/whatsapp/store.ts +198 -0
- package/templates/nextjs-standalone/src/lib/workspace.ts +89 -0
- package/templates/nextjs-standalone/src/progress.ts +121 -0
- package/templates/nextjs-standalone/src/types/data-table.ts +48 -0
- package/templates/nextjs-standalone/src/ui/atlas-chat-reexport.ts +3 -0
- package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +26 -19
- package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +3 -3
- package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +57 -39
- package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +213 -35
- package/templates/nextjs-standalone/src/ui/components/admin/delivery-status-badge.tsx +53 -0
- package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +27 -6
- package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -52
- package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +2 -2
- package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +28 -5
- package/templates/nextjs-standalone/src/ui/components/admin-content-wrapper.tsx +87 -0
- package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +449 -166
- package/templates/nextjs-standalone/src/ui/components/branding-head.tsx +41 -0
- package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +62 -5
- package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +316 -125
- package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +4 -4
- package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +45 -4
- package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +86 -5
- package/templates/nextjs-standalone/src/ui/components/chat/follow-up-chips.tsx +29 -0
- package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +24 -0
- package/templates/nextjs-standalone/src/ui/components/chat/prompt-library.tsx +206 -0
- package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +106 -78
- package/templates/nextjs-standalone/src/ui/components/chat/result-card-base.tsx +101 -0
- package/templates/nextjs-standalone/src/ui/components/chat/share-dialog.tsx +377 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +94 -73
- package/templates/nextjs-standalone/src/ui/components/chat/suggestion-chips.tsx +46 -0
- package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +16 -4
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +48 -17
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +38 -24
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +66 -7
- package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +9 -2
- package/templates/nextjs-standalone/src/ui/components/error-boundary.tsx +66 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/delete-cell-dialog.tsx +48 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/fork-branch-selector.tsx +68 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-input.tsx +76 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-output.tsx +58 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-toolbar.tsx +91 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell.tsx +119 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-empty-state.tsx +19 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-export.ts +287 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-input-bar.tsx +49 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-shell.tsx +266 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-text-cell.tsx +152 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/types.ts +39 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/use-keyboard-nav.ts +109 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/use-notebook.ts +684 -0
- package/templates/nextjs-standalone/src/ui/components/org-switcher.tsx +111 -0
- package/templates/nextjs-standalone/src/ui/components/region-picker.tsx +103 -0
- package/templates/nextjs-standalone/src/ui/components/schema-explorer/schema-explorer.tsx +522 -0
- package/templates/nextjs-standalone/src/ui/components/social-icons.tsx +26 -0
- package/templates/nextjs-standalone/src/ui/components/tour/guided-tour.tsx +81 -0
- package/templates/nextjs-standalone/src/ui/components/tour/index.ts +5 -0
- package/templates/nextjs-standalone/src/ui/components/tour/nav-bar.tsx +100 -0
- package/templates/nextjs-standalone/src/ui/components/tour/tour-overlay.tsx +298 -0
- package/templates/nextjs-standalone/src/ui/components/tour/tour-steps.ts +43 -0
- package/templates/nextjs-standalone/src/ui/components/tour/types.ts +21 -0
- package/templates/nextjs-standalone/src/ui/components/tour/use-tour.ts +193 -0
- package/templates/nextjs-standalone/src/ui/context-reexport.ts +3 -0
- package/templates/nextjs-standalone/src/ui/hooks/theme-init-script.ts +17 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +38 -30
- package/templates/nextjs-standalone/src/ui/hooks/use-admin-mutation.ts +188 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-atlas-transport.ts +225 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-branding.ts +68 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +106 -83
- package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +134 -10
- package/templates/nextjs-standalone/src/ui/hooks/use-deploy-mode.ts +36 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-platform-admin-guard.ts +49 -0
- package/templates/nextjs-standalone/src/ui/lib/action-types.ts +11 -63
- package/templates/nextjs-standalone/src/ui/lib/admin-schemas.ts +744 -0
- package/templates/nextjs-standalone/src/ui/lib/fetch-client.ts +84 -0
- package/templates/nextjs-standalone/src/ui/lib/fetch-error.ts +54 -0
- package/templates/nextjs-standalone/src/ui/lib/helpers.ts +94 -1
- package/templates/nextjs-standalone/src/ui/lib/types.ts +149 -140
- package/templates/nextjs-standalone/tsconfig.json +3 -2
- package/templates/docker/src/api/__tests__/actions.test.ts +0 -683
- package/templates/docker/src/api/__tests__/admin.test.ts +0 -820
- package/templates/docker/src/api/__tests__/auth.test.ts +0 -165
- package/templates/docker/src/api/__tests__/chat.test.ts +0 -376
- package/templates/docker/src/api/__tests__/conversations.test.ts +0 -555
- package/templates/docker/src/api/__tests__/cors.test.ts +0 -135
- package/templates/docker/src/api/__tests__/health-plugin.test.ts +0 -176
- package/templates/docker/src/api/__tests__/health.test.ts +0 -283
- package/templates/docker/src/api/__tests__/query.test.ts +0 -891
- package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +0 -601
- package/templates/docker/src/api/__tests__/slack.test.ts +0 -847
- package/templates/docker/src/lib/__tests__/agent-cache.test.ts +0 -439
- package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +0 -131
- package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +0 -166
- package/templates/docker/src/lib/__tests__/agent-integration.test.ts +0 -516
- package/templates/docker/src/lib/__tests__/config-actions.test.ts +0 -166
- package/templates/docker/src/lib/__tests__/config.test.ts +0 -1113
- package/templates/docker/src/lib/__tests__/conversations.test.ts +0 -589
- package/templates/docker/src/lib/__tests__/errors.test.ts +0 -256
- package/templates/docker/src/lib/__tests__/logger.test.ts +0 -200
- package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +0 -321
- package/templates/docker/src/lib/__tests__/providers.test.ts +0 -130
- package/templates/docker/src/lib/__tests__/rls.test.ts +0 -435
- package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +0 -124
- package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +0 -550
- package/templates/docker/src/lib/__tests__/semantic-index.test.ts +0 -547
- package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +0 -544
- package/templates/docker/src/lib/__tests__/semantic.test.ts +0 -363
- package/templates/docker/src/lib/__tests__/startup-actions.test.ts +0 -461
- package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +0 -429
- package/templates/docker/src/lib/__tests__/startup.test.ts +0 -470
- package/templates/docker/src/lib/__tests__/tracing.test.ts +0 -28
- package/templates/docker/src/lib/auth/__tests__/audit.test.ts +0 -418
- package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +0 -222
- package/templates/docker/src/lib/auth/__tests__/byot.test.ts +0 -366
- package/templates/docker/src/lib/auth/__tests__/detect.test.ts +0 -190
- package/templates/docker/src/lib/auth/__tests__/managed.test.ts +0 -173
- package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +0 -456
- package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +0 -203
- package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +0 -225
- package/templates/docker/src/lib/auth/__tests__/server.test.ts +0 -34
- package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +0 -176
- package/templates/docker/src/lib/auth/__tests__/types.test.ts +0 -44
- package/templates/docker/src/lib/db/__tests__/connection.test.ts +0 -144
- package/templates/docker/src/lib/db/__tests__/internal.test.ts +0 -387
- package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +0 -190
- package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -137
- package/templates/docker/src/lib/db/__tests__/registry.test.ts +0 -398
- package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +0 -130
- package/templates/docker/src/lib/errors.ts +0 -154
- package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +0 -204
- package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +0 -529
- package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +0 -875
- package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +0 -373
- package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +0 -49
- package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +0 -799
- package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +0 -192
- package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +0 -248
- package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +0 -96
- package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +0 -78
- package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +0 -78
- package/templates/docker/src/lib/scheduler/index.ts +0 -7
- package/templates/docker/src/lib/slack/__tests__/api.test.ts +0 -160
- package/templates/docker/src/lib/slack/__tests__/format.test.ts +0 -237
- package/templates/docker/src/lib/slack/__tests__/store.test.ts +0 -188
- package/templates/docker/src/lib/slack/__tests__/threads.test.ts +0 -112
- package/templates/docker/src/lib/slack/__tests__/verify.test.ts +0 -111
- package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +0 -594
- package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +0 -240
- package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +0 -267
- package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +0 -506
- package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +0 -374
- package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +0 -82
- package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +0 -210
- package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +0 -515
- package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +0 -397
- package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +0 -365
- package/templates/docker/src/lib/tools/__tests__/python.test.ts +0 -331
- package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +0 -132
- package/templates/docker/src/lib/tools/__tests__/registry.test.ts +0 -242
- package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +0 -227
- package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +0 -100
- package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +0 -227
- package/templates/docker/src/lib/tools/__tests__/sql.test.ts +0 -709
- package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +0 -211
- package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +0 -378
- package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +0 -681
- package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +0 -427
- package/templates/docker/src/test-setup.ts +0 -38
- package/templates/docker/src/types/vercel-sandbox.d.ts +0 -61
- package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +0 -116
- package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +0 -683
- package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +0 -820
- package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +0 -165
- package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +0 -376
- package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +0 -555
- package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +0 -135
- package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +0 -176
- package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +0 -283
- package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +0 -891
- package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +0 -601
- package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +0 -847
- package/templates/nextjs-standalone/src/app/global-error.tsx +0 -68
- package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +0 -439
- package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +0 -131
- package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +0 -166
- package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +0 -516
- package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +0 -166
- package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +0 -1113
- package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +0 -589
- package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +0 -256
- package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +0 -200
- package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +0 -321
- package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +0 -130
- package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +0 -435
- package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +0 -124
- package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +0 -550
- package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +0 -547
- package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +0 -544
- package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +0 -363
- package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +0 -461
- package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +0 -429
- package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +0 -470
- package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +0 -28
- package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +0 -418
- package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +0 -222
- package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +0 -366
- package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +0 -190
- package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +0 -173
- package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +0 -456
- package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +0 -203
- package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +0 -225
- package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +0 -34
- package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +0 -176
- package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +0 -44
- package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +0 -144
- package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +0 -387
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +0 -190
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -137
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +0 -398
- package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +0 -130
- package/templates/nextjs-standalone/src/lib/errors.ts +0 -154
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +0 -204
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +0 -529
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +0 -875
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +0 -373
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +0 -49
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +0 -799
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +0 -192
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +0 -248
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +0 -96
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +0 -78
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +0 -78
- package/templates/nextjs-standalone/src/lib/scheduler/index.ts +0 -7
- package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +0 -160
- package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +0 -237
- package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +0 -188
- package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +0 -112
- package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +0 -111
- package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +0 -594
- package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +0 -240
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +0 -267
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +0 -506
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +0 -374
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +0 -82
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +0 -210
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +0 -515
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +0 -397
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +0 -365
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +0 -331
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +0 -132
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +0 -242
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +0 -227
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +0 -100
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +0 -227
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +0 -709
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +0 -211
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +0 -378
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +0 -681
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +0 -427
- package/templates/nextjs-standalone/src/test-setup.ts +0 -38
- 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
|
-
|
|
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 =
|
|
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
|
|
98
|
-
*
|
|
99
|
-
*
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
275
|
-
|
|
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
|
-
|
|
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
|
}
|