@useatlas/create 0.0.1 → 0.0.3
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 +11 -11
- package/templates/docker/bin/atlas.ts +120 -56
- 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 +4 -41
- 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 +38 -40
- 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 +104 -1
- package/templates/docker/src/lib/plugins/registry.ts +14 -8
- 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-sdk-compat.test.ts +1 -1
- 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 +11 -11
- package/templates/nextjs-standalone/bin/atlas.ts +120 -56
- 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 +4 -41
- 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 +38 -40
- 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 +104 -1
- package/templates/nextjs-standalone/src/lib/plugins/registry.ts +14 -8
- 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-sdk-compat.test.ts +1 -1
- 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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach, mock } from "bun:test";
|
|
2
2
|
import { PluginRegistry } from "../registry";
|
|
3
3
|
import type { PluginLike, PluginContextLike } from "../registry";
|
|
4
|
-
import { wireDatasourcePlugins, wireActionPlugins, wireInteractionPlugins, wireContextPlugins } from "../wiring";
|
|
4
|
+
import { wireDatasourcePlugins, wireActionPlugins, wireInteractionPlugins, wireContextPlugins, wireSandboxPlugins } from "../wiring";
|
|
5
|
+
import type { SandboxExecBackend } from "../wiring";
|
|
5
6
|
|
|
6
7
|
const minimalCtx: PluginContextLike = {
|
|
7
8
|
db: null,
|
|
@@ -15,9 +16,9 @@ const minimalCtx: PluginContextLike = {
|
|
|
15
16
|
|
|
16
17
|
function makeMockConnectionRegistry() {
|
|
17
18
|
return {
|
|
18
|
-
registered: [] as { id: string; conn: unknown; dbType: string; description?: string; validate?: unknown }[],
|
|
19
|
-
|
|
20
|
-
this.registered.push({ id, conn, dbType, description, validate });
|
|
19
|
+
registered: [] as { id: string; conn: unknown; dbType: string; description?: string; validate?: unknown; meta?: unknown }[],
|
|
20
|
+
registerDirect(id: string, conn: unknown, dbType: string, description?: string, validate?: unknown, meta?: unknown) {
|
|
21
|
+
this.registered.push({ id, conn, dbType, description, validate, meta });
|
|
21
22
|
},
|
|
22
23
|
};
|
|
23
24
|
}
|
|
@@ -49,7 +50,7 @@ function makeDatasourcePlugin(
|
|
|
49
50
|
};
|
|
50
51
|
return {
|
|
51
52
|
id,
|
|
52
|
-
|
|
53
|
+
types: ["datasource"],
|
|
53
54
|
version: "1.0.0",
|
|
54
55
|
connection: {
|
|
55
56
|
create: () => conn,
|
|
@@ -66,7 +67,7 @@ function makeDatasourcePlugin(
|
|
|
66
67
|
function makeActionPlugin(id: string, opts?: { unhealthy?: boolean }): PluginLike {
|
|
67
68
|
return {
|
|
68
69
|
id,
|
|
69
|
-
|
|
70
|
+
types: ["action"],
|
|
70
71
|
version: "1.0.0",
|
|
71
72
|
actions: [
|
|
72
73
|
{
|
|
@@ -88,7 +89,7 @@ function makeActionPlugin(id: string, opts?: { unhealthy?: boolean }): PluginLik
|
|
|
88
89
|
function makeInteractionPlugin(id: string, routesFn: (app: unknown) => void, opts?: { unhealthy?: boolean }): PluginLike {
|
|
89
90
|
return {
|
|
90
91
|
id,
|
|
91
|
-
|
|
92
|
+
types: ["interaction"],
|
|
92
93
|
version: "1.0.0",
|
|
93
94
|
routes: routesFn,
|
|
94
95
|
...(opts?.unhealthy
|
|
@@ -127,7 +128,7 @@ describe("wireDatasourcePlugins", () => {
|
|
|
127
128
|
const validator = (q: string) => ({ valid: /^SELECT/i.test(q) });
|
|
128
129
|
const plugin: PluginLike = {
|
|
129
130
|
id: "validated-ds",
|
|
130
|
-
|
|
131
|
+
types: ["datasource"],
|
|
131
132
|
version: "1.0.0",
|
|
132
133
|
connection: {
|
|
133
134
|
create: () => ({ query: async () => ({ columns: [], rows: [] }), close: async () => {} }),
|
|
@@ -147,6 +148,47 @@ describe("wireDatasourcePlugins", () => {
|
|
|
147
148
|
expect(connRegistry.registered[0].validate).toBe(validator);
|
|
148
149
|
});
|
|
149
150
|
|
|
151
|
+
test("passes parserDialect and forbiddenPatterns through meta", async () => {
|
|
152
|
+
const patterns = [/^\s*(KILL)\b/i];
|
|
153
|
+
const plugin: PluginLike = {
|
|
154
|
+
id: "meta-ds",
|
|
155
|
+
types: ["datasource"],
|
|
156
|
+
version: "1.0.0",
|
|
157
|
+
connection: {
|
|
158
|
+
create: () => ({ query: async () => ({ columns: [], rows: [] }), close: async () => {} }),
|
|
159
|
+
dbType: "clickhouse",
|
|
160
|
+
parserDialect: "PostgresQL",
|
|
161
|
+
forbiddenPatterns: patterns,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
registry.register(plugin);
|
|
165
|
+
await registry.initializeAll(minimalCtx);
|
|
166
|
+
|
|
167
|
+
await wireDatasourcePlugins(
|
|
168
|
+
registry,
|
|
169
|
+
connRegistry as unknown as import("@atlas/api/lib/db/connection").ConnectionRegistry,
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
expect(connRegistry.registered).toHaveLength(1);
|
|
173
|
+
expect(connRegistry.registered[0].meta).toEqual({
|
|
174
|
+
parserDialect: "PostgresQL",
|
|
175
|
+
forbiddenPatterns: patterns,
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("passes undefined meta when no parserDialect or forbiddenPatterns", async () => {
|
|
180
|
+
registry.register(makeDatasourcePlugin("plain-meta"));
|
|
181
|
+
await registry.initializeAll(minimalCtx);
|
|
182
|
+
|
|
183
|
+
await wireDatasourcePlugins(
|
|
184
|
+
registry,
|
|
185
|
+
connRegistry as unknown as import("@atlas/api/lib/db/connection").ConnectionRegistry,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(connRegistry.registered).toHaveLength(1);
|
|
189
|
+
expect(connRegistry.registered[0].meta).toBeUndefined();
|
|
190
|
+
});
|
|
191
|
+
|
|
150
192
|
test("passes undefined validate when not provided", async () => {
|
|
151
193
|
registry.register(makeDatasourcePlugin("plain-ds"));
|
|
152
194
|
await registry.initializeAll(minimalCtx);
|
|
@@ -179,7 +221,7 @@ describe("wireDatasourcePlugins", () => {
|
|
|
179
221
|
test("continues when one create() throws and returns failures", async () => {
|
|
180
222
|
const failingPlugin: PluginLike = {
|
|
181
223
|
id: "failing-ds",
|
|
182
|
-
|
|
224
|
+
types: ["datasource"],
|
|
183
225
|
version: "1.0.0",
|
|
184
226
|
connection: {
|
|
185
227
|
create: () => { throw new Error("conn failed"); },
|
|
@@ -389,7 +431,7 @@ describe("wireActionPlugins", () => {
|
|
|
389
431
|
test("registers all actions from a multi-action plugin", async () => {
|
|
390
432
|
const plugin: PluginLike = {
|
|
391
433
|
id: "multi-action",
|
|
392
|
-
|
|
434
|
+
types: ["action"],
|
|
393
435
|
version: "1.0.0",
|
|
394
436
|
actions: [
|
|
395
437
|
{ name: "action-a", description: "A", tool: {}, actionType: "t", reversible: false, defaultApproval: "manual", requiredCredentials: [] },
|
|
@@ -421,7 +463,7 @@ describe("wireActionPlugins", () => {
|
|
|
421
463
|
|
|
422
464
|
const plugin: PluginLike = {
|
|
423
465
|
id: "fail-action",
|
|
424
|
-
|
|
466
|
+
types: ["action"],
|
|
425
467
|
version: "1.0.0",
|
|
426
468
|
actions: [
|
|
427
469
|
{ name: "fail-action", description: "Fails", tool: {}, actionType: "t", reversible: false, defaultApproval: "manual", requiredCredentials: [] },
|
|
@@ -485,7 +527,7 @@ describe("wireInteractionPlugins", () => {
|
|
|
485
527
|
const fakeApp = { route: mock(() => {}) };
|
|
486
528
|
const routelessPlugin: PluginLike = {
|
|
487
529
|
id: "stdio-interaction",
|
|
488
|
-
|
|
530
|
+
types: ["interaction"],
|
|
489
531
|
version: "1.0.0",
|
|
490
532
|
// No routes property — like MCP stdio transport
|
|
491
533
|
};
|
|
@@ -506,7 +548,7 @@ describe("wireInteractionPlugins", () => {
|
|
|
506
548
|
function makeContextPlugin(id: string, loadFn: () => Promise<string>, opts?: { unhealthy?: boolean }): PluginLike {
|
|
507
549
|
return {
|
|
508
550
|
id,
|
|
509
|
-
|
|
551
|
+
types: ["context"],
|
|
510
552
|
version: "1.0.0",
|
|
511
553
|
contextProvider: { load: loadFn },
|
|
512
554
|
...(opts?.unhealthy
|
|
@@ -571,7 +613,7 @@ describe("wireContextPlugins", () => {
|
|
|
571
613
|
test("skips context plugins without contextProvider", async () => {
|
|
572
614
|
const noProvider: PluginLike = {
|
|
573
615
|
id: "no-provider",
|
|
574
|
-
|
|
616
|
+
types: ["context"],
|
|
575
617
|
version: "1.0.0",
|
|
576
618
|
};
|
|
577
619
|
registry.register(noProvider);
|
|
@@ -583,3 +625,175 @@ describe("wireContextPlugins", () => {
|
|
|
583
625
|
expect(result.failed).toEqual([]);
|
|
584
626
|
});
|
|
585
627
|
});
|
|
628
|
+
|
|
629
|
+
// --- wireSandboxPlugins ---
|
|
630
|
+
|
|
631
|
+
function makeMockBackend(tag: string): SandboxExecBackend {
|
|
632
|
+
return {
|
|
633
|
+
exec: async (command: string) => ({
|
|
634
|
+
stdout: `[${tag}] ${command}`,
|
|
635
|
+
stderr: "",
|
|
636
|
+
exitCode: 0,
|
|
637
|
+
}),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function makeSandboxPlugin(
|
|
642
|
+
id: string,
|
|
643
|
+
opts?: { priority?: number; createFn?: (root: string) => Promise<SandboxExecBackend> | SandboxExecBackend; unhealthy?: boolean },
|
|
644
|
+
): PluginLike {
|
|
645
|
+
return {
|
|
646
|
+
id,
|
|
647
|
+
types: ["sandbox"],
|
|
648
|
+
version: "1.0.0",
|
|
649
|
+
sandbox: {
|
|
650
|
+
create: opts?.createFn ?? (async () => makeMockBackend(id)),
|
|
651
|
+
...(opts?.priority !== undefined ? { priority: opts.priority } : {}),
|
|
652
|
+
},
|
|
653
|
+
...(opts?.unhealthy
|
|
654
|
+
? { initialize: async () => { throw new Error("fail"); } }
|
|
655
|
+
: {}),
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
describe("wireSandboxPlugins", () => {
|
|
660
|
+
let registry: PluginRegistry;
|
|
661
|
+
|
|
662
|
+
beforeEach(() => {
|
|
663
|
+
registry = new PluginRegistry();
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
test("returns backend from a single sandbox plugin", async () => {
|
|
667
|
+
registry.register(makeSandboxPlugin("my-sandbox"));
|
|
668
|
+
await registry.initializeAll(minimalCtx);
|
|
669
|
+
|
|
670
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
671
|
+
|
|
672
|
+
expect(result.backend).not.toBeNull();
|
|
673
|
+
expect(result.pluginId).toBe("my-sandbox");
|
|
674
|
+
expect(result.failed).toEqual([]);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
test("selects highest-priority plugin", async () => {
|
|
678
|
+
registry.register(makeSandboxPlugin("low", { priority: 40 }));
|
|
679
|
+
registry.register(makeSandboxPlugin("high", { priority: 90 }));
|
|
680
|
+
await registry.initializeAll(minimalCtx);
|
|
681
|
+
|
|
682
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
683
|
+
|
|
684
|
+
expect(result.pluginId).toBe("high");
|
|
685
|
+
const output = await result.backend!.exec("ls");
|
|
686
|
+
expect(output.stdout).toContain("[high]");
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
test("uses default priority (60) when omitted", async () => {
|
|
690
|
+
registry.register(makeSandboxPlugin("explicit-low", { priority: 50 }));
|
|
691
|
+
registry.register(makeSandboxPlugin("default-priority")); // no priority → 60
|
|
692
|
+
await registry.initializeAll(minimalCtx);
|
|
693
|
+
|
|
694
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
695
|
+
|
|
696
|
+
expect(result.pluginId).toBe("default-priority");
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
test("falls through to next plugin when create() throws", async () => {
|
|
700
|
+
registry.register(makeSandboxPlugin("broken", {
|
|
701
|
+
priority: 90,
|
|
702
|
+
createFn: async () => { throw new Error("init failed"); },
|
|
703
|
+
}));
|
|
704
|
+
registry.register(makeSandboxPlugin("working", { priority: 50 }));
|
|
705
|
+
await registry.initializeAll(minimalCtx);
|
|
706
|
+
|
|
707
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
708
|
+
|
|
709
|
+
expect(result.pluginId).toBe("working");
|
|
710
|
+
expect(result.failed).toHaveLength(1);
|
|
711
|
+
expect(result.failed[0].pluginId).toBe("broken");
|
|
712
|
+
expect(result.failed[0].error).toBe("init failed");
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
test("returns null when all plugins fail", async () => {
|
|
716
|
+
registry.register(makeSandboxPlugin("broken-1", {
|
|
717
|
+
createFn: async () => { throw new Error("fail 1"); },
|
|
718
|
+
}));
|
|
719
|
+
registry.register(makeSandboxPlugin("broken-2", {
|
|
720
|
+
createFn: async () => { throw new Error("fail 2"); },
|
|
721
|
+
}));
|
|
722
|
+
await registry.initializeAll(minimalCtx);
|
|
723
|
+
|
|
724
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
725
|
+
|
|
726
|
+
expect(result.backend).toBeNull();
|
|
727
|
+
expect(result.pluginId).toBeNull();
|
|
728
|
+
expect(result.failed).toHaveLength(2);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
test("returns null when no sandbox plugins registered", async () => {
|
|
732
|
+
await registry.initializeAll(minimalCtx);
|
|
733
|
+
|
|
734
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
735
|
+
|
|
736
|
+
expect(result.backend).toBeNull();
|
|
737
|
+
expect(result.pluginId).toBeNull();
|
|
738
|
+
expect(result.failed).toEqual([]);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
test("skips unhealthy sandbox plugins", async () => {
|
|
742
|
+
registry.register(makeSandboxPlugin("healthy"));
|
|
743
|
+
registry.register(makeSandboxPlugin("unhealthy", { unhealthy: true }));
|
|
744
|
+
await registry.initializeAll(minimalCtx);
|
|
745
|
+
|
|
746
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
747
|
+
|
|
748
|
+
expect(result.pluginId).toBe("healthy");
|
|
749
|
+
expect(result.failed).toEqual([]);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
test("skips sandbox plugins missing sandbox.create()", async () => {
|
|
753
|
+
const noCreate: PluginLike = {
|
|
754
|
+
id: "no-create",
|
|
755
|
+
types: ["sandbox"],
|
|
756
|
+
version: "1.0.0",
|
|
757
|
+
// No sandbox property
|
|
758
|
+
};
|
|
759
|
+
registry.register(noCreate);
|
|
760
|
+
await registry.initializeAll(minimalCtx);
|
|
761
|
+
|
|
762
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
763
|
+
|
|
764
|
+
expect(result.backend).toBeNull();
|
|
765
|
+
expect(result.pluginId).toBeNull();
|
|
766
|
+
expect(result.failed).toEqual([]);
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
test("passes semanticRoot to sandbox.create()", async () => {
|
|
770
|
+
let receivedRoot: string | undefined;
|
|
771
|
+
registry.register(makeSandboxPlugin("root-check", {
|
|
772
|
+
createFn: async (root: string) => {
|
|
773
|
+
receivedRoot = root;
|
|
774
|
+
return makeMockBackend("root-check");
|
|
775
|
+
},
|
|
776
|
+
}));
|
|
777
|
+
await registry.initializeAll(minimalCtx);
|
|
778
|
+
|
|
779
|
+
await wireSandboxPlugins(registry, "/my/custom/semantic");
|
|
780
|
+
|
|
781
|
+
expect(receivedRoot).toBe("/my/custom/semantic");
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
test("rejects backend missing exec method from create()", async () => {
|
|
785
|
+
registry.register(makeSandboxPlugin("bad-backend", {
|
|
786
|
+
createFn: async () => ({} as SandboxExecBackend),
|
|
787
|
+
}));
|
|
788
|
+
registry.register(makeSandboxPlugin("good-backend", { priority: 40 }));
|
|
789
|
+
await registry.initializeAll(minimalCtx);
|
|
790
|
+
|
|
791
|
+
const result = await wireSandboxPlugins(registry, "/semantic");
|
|
792
|
+
|
|
793
|
+
// bad-backend has default priority (60) > good-backend (40), tried first but rejected
|
|
794
|
+
expect(result.pluginId).toBe("good-backend");
|
|
795
|
+
expect(result.failed).toHaveLength(1);
|
|
796
|
+
expect(result.failed[0].pluginId).toBe("bad-backend");
|
|
797
|
+
expect(result.failed[0].error).toContain("missing exec method");
|
|
798
|
+
});
|
|
799
|
+
});
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
export { plugins, PluginRegistry } from "./registry";
|
|
6
6
|
export type { PluginLike, PluginContextLike, PluginHealthResult, PluginType, PluginStatus, PluginDescription } from "./registry";
|
|
7
|
-
export { wireDatasourcePlugins, wireActionPlugins, wireInteractionPlugins, wireContextPlugins } from "./wiring";
|
|
7
|
+
export { wireDatasourcePlugins, wireActionPlugins, wireInteractionPlugins, wireContextPlugins, wireSandboxPlugins } from "./wiring";
|
|
8
|
+
export type { SandboxExecBackend } from "./wiring";
|
|
9
|
+
export { generateMigrationSQL, generateColumnMigrations, applyMigrations, runPluginMigrations, ensureMigrationsTable, getAppliedMigrations, diffSchema, prefixTableName } from "./migrate";
|
|
10
|
+
export type { MigrateDB, MigrationStatement, SchemaDiff } from "./migrate";
|
|
8
11
|
export { dispatchHook } from "./hooks";
|
|
9
12
|
export { getPluginTools, setPluginTools, getContextFragments, setContextFragments } from "./tools";
|
|
@@ -14,7 +14,7 @@ import { createLogger } from "@atlas/api/lib/logger";
|
|
|
14
14
|
const log = createLogger("plugins:migrate");
|
|
15
15
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
|
-
// Types (structural — no import from @
|
|
17
|
+
// Types (structural — no import from @useatlas/plugin-sdk)
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
|
|
20
20
|
interface FieldDef {
|
|
@@ -273,6 +273,109 @@ export async function applyMigrations(
|
|
|
273
273
|
return { applied, skipped };
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// Column migrations (ALTER TABLE ADD COLUMN)
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Query the actual columns for a given table from information_schema.
|
|
282
|
+
* Returns a Set of lowercase column names.
|
|
283
|
+
*/
|
|
284
|
+
async function getExistingColumns(db: MigrateDB, tableName: string): Promise<Set<string>> {
|
|
285
|
+
const result = await db.query(
|
|
286
|
+
"SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = $1",
|
|
287
|
+
[tableName],
|
|
288
|
+
);
|
|
289
|
+
return new Set(result.rows.map((r) => String(r.column_name).toLowerCase()));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Generate ALTER TABLE ADD COLUMN IF NOT EXISTS statements for fields
|
|
294
|
+
* that exist in the plugin schema but not in the actual database table.
|
|
295
|
+
*
|
|
296
|
+
* Column detection queries `information_schema.columns` in the `public`
|
|
297
|
+
* schema. Only handles column additions — column removal and type
|
|
298
|
+
* changes are not supported (require manual migration).
|
|
299
|
+
*/
|
|
300
|
+
export async function generateColumnMigrations(
|
|
301
|
+
db: MigrateDB,
|
|
302
|
+
plugins: PluginWithSchema[],
|
|
303
|
+
): Promise<MigrationStatement[]> {
|
|
304
|
+
const statements: MigrationStatement[] = [];
|
|
305
|
+
|
|
306
|
+
for (const plugin of plugins) {
|
|
307
|
+
if (!plugin.schema) continue;
|
|
308
|
+
|
|
309
|
+
for (const [tableName, tableDef] of Object.entries(plugin.schema)) {
|
|
310
|
+
const prefixed = prefixTableName(plugin.id, tableName);
|
|
311
|
+
|
|
312
|
+
// Check if table exists at all — skip if it doesn't (CREATE TABLE will handle it)
|
|
313
|
+
const existing = await getExistingColumns(db, prefixed);
|
|
314
|
+
if (existing.size === 0) {
|
|
315
|
+
log.debug({ table: prefixed, pluginId: plugin.id }, "Table not in information_schema — skipping column migration");
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
for (const [fieldName, fieldDef] of Object.entries(tableDef.fields)) {
|
|
320
|
+
if (existing.has(fieldName.toLowerCase())) continue;
|
|
321
|
+
|
|
322
|
+
let colDef: string;
|
|
323
|
+
try {
|
|
324
|
+
colDef = fieldToSQL(fieldName, fieldDef);
|
|
325
|
+
} catch (err) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Plugin "${plugin.id}", table "${prefixed}": ${err instanceof Error ? err.message : String(err)}`,
|
|
328
|
+
{ cause: err },
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
const sql = `ALTER TABLE "${prefixed}" ADD COLUMN IF NOT EXISTS ${colDef};`;
|
|
332
|
+
|
|
333
|
+
statements.push({
|
|
334
|
+
pluginId: plugin.id,
|
|
335
|
+
tableName,
|
|
336
|
+
prefixedName: prefixed,
|
|
337
|
+
sql,
|
|
338
|
+
hash: hashSQL(sql),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return statements;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ---------------------------------------------------------------------------
|
|
348
|
+
// High-level orchestrator
|
|
349
|
+
// ---------------------------------------------------------------------------
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Run all plugin schema migrations: CREATE TABLE for new tables, then
|
|
353
|
+
* ALTER TABLE ADD COLUMN for new fields on existing tables.
|
|
354
|
+
*
|
|
355
|
+
* Idempotent — safe to call on every startup.
|
|
356
|
+
*/
|
|
357
|
+
export async function runPluginMigrations(
|
|
358
|
+
db: MigrateDB,
|
|
359
|
+
plugins: PluginWithSchema[],
|
|
360
|
+
): Promise<{ applied: string[]; skipped: string[] }> {
|
|
361
|
+
// Phase 1: CREATE TABLE
|
|
362
|
+
const createStatements = generateMigrationSQL(plugins);
|
|
363
|
+
const createResult = await applyMigrations(db, createStatements);
|
|
364
|
+
|
|
365
|
+
// Phase 2: ALTER TABLE ADD COLUMN (for tables that already existed or were just created)
|
|
366
|
+
const columnStatements = await generateColumnMigrations(db, plugins);
|
|
367
|
+
if (columnStatements.length === 0) {
|
|
368
|
+
return createResult;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const columnResult = await applyMigrations(db, columnStatements);
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
applied: [...createResult.applied, ...columnResult.applied],
|
|
375
|
+
skipped: [...createResult.skipped, ...columnResult.skipped],
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
276
379
|
// ---------------------------------------------------------------------------
|
|
277
380
|
// Diff: compare declared schema vs actual tables
|
|
278
381
|
// ---------------------------------------------------------------------------
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Manages plugin lifecycle: registration → initialization → health checks → teardown.
|
|
5
5
|
* Uses a structural `PluginLike` interface defined locally so `@atlas/api` does not
|
|
6
|
-
* depend on `@
|
|
6
|
+
* depend on `@useatlas/plugin-sdk`.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { createLogger } from "@atlas/api/lib/logger";
|
|
@@ -11,7 +11,7 @@ import { createLogger } from "@atlas/api/lib/logger";
|
|
|
11
11
|
const log = createLogger("plugins");
|
|
12
12
|
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
|
-
// Structural interfaces (no import from @
|
|
14
|
+
// Structural interfaces (no import from @useatlas/plugin-sdk)
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
|
|
17
17
|
export interface PluginHealthResult {
|
|
@@ -41,7 +41,8 @@ export interface PluginContextLike {
|
|
|
41
41
|
*/
|
|
42
42
|
export interface PluginLike {
|
|
43
43
|
readonly id: string;
|
|
44
|
-
|
|
44
|
+
/** Plugin type(s). A plugin can implement multiple types. */
|
|
45
|
+
readonly types: readonly PluginType[];
|
|
45
46
|
readonly version: string;
|
|
46
47
|
readonly name?: string;
|
|
47
48
|
initialize?(ctx: PluginContextLike): Promise<void>;
|
|
@@ -59,7 +60,7 @@ interface PluginEntry {
|
|
|
59
60
|
|
|
60
61
|
export interface PluginDescription {
|
|
61
62
|
id: string;
|
|
62
|
-
|
|
63
|
+
types: readonly PluginType[];
|
|
63
64
|
version: string;
|
|
64
65
|
name: string;
|
|
65
66
|
status: PluginStatus;
|
|
@@ -83,7 +84,7 @@ export class PluginRegistry {
|
|
|
83
84
|
}
|
|
84
85
|
this.idSet.add(plugin.id);
|
|
85
86
|
this.entries.push({ plugin, status: "registered" });
|
|
86
|
-
log.info({ pluginId: plugin.id,
|
|
87
|
+
log.info({ pluginId: plugin.id, types: plugin.types }, "Plugin registered");
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
/**
|
|
@@ -190,13 +191,18 @@ export class PluginRegistry {
|
|
|
190
191
|
return this.entries.find((e) => e.plugin.id === id)?.status;
|
|
191
192
|
}
|
|
192
193
|
|
|
193
|
-
/** Return plugins
|
|
194
|
+
/** Return plugins whose types array includes the given type and are currently healthy. */
|
|
194
195
|
getByType(type: PluginType): PluginLike[] {
|
|
195
196
|
return this.entries
|
|
196
|
-
.filter((e) => e.plugin.type
|
|
197
|
+
.filter((e) => e.plugin.types.includes(type) && e.status === "healthy")
|
|
197
198
|
.map((e) => e.plugin);
|
|
198
199
|
}
|
|
199
200
|
|
|
201
|
+
/** Return all registered plugins regardless of status (for schema migrations at boot). */
|
|
202
|
+
getAll(): PluginLike[] {
|
|
203
|
+
return this.entries.map((e) => e.plugin);
|
|
204
|
+
}
|
|
205
|
+
|
|
200
206
|
/** Return all healthy plugins regardless of type (for cross-cutting hooks). */
|
|
201
207
|
getAllHealthy(): PluginLike[] {
|
|
202
208
|
return this.entries
|
|
@@ -208,7 +214,7 @@ export class PluginRegistry {
|
|
|
208
214
|
describe(): PluginDescription[] {
|
|
209
215
|
return this.entries.map((e) => ({
|
|
210
216
|
id: e.plugin.id,
|
|
211
|
-
|
|
217
|
+
types: e.plugin.types,
|
|
212
218
|
version: e.plugin.version,
|
|
213
219
|
name: e.plugin.name ?? e.plugin.id,
|
|
214
220
|
status: e.status,
|