@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
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python execution tool for data analysis and visualization.
|
|
3
|
+
*
|
|
4
|
+
* Runs Python code in an isolated sandbox — a sidecar container
|
|
5
|
+
* (ATLAS_SANDBOX_URL), Vercel Sandbox (Firecracker microVM), or nsjail
|
|
6
|
+
* (Linux namespace sandbox). Backend selection mirrors the explore tool's
|
|
7
|
+
* priority chain.
|
|
8
|
+
*
|
|
9
|
+
* Security model:
|
|
10
|
+
* - AST-based import guard runs first as defense-in-depth (catches obvious mistakes)
|
|
11
|
+
* - The sandbox backend is the actual security boundary (no secrets, no network)
|
|
12
|
+
* - Requires either a sidecar, Vercel sandbox, or nsjail — refuses to run without isolation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { tool } from "ai";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { createLogger } from "@atlas/api/lib/logger";
|
|
18
|
+
import { withSpan } from "@atlas/api/lib/tracing";
|
|
19
|
+
|
|
20
|
+
const log = createLogger("python");
|
|
21
|
+
|
|
22
|
+
// --- Import guard (defense-in-depth) ---
|
|
23
|
+
|
|
24
|
+
const BLOCKED_MODULES = new Set([
|
|
25
|
+
"subprocess",
|
|
26
|
+
"os",
|
|
27
|
+
"socket",
|
|
28
|
+
"shutil",
|
|
29
|
+
"sys",
|
|
30
|
+
"ctypes",
|
|
31
|
+
"importlib",
|
|
32
|
+
"code",
|
|
33
|
+
"signal",
|
|
34
|
+
"multiprocessing",
|
|
35
|
+
"threading",
|
|
36
|
+
"pty",
|
|
37
|
+
"fcntl",
|
|
38
|
+
"termios",
|
|
39
|
+
"resource",
|
|
40
|
+
"posixpath",
|
|
41
|
+
// Network modules
|
|
42
|
+
"http",
|
|
43
|
+
"urllib",
|
|
44
|
+
"requests",
|
|
45
|
+
"httpx",
|
|
46
|
+
"aiohttp",
|
|
47
|
+
"webbrowser",
|
|
48
|
+
// Dangerous serialization/filesystem
|
|
49
|
+
"pickle",
|
|
50
|
+
"tempfile",
|
|
51
|
+
"pathlib",
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const BLOCKED_BUILTINS = new Set([
|
|
55
|
+
"compile",
|
|
56
|
+
"exec",
|
|
57
|
+
"eval",
|
|
58
|
+
"__import__",
|
|
59
|
+
"open",
|
|
60
|
+
"breakpoint",
|
|
61
|
+
"getattr",
|
|
62
|
+
"globals",
|
|
63
|
+
"locals",
|
|
64
|
+
"vars",
|
|
65
|
+
"dir",
|
|
66
|
+
"delattr",
|
|
67
|
+
"setattr",
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Validate Python code for blocked imports and dangerous builtins.
|
|
72
|
+
*
|
|
73
|
+
* Uses Python's own `ast` module to parse the code, then checks for:
|
|
74
|
+
* - `import X` / `from X import ...` where X is in BLOCKED_MODULES
|
|
75
|
+
* - Calls to blocked builtins (exec, eval, compile, __import__, open, getattr, etc.)
|
|
76
|
+
*
|
|
77
|
+
* This is defense-in-depth — the sidecar container is the security boundary.
|
|
78
|
+
* Returns { safe: true } or { safe: false, reason: string }.
|
|
79
|
+
*/
|
|
80
|
+
export async function validatePythonCode(
|
|
81
|
+
code: string,
|
|
82
|
+
): Promise<{ safe: true } | { safe: false; reason: string }> {
|
|
83
|
+
// Build a Python script that uses ast to extract imports and dangerous calls
|
|
84
|
+
const checkerScript = `
|
|
85
|
+
import ast, json, sys
|
|
86
|
+
|
|
87
|
+
code = sys.stdin.read()
|
|
88
|
+
try:
|
|
89
|
+
tree = ast.parse(code)
|
|
90
|
+
except SyntaxError as e:
|
|
91
|
+
json.dump({"error": f"SyntaxError: {e.msg} (line {e.lineno})"}, sys.stdout)
|
|
92
|
+
sys.exit(0)
|
|
93
|
+
|
|
94
|
+
imports = []
|
|
95
|
+
calls = []
|
|
96
|
+
|
|
97
|
+
for node in ast.walk(tree):
|
|
98
|
+
if isinstance(node, ast.Import):
|
|
99
|
+
for alias in node.names:
|
|
100
|
+
imports.append(alias.name.split('.')[0])
|
|
101
|
+
elif isinstance(node, ast.ImportFrom):
|
|
102
|
+
if node.module:
|
|
103
|
+
imports.append(node.module.split('.')[0])
|
|
104
|
+
elif isinstance(node, ast.Call):
|
|
105
|
+
if isinstance(node.func, ast.Name):
|
|
106
|
+
calls.append(node.func.id)
|
|
107
|
+
elif isinstance(node.func, ast.Attribute):
|
|
108
|
+
calls.append(node.func.attr)
|
|
109
|
+
|
|
110
|
+
json.dump({"imports": imports, "calls": calls}, sys.stdout)
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
let proc;
|
|
114
|
+
try {
|
|
115
|
+
proc = Bun.spawn(["python3", "-c", checkerScript], {
|
|
116
|
+
stdin: "pipe",
|
|
117
|
+
stdout: "pipe",
|
|
118
|
+
stderr: "pipe",
|
|
119
|
+
});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
122
|
+
log.warn({ err: detail }, "python3 not available for AST validation — guard skipped, sandbox backend will enforce");
|
|
123
|
+
// If python3 isn't available locally, skip the guard.
|
|
124
|
+
// The sidecar is the security boundary, not this check.
|
|
125
|
+
return { safe: true };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
proc.stdin.write(code);
|
|
130
|
+
proc.stdin.end();
|
|
131
|
+
} catch (err) {
|
|
132
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
133
|
+
log.warn({ err: detail }, "Failed to write to python3 stdin");
|
|
134
|
+
return { safe: false, reason: `Code analysis failed: ${detail}` };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const [stdout, stderr] = await Promise.all([
|
|
138
|
+
new Response(proc.stdout).text(),
|
|
139
|
+
new Response(proc.stderr).text(),
|
|
140
|
+
]);
|
|
141
|
+
const exitCode = await proc.exited;
|
|
142
|
+
|
|
143
|
+
if (exitCode !== 0) {
|
|
144
|
+
log.warn({ stderr, exitCode }, "Python AST checker failed");
|
|
145
|
+
return { safe: false, reason: `Code analysis failed: ${stderr.trim() || "unknown error"}` };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let result: { error?: string; imports?: string[]; calls?: string[] };
|
|
149
|
+
try {
|
|
150
|
+
result = JSON.parse(stdout);
|
|
151
|
+
} catch {
|
|
152
|
+
log.warn({ stdout: stdout.slice(0, 500) }, "Python AST checker produced unparseable output");
|
|
153
|
+
return { safe: false, reason: "Code analysis produced invalid output" };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (result.error) {
|
|
157
|
+
return { safe: false, reason: result.error };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check imports
|
|
161
|
+
for (const mod of result.imports ?? []) {
|
|
162
|
+
if (BLOCKED_MODULES.has(mod)) {
|
|
163
|
+
return { safe: false, reason: `Blocked import: "${mod}" is not allowed` };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check dangerous builtins
|
|
168
|
+
for (const call of result.calls ?? []) {
|
|
169
|
+
if (BLOCKED_BUILTINS.has(call)) {
|
|
170
|
+
return { safe: false, reason: `Blocked builtin: "${call}()" is not allowed` };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { safe: true };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// --- Output types ---
|
|
178
|
+
|
|
179
|
+
export interface PythonChart {
|
|
180
|
+
base64: string;
|
|
181
|
+
mimeType: "image/png";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface RechartsChart {
|
|
185
|
+
type: "line" | "bar" | "pie";
|
|
186
|
+
data: Record<string, unknown>[];
|
|
187
|
+
categoryKey: string;
|
|
188
|
+
valueKeys: string[];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export type PythonResult =
|
|
192
|
+
| {
|
|
193
|
+
success: true;
|
|
194
|
+
output?: string;
|
|
195
|
+
table?: { columns: string[]; rows: unknown[][] };
|
|
196
|
+
charts?: PythonChart[];
|
|
197
|
+
rechartsCharts?: RechartsChart[];
|
|
198
|
+
}
|
|
199
|
+
| {
|
|
200
|
+
success: false;
|
|
201
|
+
error: string;
|
|
202
|
+
output?: string;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// --- Backend interface ---
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Python execution backend. Implementations handle isolation (sidecar, Vercel sandbox, nsjail).
|
|
209
|
+
* Each backend receives validated code + optional data and returns a structured result.
|
|
210
|
+
*/
|
|
211
|
+
export interface PythonBackend {
|
|
212
|
+
exec(code: string, data?: { columns: string[]; rows: unknown[][] }): Promise<PythonResult>;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// --- Backend selection ---
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Resolve the Python execution backend.
|
|
219
|
+
*
|
|
220
|
+
* Priority:
|
|
221
|
+
* 1. Sidecar (ATLAS_SANDBOX_URL) — HTTP-isolated container
|
|
222
|
+
* 2. Vercel (ATLAS_RUNTIME=vercel) — Python 3.13 in Firecracker microVM
|
|
223
|
+
* 3. nsjail explicit (ATLAS_SANDBOX=nsjail) — hard-fail if unavailable
|
|
224
|
+
* 4. nsjail auto-detect (on PATH or ATLAS_NSJAIL_PATH) — graceful fallback
|
|
225
|
+
* 5. No backend — error
|
|
226
|
+
*/
|
|
227
|
+
async function getPythonBackend(): Promise<PythonBackend | { error: string }> {
|
|
228
|
+
// 1. Sidecar
|
|
229
|
+
const sidecarUrl = process.env.ATLAS_SANDBOX_URL;
|
|
230
|
+
if (sidecarUrl) {
|
|
231
|
+
const { executePythonViaSidecar } = await import("./python-sidecar");
|
|
232
|
+
return {
|
|
233
|
+
exec: (code, data) => executePythonViaSidecar(sidecarUrl, code, data),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 2. Vercel sandbox (Python 3.13 runtime)
|
|
238
|
+
if (process.env.ATLAS_RUNTIME === "vercel" || process.env.VERCEL) {
|
|
239
|
+
let createPythonSandboxBackend;
|
|
240
|
+
try {
|
|
241
|
+
({ createPythonSandboxBackend } = await import("./python-sandbox"));
|
|
242
|
+
} catch (err) {
|
|
243
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
244
|
+
log.error({ err: detail }, "Vercel Python sandbox module not available");
|
|
245
|
+
return { error: `Vercel Python sandbox unavailable: ${detail}` };
|
|
246
|
+
}
|
|
247
|
+
return createPythonSandboxBackend();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 3. nsjail explicit (ATLAS_SANDBOX=nsjail) — hard-fail
|
|
251
|
+
if (process.env.ATLAS_SANDBOX === "nsjail") {
|
|
252
|
+
try {
|
|
253
|
+
const { findNsjailBinary } = await import("./explore-nsjail");
|
|
254
|
+
const nsjailPath = findNsjailBinary();
|
|
255
|
+
if (nsjailPath) {
|
|
256
|
+
const { createPythonNsjailBackend } = await import("./python-nsjail");
|
|
257
|
+
return createPythonNsjailBackend(nsjailPath);
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
261
|
+
log.error({ err: detail }, "nsjail explicitly requested but Python nsjail backend failed to load");
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
error: "ATLAS_SANDBOX=nsjail but nsjail binary not found. Python execution unavailable.",
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// 4. nsjail auto-detect
|
|
269
|
+
try {
|
|
270
|
+
const { findNsjailBinary } = await import("./explore-nsjail");
|
|
271
|
+
const nsjailPath = findNsjailBinary();
|
|
272
|
+
if (nsjailPath) {
|
|
273
|
+
const { createPythonNsjailBackend } = await import("./python-nsjail");
|
|
274
|
+
return createPythonNsjailBackend(nsjailPath);
|
|
275
|
+
}
|
|
276
|
+
} catch (err) {
|
|
277
|
+
if (
|
|
278
|
+
err != null &&
|
|
279
|
+
typeof err === "object" &&
|
|
280
|
+
"code" in err &&
|
|
281
|
+
(err as NodeJS.ErrnoException).code === "MODULE_NOT_FOUND"
|
|
282
|
+
) {
|
|
283
|
+
log.debug("explore-nsjail module not available, skipping nsjail Python backend");
|
|
284
|
+
} else {
|
|
285
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
286
|
+
log.error({ err: detail }, "Unexpected error initializing nsjail Python backend");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// 5. No backend
|
|
291
|
+
return {
|
|
292
|
+
error: "Python execution requires a sandbox (ATLAS_SANDBOX_URL or nsjail). See deployment docs.",
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// --- Tool definition ---
|
|
297
|
+
|
|
298
|
+
export const executePython = tool({
|
|
299
|
+
description: `Execute Python code for data analysis and visualization.
|
|
300
|
+
|
|
301
|
+
The code runs in an isolated Python sandbox with access to common data science libraries (pandas, numpy, matplotlib, scipy, scikit-learn, statsmodels).
|
|
302
|
+
|
|
303
|
+
When data is provided (from a previous SQL query), it is available as:
|
|
304
|
+
- \`df\`: a pandas DataFrame (if pandas is installed)
|
|
305
|
+
- \`data\`: the raw dict with "columns" and "rows" keys
|
|
306
|
+
|
|
307
|
+
Output options:
|
|
308
|
+
- Table: set \`_atlas_table = {"columns": [...], "rows": [...]}\`
|
|
309
|
+
- Interactive chart (Recharts): set \`_atlas_chart = {"type": "line", "data": [...], "categoryKey": "month", "valueKeys": ["revenue"]}\` (type can be line, bar, pie)
|
|
310
|
+
- PNG chart (matplotlib): save to \`chart_path(0)\`, \`chart_path(1)\`, etc.
|
|
311
|
+
- Text: use \`print()\` for narrative output
|
|
312
|
+
|
|
313
|
+
Blocked: subprocess, os, socket, shutil, sys, ctypes, importlib, exec(), eval(), open(), compile().`,
|
|
314
|
+
|
|
315
|
+
inputSchema: z.object({
|
|
316
|
+
code: z.string().describe("Python code to execute"),
|
|
317
|
+
explanation: z.string().describe("Brief explanation of what this code does and why"),
|
|
318
|
+
data: z
|
|
319
|
+
.object({
|
|
320
|
+
columns: z.array(z.string()),
|
|
321
|
+
rows: z.array(z.array(z.unknown())),
|
|
322
|
+
})
|
|
323
|
+
.optional()
|
|
324
|
+
.describe("Optional data payload from a previous SQL query (columns + rows)"),
|
|
325
|
+
}),
|
|
326
|
+
|
|
327
|
+
execute: async ({ code, explanation, data }) => {
|
|
328
|
+
// 0. Resolve backend
|
|
329
|
+
const backend = await getPythonBackend();
|
|
330
|
+
if ("error" in backend) {
|
|
331
|
+
log.error(backend.error);
|
|
332
|
+
return { success: false, error: backend.error };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 1. Validate imports (defense-in-depth — sandbox is the real boundary)
|
|
336
|
+
const validation = await validatePythonCode(code);
|
|
337
|
+
if (!validation.safe) {
|
|
338
|
+
log.warn({ reason: validation.reason }, "Python code rejected by import guard");
|
|
339
|
+
return { success: false, error: validation.reason };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 2. Execute via selected backend
|
|
343
|
+
const start = performance.now();
|
|
344
|
+
try {
|
|
345
|
+
const result = await withSpan(
|
|
346
|
+
"atlas.python.execute",
|
|
347
|
+
{ "code.length": code.length },
|
|
348
|
+
() => backend.exec(code, data),
|
|
349
|
+
);
|
|
350
|
+
const durationMs = Math.round(performance.now() - start);
|
|
351
|
+
|
|
352
|
+
log.debug(
|
|
353
|
+
{ durationMs, success: result.success, hasCharts: result.success && !!result.charts?.length },
|
|
354
|
+
"python execution",
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
...result,
|
|
359
|
+
explanation,
|
|
360
|
+
};
|
|
361
|
+
} catch (err) {
|
|
362
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
363
|
+
log.error({ err: detail }, "Python execution failed");
|
|
364
|
+
return { success: false, error: detail };
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
});
|
|
@@ -136,14 +136,21 @@ Use the executeSQL tool to query the database:
|
|
|
136
136
|
- Include appropriate filters, groupings, and ordering
|
|
137
137
|
- If a query fails, read the error, fix the SQL, and retry (max 2 retries, never retry the same SQL)`;
|
|
138
138
|
|
|
139
|
-
const
|
|
140
|
-
Use the
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
139
|
+
const EXECUTE_PYTHON_DESCRIPTION = `### 4. Analyze Data with Python
|
|
140
|
+
Use the executePython tool for analysis that SQL alone cannot handle:
|
|
141
|
+
- Statistical analysis (correlations, regressions, hypothesis tests)
|
|
142
|
+
- Data transformations (pivoting, reshaping, time series decomposition)
|
|
143
|
+
- Visualizations and advanced charts
|
|
144
|
+
|
|
145
|
+
**Always run executeSQL first**, then pass results to executePython via the \`data\` parameter.
|
|
146
|
+
|
|
147
|
+
**Output modes:**
|
|
148
|
+
- \`_atlas_table\` — structured table results (columns + rows)
|
|
149
|
+
- \`_atlas_chart\` — interactive Recharts chart (preferred for bar/line/pie)
|
|
150
|
+
- \`chart_path(n)\` — matplotlib PNG (use for heatmaps, scatter matrices, violin plots)
|
|
151
|
+
- \`print()\` — narrative text output
|
|
152
|
+
|
|
153
|
+
Do NOT use executePython for simple aggregations, GROUP BY, or filtering — executeSQL handles those.`;
|
|
147
154
|
|
|
148
155
|
// --- Default registry ---
|
|
149
156
|
|
|
@@ -164,15 +171,12 @@ defaultRegistry.register({
|
|
|
164
171
|
defaultRegistry.freeze();
|
|
165
172
|
|
|
166
173
|
/**
|
|
167
|
-
* Build a dynamic ToolRegistry with optional
|
|
174
|
+
* Build a dynamic ToolRegistry with optional action and Python support.
|
|
168
175
|
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* When `includeActions` is true, the action tools (createJiraTicket,
|
|
172
|
-
* sendEmailReport) are added.
|
|
176
|
+
* Python tool is included when `ATLAS_PYTHON_ENABLED=true`.
|
|
177
|
+
* Action tools are included when `includeActions` is true.
|
|
173
178
|
*/
|
|
174
179
|
export async function buildRegistry(options?: {
|
|
175
|
-
includeSalesforce?: boolean;
|
|
176
180
|
includeActions?: boolean;
|
|
177
181
|
}): Promise<ToolRegistry> {
|
|
178
182
|
const registry = new ToolRegistry();
|
|
@@ -189,14 +193,37 @@ export async function buildRegistry(options?: {
|
|
|
189
193
|
tool: executeSQL,
|
|
190
194
|
});
|
|
191
195
|
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
if (process.env.ATLAS_PYTHON_ENABLED === "true") {
|
|
197
|
+
if (!process.env.ATLAS_SANDBOX_URL) {
|
|
198
|
+
const { createLogger } = await import("@atlas/api/lib/logger");
|
|
199
|
+
const pyLog = createLogger("registry");
|
|
200
|
+
pyLog.error(
|
|
201
|
+
"ATLAS_PYTHON_ENABLED=true but ATLAS_SANDBOX_URL is not set. " +
|
|
202
|
+
"Python execution requires a sandbox sidecar for isolation.",
|
|
203
|
+
);
|
|
204
|
+
throw new Error(
|
|
205
|
+
"ATLAS_PYTHON_ENABLED=true requires ATLAS_SANDBOX_URL to be set. " +
|
|
206
|
+
"The Python tool runs in the sandbox sidecar for security isolation. " +
|
|
207
|
+
"See deployment docs for sidecar setup.",
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const { executePython } = await import("./python");
|
|
213
|
+
registry.register({
|
|
214
|
+
name: "executePython",
|
|
215
|
+
description: EXECUTE_PYTHON_DESCRIPTION,
|
|
216
|
+
tool: executePython,
|
|
217
|
+
});
|
|
218
|
+
} catch (err) {
|
|
219
|
+
const { createLogger } = await import("@atlas/api/lib/logger");
|
|
220
|
+
const pyLog = createLogger("registry");
|
|
221
|
+
pyLog.error(
|
|
222
|
+
{ err: err instanceof Error ? err : new Error(String(err)) },
|
|
223
|
+
"Failed to load Python tool — executePython will be unavailable",
|
|
224
|
+
);
|
|
225
|
+
throw err;
|
|
226
|
+
}
|
|
200
227
|
}
|
|
201
228
|
|
|
202
229
|
if (options?.includeActions) {
|