@useatlas/create 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +231 -0
- package/index.ts +829 -0
- package/package.json +38 -0
- package/templates/docker/.env.example +67 -0
- package/templates/docker/Dockerfile +52 -0
- package/templates/docker/bin/__tests__/benchmark.test.ts +598 -0
- package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +171 -0
- package/templates/docker/bin/__tests__/eval.test.ts +434 -0
- package/templates/docker/bin/__tests__/matview-partition.test.ts +615 -0
- package/templates/docker/bin/__tests__/multi-source.test.ts +113 -0
- package/templates/docker/bin/__tests__/plugin-cli.test.ts +322 -0
- package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +608 -0
- package/templates/docker/bin/__tests__/query.test.ts +240 -0
- package/templates/docker/bin/__tests__/schema-drift.test.ts +542 -0
- package/templates/docker/bin/__tests__/view-yaml-generation.test.ts +146 -0
- package/templates/docker/bin/atlas.ts +5044 -0
- package/templates/docker/bin/benchmark.ts +695 -0
- package/templates/docker/bin/enrich.ts +559 -0
- package/templates/docker/bin/eval.ts +770 -0
- package/templates/docker/bin/smoke.ts +438 -0
- package/templates/docker/data/.gitkeep +0 -0
- package/templates/docker/data/cybersec.sql +1961 -0
- package/templates/docker/data/demo-semantic/catalog.yml +40 -0
- package/templates/docker/data/demo-semantic/entities/accounts.yml +170 -0
- package/templates/docker/data/demo-semantic/entities/companies.yml +207 -0
- package/templates/docker/data/demo-semantic/entities/people.yml +145 -0
- package/templates/docker/data/demo-semantic/glossary.yml +22 -0
- package/templates/docker/data/demo-semantic/metrics/accounts.yml +38 -0
- package/templates/docker/data/demo-semantic/metrics/companies.yml +89 -0
- package/templates/docker/data/demo.sql +373 -0
- package/templates/docker/data/ecommerce.sql +1690 -0
- package/templates/docker/data/init-demo-db.sql +8 -0
- package/templates/docker/docker-compose.yml +34 -0
- package/templates/docker/docs/deploy.md +390 -0
- package/templates/docker/eslint.config.mjs +18 -0
- package/templates/docker/gitignore +5 -0
- package/templates/docker/next.config.ts +9 -0
- package/templates/docker/package.json +59 -0
- package/templates/docker/postcss.config.mjs +8 -0
- package/templates/docker/public/.gitkeep +0 -0
- package/templates/docker/public/favicon.svg +4 -0
- package/templates/docker/railway.json +13 -0
- package/templates/docker/render.yaml +34 -0
- package/templates/docker/semantic/catalog.yml +5 -0
- package/templates/docker/semantic/entities/.gitkeep +0 -0
- package/templates/docker/semantic/glossary.yml +6 -0
- package/templates/docker/semantic/metrics/.gitkeep +0 -0
- package/templates/docker/sidecar/Dockerfile +28 -0
- package/templates/docker/sidecar/railway.json +14 -0
- package/templates/docker/sidecar/server.ts +188 -0
- package/templates/docker/src/api/__tests__/actions.test.ts +683 -0
- package/templates/docker/src/api/__tests__/admin.test.ts +820 -0
- package/templates/docker/src/api/__tests__/auth.test.ts +165 -0
- package/templates/docker/src/api/__tests__/chat.test.ts +376 -0
- package/templates/docker/src/api/__tests__/conversations.test.ts +555 -0
- package/templates/docker/src/api/__tests__/cors.test.ts +135 -0
- package/templates/docker/src/api/__tests__/health-plugin.test.ts +169 -0
- package/templates/docker/src/api/__tests__/health.test.ts +261 -0
- package/templates/docker/src/api/__tests__/query.test.ts +891 -0
- package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +601 -0
- package/templates/docker/src/api/__tests__/slack.test.ts +847 -0
- package/templates/docker/src/api/index.ts +117 -0
- package/templates/docker/src/api/routes/actions.ts +274 -0
- package/templates/docker/src/api/routes/admin.ts +757 -0
- package/templates/docker/src/api/routes/auth.ts +48 -0
- package/templates/docker/src/api/routes/chat.ts +465 -0
- package/templates/docker/src/api/routes/conversations.ts +266 -0
- package/templates/docker/src/api/routes/health.ts +287 -0
- package/templates/docker/src/api/routes/openapi.ts +390 -0
- package/templates/docker/src/api/routes/query.ts +318 -0
- package/templates/docker/src/api/routes/scheduled-tasks.ts +467 -0
- package/templates/docker/src/api/routes/slack.ts +611 -0
- package/templates/docker/src/api/server.ts +226 -0
- package/templates/docker/src/app/api/[...route]/route.ts +33 -0
- package/templates/docker/src/app/error.tsx +24 -0
- package/templates/docker/src/app/globals.css +126 -0
- package/templates/docker/src/app/layout.tsx +19 -0
- package/templates/docker/src/app/page.tsx +14 -0
- package/templates/docker/src/global.d.ts +1 -0
- package/templates/docker/src/lib/__tests__/agent-cache.test.ts +437 -0
- package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +114 -0
- package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
- package/templates/docker/src/lib/__tests__/agent-integration.test.ts +514 -0
- package/templates/docker/src/lib/__tests__/config-actions.test.ts +166 -0
- package/templates/docker/src/lib/__tests__/config.test.ts +1063 -0
- package/templates/docker/src/lib/__tests__/conversations.test.ts +589 -0
- package/templates/docker/src/lib/__tests__/errors.test.ts +256 -0
- package/templates/docker/src/lib/__tests__/logger.test.ts +200 -0
- package/templates/docker/src/lib/__tests__/providers.test.ts +99 -0
- package/templates/docker/src/lib/__tests__/rls.test.ts +435 -0
- package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
- package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
- package/templates/docker/src/lib/__tests__/semantic-index.test.ts +547 -0
- package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +544 -0
- package/templates/docker/src/lib/__tests__/semantic.test.ts +363 -0
- package/templates/docker/src/lib/__tests__/startup-actions.test.ts +452 -0
- package/templates/docker/src/lib/__tests__/startup.test.ts +465 -0
- package/templates/docker/src/lib/__tests__/tracing.test.ts +28 -0
- package/templates/docker/src/lib/action-types.ts +95 -0
- package/templates/docker/src/lib/agent-query.ts +178 -0
- package/templates/docker/src/lib/agent.ts +505 -0
- package/templates/docker/src/lib/api-url.ts +2 -0
- package/templates/docker/src/lib/auth/__tests__/audit.test.ts +418 -0
- package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
- package/templates/docker/src/lib/auth/__tests__/byot.test.ts +366 -0
- package/templates/docker/src/lib/auth/__tests__/detect.test.ts +190 -0
- package/templates/docker/src/lib/auth/__tests__/managed.test.ts +173 -0
- package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +456 -0
- package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +201 -0
- package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +225 -0
- package/templates/docker/src/lib/auth/__tests__/server.test.ts +34 -0
- package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +176 -0
- package/templates/docker/src/lib/auth/__tests__/types.test.ts +44 -0
- package/templates/docker/src/lib/auth/audit.ts +89 -0
- package/templates/docker/src/lib/auth/byot.ts +158 -0
- package/templates/docker/src/lib/auth/client.ts +35 -0
- package/templates/docker/src/lib/auth/detect.ts +83 -0
- package/templates/docker/src/lib/auth/managed.ts +73 -0
- package/templates/docker/src/lib/auth/middleware.ts +208 -0
- package/templates/docker/src/lib/auth/migrate.ts +111 -0
- package/templates/docker/src/lib/auth/permissions.ts +156 -0
- package/templates/docker/src/lib/auth/server.ts +142 -0
- package/templates/docker/src/lib/auth/simple-key.ts +92 -0
- package/templates/docker/src/lib/auth/types.ts +49 -0
- package/templates/docker/src/lib/config.ts +704 -0
- package/templates/docker/src/lib/conversation-types.ts +29 -0
- package/templates/docker/src/lib/conversations.ts +270 -0
- package/templates/docker/src/lib/db/__tests__/connection.test.ts +69 -0
- package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +141 -0
- package/templates/docker/src/lib/db/__tests__/internal.test.ts +387 -0
- package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +207 -0
- package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
- package/templates/docker/src/lib/db/__tests__/registry.test.ts +595 -0
- package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +339 -0
- package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +217 -0
- package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
- package/templates/docker/src/lib/db/connection.ts +753 -0
- package/templates/docker/src/lib/db/duckdb.ts +122 -0
- package/templates/docker/src/lib/db/internal.ts +273 -0
- package/templates/docker/src/lib/db/salesforce.ts +342 -0
- package/templates/docker/src/lib/db/source-rate-limit.ts +191 -0
- package/templates/docker/src/lib/errors.ts +154 -0
- package/templates/docker/src/lib/logger.ts +98 -0
- package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
- package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +529 -0
- package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +521 -0
- package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +346 -0
- package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +49 -0
- package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +585 -0
- package/templates/docker/src/lib/plugins/hooks.ts +162 -0
- package/templates/docker/src/lib/plugins/index.ts +9 -0
- package/templates/docker/src/lib/plugins/migrate.ts +309 -0
- package/templates/docker/src/lib/plugins/registry.ts +231 -0
- package/templates/docker/src/lib/plugins/tools.ts +39 -0
- package/templates/docker/src/lib/plugins/wiring.ts +291 -0
- package/templates/docker/src/lib/providers.ts +102 -0
- package/templates/docker/src/lib/rls.ts +321 -0
- package/templates/docker/src/lib/scheduled-task-types.ts +132 -0
- package/templates/docker/src/lib/scheduled-tasks.ts +475 -0
- package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
- package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +248 -0
- package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
- package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
- package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
- package/templates/docker/src/lib/scheduler/delivery.ts +248 -0
- package/templates/docker/src/lib/scheduler/engine.ts +317 -0
- package/templates/docker/src/lib/scheduler/executor.ts +73 -0
- package/templates/docker/src/lib/scheduler/format-email.ts +109 -0
- package/templates/docker/src/lib/scheduler/format-slack.ts +35 -0
- package/templates/docker/src/lib/scheduler/format-webhook.ts +37 -0
- package/templates/docker/src/lib/scheduler/index.ts +7 -0
- package/templates/docker/src/lib/security.ts +11 -0
- package/templates/docker/src/lib/semantic-index.ts +503 -0
- package/templates/docker/src/lib/semantic.ts +387 -0
- package/templates/docker/src/lib/sidecar-types.ts +16 -0
- package/templates/docker/src/lib/slack/__tests__/api.test.ts +160 -0
- package/templates/docker/src/lib/slack/__tests__/format.test.ts +237 -0
- package/templates/docker/src/lib/slack/__tests__/store.test.ts +188 -0
- package/templates/docker/src/lib/slack/__tests__/threads.test.ts +112 -0
- package/templates/docker/src/lib/slack/__tests__/verify.test.ts +111 -0
- package/templates/docker/src/lib/slack/api.ts +102 -0
- package/templates/docker/src/lib/slack/format.ts +209 -0
- package/templates/docker/src/lib/slack/store.ts +107 -0
- package/templates/docker/src/lib/slack/threads.ts +64 -0
- package/templates/docker/src/lib/slack/verify.ts +71 -0
- package/templates/docker/src/lib/startup.ts +730 -0
- package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
- package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
- package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
- package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
- package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
- package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
- package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
- package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
- package/templates/docker/src/lib/tools/__tests__/registry.test.ts +235 -0
- package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
- package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
- package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
- package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
- package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
- package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
- package/templates/docker/src/lib/tools/__tests__/sql.test.ts +1012 -0
- package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
- package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +378 -0
- package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
- package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
- package/templates/docker/src/lib/tools/actions/audit.ts +47 -0
- package/templates/docker/src/lib/tools/actions/email.ts +191 -0
- package/templates/docker/src/lib/tools/actions/handler.ts +591 -0
- package/templates/docker/src/lib/tools/actions/index.ts +23 -0
- package/templates/docker/src/lib/tools/actions/jira.ts +220 -0
- package/templates/docker/src/lib/tools/explore-nsjail.ts +343 -0
- package/templates/docker/src/lib/tools/explore-sandbox.ts +264 -0
- package/templates/docker/src/lib/tools/explore-sidecar.ts +163 -0
- package/templates/docker/src/lib/tools/explore.ts +379 -0
- package/templates/docker/src/lib/tools/registry.ts +221 -0
- package/templates/docker/src/lib/tools/salesforce.ts +138 -0
- package/templates/docker/src/lib/tools/soql-validation.ts +172 -0
- package/templates/docker/src/lib/tools/sql.ts +680 -0
- package/templates/docker/src/lib/tracing.ts +40 -0
- package/templates/docker/src/lib/utils.ts +6 -0
- package/templates/docker/src/test-setup.ts +38 -0
- package/templates/docker/src/types/vercel-sandbox.d.ts +54 -0
- package/templates/docker/src/ui/components/actions/action-approval-card.tsx +295 -0
- package/templates/docker/src/ui/components/actions/action-status-badge.tsx +50 -0
- package/templates/docker/src/ui/components/admin/admin-layout.tsx +26 -0
- package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +96 -0
- package/templates/docker/src/ui/components/admin/empty-state.tsx +24 -0
- package/templates/docker/src/ui/components/admin/entity-detail.tsx +233 -0
- package/templates/docker/src/ui/components/admin/entity-list.tsx +96 -0
- package/templates/docker/src/ui/components/admin/error-banner.tsx +22 -0
- package/templates/docker/src/ui/components/admin/feature-disabled.tsx +44 -0
- package/templates/docker/src/ui/components/admin/health-badge.tsx +30 -0
- package/templates/docker/src/ui/components/admin/loading-state.tsx +14 -0
- package/templates/docker/src/ui/components/admin/stat-card.tsx +32 -0
- package/templates/docker/src/ui/components/atlas-chat.tsx +370 -0
- package/templates/docker/src/ui/components/chart/chart-detection.ts +261 -0
- package/templates/docker/src/ui/components/chart/result-chart.tsx +375 -0
- package/templates/docker/src/ui/components/chat/api-key-bar.tsx +66 -0
- package/templates/docker/src/ui/components/chat/copy-button.tsx +25 -0
- package/templates/docker/src/ui/components/chat/data-table.tsx +102 -0
- package/templates/docker/src/ui/components/chat/error-banner.tsx +32 -0
- package/templates/docker/src/ui/components/chat/explore-card.tsx +41 -0
- package/templates/docker/src/ui/components/chat/loading-card.tsx +10 -0
- package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +116 -0
- package/templates/docker/src/ui/components/chat/markdown.tsx +72 -0
- package/templates/docker/src/ui/components/chat/sql-block.tsx +30 -0
- package/templates/docker/src/ui/components/chat/sql-result-card.tsx +144 -0
- package/templates/docker/src/ui/components/chat/starter-prompts.ts +6 -0
- package/templates/docker/src/ui/components/chat/tool-part.tsx +40 -0
- package/templates/docker/src/ui/components/chat/typing-indicator.tsx +19 -0
- package/templates/docker/src/ui/components/conversations/conversation-item.tsx +120 -0
- package/templates/docker/src/ui/components/conversations/conversation-list.tsx +66 -0
- package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
- package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +27 -0
- package/templates/docker/src/ui/context.tsx +78 -0
- package/templates/docker/src/ui/hooks/use-admin-fetch.ts +104 -0
- package/templates/docker/src/ui/hooks/use-conversations.ts +184 -0
- package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -0
- package/templates/docker/src/ui/lib/action-types.ts +63 -0
- package/templates/docker/src/ui/lib/helpers.ts +104 -0
- package/templates/docker/src/ui/lib/types.ts +145 -0
- package/templates/docker/tsconfig.json +41 -0
- package/templates/docker/vercel.json +3 -0
- package/templates/nextjs-standalone/.env.example +68 -0
- package/templates/nextjs-standalone/bin/__tests__/benchmark.test.ts +598 -0
- package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +171 -0
- package/templates/nextjs-standalone/bin/__tests__/eval.test.ts +434 -0
- package/templates/nextjs-standalone/bin/__tests__/matview-partition.test.ts +615 -0
- package/templates/nextjs-standalone/bin/__tests__/multi-source.test.ts +113 -0
- package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +322 -0
- package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +608 -0
- package/templates/nextjs-standalone/bin/__tests__/query.test.ts +240 -0
- package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +542 -0
- package/templates/nextjs-standalone/bin/__tests__/view-yaml-generation.test.ts +146 -0
- package/templates/nextjs-standalone/bin/atlas.ts +5044 -0
- package/templates/nextjs-standalone/bin/benchmark.ts +695 -0
- package/templates/nextjs-standalone/bin/enrich.ts +559 -0
- package/templates/nextjs-standalone/bin/eval.ts +770 -0
- package/templates/nextjs-standalone/bin/smoke.ts +438 -0
- package/templates/nextjs-standalone/data/.gitkeep +0 -0
- package/templates/nextjs-standalone/data/cybersec.sql +1961 -0
- package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +40 -0
- package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +170 -0
- package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +207 -0
- package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +145 -0
- package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +22 -0
- package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +38 -0
- package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +89 -0
- package/templates/nextjs-standalone/data/demo.sql +373 -0
- package/templates/nextjs-standalone/data/ecommerce.sql +1690 -0
- package/templates/nextjs-standalone/data/init-demo-db.sql +8 -0
- package/templates/nextjs-standalone/docs/deploy.md +390 -0
- package/templates/nextjs-standalone/eslint.config.mjs +18 -0
- package/templates/nextjs-standalone/gitignore +5 -0
- package/templates/nextjs-standalone/next.config.ts +10 -0
- package/templates/nextjs-standalone/package.json +63 -0
- package/templates/nextjs-standalone/postcss.config.mjs +8 -0
- package/templates/nextjs-standalone/semantic/catalog.yml +5 -0
- package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
- package/templates/nextjs-standalone/semantic/glossary.yml +6 -0
- package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
- package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +683 -0
- package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +820 -0
- package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +165 -0
- package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +376 -0
- package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +555 -0
- package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +135 -0
- package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +169 -0
- package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +261 -0
- package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +891 -0
- package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +601 -0
- package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +847 -0
- package/templates/nextjs-standalone/src/api/index.ts +117 -0
- package/templates/nextjs-standalone/src/api/routes/actions.ts +274 -0
- package/templates/nextjs-standalone/src/api/routes/admin.ts +757 -0
- package/templates/nextjs-standalone/src/api/routes/auth.ts +48 -0
- package/templates/nextjs-standalone/src/api/routes/chat.ts +465 -0
- package/templates/nextjs-standalone/src/api/routes/conversations.ts +266 -0
- package/templates/nextjs-standalone/src/api/routes/health.ts +287 -0
- package/templates/nextjs-standalone/src/api/routes/openapi.ts +390 -0
- package/templates/nextjs-standalone/src/api/routes/query.ts +318 -0
- package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +467 -0
- package/templates/nextjs-standalone/src/api/routes/slack.ts +611 -0
- package/templates/nextjs-standalone/src/api/server.ts +226 -0
- package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +33 -0
- package/templates/nextjs-standalone/src/app/error.tsx +24 -0
- package/templates/nextjs-standalone/src/app/global-error.tsx +68 -0
- package/templates/nextjs-standalone/src/app/globals.css +126 -0
- package/templates/nextjs-standalone/src/app/layout.tsx +19 -0
- package/templates/nextjs-standalone/src/app/page.tsx +14 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +437 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +114 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +514 -0
- package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +166 -0
- package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +1063 -0
- package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +589 -0
- package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +256 -0
- package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +200 -0
- package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +99 -0
- package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +435 -0
- package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
- package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
- package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +547 -0
- package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +544 -0
- package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +363 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +452 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +465 -0
- package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +28 -0
- package/templates/nextjs-standalone/src/lib/action-types.ts +95 -0
- package/templates/nextjs-standalone/src/lib/agent-query.ts +178 -0
- package/templates/nextjs-standalone/src/lib/agent.ts +505 -0
- package/templates/nextjs-standalone/src/lib/api-url.ts +3 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +418 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +366 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +190 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +173 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +456 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +201 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +225 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +34 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +176 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +44 -0
- package/templates/nextjs-standalone/src/lib/auth/audit.ts +89 -0
- package/templates/nextjs-standalone/src/lib/auth/byot.ts +158 -0
- package/templates/nextjs-standalone/src/lib/auth/client.ts +23 -0
- package/templates/nextjs-standalone/src/lib/auth/detect.ts +83 -0
- package/templates/nextjs-standalone/src/lib/auth/managed.ts +73 -0
- package/templates/nextjs-standalone/src/lib/auth/middleware.ts +208 -0
- package/templates/nextjs-standalone/src/lib/auth/migrate.ts +111 -0
- package/templates/nextjs-standalone/src/lib/auth/permissions.ts +156 -0
- package/templates/nextjs-standalone/src/lib/auth/server.ts +142 -0
- package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +92 -0
- package/templates/nextjs-standalone/src/lib/auth/types.ts +49 -0
- package/templates/nextjs-standalone/src/lib/config.ts +704 -0
- package/templates/nextjs-standalone/src/lib/conversation-types.ts +29 -0
- package/templates/nextjs-standalone/src/lib/conversations.ts +270 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +69 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +141 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +387 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +207 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +595 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +339 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +217 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
- package/templates/nextjs-standalone/src/lib/db/connection.ts +753 -0
- package/templates/nextjs-standalone/src/lib/db/duckdb.ts +122 -0
- package/templates/nextjs-standalone/src/lib/db/internal.ts +273 -0
- package/templates/nextjs-standalone/src/lib/db/salesforce.ts +342 -0
- package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +191 -0
- package/templates/nextjs-standalone/src/lib/errors.ts +154 -0
- package/templates/nextjs-standalone/src/lib/logger.ts +98 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +529 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +521 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +346 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +49 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +585 -0
- package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +162 -0
- package/templates/nextjs-standalone/src/lib/plugins/index.ts +9 -0
- package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +309 -0
- package/templates/nextjs-standalone/src/lib/plugins/registry.ts +231 -0
- package/templates/nextjs-standalone/src/lib/plugins/tools.ts +39 -0
- package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +291 -0
- package/templates/nextjs-standalone/src/lib/providers.ts +102 -0
- package/templates/nextjs-standalone/src/lib/rls.ts +321 -0
- package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +132 -0
- package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +475 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +248 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
- package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -0
- package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +317 -0
- package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +73 -0
- package/templates/nextjs-standalone/src/lib/scheduler/format-email.ts +109 -0
- package/templates/nextjs-standalone/src/lib/scheduler/format-slack.ts +35 -0
- package/templates/nextjs-standalone/src/lib/scheduler/format-webhook.ts +37 -0
- package/templates/nextjs-standalone/src/lib/scheduler/index.ts +7 -0
- package/templates/nextjs-standalone/src/lib/security.ts +11 -0
- package/templates/nextjs-standalone/src/lib/semantic-index.ts +503 -0
- package/templates/nextjs-standalone/src/lib/semantic.ts +387 -0
- package/templates/nextjs-standalone/src/lib/sidecar-types.ts +16 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +160 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +237 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +188 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +112 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +111 -0
- package/templates/nextjs-standalone/src/lib/slack/api.ts +102 -0
- package/templates/nextjs-standalone/src/lib/slack/format.ts +209 -0
- package/templates/nextjs-standalone/src/lib/slack/store.ts +107 -0
- package/templates/nextjs-standalone/src/lib/slack/threads.ts +64 -0
- package/templates/nextjs-standalone/src/lib/slack/verify.ts +71 -0
- package/templates/nextjs-standalone/src/lib/startup.ts +730 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +235 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +1012 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +378 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +47 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +191 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +591 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/index.ts +23 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +220 -0
- package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +343 -0
- package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +264 -0
- package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +163 -0
- package/templates/nextjs-standalone/src/lib/tools/explore.ts +379 -0
- package/templates/nextjs-standalone/src/lib/tools/registry.ts +221 -0
- package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +138 -0
- package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +172 -0
- package/templates/nextjs-standalone/src/lib/tools/sql.ts +680 -0
- package/templates/nextjs-standalone/src/lib/tracing.ts +40 -0
- package/templates/nextjs-standalone/src/lib/utils.ts +6 -0
- package/templates/nextjs-standalone/src/test-setup.ts +38 -0
- package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +295 -0
- package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +50 -0
- package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +26 -0
- package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +96 -0
- package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +24 -0
- package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +233 -0
- package/templates/nextjs-standalone/src/ui/components/admin/entity-list.tsx +96 -0
- package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +22 -0
- package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +44 -0
- package/templates/nextjs-standalone/src/ui/components/admin/health-badge.tsx +30 -0
- package/templates/nextjs-standalone/src/ui/components/admin/loading-state.tsx +14 -0
- package/templates/nextjs-standalone/src/ui/components/admin/stat-card.tsx +32 -0
- package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +370 -0
- package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +261 -0
- package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +375 -0
- package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +66 -0
- package/templates/nextjs-standalone/src/ui/components/chat/copy-button.tsx +25 -0
- package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +102 -0
- package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +32 -0
- package/templates/nextjs-standalone/src/ui/components/chat/explore-card.tsx +41 -0
- package/templates/nextjs-standalone/src/ui/components/chat/loading-card.tsx +10 -0
- package/templates/nextjs-standalone/src/ui/components/chat/managed-auth-card.tsx +116 -0
- package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +72 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +30 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +144 -0
- package/templates/nextjs-standalone/src/ui/components/chat/starter-prompts.ts +6 -0
- package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +40 -0
- package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +19 -0
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +120 -0
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +66 -0
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
- package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +27 -0
- package/templates/nextjs-standalone/src/ui/context.tsx +78 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +104 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +184 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -0
- package/templates/nextjs-standalone/src/ui/lib/action-types.ts +63 -0
- package/templates/nextjs-standalone/src/ui/lib/helpers.ts +104 -0
- package/templates/nextjs-standalone/src/ui/lib/types.ts +145 -0
- package/templates/nextjs-standalone/tsconfig.json +32 -0
- package/templates/nextjs-standalone/vercel.json +4 -0
package/index.ts
ADDED
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
|
|
8
|
+
// Read version from package.json to stay in sync
|
|
9
|
+
const pkg = JSON.parse(
|
|
10
|
+
fs.readFileSync(path.join(import.meta.dir, "package.json"), "utf-8")
|
|
11
|
+
);
|
|
12
|
+
const ATLAS_VERSION: string = pkg.version;
|
|
13
|
+
|
|
14
|
+
// Provider → API key env var mapping
|
|
15
|
+
const PROVIDER_KEY_MAP: Record<string, { envVar: string; placeholder: string }> = {
|
|
16
|
+
anthropic: { envVar: "ANTHROPIC_API_KEY", placeholder: "sk-ant-..." },
|
|
17
|
+
openai: { envVar: "OPENAI_API_KEY", placeholder: "sk-..." },
|
|
18
|
+
bedrock: { envVar: "AWS_ACCESS_KEY_ID", placeholder: "AKIA..." },
|
|
19
|
+
ollama: { envVar: "OLLAMA_BASE_URL", placeholder: "http://localhost:11434" },
|
|
20
|
+
gateway: { envVar: "AI_GATEWAY_API_KEY", placeholder: "vcel_gw_..." },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Default models per provider
|
|
24
|
+
const PROVIDER_DEFAULT_MODEL: Record<string, string> = {
|
|
25
|
+
anthropic: "claude-opus-4-6",
|
|
26
|
+
openai: "gpt-4o",
|
|
27
|
+
bedrock: "anthropic.claude-opus-4-6-v1",
|
|
28
|
+
ollama: "llama3.1",
|
|
29
|
+
gateway: "anthropic/claude-opus-4.6",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function copyDirRecursive(src: string, dest: string): void {
|
|
33
|
+
if (!fs.existsSync(dest)) {
|
|
34
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const srcPath = path.join(src, entry.name);
|
|
40
|
+
const destPath = path.join(dest, entry.name);
|
|
41
|
+
|
|
42
|
+
if (entry.isDirectory()) {
|
|
43
|
+
copyDirRecursive(srcPath, destPath);
|
|
44
|
+
} else {
|
|
45
|
+
fs.copyFileSync(srcPath, destPath);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function bail(message?: string): never {
|
|
51
|
+
p.cancel(message ?? "Setup cancelled.");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Parse --defaults / -y flag for non-interactive mode
|
|
56
|
+
const args = process.argv.slice(2);
|
|
57
|
+
const useDefaults = args.includes("--defaults") || args.includes("-y");
|
|
58
|
+
const positionalArgs = args.filter((a) => !a.startsWith("-"));
|
|
59
|
+
|
|
60
|
+
// Platform → template mapping
|
|
61
|
+
const VALID_PLATFORMS = ["vercel", "railway", "render", "docker", "other"] as const;
|
|
62
|
+
type Platform = (typeof VALID_PLATFORMS)[number];
|
|
63
|
+
|
|
64
|
+
const VALID_SANDBOX_CHOICES = ["nsjail", "sidecar", "e2b", "daytona", "none"] as const;
|
|
65
|
+
type SandboxChoice = (typeof VALID_SANDBOX_CHOICES)[number];
|
|
66
|
+
|
|
67
|
+
type Template = "docker" | "nextjs-standalone";
|
|
68
|
+
|
|
69
|
+
function templateForPlatform(platform: Platform): Template {
|
|
70
|
+
return platform === "vercel" ? "nextjs-standalone" : "docker";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Parse --platform flag
|
|
74
|
+
let platformFlag: Platform | undefined;
|
|
75
|
+
const platformIdx = args.indexOf("--platform");
|
|
76
|
+
if (platformIdx !== -1) {
|
|
77
|
+
const val = args[platformIdx + 1];
|
|
78
|
+
if (!val || val.startsWith("-")) {
|
|
79
|
+
console.error("--platform requires a value. Available: " + VALID_PLATFORMS.join(", "));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
if (!VALID_PLATFORMS.includes(val as Platform)) {
|
|
83
|
+
console.error(`Unknown platform "${val}". Available: ${VALID_PLATFORMS.join(", ")}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
platformFlag = val as Platform;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Handle --help / -h
|
|
90
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
91
|
+
console.log(`
|
|
92
|
+
Usage: bun create @useatlas [project-name] [options]
|
|
93
|
+
|
|
94
|
+
Options:
|
|
95
|
+
--platform <name> Deploy target (${VALID_PLATFORMS.join(", ")}) [default: docker]
|
|
96
|
+
--defaults, -y Use all default values (non-interactive)
|
|
97
|
+
--help, -h Show this help message
|
|
98
|
+
|
|
99
|
+
Platforms:
|
|
100
|
+
vercel Next.js + embedded API — auto-detects Vercel sandbox
|
|
101
|
+
railway Hono API + Docker — sidecar sandbox (internal networking)
|
|
102
|
+
render Hono API + Docker — sidecar sandbox (private service)
|
|
103
|
+
docker Hono API + Docker — nsjail sandbox (built into image)
|
|
104
|
+
other Hono API + Docker — choose sandbox: nsjail, sidecar, E2B, Daytona, or none
|
|
105
|
+
|
|
106
|
+
Examples:
|
|
107
|
+
bun create @useatlas my-app
|
|
108
|
+
bun create @useatlas my-app --platform vercel
|
|
109
|
+
bun create @useatlas my-app --platform railway
|
|
110
|
+
bun create @useatlas my-app --defaults
|
|
111
|
+
`);
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Reject unknown flags
|
|
116
|
+
const knownFlags = new Set(["--defaults", "-y", "--help", "-h", "--platform"]);
|
|
117
|
+
const unknownFlags = args.filter((a, i) => {
|
|
118
|
+
if (!a.startsWith("-")) return false;
|
|
119
|
+
if (knownFlags.has(a)) return false;
|
|
120
|
+
// --platform's value argument
|
|
121
|
+
if (i > 0 && args[i - 1] === "--platform") return false;
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
if (unknownFlags.length > 0) {
|
|
125
|
+
console.error(`Unknown flag(s): ${unknownFlags.join(", ")}`);
|
|
126
|
+
console.error("Run with --help for usage information.");
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Helpers to deduplicate useDefaults branches
|
|
131
|
+
async function selectOrDefault<T extends string>(opts: {
|
|
132
|
+
label: string;
|
|
133
|
+
message: string;
|
|
134
|
+
options: { value: T; label: string; hint?: string }[];
|
|
135
|
+
initialValue: T;
|
|
136
|
+
defaultDisplay: string;
|
|
137
|
+
}): Promise<T> {
|
|
138
|
+
if (useDefaults) {
|
|
139
|
+
p.log.info(`${opts.label}: ${pc.cyan(opts.defaultDisplay)} ${pc.dim("(default)")}`);
|
|
140
|
+
return opts.initialValue;
|
|
141
|
+
}
|
|
142
|
+
const result = await p.select({
|
|
143
|
+
message: opts.message,
|
|
144
|
+
options: opts.options,
|
|
145
|
+
initialValue: opts.initialValue,
|
|
146
|
+
});
|
|
147
|
+
if (p.isCancel(result)) bail();
|
|
148
|
+
return result as T;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function confirmOrDefault(opts: {
|
|
152
|
+
label: string;
|
|
153
|
+
message: string;
|
|
154
|
+
initialValue: boolean;
|
|
155
|
+
defaultDisplay: string;
|
|
156
|
+
}): Promise<boolean> {
|
|
157
|
+
if (useDefaults) {
|
|
158
|
+
p.log.info(`${opts.label}: ${pc.cyan(opts.defaultDisplay)} ${pc.dim("(default)")}`);
|
|
159
|
+
return opts.initialValue;
|
|
160
|
+
}
|
|
161
|
+
const result = await p.confirm({
|
|
162
|
+
message: opts.message,
|
|
163
|
+
initialValue: opts.initialValue,
|
|
164
|
+
});
|
|
165
|
+
if (p.isCancel(result)) bail();
|
|
166
|
+
return result as boolean;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function main() {
|
|
170
|
+
console.log("");
|
|
171
|
+
p.intro(
|
|
172
|
+
`${pc.bgCyan(pc.black(" @useatlas/create "))} ${pc.dim(`v${ATLAS_VERSION}`)}`
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// ── 1. Project name ──────────────────────────────────────────────
|
|
176
|
+
let projectName: string;
|
|
177
|
+
|
|
178
|
+
if (positionalArgs[0]) {
|
|
179
|
+
projectName = positionalArgs[0];
|
|
180
|
+
p.log.info(`Project name: ${pc.cyan(projectName)}`);
|
|
181
|
+
} else if (useDefaults) {
|
|
182
|
+
projectName = "my-atlas-app";
|
|
183
|
+
p.log.info(`Project name: ${pc.cyan(projectName)} ${pc.dim("(default)")}`);
|
|
184
|
+
} else {
|
|
185
|
+
const result = await p.text({
|
|
186
|
+
message: "What is your project name?",
|
|
187
|
+
placeholder: "my-atlas-app",
|
|
188
|
+
defaultValue: "my-atlas-app",
|
|
189
|
+
validate(value) {
|
|
190
|
+
if (!value.trim()) return "Project name is required.";
|
|
191
|
+
if (!/^[a-z0-9._-]+$/i.test(value))
|
|
192
|
+
return "Project name can only contain letters, numbers, dots, hyphens, and underscores.";
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
if (p.isCancel(result)) bail();
|
|
196
|
+
projectName = result as string;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
200
|
+
|
|
201
|
+
if (fs.existsSync(targetDir)) {
|
|
202
|
+
if (useDefaults) {
|
|
203
|
+
bail(`Directory ${projectName} already exists.`);
|
|
204
|
+
}
|
|
205
|
+
const overwrite = await p.confirm({
|
|
206
|
+
message: `Directory ${pc.yellow(projectName)} already exists. Overwrite?`,
|
|
207
|
+
initialValue: false,
|
|
208
|
+
});
|
|
209
|
+
if (p.isCancel(overwrite) || !overwrite) bail("Directory already exists.");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── 1b. Platform selection ──────────────────────────────────────────
|
|
213
|
+
let platform: Platform;
|
|
214
|
+
if (platformFlag) {
|
|
215
|
+
platform = platformFlag;
|
|
216
|
+
p.log.info(`Platform: ${pc.cyan(platform)}`);
|
|
217
|
+
} else {
|
|
218
|
+
platform = await selectOrDefault({
|
|
219
|
+
label: "Platform",
|
|
220
|
+
message: "Where will you deploy?",
|
|
221
|
+
options: [
|
|
222
|
+
{ value: "docker", label: "Docker", hint: "nsjail sandbox built into image (default)" },
|
|
223
|
+
{ value: "railway", label: "Railway", hint: "Sidecar sandbox via internal networking" },
|
|
224
|
+
{ value: "render", label: "Render", hint: "Sidecar sandbox via private service" },
|
|
225
|
+
{ value: "vercel", label: "Vercel", hint: "Next.js + embedded API — auto-detected sandbox" },
|
|
226
|
+
{ value: "other", label: "Other", hint: "Choose your sandbox backend" },
|
|
227
|
+
],
|
|
228
|
+
initialValue: "docker" as Platform,
|
|
229
|
+
defaultDisplay: "Docker",
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const template = templateForPlatform(platform);
|
|
234
|
+
|
|
235
|
+
// ── 1c. Sandbox choice (only for "other" platform) ────────────────
|
|
236
|
+
let sandboxChoice: SandboxChoice = "nsjail";
|
|
237
|
+
let sandboxApiKey = "";
|
|
238
|
+
|
|
239
|
+
if (platform === "other") {
|
|
240
|
+
sandboxChoice = await selectOrDefault({
|
|
241
|
+
label: "Sandbox",
|
|
242
|
+
message: "Which explore sandbox backend?",
|
|
243
|
+
options: [
|
|
244
|
+
{ value: "nsjail", label: "nsjail", hint: "Process-level isolation (default, requires Linux)" },
|
|
245
|
+
{ value: "sidecar", label: "Sidecar", hint: "HTTP-isolated container (deploy separately)" },
|
|
246
|
+
{ value: "e2b", label: "E2B", hint: "Cloud sandbox (requires API key)" },
|
|
247
|
+
{ value: "daytona", label: "Daytona", hint: "Cloud sandbox (requires API key)" },
|
|
248
|
+
{ value: "none", label: "None", hint: "No isolation (dev only — not for production)" },
|
|
249
|
+
],
|
|
250
|
+
initialValue: "nsjail" as SandboxChoice,
|
|
251
|
+
defaultDisplay: "nsjail",
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Collect API key for cloud sandboxes
|
|
255
|
+
if (sandboxChoice === "e2b" || sandboxChoice === "daytona") {
|
|
256
|
+
const envVarName = sandboxChoice === "e2b" ? "E2B_API_KEY" : "DAYTONA_API_KEY";
|
|
257
|
+
if (useDefaults) {
|
|
258
|
+
sandboxApiKey = "your-api-key-here";
|
|
259
|
+
p.log.warn(`${envVarName} set to placeholder. Edit .env and set a real key before running.`);
|
|
260
|
+
} else {
|
|
261
|
+
const keyResult = await p.text({
|
|
262
|
+
message: `Enter your ${pc.cyan(envVarName)}:`,
|
|
263
|
+
placeholder: "your-api-key-here",
|
|
264
|
+
validate(value) {
|
|
265
|
+
if (!value.trim()) return `${envVarName} is required.`;
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
if (p.isCancel(keyResult)) bail();
|
|
269
|
+
sandboxApiKey = keyResult as string;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ── 2. Database choice ────────────────────────────────────────────
|
|
275
|
+
const dbChoice = await selectOrDefault({
|
|
276
|
+
label: "Database",
|
|
277
|
+
message: "Which database?",
|
|
278
|
+
options: [
|
|
279
|
+
{ value: "postgres", label: "PostgreSQL", hint: "Bring your connection string (default)" },
|
|
280
|
+
{ value: "mysql", label: "MySQL", hint: "Bring your connection string" },
|
|
281
|
+
],
|
|
282
|
+
initialValue: "postgres",
|
|
283
|
+
defaultDisplay: "PostgreSQL",
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// ── 3. Database connection string ──────────────────────────────────
|
|
287
|
+
let databaseUrl: string;
|
|
288
|
+
if (dbChoice === "postgres") {
|
|
289
|
+
if (useDefaults) {
|
|
290
|
+
databaseUrl = "postgresql://atlas:atlas@localhost:5432/atlas";
|
|
291
|
+
p.log.info(`Database URL: ${pc.cyan(databaseUrl)} ${pc.dim("(default)")}`);
|
|
292
|
+
} else {
|
|
293
|
+
const connResult = await p.text({
|
|
294
|
+
message: "PostgreSQL connection string:",
|
|
295
|
+
placeholder: "postgresql://atlas:atlas@localhost:5432/atlas",
|
|
296
|
+
defaultValue: "postgresql://atlas:atlas@localhost:5432/atlas",
|
|
297
|
+
validate(value) {
|
|
298
|
+
if (!value.trim()) return "Database URL is required.";
|
|
299
|
+
if (!value.startsWith("postgresql://") && !value.startsWith("postgres://"))
|
|
300
|
+
return "Must be a PostgreSQL connection string (postgresql://...).";
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
if (p.isCancel(connResult)) bail();
|
|
304
|
+
databaseUrl = connResult as string;
|
|
305
|
+
}
|
|
306
|
+
} else if (dbChoice === "mysql") {
|
|
307
|
+
if (useDefaults) {
|
|
308
|
+
databaseUrl = "mysql://root:root@localhost:3306/atlas";
|
|
309
|
+
p.log.info(`Database URL: ${pc.cyan(databaseUrl)} ${pc.dim("(default)")}`);
|
|
310
|
+
} else {
|
|
311
|
+
const connResult = await p.text({
|
|
312
|
+
message: "MySQL connection string:",
|
|
313
|
+
placeholder: "mysql://user:pass@localhost:3306/dbname",
|
|
314
|
+
defaultValue: "mysql://root:root@localhost:3306/atlas",
|
|
315
|
+
validate(value) {
|
|
316
|
+
if (!value.trim()) return "Database URL is required.";
|
|
317
|
+
if (!value.startsWith("mysql://") && !value.startsWith("mysql2://"))
|
|
318
|
+
return "Must be a MySQL connection string (mysql://...).";
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
if (p.isCancel(connResult)) bail();
|
|
322
|
+
databaseUrl = connResult as string;
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
// Exhaustive — only postgres and mysql are offered
|
|
326
|
+
bail("Unexpected database choice.");
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ── 4. LLM Provider ──────────────────────────────────────────────
|
|
330
|
+
const provider = await selectOrDefault({
|
|
331
|
+
label: "LLM provider",
|
|
332
|
+
message: "Which LLM provider?",
|
|
333
|
+
options: [
|
|
334
|
+
{ value: "anthropic", label: "Anthropic", hint: "Claude (default)" },
|
|
335
|
+
{ value: "openai", label: "OpenAI", hint: "GPT-4o" },
|
|
336
|
+
{ value: "bedrock", label: "AWS Bedrock", hint: "Region-specific" },
|
|
337
|
+
{ value: "ollama", label: "Ollama", hint: "Local models" },
|
|
338
|
+
{ value: "gateway", label: "Vercel AI Gateway", hint: "One key, hundreds of models" },
|
|
339
|
+
],
|
|
340
|
+
initialValue: "anthropic",
|
|
341
|
+
defaultDisplay: "Anthropic",
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// ── 5. API Key ────────────────────────────────────────────────────
|
|
345
|
+
const keyInfo = PROVIDER_KEY_MAP[provider];
|
|
346
|
+
let apiKey = "";
|
|
347
|
+
|
|
348
|
+
if (useDefaults) {
|
|
349
|
+
apiKey = keyInfo.placeholder;
|
|
350
|
+
p.log.warn(
|
|
351
|
+
`${keyInfo.envVar} set to placeholder value. Edit .env and set a real API key before running.`
|
|
352
|
+
);
|
|
353
|
+
} else if (provider === "bedrock") {
|
|
354
|
+
// Bedrock needs multiple AWS credentials
|
|
355
|
+
const accessKeyId = await p.text({
|
|
356
|
+
message: `Enter your ${pc.cyan("AWS_ACCESS_KEY_ID")}:`,
|
|
357
|
+
placeholder: "AKIA...",
|
|
358
|
+
validate(value) {
|
|
359
|
+
if (!value.trim()) return "AWS Access Key ID is required.";
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
if (p.isCancel(accessKeyId)) bail();
|
|
363
|
+
|
|
364
|
+
const secretAccessKey = await p.text({
|
|
365
|
+
message: `Enter your ${pc.cyan("AWS_SECRET_ACCESS_KEY")}:`,
|
|
366
|
+
placeholder: "wJalr...",
|
|
367
|
+
validate(value) {
|
|
368
|
+
if (!value.trim()) return "AWS Secret Access Key is required.";
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
if (p.isCancel(secretAccessKey)) bail();
|
|
372
|
+
|
|
373
|
+
const awsRegion = await p.text({
|
|
374
|
+
message: `Enter your ${pc.cyan("AWS_REGION")}:`,
|
|
375
|
+
placeholder: "us-east-1",
|
|
376
|
+
defaultValue: "us-east-1",
|
|
377
|
+
});
|
|
378
|
+
if (p.isCancel(awsRegion)) bail();
|
|
379
|
+
|
|
380
|
+
// Store all three as a composite — we'll unpack when writing .env
|
|
381
|
+
apiKey = `AWS_ACCESS_KEY_ID=${accessKeyId}\nAWS_SECRET_ACCESS_KEY=${secretAccessKey}\nAWS_REGION=${awsRegion}`;
|
|
382
|
+
} else {
|
|
383
|
+
const keyPrompt = await p.text({
|
|
384
|
+
message: `Enter your ${pc.cyan(keyInfo.envVar)}:`,
|
|
385
|
+
placeholder: keyInfo.placeholder,
|
|
386
|
+
validate(value) {
|
|
387
|
+
if (provider !== "ollama" && !value.trim())
|
|
388
|
+
return `${keyInfo.envVar} is required.`;
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
if (p.isCancel(keyPrompt)) bail();
|
|
392
|
+
apiKey = (keyPrompt as string) || keyInfo.placeholder;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ── 6. Model override ────────────────────────────────────────────
|
|
396
|
+
const defaultModel = PROVIDER_DEFAULT_MODEL[provider];
|
|
397
|
+
let modelOverride = "";
|
|
398
|
+
|
|
399
|
+
if (!useDefaults) {
|
|
400
|
+
const result = await p.text({
|
|
401
|
+
message: `Model override? ${pc.dim(`(default: ${defaultModel})`)}`,
|
|
402
|
+
placeholder: defaultModel,
|
|
403
|
+
defaultValue: "",
|
|
404
|
+
});
|
|
405
|
+
if (p.isCancel(result)) bail();
|
|
406
|
+
modelOverride = result as string;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ── 7. Semantic layer / demo data ─────────────────────────────────
|
|
410
|
+
let loadDemo = false;
|
|
411
|
+
let demoDataset: "simple" | "cybersec" | "ecommerce" = "simple";
|
|
412
|
+
let generateSemantic = false;
|
|
413
|
+
|
|
414
|
+
// Demo data is not available for MySQL (SQL files use PostgreSQL-specific syntax)
|
|
415
|
+
if (dbChoice === "mysql") {
|
|
416
|
+
p.log.info(`Demo data: ${pc.dim("not available for MySQL — use your own database")}`);
|
|
417
|
+
generateSemantic = await confirmOrDefault({
|
|
418
|
+
label: "Generate semantic layer",
|
|
419
|
+
message: "Generate semantic layer now? (requires database access)",
|
|
420
|
+
initialValue: false,
|
|
421
|
+
defaultDisplay: "no",
|
|
422
|
+
});
|
|
423
|
+
} else {
|
|
424
|
+
loadDemo = await confirmOrDefault({
|
|
425
|
+
label: "Demo data",
|
|
426
|
+
message: "Load a demo dataset?",
|
|
427
|
+
initialValue: false,
|
|
428
|
+
defaultDisplay: "no",
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
if (loadDemo) {
|
|
432
|
+
demoDataset = await selectOrDefault({
|
|
433
|
+
label: "Demo dataset",
|
|
434
|
+
message: "Which demo dataset?",
|
|
435
|
+
options: [
|
|
436
|
+
{ value: "simple", label: "Simple", hint: "3 tables, ~330 rows — quick start" },
|
|
437
|
+
{ value: "cybersec", label: "Cybersecurity SaaS", hint: "62 tables, ~500K rows — realistic evaluation" },
|
|
438
|
+
{ value: "ecommerce", label: "E-commerce (NovaMart)", hint: "52 tables, ~480K rows — DTC brand + marketplace" },
|
|
439
|
+
],
|
|
440
|
+
initialValue: "simple" as const,
|
|
441
|
+
defaultDisplay: "Simple",
|
|
442
|
+
});
|
|
443
|
+
} else {
|
|
444
|
+
generateSemantic = await confirmOrDefault({
|
|
445
|
+
label: "Generate semantic layer",
|
|
446
|
+
message: "Generate semantic layer now? (requires database access)",
|
|
447
|
+
initialValue: false,
|
|
448
|
+
defaultDisplay: "no",
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// ── Pre-flight checks ───────────────────────────────────────────
|
|
454
|
+
try {
|
|
455
|
+
const bunVersion = execSync("bun --version", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
456
|
+
const major = parseInt(bunVersion.split(".")[0], 10);
|
|
457
|
+
if (isNaN(major) || major < 1) {
|
|
458
|
+
p.log.warn(`Bun ${bunVersion} detected. Atlas requires Bun 1.0+.`);
|
|
459
|
+
}
|
|
460
|
+
} catch (err) {
|
|
461
|
+
p.log.warn(`Could not detect bun version: ${err instanceof Error ? err.message : String(err)}`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// ── DB connectivity check (Postgres/MySQL) ──────────────────────
|
|
465
|
+
if (generateSemantic && (dbChoice === "postgres" || dbChoice === "mysql")) {
|
|
466
|
+
const connSpinner = p.spinner();
|
|
467
|
+
connSpinner.start("Checking database connectivity...");
|
|
468
|
+
try {
|
|
469
|
+
if (dbChoice === "mysql") {
|
|
470
|
+
execSync(
|
|
471
|
+
`bun -e "const m=require('mysql2/promise');const p=m.createPool({uri:process.env.ATLAS_DATASOURCE_URL,connectionLimit:1,connectTimeout:5000});const c=await p.getConnection();c.release();await p.end()"`,
|
|
472
|
+
{ stdio: "pipe", timeout: 15_000, env: { ...process.env, ATLAS_DATASOURCE_URL: databaseUrl } }
|
|
473
|
+
);
|
|
474
|
+
} else {
|
|
475
|
+
execSync(
|
|
476
|
+
`bun -e "const{Pool}=require('pg');const p=new Pool({connectionString:process.env.ATLAS_DATASOURCE_URL,connectionTimeoutMillis:5000});const c=await p.connect();c.release();await p.end()"`,
|
|
477
|
+
{ stdio: "pipe", timeout: 15_000, env: { ...process.env, ATLAS_DATASOURCE_URL: databaseUrl } }
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
connSpinner.stop("Database is reachable.");
|
|
481
|
+
} catch (err) {
|
|
482
|
+
connSpinner.stop("Could not connect to database.");
|
|
483
|
+
if (err && typeof err === "object" && "stderr" in err) {
|
|
484
|
+
const stderr = String((err as { stderr: unknown }).stderr).trim();
|
|
485
|
+
if (stderr) p.log.warn(stderr);
|
|
486
|
+
}
|
|
487
|
+
const proceed = await p.confirm({
|
|
488
|
+
message: "Database is not reachable. Try generating semantic layer anyway?",
|
|
489
|
+
initialValue: false,
|
|
490
|
+
});
|
|
491
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
492
|
+
generateSemantic = false;
|
|
493
|
+
p.log.info("Skipping. Run 'bun run atlas -- init' later when the DB is available.");
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ── Scaffold ──────────────────────────────────────────────────────
|
|
499
|
+
const s = p.spinner();
|
|
500
|
+
|
|
501
|
+
// Step 1: Copy template (self-contained — includes src/, bin/, data/)
|
|
502
|
+
s.start("Copying project files...");
|
|
503
|
+
const templateDir = path.join(import.meta.dir, "templates", template);
|
|
504
|
+
|
|
505
|
+
if (!fs.existsSync(templateDir)) {
|
|
506
|
+
s.stop("Template directory not found.");
|
|
507
|
+
bail(`Could not find templates/${template}/ directory. Is the package installed correctly?`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
copyDirRecursive(templateDir, targetDir);
|
|
512
|
+
} catch (err) {
|
|
513
|
+
s.stop("Failed to copy project files.");
|
|
514
|
+
p.log.error(`Copy failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
515
|
+
if (fs.existsSync(targetDir)) {
|
|
516
|
+
p.log.warn(
|
|
517
|
+
`Partial directory may remain at ${pc.yellow(targetDir)}. Remove it manually before retrying.`
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Rename gitignore → .gitignore (npm/bun strips .gitignore from published tarballs)
|
|
524
|
+
const gitignoreSrc = path.join(targetDir, "gitignore");
|
|
525
|
+
const gitignoreDest = path.join(targetDir, ".gitignore");
|
|
526
|
+
if (fs.existsSync(gitignoreSrc)) {
|
|
527
|
+
try {
|
|
528
|
+
fs.renameSync(gitignoreSrc, gitignoreDest);
|
|
529
|
+
} catch (err) {
|
|
530
|
+
p.log.warn(
|
|
531
|
+
`Failed to rename gitignore to .gitignore: ${err instanceof Error ? err.message : String(err)}`
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
} else if (!fs.existsSync(gitignoreDest)) {
|
|
535
|
+
p.log.warn(
|
|
536
|
+
"No .gitignore found in template. Your project may accidentally commit secrets (.env). Add one manually."
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Remove platform-irrelevant files from the docker template
|
|
541
|
+
if (template === "docker") {
|
|
542
|
+
const needsSidecar = platform === "railway" || platform === "render" ||
|
|
543
|
+
(platform === "other" && sandboxChoice === "sidecar");
|
|
544
|
+
if (!needsSidecar) {
|
|
545
|
+
fs.rmSync(path.join(targetDir, "sidecar"), { recursive: true, force: true });
|
|
546
|
+
}
|
|
547
|
+
if (platform !== "render") {
|
|
548
|
+
fs.rmSync(path.join(targetDir, "render.yaml"), { force: true });
|
|
549
|
+
}
|
|
550
|
+
if (platform !== "railway") {
|
|
551
|
+
fs.rmSync(path.join(targetDir, "railway.json"), { force: true });
|
|
552
|
+
}
|
|
553
|
+
// vercel.json in docker template is noise
|
|
554
|
+
fs.rmSync(path.join(targetDir, "vercel.json"), { force: true });
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Replace %PROJECT_NAME% in templated files (only files that exist in the template)
|
|
558
|
+
const filesToReplace = ["package.json"];
|
|
559
|
+
if (platform === "render") filesToReplace.push("render.yaml");
|
|
560
|
+
for (const file of filesToReplace) {
|
|
561
|
+
const filePath = path.join(targetDir, file);
|
|
562
|
+
if (!fs.existsSync(filePath)) continue; // not all templates have every file
|
|
563
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
564
|
+
const replaced = content.replace(/%PROJECT_NAME%/g, projectName);
|
|
565
|
+
if (content === replaced && content.includes("PROJECT_NAME")) {
|
|
566
|
+
p.log.warn(`${file} may contain unreplaced template variables.`);
|
|
567
|
+
}
|
|
568
|
+
fs.writeFileSync(filePath, replaced);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
s.stop("Project files copied.");
|
|
572
|
+
|
|
573
|
+
// Track partial failures — we continue scaffolding but warn at the end
|
|
574
|
+
let setupFailed = false;
|
|
575
|
+
|
|
576
|
+
// Step 2: Write .env
|
|
577
|
+
s.start("Writing environment configuration...");
|
|
578
|
+
|
|
579
|
+
let envContent = `# Generated by @useatlas/create v${ATLAS_VERSION}\n\n`;
|
|
580
|
+
|
|
581
|
+
envContent += `# Database\n`;
|
|
582
|
+
envContent += `ATLAS_DATASOURCE_URL=${databaseUrl}\n`;
|
|
583
|
+
|
|
584
|
+
envContent += `\n# LLM Provider\n`;
|
|
585
|
+
envContent += `ATLAS_PROVIDER=${provider}\n`;
|
|
586
|
+
|
|
587
|
+
if (provider === "bedrock") {
|
|
588
|
+
envContent += `${apiKey}\n`;
|
|
589
|
+
} else {
|
|
590
|
+
envContent += `${keyInfo.envVar}=${apiKey}\n`;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (modelOverride) {
|
|
594
|
+
envContent += `\n# Model override\n`;
|
|
595
|
+
envContent += `ATLAS_MODEL=${modelOverride}\n`;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
envContent += `\n# Security (defaults)\n`;
|
|
599
|
+
envContent += `ATLAS_TABLE_WHITELIST=true\n`;
|
|
600
|
+
envContent += `ATLAS_ROW_LIMIT=1000\n`;
|
|
601
|
+
envContent += `ATLAS_QUERY_TIMEOUT=30000\n`;
|
|
602
|
+
|
|
603
|
+
// Platform-specific sandbox configuration
|
|
604
|
+
switch (platform) {
|
|
605
|
+
case "vercel":
|
|
606
|
+
envContent += `\n# Explore Sandbox\n`;
|
|
607
|
+
envContent += `# Vercel auto-detects @vercel/sandbox — no config needed.\n`;
|
|
608
|
+
envContent += `\n# Scheduled Tasks (Vercel Cron)\n`;
|
|
609
|
+
envContent += `# ATLAS_SCHEDULER_ENABLED=true\n`;
|
|
610
|
+
envContent += `# ATLAS_SCHEDULER_BACKEND=vercel\n`;
|
|
611
|
+
envContent += `# CRON_SECRET=... # Set in Vercel dashboard\n`;
|
|
612
|
+
break;
|
|
613
|
+
case "railway": {
|
|
614
|
+
const sidecarToken = crypto.randomUUID();
|
|
615
|
+
envContent += `\n# Explore Sandbox (sidecar)\n`;
|
|
616
|
+
envContent += `# Deploy the sidecar/ directory as a second Railway service.\n`;
|
|
617
|
+
envContent += `ATLAS_SANDBOX_URL=http://sidecar.railway.internal:8080\n`;
|
|
618
|
+
envContent += `SIDECAR_AUTH_TOKEN=${sidecarToken}\n`;
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
case "render": {
|
|
622
|
+
const sidecarToken = crypto.randomUUID();
|
|
623
|
+
envContent += `\n# Explore Sandbox (sidecar)\n`;
|
|
624
|
+
envContent += `# Deploy sidecar/ as a Render private service, then update the URL below.\n`;
|
|
625
|
+
envContent += `# ATLAS_SANDBOX_URL=http://<sidecar-private-url>:8080 # Update after deploying sidecar\n`;
|
|
626
|
+
envContent += `SIDECAR_AUTH_TOKEN=${sidecarToken}\n`;
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
case "docker":
|
|
630
|
+
envContent += `\n# Explore Sandbox (nsjail — built into Docker image)\n`;
|
|
631
|
+
envContent += `ATLAS_SANDBOX=nsjail\n`;
|
|
632
|
+
break;
|
|
633
|
+
case "other":
|
|
634
|
+
switch (sandboxChoice) {
|
|
635
|
+
case "nsjail":
|
|
636
|
+
envContent += `\n# Explore Sandbox (nsjail)\n`;
|
|
637
|
+
envContent += `ATLAS_SANDBOX=nsjail\n`;
|
|
638
|
+
break;
|
|
639
|
+
case "sidecar": {
|
|
640
|
+
const sidecarToken = crypto.randomUUID();
|
|
641
|
+
envContent += `\n# Explore Sandbox (sidecar)\n`;
|
|
642
|
+
envContent += `# Deploy sidecar/ as a separate service, then update the URL below.\n`;
|
|
643
|
+
envContent += `ATLAS_SANDBOX_URL=http://localhost:8080\n`;
|
|
644
|
+
envContent += `SIDECAR_AUTH_TOKEN=${sidecarToken}\n`;
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
case "e2b":
|
|
648
|
+
envContent += `\n# Explore Sandbox (E2B)\n`;
|
|
649
|
+
envContent += `E2B_API_KEY=${sandboxApiKey}\n`;
|
|
650
|
+
break;
|
|
651
|
+
case "daytona":
|
|
652
|
+
envContent += `\n# Explore Sandbox (Daytona)\n`;
|
|
653
|
+
envContent += `DAYTONA_API_KEY=${sandboxApiKey}\n`;
|
|
654
|
+
break;
|
|
655
|
+
case "none":
|
|
656
|
+
envContent += `\n# Explore Sandbox\n`;
|
|
657
|
+
envContent += `# No sandbox configured — explore runs without isolation.\n`;
|
|
658
|
+
envContent += `# This is acceptable for development but NOT for production.\n`;
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
try {
|
|
665
|
+
fs.writeFileSync(path.join(targetDir, ".env"), envContent);
|
|
666
|
+
s.stop("Environment file written.");
|
|
667
|
+
} catch (err) {
|
|
668
|
+
s.stop("Failed to write .env file.");
|
|
669
|
+
p.log.error(`Could not write .env: ${err instanceof Error ? err.message : String(err)}`);
|
|
670
|
+
p.log.info(`Create ${pc.cyan(path.join(projectName, ".env"))} manually with your configuration.`);
|
|
671
|
+
setupFailed = true;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Generate atlas.config.ts for plugin-based sandboxes (E2B, Daytona)
|
|
675
|
+
if (platform === "other" && (sandboxChoice === "e2b" || sandboxChoice === "daytona")) {
|
|
676
|
+
const pluginPkg = sandboxChoice === "e2b"
|
|
677
|
+
? "@atlas/plugin-e2b-sandbox"
|
|
678
|
+
: "@atlas/plugin-daytona-sandbox";
|
|
679
|
+
const pluginExport = sandboxChoice === "e2b"
|
|
680
|
+
? "e2bSandboxPlugin"
|
|
681
|
+
: "daytonaSandboxPlugin";
|
|
682
|
+
|
|
683
|
+
const configContent = `import { defineConfig } from "@atlas/api/lib/config";
|
|
684
|
+
import { ${pluginExport} } from "${pluginPkg}";
|
|
685
|
+
|
|
686
|
+
export default defineConfig({
|
|
687
|
+
plugins: [${pluginExport}()],
|
|
688
|
+
});
|
|
689
|
+
`;
|
|
690
|
+
try {
|
|
691
|
+
fs.writeFileSync(path.join(targetDir, "atlas.config.ts"), configContent);
|
|
692
|
+
p.log.warn(
|
|
693
|
+
`Generated atlas.config.ts with ${pc.cyan(pluginPkg)}. ` +
|
|
694
|
+
`This plugin is not yet published — install it manually when available.`
|
|
695
|
+
);
|
|
696
|
+
} catch (err) {
|
|
697
|
+
p.log.error(`Could not write atlas.config.ts: ${err instanceof Error ? err.message : String(err)}`);
|
|
698
|
+
p.log.info(`Create atlas.config.ts manually:\n${configContent}`);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Step 3: Install dependencies
|
|
703
|
+
s.start("Installing dependencies with bun...");
|
|
704
|
+
try {
|
|
705
|
+
execSync("bun install", {
|
|
706
|
+
cwd: targetDir,
|
|
707
|
+
stdio: "pipe",
|
|
708
|
+
timeout: 120_000,
|
|
709
|
+
});
|
|
710
|
+
s.stop("Dependencies installed.");
|
|
711
|
+
} catch (err) {
|
|
712
|
+
s.stop("Failed to install dependencies.");
|
|
713
|
+
p.log.warn(
|
|
714
|
+
`Could not run ${pc.cyan("bun install")}: ${err instanceof Error ? err.message : String(err)}`
|
|
715
|
+
);
|
|
716
|
+
p.log.warn(`Run it manually in ${pc.yellow(projectName)}/`);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Step 4: Load demo data + generate semantic layer
|
|
720
|
+
if (loadDemo) {
|
|
721
|
+
const demoFlag = `--demo ${demoDataset}`;
|
|
722
|
+
const timeoutMs = demoDataset === "cybersec" || demoDataset === "ecommerce" ? 120_000 : 60_000;
|
|
723
|
+
s.start(`Loading ${demoDataset} demo data and generating semantic layer...`);
|
|
724
|
+
try {
|
|
725
|
+
execSync(`bun run atlas -- init ${demoFlag}`, {
|
|
726
|
+
cwd: targetDir,
|
|
727
|
+
stdio: "pipe",
|
|
728
|
+
timeout: timeoutMs,
|
|
729
|
+
env: { ...process.env, ATLAS_DATASOURCE_URL: databaseUrl },
|
|
730
|
+
});
|
|
731
|
+
s.stop("Demo data loaded and semantic layer generated.");
|
|
732
|
+
} catch (err) {
|
|
733
|
+
s.stop("Failed to load demo data.");
|
|
734
|
+
setupFailed = true;
|
|
735
|
+
let detail = err instanceof Error ? err.message : String(err);
|
|
736
|
+
if (err && typeof err === "object" && "stderr" in err) {
|
|
737
|
+
const stderr = String((err as { stderr: unknown }).stderr).trim();
|
|
738
|
+
if (stderr) detail = stderr;
|
|
739
|
+
}
|
|
740
|
+
if (err && typeof err === "object" && "signal" in err && (err as { signal: unknown }).signal === "SIGTERM") {
|
|
741
|
+
detail = `Timed out after ${timeoutMs / 1000}s. The ${demoDataset} dataset may need more time on slow connections. Run the command manually without a timeout.`;
|
|
742
|
+
}
|
|
743
|
+
p.log.error(`Demo seeding failed: ${detail}`);
|
|
744
|
+
p.log.error(
|
|
745
|
+
`Run ${pc.cyan(`bun run atlas -- init ${demoFlag}`)} manually after resolving the issue.`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Step 4b: Generate semantic layer (Postgres/MySQL)
|
|
751
|
+
if (generateSemantic && (dbChoice === "postgres" || dbChoice === "mysql")) {
|
|
752
|
+
s.start("Generating semantic layer from database...");
|
|
753
|
+
try {
|
|
754
|
+
execSync("bun run atlas -- init --enrich", {
|
|
755
|
+
cwd: targetDir,
|
|
756
|
+
stdio: "pipe",
|
|
757
|
+
timeout: 300_000,
|
|
758
|
+
env: { ...process.env, ATLAS_DATASOURCE_URL: databaseUrl },
|
|
759
|
+
});
|
|
760
|
+
s.stop("Semantic layer generated.");
|
|
761
|
+
} catch (err) {
|
|
762
|
+
s.stop("Failed to generate semantic layer.");
|
|
763
|
+
setupFailed = true;
|
|
764
|
+
p.log.error(
|
|
765
|
+
`Semantic layer generation failed: ${err instanceof Error ? err.message : String(err)}`
|
|
766
|
+
);
|
|
767
|
+
p.log.error(
|
|
768
|
+
`Run ${pc.cyan("bun run atlas -- init --enrich")} manually after resolving the issue.`
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// ── Success ───────────────────────────────────────────────────────
|
|
774
|
+
const nextSteps = [`cd ${projectName}`, "bun run dev"];
|
|
775
|
+
|
|
776
|
+
let noteBody =
|
|
777
|
+
nextSteps.map((step) => pc.cyan(step)).join("\n");
|
|
778
|
+
|
|
779
|
+
if (useDefaults) {
|
|
780
|
+
noteBody += "\n\n" + pc.yellow("Note: .env contains a placeholder API key. Edit it before running.");
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Platform-specific deployment guidance
|
|
784
|
+
switch (platform) {
|
|
785
|
+
case "vercel":
|
|
786
|
+
noteBody += "\n\n" + pc.dim("Deploy: push to GitHub + connect in Vercel, or run `vercel deploy`.");
|
|
787
|
+
break;
|
|
788
|
+
case "railway":
|
|
789
|
+
noteBody += "\n\n" + pc.dim(
|
|
790
|
+
"Deploy: create 2 Railway services (main + sidecar/).\n" +
|
|
791
|
+
"Set SIDECAR_AUTH_TOKEN on both. Internal networking is pre-configured."
|
|
792
|
+
);
|
|
793
|
+
break;
|
|
794
|
+
case "render":
|
|
795
|
+
noteBody += "\n\n" + pc.dim(
|
|
796
|
+
"Deploy: push to GitHub → New > Blueprint.\n" +
|
|
797
|
+
"Set SIDECAR_AUTH_TOKEN on both services. Update ATLAS_SANDBOX_URL with the sidecar private URL."
|
|
798
|
+
);
|
|
799
|
+
break;
|
|
800
|
+
case "docker":
|
|
801
|
+
noteBody += "\n\n" + pc.dim(
|
|
802
|
+
"Deploy: docker build -f Dockerfile -t atlas . && docker run -p 3001:3001 atlas\n" +
|
|
803
|
+
"nsjail isolation is built into the Docker image."
|
|
804
|
+
);
|
|
805
|
+
break;
|
|
806
|
+
case "other":
|
|
807
|
+
if (sandboxChoice === "sidecar") {
|
|
808
|
+
noteBody += "\n\n" + pc.dim("Deploy sidecar/ as a separate service. Set SIDECAR_AUTH_TOKEN on both.");
|
|
809
|
+
} else if (sandboxChoice === "e2b" || sandboxChoice === "daytona") {
|
|
810
|
+
noteBody += "\n\n" + pc.dim(`Install ${sandboxChoice === "e2b" ? "@atlas/plugin-e2b-sandbox" : "@atlas/plugin-daytona-sandbox"} when available.`);
|
|
811
|
+
}
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
p.note(noteBody, "Next steps");
|
|
816
|
+
|
|
817
|
+
if (setupFailed) {
|
|
818
|
+
p.outro(`${pc.yellow("Partial setup.")} See errors above.`);
|
|
819
|
+
} else {
|
|
820
|
+
p.outro(
|
|
821
|
+
`${pc.green("Done!")} Your Atlas project is ready at ${pc.cyan(`./${projectName}`)}`
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
main().catch((err) => {
|
|
827
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
828
|
+
process.exit(1);
|
|
829
|
+
});
|