@useatlas/create 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -18
- package/index.ts +191 -31
- package/package.json +1 -1
- package/templates/docker/.env.example +3 -3
- package/templates/docker/Dockerfile.sidecar +28 -0
- package/templates/docker/bin/__tests__/plugin-cli.test.ts +9 -9
- package/templates/docker/bin/atlas.ts +108 -44
- package/templates/docker/data/demo-semantic/catalog.yml +51 -27
- package/templates/docker/data/demo-semantic/entities/accounts.yml +95 -103
- package/templates/docker/data/demo-semantic/entities/companies.yml +88 -152
- package/templates/docker/data/demo-semantic/entities/people.yml +82 -95
- package/templates/docker/data/demo-semantic/glossary.yml +104 -8
- package/templates/docker/data/demo-semantic/metrics/accounts.yml +62 -23
- package/templates/docker/data/demo-semantic/metrics/companies.yml +52 -78
- package/templates/docker/docker-compose.yml +1 -1
- package/templates/docker/docs/deploy.md +2 -39
- package/templates/docker/package.json +17 -1
- package/templates/docker/semantic/catalog.yml +62 -3
- package/templates/docker/semantic/entities/accounts.yml +162 -0
- package/templates/docker/semantic/entities/companies.yml +143 -0
- package/templates/docker/semantic/entities/people.yml +132 -0
- package/templates/docker/semantic/glossary.yml +116 -4
- package/templates/docker/semantic/metrics/accounts.yml +77 -0
- package/templates/docker/semantic/metrics/companies.yml +63 -0
- package/templates/docker/sidecar/Dockerfile +5 -6
- package/templates/docker/sidecar/railway.json +1 -2
- package/templates/docker/src/api/__tests__/admin.test.ts +7 -7
- package/templates/docker/src/api/__tests__/health-plugin.test.ts +7 -0
- package/templates/docker/src/api/__tests__/health.test.ts +30 -8
- package/templates/docker/src/api/routes/admin.ts +549 -8
- package/templates/docker/src/api/routes/chat.ts +5 -20
- package/templates/docker/src/api/routes/health.ts +39 -27
- package/templates/docker/src/api/routes/openapi.ts +1329 -74
- package/templates/docker/src/api/routes/query.ts +2 -1
- package/templates/docker/src/api/server.ts +27 -0
- package/templates/docker/src/app/api/[...route]/route.ts +2 -2
- package/templates/docker/src/app/globals.css +13 -12
- package/templates/docker/src/app/layout.tsx +9 -2
- package/templates/docker/src/components/ui/alert-dialog.tsx +196 -0
- package/templates/docker/src/components/ui/badge.tsx +48 -0
- package/templates/docker/src/components/ui/button.tsx +64 -0
- package/templates/docker/src/components/ui/card.tsx +92 -0
- package/templates/docker/src/components/ui/collapsible.tsx +33 -0
- package/templates/docker/src/components/ui/command.tsx +184 -0
- package/templates/docker/src/components/ui/dialog.tsx +158 -0
- package/templates/docker/src/components/ui/dropdown-menu.tsx +257 -0
- package/templates/docker/src/components/ui/input.tsx +21 -0
- package/templates/docker/src/components/ui/scroll-area.tsx +58 -0
- package/templates/docker/src/components/ui/select.tsx +190 -0
- package/templates/docker/src/components/ui/separator.tsx +28 -0
- package/templates/docker/src/components/ui/sheet.tsx +143 -0
- package/templates/docker/src/components/ui/sidebar.tsx +726 -0
- package/templates/docker/src/components/ui/skeleton.tsx +13 -0
- package/templates/docker/src/components/ui/table.tsx +116 -0
- package/templates/docker/src/components/ui/tabs.tsx +91 -0
- package/templates/docker/src/components/ui/toggle-group.tsx +83 -0
- package/templates/docker/src/components/ui/toggle.tsx +47 -0
- package/templates/docker/src/components/ui/tooltip.tsx +57 -0
- package/templates/docker/src/hooks/use-mobile.ts +19 -0
- package/templates/docker/src/lib/__tests__/agent-cache.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +17 -0
- package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/agent-integration.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/config.test.ts +69 -19
- package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
- package/templates/docker/src/lib/__tests__/providers.test.ts +32 -1
- package/templates/docker/src/lib/__tests__/startup-actions.test.ts +9 -0
- package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +429 -0
- package/templates/docker/src/lib/__tests__/startup.test.ts +5 -0
- package/templates/docker/src/lib/agent-query.ts +5 -23
- package/templates/docker/src/lib/agent.ts +32 -112
- package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +5 -3
- package/templates/docker/src/lib/auth/middleware.ts +30 -4
- package/templates/docker/src/lib/auth/migrate.ts +97 -0
- package/templates/docker/src/lib/auth/server.ts +12 -1
- package/templates/docker/src/lib/config.ts +37 -39
- package/templates/docker/src/lib/db/__tests__/connection.test.ts +89 -14
- package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +1 -18
- package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
- package/templates/docker/src/lib/db/__tests__/registry.test.ts +11 -208
- package/templates/docker/src/lib/db/connection.ts +87 -265
- package/templates/docker/src/lib/db/internal.ts +6 -1
- package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
- package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +2 -2
- package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +355 -1
- package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +32 -5
- package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +228 -14
- package/templates/docker/src/lib/plugins/index.ts +4 -1
- package/templates/docker/src/lib/plugins/migrate.ts +103 -0
- package/templates/docker/src/lib/plugins/registry.ts +12 -6
- package/templates/docker/src/lib/plugins/wiring.ts +113 -4
- package/templates/docker/src/lib/providers.ts +6 -1
- package/templates/docker/src/lib/security.ts +24 -0
- package/templates/docker/src/lib/semantic.ts +2 -0
- package/templates/docker/src/lib/sidecar-types.ts +12 -1
- package/templates/docker/src/lib/startup.ts +71 -101
- package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
- package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
- package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
- package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
- package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
- package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
- package/templates/docker/src/lib/tools/__tests__/python.test.ts +331 -0
- package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
- package/templates/docker/src/lib/tools/__tests__/registry.test.ts +38 -31
- package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql.test.ts +5 -308
- package/templates/docker/src/lib/tools/explore-nsjail.ts +17 -12
- package/templates/docker/src/lib/tools/explore-sidecar.ts +25 -0
- package/templates/docker/src/lib/tools/explore.ts +28 -32
- package/templates/docker/src/lib/tools/python-nsjail.ts +396 -0
- package/templates/docker/src/lib/tools/python-sandbox.ts +476 -0
- package/templates/docker/src/lib/tools/python-sidecar.ts +150 -0
- package/templates/docker/src/lib/tools/python.ts +367 -0
- package/templates/docker/src/lib/tools/registry.ts +49 -22
- package/templates/docker/src/lib/tools/sql.ts +88 -88
- package/templates/docker/src/types/vercel-sandbox.d.ts +7 -0
- package/templates/docker/src/ui/components/admin/admin-layout.tsx +77 -8
- package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +25 -17
- package/templates/docker/src/ui/components/admin/change-password-dialog.tsx +128 -0
- package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -3
- package/templates/docker/src/ui/components/admin/semantic-file-tree.tsx +159 -0
- package/templates/docker/src/ui/components/atlas-chat.tsx +64 -12
- package/templates/docker/src/ui/components/chart/result-chart.tsx +25 -15
- package/templates/docker/src/ui/components/chat/markdown.tsx +88 -42
- package/templates/docker/src/ui/components/chat/python-result-card.tsx +244 -0
- package/templates/docker/src/ui/components/chat/sql-block.tsx +39 -15
- package/templates/docker/src/ui/components/chat/sql-result-card.tsx +6 -1
- package/templates/docker/src/ui/components/chat/tool-part.tsx +12 -3
- package/templates/docker/src/ui/components/chat/typing-indicator.tsx +5 -2
- package/templates/docker/src/ui/components/conversations/conversation-item.tsx +25 -20
- package/templates/docker/src/ui/context.tsx +1 -1
- package/templates/docker/src/ui/hooks/use-conversations.ts +3 -3
- package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -10
- package/templates/docker/tsconfig.json +2 -2
- package/templates/nextjs-standalone/.env.example +1 -1
- package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +9 -9
- package/templates/nextjs-standalone/bin/atlas.ts +108 -44
- package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +51 -27
- package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +95 -103
- package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +88 -152
- package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +82 -95
- package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +104 -8
- package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +62 -23
- package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +52 -78
- package/templates/nextjs-standalone/docs/deploy.md +2 -39
- package/templates/nextjs-standalone/package.json +11 -2
- package/templates/nextjs-standalone/scripts/migrate-auth.ts +25 -0
- package/templates/nextjs-standalone/scripts/seed-demo.ts +94 -0
- package/templates/nextjs-standalone/semantic/catalog.yml +62 -3
- package/templates/nextjs-standalone/semantic/entities/accounts.yml +162 -0
- package/templates/nextjs-standalone/semantic/entities/companies.yml +143 -0
- package/templates/nextjs-standalone/semantic/entities/people.yml +132 -0
- package/templates/nextjs-standalone/semantic/glossary.yml +116 -4
- package/templates/nextjs-standalone/semantic/metrics/accounts.yml +77 -0
- package/templates/nextjs-standalone/semantic/metrics/companies.yml +63 -0
- package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +7 -7
- package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +7 -0
- package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +30 -8
- package/templates/nextjs-standalone/src/api/routes/admin.ts +549 -8
- package/templates/nextjs-standalone/src/api/routes/chat.ts +5 -20
- package/templates/nextjs-standalone/src/api/routes/health.ts +39 -27
- package/templates/nextjs-standalone/src/api/routes/openapi.ts +1329 -74
- package/templates/nextjs-standalone/src/api/routes/query.ts +2 -1
- package/templates/nextjs-standalone/src/api/server.ts +27 -0
- package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +2 -2
- package/templates/nextjs-standalone/src/app/globals.css +13 -12
- package/templates/nextjs-standalone/src/app/layout.tsx +9 -2
- package/templates/nextjs-standalone/src/components/ui/alert-dialog.tsx +196 -0
- package/templates/nextjs-standalone/src/components/ui/badge.tsx +48 -0
- package/templates/nextjs-standalone/src/components/ui/button.tsx +64 -0
- package/templates/nextjs-standalone/src/components/ui/card.tsx +92 -0
- package/templates/nextjs-standalone/src/components/ui/collapsible.tsx +33 -0
- package/templates/nextjs-standalone/src/components/ui/command.tsx +184 -0
- package/templates/nextjs-standalone/src/components/ui/dialog.tsx +158 -0
- package/templates/nextjs-standalone/src/components/ui/dropdown-menu.tsx +257 -0
- package/templates/nextjs-standalone/src/components/ui/input.tsx +21 -0
- package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +58 -0
- package/templates/nextjs-standalone/src/components/ui/select.tsx +190 -0
- package/templates/nextjs-standalone/src/components/ui/separator.tsx +28 -0
- package/templates/nextjs-standalone/src/components/ui/sheet.tsx +143 -0
- package/templates/nextjs-standalone/src/components/ui/sidebar.tsx +726 -0
- package/templates/nextjs-standalone/src/components/ui/skeleton.tsx +13 -0
- package/templates/nextjs-standalone/src/components/ui/table.tsx +116 -0
- package/templates/nextjs-standalone/src/components/ui/tabs.tsx +91 -0
- package/templates/nextjs-standalone/src/components/ui/toggle-group.tsx +83 -0
- package/templates/nextjs-standalone/src/components/ui/toggle.tsx +47 -0
- package/templates/nextjs-standalone/src/components/ui/tooltip.tsx +57 -0
- package/templates/nextjs-standalone/src/hooks/use-mobile.ts +19 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +17 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +69 -19
- package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
- package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +32 -1
- package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +9 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +429 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +5 -0
- package/templates/nextjs-standalone/src/lib/agent-query.ts +5 -23
- package/templates/nextjs-standalone/src/lib/agent.ts +32 -112
- package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +5 -3
- package/templates/nextjs-standalone/src/lib/auth/middleware.ts +30 -4
- package/templates/nextjs-standalone/src/lib/auth/migrate.ts +97 -0
- package/templates/nextjs-standalone/src/lib/auth/server.ts +12 -1
- package/templates/nextjs-standalone/src/lib/config.ts +37 -39
- package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +89 -14
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +1 -18
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +11 -208
- package/templates/nextjs-standalone/src/lib/db/connection.ts +87 -265
- package/templates/nextjs-standalone/src/lib/db/internal.ts +6 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +2 -2
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +355 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +32 -5
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +228 -14
- package/templates/nextjs-standalone/src/lib/plugins/index.ts +4 -1
- package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +103 -0
- package/templates/nextjs-standalone/src/lib/plugins/registry.ts +12 -6
- package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +113 -4
- package/templates/nextjs-standalone/src/lib/providers.ts +6 -1
- package/templates/nextjs-standalone/src/lib/security.ts +24 -0
- package/templates/nextjs-standalone/src/lib/semantic.ts +2 -0
- package/templates/nextjs-standalone/src/lib/sidecar-types.ts +12 -1
- package/templates/nextjs-standalone/src/lib/startup.ts +71 -101
- package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +331 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +38 -31
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +5 -308
- package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +17 -12
- package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +25 -0
- package/templates/nextjs-standalone/src/lib/tools/explore.ts +28 -32
- package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +396 -0
- package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +476 -0
- package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +150 -0
- package/templates/nextjs-standalone/src/lib/tools/python.ts +367 -0
- package/templates/nextjs-standalone/src/lib/tools/registry.ts +49 -22
- package/templates/nextjs-standalone/src/lib/tools/sql.ts +88 -88
- package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +77 -8
- package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +25 -17
- package/templates/nextjs-standalone/src/ui/components/admin/change-password-dialog.tsx +128 -0
- package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -3
- package/templates/nextjs-standalone/src/ui/components/admin/semantic-file-tree.tsx +159 -0
- package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +64 -12
- package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +25 -15
- package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +88 -42
- package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +244 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +39 -15
- package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +6 -1
- package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +12 -3
- package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +5 -2
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +25 -20
- package/templates/nextjs-standalone/src/ui/context.tsx +1 -1
- package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +3 -3
- package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -10
- package/templates/nextjs-standalone/tsconfig.json +0 -1
- package/templates/nextjs-standalone/vercel.json +4 -1
- package/templates/docker/render.yaml +0 -34
- package/templates/docker/semantic/entities/.gitkeep +0 -0
- package/templates/docker/semantic/metrics/.gitkeep +0 -0
- package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +0 -141
- package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +0 -339
- package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +0 -217
- package/templates/docker/src/lib/db/duckdb.ts +0 -122
- package/templates/docker/src/lib/db/salesforce.ts +0 -342
- package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
- package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
- package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
- package/templates/docker/src/lib/tools/salesforce.ts +0 -138
- package/templates/docker/src/lib/tools/soql-validation.ts +0 -172
- package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
- package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +0 -141
- package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +0 -339
- package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +0 -217
- package/templates/nextjs-standalone/src/lib/db/duckdb.ts +0 -122
- package/templates/nextjs-standalone/src/lib/db/salesforce.ts +0 -342
- package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
- package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
- package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +0 -138
- package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +0 -172
|
@@ -34,6 +34,11 @@ const PROVIDER_DEFAULTS: Record<ConfigProvider, string> = {
|
|
|
34
34
|
gateway: "anthropic/claude-opus-4.6",
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
/** Returns the default provider string based on runtime environment. */
|
|
38
|
+
export function getDefaultProvider(): ConfigProvider {
|
|
39
|
+
return process.env.VERCEL ? "gateway" : "anthropic";
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
function isBedrockAnthropicModel(modelId: string): boolean {
|
|
38
43
|
return modelId.includes("anthropic") || modelId.includes("claude");
|
|
39
44
|
}
|
|
@@ -43,7 +48,7 @@ function isBedrockAnthropicModel(modelId: string): boolean {
|
|
|
43
48
|
* Returns the validated config provider string and the resolved model ID.
|
|
44
49
|
*/
|
|
45
50
|
function resolveProvider(): { provider: ConfigProvider; modelId: string } {
|
|
46
|
-
const raw = process.env.ATLAS_PROVIDER ??
|
|
51
|
+
const raw = process.env.ATLAS_PROVIDER ?? getDefaultProvider();
|
|
47
52
|
if (!VALID_PROVIDERS.has(raw as ConfigProvider)) {
|
|
48
53
|
throw new Error(
|
|
49
54
|
`Unknown provider "${raw}". Supported: ${[...VALID_PROVIDERS].join(", ")}`
|
|
@@ -9,3 +9,27 @@
|
|
|
9
9
|
|
|
10
10
|
export const SENSITIVE_PATTERNS =
|
|
11
11
|
/password|secret|credential|connection.?string|pg_hba\.conf|SSL|certificate|Access denied for user|ER_ACCESS_DENIED_ERROR|ER_DBACCESS_DENIED_ERROR|ER_BAD_HOST_ERROR|ER_HOST_NOT_PRIVILEGED|ER_SPECIFIC_ACCESS_DENIED_ERROR|PROTOCOL_CONNECTION_LOST|Can't connect to MySQL server|Authentication failed|DB::Exception.*Authentication|UNKNOWN_USER|WRONG_PASSWORD|REQUIRED_PASSWORD|IP_ADDRESS_NOT_ALLOWED|ALL_CONNECTION_TRIES_FAILED|CLIENT_HAS_CONNECTED_TO_WRONG_PORT|AUTHENTICATION_FAILED|INVALID_SESSION_ID|LOGIN_MUST_USE_SECURITY_TOKEN|INVALID_LOGIN|INVALID_CLIENT_ID/i;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mask credentials in a database connection URL.
|
|
15
|
+
* Returns "<invalid-url>" for unparseable URLs to avoid leaking raw strings.
|
|
16
|
+
*/
|
|
17
|
+
const SENSITIVE_PARAMS = /^(password|secret|token|key|credential|auth)$/i;
|
|
18
|
+
|
|
19
|
+
export function maskConnectionUrl(url: string): string {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = new URL(url);
|
|
22
|
+
if (parsed.username || parsed.password) {
|
|
23
|
+
parsed.username = "***";
|
|
24
|
+
parsed.password = "";
|
|
25
|
+
}
|
|
26
|
+
for (const key of [...parsed.searchParams.keys()]) {
|
|
27
|
+
if (SENSITIVE_PARAMS.test(key)) {
|
|
28
|
+
parsed.searchParams.set(key, "***");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return parsed.toString();
|
|
32
|
+
} catch {
|
|
33
|
+
return "<invalid-url>";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -86,6 +86,8 @@ function loadEntitiesFromDir(
|
|
|
86
86
|
for (const file of files) {
|
|
87
87
|
try {
|
|
88
88
|
const content = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
89
|
+
// js-yaml v4+ yaml.load() uses DEFAULT_SCHEMA (JSON + core YAML types) which is
|
|
90
|
+
// safe — it does not instantiate arbitrary JS objects (unlike v3's yaml.load).
|
|
89
91
|
const raw = yaml.load(content);
|
|
90
92
|
const parsed = EntityShape.safeParse(raw);
|
|
91
93
|
if (!parsed.success) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Types for the sidecar HTTP contract.
|
|
3
3
|
* Used by both the sidecar server (packages/sandbox-sidecar) and
|
|
4
|
-
* the sidecar
|
|
4
|
+
* the sidecar clients (explore-sidecar.ts, python-sidecar.ts).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export interface SidecarExecRequest {
|
|
@@ -14,3 +14,14 @@ export interface SidecarExecResponse {
|
|
|
14
14
|
stderr: string;
|
|
15
15
|
exitCode: number;
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
// --- Python execution ---
|
|
19
|
+
|
|
20
|
+
export interface SidecarPythonRequest {
|
|
21
|
+
code: string;
|
|
22
|
+
data?: { columns: string[]; rows: unknown[][] };
|
|
23
|
+
timeout?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Wire-format alias — canonical type lives in python.ts. */
|
|
27
|
+
export type { PythonResult as SidecarPythonResponse } from "@atlas/api/lib/tools/python";
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
import * as fs from "fs";
|
|
9
9
|
import * as path from "path";
|
|
10
|
-
import { detectDBType } from "./db/connection";
|
|
10
|
+
import { detectDBType, resolveDatasourceUrl } from "./db/connection";
|
|
11
|
+
import { maskConnectionUrl } from "./security";
|
|
12
|
+
import { getDefaultProvider } from "./providers";
|
|
11
13
|
import { detectAuthMode, getAuthModeSource } from "./auth/detect";
|
|
12
14
|
import { createLogger } from "./logger";
|
|
13
15
|
|
|
@@ -18,7 +20,8 @@ export type DiagnosticCode =
|
|
|
18
20
|
| "MISSING_SEMANTIC_LAYER" | "INVALID_SCHEMA" | "INTERNAL_DB_UNREACHABLE"
|
|
19
21
|
| "WEAK_AUTH_SECRET" | "INVALID_JWKS_URL" | "MISSING_AUTH_ISSUER"
|
|
20
22
|
| "MISSING_AUTH_PREREQ"
|
|
21
|
-
| "ACTIONS_REQUIRE_AUTH" | "ACTIONS_MISSING_CREDENTIALS"
|
|
23
|
+
| "ACTIONS_REQUIRE_AUTH" | "ACTIONS_MISSING_CREDENTIALS"
|
|
24
|
+
| "INVALID_CONFIG";
|
|
22
25
|
|
|
23
26
|
export interface DiagnosticError {
|
|
24
27
|
code: DiagnosticCode;
|
|
@@ -33,6 +36,12 @@ const PROVIDER_KEY_MAP: Record<string, string> = {
|
|
|
33
36
|
gateway: "AI_GATEWAY_API_KEY",
|
|
34
37
|
};
|
|
35
38
|
|
|
39
|
+
const PROVIDER_SIGNUP_URL: Record<string, string> = {
|
|
40
|
+
anthropic: "https://console.anthropic.com/settings/keys",
|
|
41
|
+
openai: "https://platform.openai.com/api-keys",
|
|
42
|
+
gateway: "https://vercel.com/~/ai/api-keys",
|
|
43
|
+
};
|
|
44
|
+
|
|
36
45
|
let _cached: DiagnosticError[] | null = null;
|
|
37
46
|
let _cachedAt = 0;
|
|
38
47
|
const _startupWarnings: string[] = [];
|
|
@@ -67,14 +76,23 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
67
76
|
|
|
68
77
|
const errors: DiagnosticError[] = [];
|
|
69
78
|
|
|
70
|
-
// 1.
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
// 1. Analytics datasource — resolve from ATLAS_DATASOURCE_URL or Neon fallback
|
|
80
|
+
const resolvedDatasourceUrl = resolveDatasourceUrl();
|
|
81
|
+
if (!resolvedDatasourceUrl) {
|
|
82
|
+
if (process.env.ATLAS_DEMO_DATA === "true") {
|
|
83
|
+
const msg =
|
|
84
|
+
"ATLAS_DEMO_DATA=true but neither DATABASE_URL_UNPOOLED nor DATABASE_URL is set. " +
|
|
85
|
+
"The Neon integration may not have provisioned a database. " +
|
|
86
|
+
"Check your Vercel project's storage integrations.";
|
|
87
|
+
log.error(msg);
|
|
88
|
+
errors.push({ code: "MISSING_DATASOURCE_URL", message: msg });
|
|
89
|
+
} else if (process.env.DATABASE_URL) {
|
|
73
90
|
const msg =
|
|
74
91
|
"DATABASE_URL is set but ATLAS_DATASOURCE_URL is not. " +
|
|
75
92
|
"As of v0.5, the analytics datasource uses ATLAS_DATASOURCE_URL. " +
|
|
76
93
|
"DATABASE_URL is now reserved for Atlas's internal Postgres. " +
|
|
77
|
-
"Rename your analytics connection to ATLAS_DATASOURCE_URL
|
|
94
|
+
"Rename your analytics connection to ATLAS_DATASOURCE_URL, " +
|
|
95
|
+
"or set ATLAS_DEMO_DATA=true to use the same database for demo data.";
|
|
78
96
|
log.error(msg);
|
|
79
97
|
errors.push({ code: "MISSING_DATASOURCE_URL", message: msg });
|
|
80
98
|
} else {
|
|
@@ -87,19 +105,23 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
87
105
|
}
|
|
88
106
|
log.warn(msg);
|
|
89
107
|
}
|
|
108
|
+
} else if (!process.env.ATLAS_DATASOURCE_URL && process.env.ATLAS_DEMO_DATA === "true") {
|
|
109
|
+
const source = process.env.DATABASE_URL_UNPOOLED ? "DATABASE_URL_UNPOOLED" : "DATABASE_URL";
|
|
110
|
+
log.info("Demo mode: using %s as analytics datasource", source);
|
|
90
111
|
}
|
|
91
112
|
|
|
92
113
|
// 2. API key for configured provider
|
|
93
|
-
const provider = process.env.ATLAS_PROVIDER ??
|
|
114
|
+
const provider = process.env.ATLAS_PROVIDER ?? getDefaultProvider();
|
|
94
115
|
const requiredKey = PROVIDER_KEY_MAP[provider];
|
|
95
116
|
|
|
96
117
|
if (requiredKey === undefined) {
|
|
97
118
|
// Unknown provider — providers.ts will throw a descriptive error at model init,
|
|
98
119
|
// so we don't duplicate that check here.
|
|
99
120
|
} else if (requiredKey && !process.env[requiredKey]) {
|
|
100
|
-
let message = `${requiredKey} is not set. Atlas needs an API key for the ${provider} provider.`;
|
|
101
|
-
|
|
102
|
-
|
|
121
|
+
let message = `${requiredKey} is not set. Atlas needs an API key for the "${provider}" provider. Set it in your .env file.`;
|
|
122
|
+
const signupUrl = PROVIDER_SIGNUP_URL[provider];
|
|
123
|
+
if (signupUrl) {
|
|
124
|
+
message += ` Get one at ${signupUrl}`;
|
|
103
125
|
}
|
|
104
126
|
errors.push({ code: "MISSING_API_KEY", message });
|
|
105
127
|
}
|
|
@@ -129,11 +151,11 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
129
151
|
});
|
|
130
152
|
}
|
|
131
153
|
|
|
132
|
-
// 4. Datasource connectivity (only if
|
|
133
|
-
if (
|
|
154
|
+
// 4. Datasource connectivity (only if a datasource URL is resolved)
|
|
155
|
+
if (resolvedDatasourceUrl) {
|
|
134
156
|
let dbType: ReturnType<typeof detectDBType> | null = null;
|
|
135
157
|
try {
|
|
136
|
-
dbType = detectDBType();
|
|
158
|
+
dbType = detectDBType(resolvedDatasourceUrl);
|
|
137
159
|
} catch (err) {
|
|
138
160
|
const detail = err instanceof Error ? err.message : String(err);
|
|
139
161
|
log.error({ err: detail }, "Unsupported datasource URL");
|
|
@@ -142,7 +164,7 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
142
164
|
|
|
143
165
|
if (dbType === "mysql") {
|
|
144
166
|
// MySQL: URL validation + connection test
|
|
145
|
-
if (!isValidUrl(
|
|
167
|
+
if (!isValidUrl(resolvedDatasourceUrl)) {
|
|
146
168
|
errors.push({
|
|
147
169
|
code: "DB_UNREACHABLE",
|
|
148
170
|
message: "ATLAS_DATASOURCE_URL appears malformed. Expected format: mysql://user:pass@host:3306/dbname",
|
|
@@ -153,7 +175,7 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
153
175
|
let pool;
|
|
154
176
|
try {
|
|
155
177
|
pool = mysql.createPool({
|
|
156
|
-
uri:
|
|
178
|
+
uri: resolvedDatasourceUrl,
|
|
157
179
|
connectionLimit: 1,
|
|
158
180
|
connectTimeout: 5000,
|
|
159
181
|
});
|
|
@@ -163,7 +185,7 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
163
185
|
const detail = err instanceof Error ? err.message : "";
|
|
164
186
|
log.error({ err: detail }, "MySQL connection check failed");
|
|
165
187
|
|
|
166
|
-
let message =
|
|
188
|
+
let message = `Cannot connect to ${maskConnectionUrl(resolvedDatasourceUrl)}. Check the connection string and ensure the database is running.`;
|
|
167
189
|
|
|
168
190
|
if (/ECONNREFUSED/i.test(detail)) {
|
|
169
191
|
message += " The connection was refused — is the MySQL server running?";
|
|
@@ -184,47 +206,6 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
184
206
|
}
|
|
185
207
|
}
|
|
186
208
|
}
|
|
187
|
-
} else if (dbType === "clickhouse") {
|
|
188
|
-
// ClickHouse: connectivity test via SELECT 1
|
|
189
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
190
|
-
let createClient: any = null;
|
|
191
|
-
try {
|
|
192
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
193
|
-
({ createClient } = require("@clickhouse/client"));
|
|
194
|
-
} catch {
|
|
195
|
-
errors.push({
|
|
196
|
-
code: "DB_UNREACHABLE",
|
|
197
|
-
message: "ClickHouse support requires the @clickhouse/client package. Install it with: bun add @clickhouse/client",
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (createClient) {
|
|
202
|
-
const { rewriteClickHouseUrl } = await import("./db/connection");
|
|
203
|
-
const httpUrl = rewriteClickHouseUrl(process.env.ATLAS_DATASOURCE_URL!);
|
|
204
|
-
const client = createClient({ url: httpUrl });
|
|
205
|
-
try {
|
|
206
|
-
await client.query({ query: "SELECT 1", format: "JSON" });
|
|
207
|
-
} catch (err) {
|
|
208
|
-
const detail = err instanceof Error ? err.message : "";
|
|
209
|
-
log.error({ err: detail }, "ClickHouse connection check failed");
|
|
210
|
-
|
|
211
|
-
let message = "Cannot connect to ClickHouse. Check that the server is running and the connection string is correct.";
|
|
212
|
-
|
|
213
|
-
if (/ECONNREFUSED/i.test(detail)) {
|
|
214
|
-
message += " The connection was refused — is the ClickHouse server running?";
|
|
215
|
-
} else if (/Authentication/i.test(detail) || /AUTHENTICATION_FAILED/i.test(detail)) {
|
|
216
|
-
message += " Authentication failed — check your username and password.";
|
|
217
|
-
} else if (/timeout/i.test(detail)) {
|
|
218
|
-
message += " The connection timed out — check network/firewall settings.";
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
errors.push({ code: "DB_UNREACHABLE", message });
|
|
222
|
-
} finally {
|
|
223
|
-
await client.close().catch((err: unknown) => {
|
|
224
|
-
log.warn({ err: err instanceof Error ? err.message : String(err) }, "ClickHouse client cleanup warning");
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
209
|
} else if (dbType === "postgres") {
|
|
229
210
|
// PostgreSQL: existing URL validation + connection test + schema validation
|
|
230
211
|
const atlasSchema = process.env.ATLAS_SCHEMA;
|
|
@@ -238,7 +219,7 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
238
219
|
});
|
|
239
220
|
}
|
|
240
221
|
|
|
241
|
-
if (!isValidUrl(
|
|
222
|
+
if (!isValidUrl(resolvedDatasourceUrl)) {
|
|
242
223
|
errors.push({
|
|
243
224
|
code: "DB_UNREACHABLE",
|
|
244
225
|
message: "ATLAS_DATASOURCE_URL appears malformed. Expected format: postgresql://user:pass@host:5432/dbname",
|
|
@@ -247,7 +228,7 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
247
228
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
248
229
|
const { Pool } = require("pg");
|
|
249
230
|
const pool = new Pool({
|
|
250
|
-
connectionString:
|
|
231
|
+
connectionString: resolvedDatasourceUrl,
|
|
251
232
|
max: 1,
|
|
252
233
|
connectionTimeoutMillis: 5000,
|
|
253
234
|
});
|
|
@@ -281,7 +262,7 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
281
262
|
const detail = err instanceof Error ? err.message : "";
|
|
282
263
|
log.error({ err: detail }, "DB connection check failed");
|
|
283
264
|
|
|
284
|
-
let message =
|
|
265
|
+
let message = `Cannot connect to ${maskConnectionUrl(resolvedDatasourceUrl)}. Check the connection string and ensure the database is running.`;
|
|
285
266
|
|
|
286
267
|
if (/ECONNREFUSED/i.test(detail)) {
|
|
287
268
|
message += " The connection was refused — is the database server running?";
|
|
@@ -298,46 +279,14 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
298
279
|
});
|
|
299
280
|
}
|
|
300
281
|
}
|
|
301
|
-
} else if (dbType === "salesforce") {
|
|
302
|
-
// Salesforce: test login + listObjects
|
|
303
|
-
try {
|
|
304
|
-
const { parseSalesforceURL, createSalesforceDataSource } = await import("./db/salesforce");
|
|
305
|
-
const config = parseSalesforceURL(process.env.ATLAS_DATASOURCE_URL!);
|
|
306
|
-
const source = createSalesforceDataSource(config);
|
|
307
|
-
try {
|
|
308
|
-
await source.listObjects();
|
|
309
|
-
} finally {
|
|
310
|
-
await source.close().catch((err: unknown) => {
|
|
311
|
-
log.warn({ err: err instanceof Error ? err.message : String(err) }, "Salesforce cleanup warning");
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
} catch (err) {
|
|
315
|
-
const detail = err instanceof Error ? err.message : "";
|
|
316
|
-
log.error({ err: detail }, "Salesforce connection check failed");
|
|
317
|
-
|
|
318
|
-
let message = "Cannot connect to Salesforce. Check that your credentials and connection string are correct.";
|
|
319
|
-
|
|
320
|
-
if (/LOGIN_MUST_USE_SECURITY_TOKEN/i.test(detail)) {
|
|
321
|
-
message += " A security token is required — add ?token=YOUR_TOKEN to the connection URL.";
|
|
322
|
-
} else if (/INVALID_LOGIN/i.test(detail)) {
|
|
323
|
-
message += " Authentication failed — check your username and password.";
|
|
324
|
-
} else if (/timeout/i.test(detail)) {
|
|
325
|
-
message += " The connection timed out — check network/firewall settings.";
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
errors.push({ code: "DB_UNREACHABLE", message });
|
|
329
|
-
}
|
|
330
282
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
"
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
_startupWarnings.push(msg);
|
|
339
|
-
}
|
|
340
|
-
log.warn(msg);
|
|
283
|
+
// Non-core database types are validated by their respective datasource plugins.
|
|
284
|
+
if (dbType && dbType !== "postgres" && dbType !== "mysql") {
|
|
285
|
+
log.info(
|
|
286
|
+
{ dbType },
|
|
287
|
+
"Non-core datasource type '%s' — connectivity validation deferred to plugin initialize()",
|
|
288
|
+
dbType,
|
|
289
|
+
);
|
|
341
290
|
}
|
|
342
291
|
}
|
|
343
292
|
|
|
@@ -363,7 +312,7 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
363
312
|
const detail = err instanceof Error ? err.message : "";
|
|
364
313
|
log.error({ err: detail }, "Internal DB connection check failed");
|
|
365
314
|
|
|
366
|
-
let message =
|
|
315
|
+
let message = `Cannot connect to the internal database at ${maskConnectionUrl(process.env.DATABASE_URL!)}. Check the connection string and ensure the database is running.`;
|
|
367
316
|
if (/ECONNREFUSED/i.test(detail)) {
|
|
368
317
|
message += " The connection was refused — is the database server running?";
|
|
369
318
|
} else if (/timeout/i.test(detail)) {
|
|
@@ -394,6 +343,18 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
394
343
|
errors.push({ code: "INTERNAL_DB_UNREACHABLE", message: migrationErr });
|
|
395
344
|
}
|
|
396
345
|
|
|
346
|
+
// 5.5. Config file validation (atlas.config.ts)
|
|
347
|
+
try {
|
|
348
|
+
const configMod = await import("@atlas/api/lib/config");
|
|
349
|
+
if (typeof configMod.loadConfig === "function" && !configMod.getConfig()) {
|
|
350
|
+
await configMod.loadConfig();
|
|
351
|
+
}
|
|
352
|
+
} catch (err) {
|
|
353
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
354
|
+
log.error({ err: detail }, "Config validation failed");
|
|
355
|
+
errors.push({ code: "INVALID_CONFIG", message: detail });
|
|
356
|
+
}
|
|
357
|
+
|
|
397
358
|
// 6. Auth mode diagnostics
|
|
398
359
|
const authMode = detectAuthMode();
|
|
399
360
|
const authSource = getAuthModeSource();
|
|
@@ -441,7 +402,8 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
441
402
|
errors.push({
|
|
442
403
|
code: "WEAK_AUTH_SECRET",
|
|
443
404
|
message:
|
|
444
|
-
|
|
405
|
+
`BETTER_AUTH_SECRET must be at least 32 characters (currently ${secret.length}). ` +
|
|
406
|
+
"Generate one with: openssl rand -base64 32",
|
|
445
407
|
});
|
|
446
408
|
}
|
|
447
409
|
if (!process.env.BETTER_AUTH_URL) {
|
|
@@ -490,6 +452,14 @@ export async function validateEnvironment(): Promise<DiagnosticError[]> {
|
|
|
490
452
|
"ATLAS_AUTH_ISSUER is required for BYOT auth mode. Set it to your identity provider's issuer URL (e.g. https://your-idp.com/).",
|
|
491
453
|
});
|
|
492
454
|
}
|
|
455
|
+
|
|
456
|
+
if (process.env.ATLAS_AUTH_AUDIENCE === "") {
|
|
457
|
+
const msg =
|
|
458
|
+
"ATLAS_AUTH_AUDIENCE is set to an empty string — audience validation will be skipped. " +
|
|
459
|
+
"Remove the variable entirely if audience checking is not needed, or set it to a valid audience value.";
|
|
460
|
+
if (!_startupWarnings.includes(msg)) _startupWarnings.push(msg);
|
|
461
|
+
log.warn(msg);
|
|
462
|
+
}
|
|
493
463
|
}
|
|
494
464
|
|
|
495
465
|
// Warn about orphaned auth env vars that suggest misconfiguration
|
|
@@ -72,6 +72,8 @@ mock.module("@atlas/api/lib/db/connection", () => ({
|
|
|
72
72
|
getTargetHost: () => "localhost",
|
|
73
73
|
list: () => ["default", "salesforce-plugin"],
|
|
74
74
|
getValidator: (id: string) => validatorMap.get(id),
|
|
75
|
+
getParserDialect: () => undefined,
|
|
76
|
+
getForbiddenPatterns: () => [],
|
|
75
77
|
},
|
|
76
78
|
detectDBType: () => "postgres",
|
|
77
79
|
}));
|
|
@@ -1,10 +1,33 @@
|
|
|
1
|
-
import { describe, expect, it, beforeEach, afterEach, spyOn } from "bun:test";
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach, spyOn, mock } from "bun:test";
|
|
2
2
|
import * as fs from "fs";
|
|
3
3
|
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// Mocks
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
|
|
8
|
+
// Mock structured logger — must be before explore-nsjail import
|
|
9
|
+
let logWarnCalls: unknown[][] = [];
|
|
10
|
+
let logErrorCalls: unknown[][] = [];
|
|
11
|
+
|
|
12
|
+
const mockLogger = {
|
|
13
|
+
info: () => {},
|
|
14
|
+
warn: (...args: unknown[]) => { logWarnCalls.push(args); },
|
|
15
|
+
error: (...args: unknown[]) => { logErrorCalls.push(args); },
|
|
16
|
+
debug: () => {},
|
|
17
|
+
fatal: () => {},
|
|
18
|
+
trace: () => {},
|
|
19
|
+
child: () => mockLogger,
|
|
20
|
+
level: "info",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
mock.module("@atlas/api/lib/logger", () => ({
|
|
24
|
+
createLogger: () => mockLogger,
|
|
25
|
+
getLogger: () => mockLogger,
|
|
26
|
+
withRequestContext: (_ctx: unknown, fn: () => unknown) => fn(),
|
|
27
|
+
getRequestContext: () => undefined,
|
|
28
|
+
redactPaths: [],
|
|
29
|
+
}));
|
|
30
|
+
|
|
8
31
|
// Track Bun.spawn calls
|
|
9
32
|
let spawnCalls: { args: unknown[]; options: unknown }[] = [];
|
|
10
33
|
let spawnResult: {
|
|
@@ -50,7 +73,8 @@ const callbacks = {
|
|
|
50
73
|
},
|
|
51
74
|
};
|
|
52
75
|
|
|
53
|
-
|
|
76
|
+
const { isNsjailAvailable, createNsjailBackend, testNsjailCapabilities } =
|
|
77
|
+
await import("@atlas/api/lib/tools/explore-nsjail");
|
|
54
78
|
|
|
55
79
|
// ---------------------------------------------------------------------------
|
|
56
80
|
// Helpers
|
|
@@ -145,6 +169,8 @@ describe("exec", () => {
|
|
|
145
169
|
spawnCalls = [];
|
|
146
170
|
invalidateCalled = false;
|
|
147
171
|
markFailedCalled = false;
|
|
172
|
+
logWarnCalls = [];
|
|
173
|
+
logErrorCalls = [];
|
|
148
174
|
setSpawnResult("", "", 0);
|
|
149
175
|
});
|
|
150
176
|
|
|
@@ -311,8 +337,6 @@ describe("exec", () => {
|
|
|
311
337
|
new Set(["/usr/local/bin/nsjail", SEMANTIC_ROOT]),
|
|
312
338
|
);
|
|
313
339
|
|
|
314
|
-
const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
315
|
-
|
|
316
340
|
const backend = await createNsjailBackend(SEMANTIC_ROOT, callbacks);
|
|
317
341
|
await backend.exec("ls");
|
|
318
342
|
|
|
@@ -320,7 +344,6 @@ describe("exec", () => {
|
|
|
320
344
|
const tIndex = args.indexOf("-t");
|
|
321
345
|
expect(args[tIndex + 1]).toBe("10"); // default
|
|
322
346
|
|
|
323
|
-
warnSpy.mockRestore();
|
|
324
347
|
spy.mockRestore();
|
|
325
348
|
});
|
|
326
349
|
|
|
@@ -331,8 +354,6 @@ describe("exec", () => {
|
|
|
331
354
|
new Set(["/usr/local/bin/nsjail", SEMANTIC_ROOT]),
|
|
332
355
|
);
|
|
333
356
|
|
|
334
|
-
const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
335
|
-
|
|
336
357
|
const backend = await createNsjailBackend(SEMANTIC_ROOT, callbacks);
|
|
337
358
|
await backend.exec("ls");
|
|
338
359
|
|
|
@@ -340,7 +361,6 @@ describe("exec", () => {
|
|
|
340
361
|
const tIndex = args.indexOf("-t");
|
|
341
362
|
expect(args[tIndex + 1]).toBe("10"); // default
|
|
342
363
|
|
|
343
|
-
warnSpy.mockRestore();
|
|
344
364
|
spy.mockRestore();
|
|
345
365
|
});
|
|
346
366
|
|
|
@@ -351,8 +371,6 @@ describe("exec", () => {
|
|
|
351
371
|
new Set(["/usr/local/bin/nsjail", SEMANTIC_ROOT]),
|
|
352
372
|
);
|
|
353
373
|
|
|
354
|
-
const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
355
|
-
|
|
356
374
|
const backend = await createNsjailBackend(SEMANTIC_ROOT, callbacks);
|
|
357
375
|
await backend.exec("ls");
|
|
358
376
|
|
|
@@ -360,7 +378,6 @@ describe("exec", () => {
|
|
|
360
378
|
const memIndex = args.indexOf("--rlimit_as");
|
|
361
379
|
expect(args[memIndex + 1]).toBe("256"); // default
|
|
362
380
|
|
|
363
|
-
warnSpy.mockRestore();
|
|
364
381
|
spy.mockRestore();
|
|
365
382
|
});
|
|
366
383
|
|
|
@@ -371,7 +388,6 @@ describe("exec", () => {
|
|
|
371
388
|
);
|
|
372
389
|
|
|
373
390
|
setSpawnResult("", "nsjail setup failure", 109);
|
|
374
|
-
const errorSpy = spyOn(console, "error").mockImplementation(() => {});
|
|
375
391
|
|
|
376
392
|
const backend = await createNsjailBackend(SEMANTIC_ROOT, callbacks);
|
|
377
393
|
const result = await backend.exec("ls");
|
|
@@ -379,7 +395,6 @@ describe("exec", () => {
|
|
|
379
395
|
expect(result.exitCode).toBe(109);
|
|
380
396
|
expect(markFailedCalled).toBe(true);
|
|
381
397
|
|
|
382
|
-
errorSpy.mockRestore();
|
|
383
398
|
spy.mockRestore();
|
|
384
399
|
});
|
|
385
400
|
|
|
@@ -391,17 +406,16 @@ describe("exec", () => {
|
|
|
391
406
|
|
|
392
407
|
// Exit code 137 = 128 + 9 (SIGKILL)
|
|
393
408
|
setSpawnResult("", "", 137);
|
|
394
|
-
const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
395
409
|
|
|
396
410
|
const backend = await createNsjailBackend(SEMANTIC_ROOT, callbacks);
|
|
397
411
|
const result = await backend.exec("sleep 999");
|
|
398
412
|
|
|
399
413
|
expect(result.exitCode).toBe(137);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
414
|
+
// log.warn({ signal, command }, "nsjail child killed by signal")
|
|
415
|
+
expect(logWarnCalls.length).toBeGreaterThanOrEqual(1);
|
|
416
|
+
const warnObj = logWarnCalls[0][0] as { signal: number };
|
|
417
|
+
expect(warnObj.signal).toBe(9);
|
|
403
418
|
|
|
404
|
-
warnSpy.mockRestore();
|
|
405
419
|
spy.mockRestore();
|
|
406
420
|
});
|
|
407
421
|
});
|
|
@@ -67,7 +67,7 @@ mock.module("@atlas/api/lib/plugins/hooks", () => ({
|
|
|
67
67
|
|
|
68
68
|
let mockSandboxPlugins: Array<{
|
|
69
69
|
id: string;
|
|
70
|
-
|
|
70
|
+
types: string[];
|
|
71
71
|
version: string;
|
|
72
72
|
sandbox: {
|
|
73
73
|
create(root: string): Promise<ExploreBackend> | ExploreBackend;
|
|
@@ -146,7 +146,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
146
146
|
mockSandboxPlugins = [
|
|
147
147
|
{
|
|
148
148
|
id: "plugin-a",
|
|
149
|
-
|
|
149
|
+
types: ["sandbox"],
|
|
150
150
|
version: "1.0.0",
|
|
151
151
|
sandbox: { create: async () => backend },
|
|
152
152
|
},
|
|
@@ -165,7 +165,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
165
165
|
mockSandboxPlugins = [
|
|
166
166
|
{
|
|
167
167
|
id: "low-priority",
|
|
168
|
-
|
|
168
|
+
types: ["sandbox"],
|
|
169
169
|
version: "1.0.0",
|
|
170
170
|
sandbox: {
|
|
171
171
|
create: async () => makeMockBackend("low"),
|
|
@@ -174,7 +174,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
174
174
|
},
|
|
175
175
|
{
|
|
176
176
|
id: "high-priority",
|
|
177
|
-
|
|
177
|
+
types: ["sandbox"],
|
|
178
178
|
version: "1.0.0",
|
|
179
179
|
sandbox: {
|
|
180
180
|
create: async () => makeMockBackend("high"),
|
|
@@ -196,7 +196,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
196
196
|
mockSandboxPlugins = [
|
|
197
197
|
{
|
|
198
198
|
id: "broken-plugin",
|
|
199
|
-
|
|
199
|
+
types: ["sandbox"],
|
|
200
200
|
version: "1.0.0",
|
|
201
201
|
sandbox: {
|
|
202
202
|
create: async () => { throw new Error("init failed"); },
|
|
@@ -205,7 +205,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
205
205
|
},
|
|
206
206
|
{
|
|
207
207
|
id: "working-plugin",
|
|
208
|
-
|
|
208
|
+
types: ["sandbox"],
|
|
209
209
|
version: "1.0.0",
|
|
210
210
|
sandbox: {
|
|
211
211
|
create: async () => makeMockBackend("working"),
|
|
@@ -228,7 +228,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
228
228
|
mockSandboxPlugins = [
|
|
229
229
|
{
|
|
230
230
|
id: "broken-1",
|
|
231
|
-
|
|
231
|
+
types: ["sandbox"],
|
|
232
232
|
version: "1.0.0",
|
|
233
233
|
sandbox: {
|
|
234
234
|
create: async () => { throw new Error("fail 1"); },
|
|
@@ -236,7 +236,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
236
236
|
},
|
|
237
237
|
{
|
|
238
238
|
id: "broken-2",
|
|
239
|
-
|
|
239
|
+
types: ["sandbox"],
|
|
240
240
|
version: "1.0.0",
|
|
241
241
|
sandbox: {
|
|
242
242
|
create: async () => { throw new Error("fail 2"); },
|
|
@@ -255,7 +255,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
255
255
|
mockSandboxPlugins = [
|
|
256
256
|
{
|
|
257
257
|
id: "my-sandbox",
|
|
258
|
-
|
|
258
|
+
types: ["sandbox"],
|
|
259
259
|
version: "2.0.0",
|
|
260
260
|
sandbox: { create: async () => makeMockBackend("my-sandbox") },
|
|
261
261
|
},
|
|
@@ -274,7 +274,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
274
274
|
mockSandboxPlugins = [
|
|
275
275
|
{
|
|
276
276
|
id: "test-plugin",
|
|
277
|
-
|
|
277
|
+
types: ["sandbox"],
|
|
278
278
|
version: "1.0.0",
|
|
279
279
|
sandbox: { create: async () => makeMockBackend("test") },
|
|
280
280
|
},
|
|
@@ -297,7 +297,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
297
297
|
mockSandboxPlugins = [
|
|
298
298
|
{
|
|
299
299
|
id: "clearable-plugin",
|
|
300
|
-
|
|
300
|
+
types: ["sandbox"],
|
|
301
301
|
version: "1.0.0",
|
|
302
302
|
sandbox: { create: async () => makeMockBackend("clearable") },
|
|
303
303
|
},
|
|
@@ -321,7 +321,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
321
321
|
mockSandboxPlugins = [
|
|
322
322
|
{
|
|
323
323
|
id: "should-be-skipped",
|
|
324
|
-
|
|
324
|
+
types: ["sandbox"],
|
|
325
325
|
version: "1.0.0",
|
|
326
326
|
sandbox: { create: async () => makeMockBackend("skipped") },
|
|
327
327
|
},
|
|
@@ -343,7 +343,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
343
343
|
mockSandboxPlugins = [
|
|
344
344
|
{
|
|
345
345
|
id: "explicit-low",
|
|
346
|
-
|
|
346
|
+
types: ["sandbox"],
|
|
347
347
|
version: "1.0.0",
|
|
348
348
|
sandbox: {
|
|
349
349
|
create: async () => makeMockBackend("explicit-low"),
|
|
@@ -352,7 +352,7 @@ describe("explore sandbox plugin integration", () => {
|
|
|
352
352
|
},
|
|
353
353
|
{
|
|
354
354
|
id: "default-priority",
|
|
355
|
-
|
|
355
|
+
types: ["sandbox"],
|
|
356
356
|
version: "1.0.0",
|
|
357
357
|
sandbox: {
|
|
358
358
|
// No priority — defaults to 60 (SANDBOX_DEFAULT_PRIORITY)
|