spine-framework 0.1.0
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/.framework/README.md +129 -0
- package/.framework/cli/bin.cjs +14 -0
- package/.framework/cli/commands/agents.ts +153 -0
- package/.framework/cli/commands/auth.ts +94 -0
- package/.framework/cli/commands/create-app.ts +185 -0
- package/.framework/cli/commands/dev.ts +295 -0
- package/.framework/cli/commands/doctor.ts +442 -0
- package/.framework/cli/commands/generate.ts +332 -0
- package/.framework/cli/commands/init.ts +272 -0
- package/.framework/cli/commands/install-app.ts +391 -0
- package/.framework/cli/commands/items.ts +253 -0
- package/.framework/cli/commands/migrations.ts +141 -0
- package/.framework/cli/commands/pipelines.ts +166 -0
- package/.framework/cli/commands/status.ts +197 -0
- package/.framework/cli/commands/system.ts +184 -0
- package/.framework/cli/commands/test.ts +227 -0
- package/.framework/cli/commands/uninstall-app.ts +166 -0
- package/.framework/cli/context.ts +268 -0
- package/.framework/cli/env-loader.ts +36 -0
- package/.framework/cli/index.ts +106 -0
- package/.framework/cli/welcome.cjs +45 -0
- package/.framework/docs/API.md +384 -0
- package/.framework/docs/STABILITY.md +52 -0
- package/.framework/docs/admin-routes.md +76 -0
- package/.framework/docs/api-docs-progress.md +38 -0
- package/.framework/docs/api-governance.md +146 -0
- package/.framework/docs/api-testing-results.md +212 -0
- package/.framework/docs/apis/admin-configs.md +567 -0
- package/.framework/docs/apis/admin-data.md +272 -0
- package/.framework/docs/apis/index.md +231 -0
- package/.framework/docs/apis/internal.md +295 -0
- package/.framework/docs/apis/runtime.md +537 -0
- package/.framework/docs/assembly-launch-guide.md +138 -0
- package/.framework/docs/audit-results.md +590 -0
- package/.framework/docs/authorization-model.md +170 -0
- package/.framework/docs/db-api-inventory.md +95 -0
- package/.framework/docs/examples/custom-app/README.md +77 -0
- package/.framework/docs/examples/custom-function/README.md +27 -0
- package/.framework/docs/examples/custom-function/handler.ts +48 -0
- package/.framework/docs/examples/custom-webhook/README.md +68 -0
- package/.framework/docs/gap-remediation-backlog.md +103 -0
- package/.framework/docs/guides/cli-guide.md +224 -0
- package/.framework/docs/guides/getting-started.md +103 -0
- package/.framework/docs/guides/import-guide.md +193 -0
- package/.framework/docs/guides/testing-guide.md +229 -0
- package/.framework/docs/permission-examples.md +326 -0
- package/.framework/docs/ui-adoption-verification.md +111 -0
- package/.framework/docs/ui-api-coverage.md +84 -0
- package/.framework/docs/v2-compatibility-audit.md +228 -0
- package/.framework/functions/.gitkeep +1 -0
- package/.framework/functions/_shared/agent-runner.ts +1097 -0
- package/.framework/functions/_shared/app-manifest.ts +184 -0
- package/.framework/functions/_shared/audit.ts +150 -0
- package/.framework/functions/_shared/db.ts +174 -0
- package/.framework/functions/_shared/index.ts +382 -0
- package/.framework/functions/_shared/middleware.ts +490 -0
- package/.framework/functions/_shared/permissions.ts +1325 -0
- package/.framework/functions/_shared/pipeline-runner.ts +731 -0
- package/.framework/functions/_shared/principal.ts +760 -0
- package/.framework/functions/_shared/schema-utils.ts +967 -0
- package/.framework/functions/_shared/testing.ts +258 -0
- package/.framework/functions/_shared/trigger-engine.ts +425 -0
- package/.framework/functions/_shared/webhook-registration.ts +168 -0
- package/.framework/functions/_shared/webhook-registry.ts +129 -0
- package/.framework/functions/account-nodes.ts +111 -0
- package/.framework/functions/admin-data.ts +606 -0
- package/.framework/functions/ai-agents.ts +323 -0
- package/.framework/functions/api-keys.ts +376 -0
- package/.framework/functions/apps.ts +483 -0
- package/.framework/functions/auth.ts +196 -0
- package/.framework/functions/debug-auth.ts +107 -0
- package/.framework/functions/embeddings.ts +556 -0
- package/.framework/functions/integration-routes.ts +523 -0
- package/.framework/functions/integrations.ts +319 -0
- package/.framework/functions/item-progress.ts +272 -0
- package/.framework/functions/logs.ts +438 -0
- package/.framework/functions/observability.ts +275 -0
- package/.framework/functions/pipeline-executions.ts +494 -0
- package/.framework/functions/pipelines.ts +485 -0
- package/.framework/functions/prompt-configs.ts +339 -0
- package/.framework/functions/roles.ts +387 -0
- package/.framework/functions/system-cron.ts +742 -0
- package/.framework/functions/system.ts +323 -0
- package/.framework/functions/tests.ts +119 -0
- package/.framework/functions/timers.ts +357 -0
- package/.framework/functions/triggers.ts +563 -0
- package/.framework/functions/types.ts +604 -0
- package/.framework/migrations/000_foundation.sql +1256 -0
- package/.framework/migrations/001_seed.sql +92 -0
- package/.framework/migrations/002_seed_constraints.sql +13 -0
- package/.framework/migrations/003_auth_user_trigger.sql +59 -0
- package/.framework/src/App.tsx +126 -0
- package/.framework/src/apps/admin/index.tsx +173 -0
- package/.framework/src/components/AppWrapper.tsx +56 -0
- package/.framework/src/components/CustomAppLoader.tsx +116 -0
- package/.framework/src/components/admin/AdminListPage.tsx +151 -0
- package/.framework/src/components/admin/AdminSidebar.tsx +166 -0
- package/.framework/src/components/admin/AdminStatsCard.tsx +62 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +42 -0
- package/.framework/src/components/app-shell/GenericAppShell.tsx +181 -0
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +200 -0
- package/.framework/src/components/app-shell/GenericListPage.tsx +116 -0
- package/.framework/src/components/app-sidebar.tsx +228 -0
- package/.framework/src/components/auth/ProtectedRoute.tsx +88 -0
- package/.framework/src/components/layout/AppShell.tsx +91 -0
- package/.framework/src/components/layout/Header.tsx +88 -0
- package/.framework/src/components/layout/Layout.tsx +95 -0
- package/.framework/src/components/layout/Sidebar.tsx +329 -0
- package/.framework/src/components/runtime/DataDetailHeader.tsx +77 -0
- package/.framework/src/components/runtime/DataDetailPage.tsx +171 -0
- package/.framework/src/components/runtime/DataFilters.tsx +91 -0
- package/.framework/src/components/runtime/DataHeader.tsx +68 -0
- package/.framework/src/components/runtime/DataListPage.tsx +124 -0
- package/.framework/src/components/runtime/DataStats.tsx +70 -0
- package/.framework/src/components/runtime/DataTable.tsx +174 -0
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +134 -0
- package/.framework/src/components/runtime/index.ts +18 -0
- package/.framework/src/components/search-form.tsx +29 -0
- package/.framework/src/components/shared/AgentView.tsx +213 -0
- package/.framework/src/components/shared/FieldRenderer.tsx +478 -0
- package/.framework/src/components/shared/SchemaFields.tsx +226 -0
- package/.framework/src/components/ui/DataTable.tsx +343 -0
- package/.framework/src/components/ui/Form.tsx +281 -0
- package/.framework/src/components/ui/ItemCard.tsx +296 -0
- package/.framework/src/components/ui/ItemListView.tsx +308 -0
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -0
- package/.framework/src/components/ui/Modal.tsx +61 -0
- package/.framework/src/components/ui/RichTextEditor.tsx +210 -0
- package/.framework/src/components/ui/accordion.tsx +82 -0
- package/.framework/src/components/ui/alert-dialog.tsx +197 -0
- package/.framework/src/components/ui/alert.tsx +76 -0
- package/.framework/src/components/ui/aspect-ratio.tsx +11 -0
- package/.framework/src/components/ui/avatar.tsx +110 -0
- package/.framework/src/components/ui/badge.tsx +49 -0
- package/.framework/src/components/ui/breadcrumb.tsx +122 -0
- package/.framework/src/components/ui/button-group.tsx +83 -0
- package/.framework/src/components/ui/button.tsx +65 -0
- package/.framework/src/components/ui/calendar.tsx +222 -0
- package/.framework/src/components/ui/card.tsx +100 -0
- package/.framework/src/components/ui/carousel.tsx +240 -0
- package/.framework/src/components/ui/chart.tsx +373 -0
- package/.framework/src/components/ui/checkbox.tsx +31 -0
- package/.framework/src/components/ui/collapsible.tsx +33 -0
- package/.framework/src/components/ui/combobox.tsx +299 -0
- package/.framework/src/components/ui/command.tsx +193 -0
- package/.framework/src/components/ui/context-menu.tsx +261 -0
- package/.framework/src/components/ui/dialog.tsx +165 -0
- package/.framework/src/components/ui/direction.tsx +22 -0
- package/.framework/src/components/ui/drawer.tsx +132 -0
- package/.framework/src/components/ui/dropdown-menu.tsx +269 -0
- package/.framework/src/components/ui/empty.tsx +104 -0
- package/.framework/src/components/ui/field.tsx +238 -0
- package/.framework/src/components/ui/hover-card.tsx +42 -0
- package/.framework/src/components/ui/input-group.tsx +153 -0
- package/.framework/src/components/ui/input-otp.tsx +87 -0
- package/.framework/src/components/ui/input.tsx +19 -0
- package/.framework/src/components/ui/item.tsx +196 -0
- package/.framework/src/components/ui/kbd.tsx +26 -0
- package/.framework/src/components/ui/label.tsx +22 -0
- package/.framework/src/components/ui/menubar.tsx +277 -0
- package/.framework/src/components/ui/native-select.tsx +61 -0
- package/.framework/src/components/ui/navigation-menu.tsx +164 -0
- package/.framework/src/components/ui/pagination.tsx +129 -0
- package/.framework/src/components/ui/popover.tsx +87 -0
- package/.framework/src/components/ui/progress.tsx +31 -0
- package/.framework/src/components/ui/radio-group.tsx +42 -0
- package/.framework/src/components/ui/resizable.tsx +50 -0
- package/.framework/src/components/ui/scroll-area.tsx +53 -0
- package/.framework/src/components/ui/select.tsx +195 -0
- package/.framework/src/components/ui/separator.tsx +26 -0
- package/.framework/src/components/ui/sheet.tsx +145 -0
- package/.framework/src/components/ui/sidebar.tsx +706 -0
- package/.framework/src/components/ui/skeleton.tsx +13 -0
- package/.framework/src/components/ui/slider.tsx +59 -0
- package/.framework/src/components/ui/sonner.tsx +47 -0
- package/.framework/src/components/ui/spinner.tsx +10 -0
- package/.framework/src/components/ui/switch.tsx +33 -0
- package/.framework/src/components/ui/table-primitives.tsx +141 -0
- package/.framework/src/components/ui/table.tsx +114 -0
- package/.framework/src/components/ui/tabs.tsx +90 -0
- package/.framework/src/components/ui/textarea.tsx +18 -0
- package/.framework/src/components/ui/toggle-group.tsx +89 -0
- package/.framework/src/components/ui/toggle.tsx +45 -0
- package/.framework/src/components/ui/tooltip.tsx +57 -0
- package/.framework/src/contexts/AppContext.tsx +133 -0
- package/.framework/src/contexts/AuthContext.tsx +371 -0
- package/.framework/src/hooks/use-mobile.ts +19 -0
- package/.framework/src/hooks/useApi.ts +526 -0
- package/.framework/src/hooks/useApps.ts +114 -0
- package/.framework/src/hooks/useEntityList.ts +190 -0
- package/.framework/src/hooks/useEntityRecord.ts +308 -0
- package/.framework/src/hooks/useForm.ts +307 -0
- package/.framework/src/hooks/useListSchema.ts +264 -0
- package/.framework/src/hooks/useSchemaRecord.ts +223 -0
- package/.framework/src/index.css +128 -0
- package/.framework/src/lib/api.ts +156 -0
- package/.framework/src/lib/supabase.ts +94 -0
- package/.framework/src/lib/utils.ts +317 -0
- package/.framework/src/main.tsx +27 -0
- package/.framework/src/pages/DashboardPage.tsx +181 -0
- package/.framework/src/pages/NotFoundPage.tsx +39 -0
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +161 -0
- package/.framework/src/pages/admin/AIAgentsPage.tsx +318 -0
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +199 -0
- package/.framework/src/pages/admin/APIKeysPage.tsx +303 -0
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +523 -0
- package/.framework/src/pages/admin/AppDetailPage.tsx +493 -0
- package/.framework/src/pages/admin/AppsPage.tsx +355 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +491 -0
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +534 -0
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +424 -0
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +176 -0
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +109 -0
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +402 -0
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +187 -0
- package/.framework/src/pages/admin/IntegrationsPage.tsx +301 -0
- package/.framework/src/pages/admin/LogsPage.tsx +283 -0
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +85 -0
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +470 -0
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +183 -0
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +279 -0
- package/.framework/src/pages/admin/PipelinesPage.tsx +390 -0
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +299 -0
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +292 -0
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +434 -0
- package/.framework/src/pages/admin/RoleDetailPage.tsx +273 -0
- package/.framework/src/pages/admin/RolesPage.tsx +292 -0
- package/.framework/src/pages/admin/SelectTestPage.tsx +61 -0
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +588 -0
- package/.framework/src/pages/admin/SimpleDashboard.tsx +387 -0
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +172 -0
- package/.framework/src/pages/admin/TestingDashboard.tsx +257 -0
- package/.framework/src/pages/admin/TimerDetailPage.tsx +151 -0
- package/.framework/src/pages/admin/TimersPage.tsx +376 -0
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +149 -0
- package/.framework/src/pages/admin/TriggersPage.tsx +381 -0
- package/.framework/src/pages/admin/TypeDetailPage.tsx +694 -0
- package/.framework/src/pages/admin/TypesPage.tsx +295 -0
- package/.framework/src/pages/auth/LoginPage.tsx +188 -0
- package/.framework/src/pages/auth/RegisterPage.tsx +163 -0
- package/.framework/src/pages/spine-framework/APIPage.tsx +17 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +25 -0
- package/.framework/src/types/auth.ts +125 -0
- package/.framework/src/types/types.ts +407 -0
- package/STRUCTURE.md +150 -0
- package/config/components.json +25 -0
- package/config/deno.lock +108 -0
- package/config/package-lock.json +17183 -0
- package/config/postcss.config.cjs +10 -0
- package/config/tailwind.config.cjs +78 -0
- package/config/tsconfig.build.json +32 -0
- package/config/tsconfig.cli.json +18 -0
- package/config/tsconfig.json +41 -0
- package/config/tsconfig.node.json +17 -0
- package/config/tsconfig.node.tsbuildinfo +1 -0
- package/config/tsconfig.tsbuildinfo +1 -0
- package/config/typedoc.json +16 -0
- package/config/vite.config.d.ts +2 -0
- package/config/vite.config.ts +72 -0
- package/dist/cli/commands/agents.d.ts +39 -0
- package/dist/cli/commands/agents.d.ts.map +1 -0
- package/dist/cli/commands/auth.d.ts +36 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/create-app.d.ts +23 -0
- package/dist/cli/commands/create-app.d.ts.map +1 -0
- package/dist/cli/commands/dev.d.ts +39 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/doctor.d.ts +42 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/generate.d.ts +36 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts +30 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/install-app.d.ts +30 -0
- package/dist/cli/commands/install-app.d.ts.map +1 -0
- package/dist/cli/commands/items.d.ts +45 -0
- package/dist/cli/commands/items.d.ts.map +1 -0
- package/dist/cli/commands/migrations.d.ts +41 -0
- package/dist/cli/commands/migrations.d.ts.map +1 -0
- package/dist/cli/commands/pipelines.d.ts +40 -0
- package/dist/cli/commands/pipelines.d.ts.map +1 -0
- package/dist/cli/commands/status.d.ts +23 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/system.d.ts +29 -0
- package/dist/cli/commands/system.d.ts.map +1 -0
- package/dist/cli/commands/test.d.ts +46 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/uninstall-app.d.ts +23 -0
- package/dist/cli/commands/uninstall-app.d.ts.map +1 -0
- package/dist/cli/context.d.ts +88 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/env-loader.d.ts +14 -0
- package/dist/cli/env-loader.d.ts.map +1 -0
- package/dist/cli/index.d.ts +41 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/functions/_shared/agent-runner.d.ts +156 -0
- package/dist/functions/_shared/agent-runner.d.ts.map +1 -0
- package/dist/functions/_shared/app-manifest.d.ts +68 -0
- package/dist/functions/_shared/app-manifest.d.ts.map +1 -0
- package/dist/functions/_shared/audit.d.ts +91 -0
- package/dist/functions/_shared/audit.d.ts.map +1 -0
- package/dist/functions/_shared/db.d.ts +125 -0
- package/dist/functions/_shared/db.d.ts.map +1 -0
- package/dist/functions/_shared/index.d.ts +298 -0
- package/dist/functions/_shared/index.d.ts.map +1 -0
- package/dist/functions/_shared/middleware.d.ts +315 -0
- package/dist/functions/_shared/middleware.d.ts.map +1 -0
- package/dist/functions/_shared/permissions.d.ts +626 -0
- package/dist/functions/_shared/permissions.d.ts.map +1 -0
- package/dist/functions/_shared/pipeline-runner.d.ts +124 -0
- package/dist/functions/_shared/pipeline-runner.d.ts.map +1 -0
- package/dist/functions/_shared/principal.d.ts +284 -0
- package/dist/functions/_shared/principal.d.ts.map +1 -0
- package/dist/functions/_shared/schema-utils.d.ts +181 -0
- package/dist/functions/_shared/schema-utils.d.ts.map +1 -0
- package/dist/functions/_shared/testing.d.ts +172 -0
- package/dist/functions/_shared/testing.d.ts.map +1 -0
- package/dist/functions/_shared/trigger-engine.d.ts +140 -0
- package/dist/functions/_shared/trigger-engine.d.ts.map +1 -0
- package/dist/functions/_shared/webhook-registration.d.ts +81 -0
- package/dist/functions/_shared/webhook-registration.d.ts.map +1 -0
- package/dist/functions/_shared/webhook-registry.d.ts +57 -0
- package/dist/functions/_shared/webhook-registry.d.ts.map +1 -0
- package/dist/functions/account-nodes.d.ts +48 -0
- package/dist/functions/account-nodes.d.ts.map +1 -0
- package/dist/functions/admin-data.d.ts +178 -0
- package/dist/functions/admin-data.d.ts.map +1 -0
- package/dist/functions/ai-agents.d.ts +125 -0
- package/dist/functions/ai-agents.d.ts.map +1 -0
- package/dist/functions/api-keys.d.ts +140 -0
- package/dist/functions/api-keys.d.ts.map +1 -0
- package/dist/functions/apps.d.ts +163 -0
- package/dist/functions/apps.d.ts.map +1 -0
- package/dist/functions/auth.d.ts +74 -0
- package/dist/functions/auth.d.ts.map +1 -0
- package/dist/functions/debug-auth.d.ts +33 -0
- package/dist/functions/debug-auth.d.ts.map +1 -0
- package/dist/functions/embeddings.d.ts +205 -0
- package/dist/functions/embeddings.d.ts.map +1 -0
- package/dist/functions/integration-routes.d.ts +45 -0
- package/dist/functions/integration-routes.d.ts.map +1 -0
- package/dist/functions/integrations.d.ts +124 -0
- package/dist/functions/integrations.d.ts.map +1 -0
- package/dist/functions/item-progress.d.ts +41 -0
- package/dist/functions/item-progress.d.ts.map +1 -0
- package/dist/functions/logs.d.ts +162 -0
- package/dist/functions/logs.d.ts.map +1 -0
- package/dist/functions/observability.d.ts +123 -0
- package/dist/functions/observability.d.ts.map +1 -0
- package/dist/functions/pipeline-executions.d.ts +190 -0
- package/dist/functions/pipeline-executions.d.ts.map +1 -0
- package/dist/functions/pipelines.d.ts +171 -0
- package/dist/functions/pipelines.d.ts.map +1 -0
- package/dist/functions/prompt-configs.d.ts +125 -0
- package/dist/functions/prompt-configs.d.ts.map +1 -0
- package/dist/functions/roles.d.ts +118 -0
- package/dist/functions/roles.d.ts.map +1 -0
- package/dist/functions/system-cron.d.ts +65 -0
- package/dist/functions/system-cron.d.ts.map +1 -0
- package/dist/functions/system.d.ts +29 -0
- package/dist/functions/system.d.ts.map +1 -0
- package/dist/functions/tests.d.ts +28 -0
- package/dist/functions/tests.d.ts.map +1 -0
- package/dist/functions/timers.d.ts +139 -0
- package/dist/functions/timers.d.ts.map +1 -0
- package/dist/functions/triggers.d.ts +203 -0
- package/dist/functions/triggers.d.ts.map +1 -0
- package/dist/functions/types.d.ts +151 -0
- package/dist/functions/types.d.ts.map +1 -0
- package/dist/src/types/types.d.ts +364 -0
- package/dist/src/types/types.d.ts.map +1 -0
- package/package.json +192 -0
- package/scripts/app-install-cli.ts +286 -0
- package/scripts/assemble-frontend.sh +79 -0
- package/scripts/assemble-functions.sh +62 -0
- package/scripts/assemble.sh +35 -0
- package/scripts/boundary-check.sh +106 -0
- package/scripts/build-manifest.sh +80 -0
- package/scripts/check-core-integrity.sh +82 -0
- package/scripts/ingest-chunks.cjs +202 -0
- package/scripts/kb-chunk-parser.cjs +312 -0
- package/scripts/kb-chunk-parser.ts +330 -0
- package/scripts/load-test-app-install.ts +484 -0
- package/scripts/netlify-dev-wrapper.sh +22 -0
- package/scripts/verify-integrity.sh +69 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
-- =============================================================================
|
|
2
|
+
-- 001_seed.sql
|
|
3
|
+
-- Spine Framework — Bare minimum bootstrap data for a fresh install
|
|
4
|
+
-- Generated from live production DB audit: May 28, 2026
|
|
5
|
+
-- =============================================================================
|
|
6
|
+
-- Run AFTER 000_foundation.sql.
|
|
7
|
+
--
|
|
8
|
+
-- This file contains ONLY what is required for Spine to boot and for a
|
|
9
|
+
-- system_admin to log in and interact with the framework. Nothing more.
|
|
10
|
+
--
|
|
11
|
+
-- What requires seeding before the app can function:
|
|
12
|
+
-- 1. system_admin role — people.role_id FK target
|
|
13
|
+
-- 2. spine-core app — required by runtime; people/accounts ref app_id
|
|
14
|
+
-- 3. account type — accounts.type_id is NOT NULL
|
|
15
|
+
-- 4. person type — people.type_id is NOT NULL
|
|
16
|
+
-- 5. root system account — people.account_id is NOT NULL; the bootstrap
|
|
17
|
+
-- system_admin must belong to an account
|
|
18
|
+
--
|
|
19
|
+
-- Everything else (additional roles, app-specific types, tenant accounts,
|
|
20
|
+
-- app-owned link types) belongs in app seed files or is created via the admin UI.
|
|
21
|
+
-- =============================================================================
|
|
22
|
+
|
|
23
|
+
-- ---------------------------------------------------------------------------
|
|
24
|
+
-- 1. System Role
|
|
25
|
+
-- ---------------------------------------------------------------------------
|
|
26
|
+
INSERT INTO public.roles (slug, name, description, permissions, is_system, is_active, is_protected)
|
|
27
|
+
VALUES (
|
|
28
|
+
'system_admin',
|
|
29
|
+
'System Admin',
|
|
30
|
+
'Full system access — can manage all accounts, types, and system configuration',
|
|
31
|
+
'["*"]'::jsonb,
|
|
32
|
+
true,
|
|
33
|
+
true,
|
|
34
|
+
true
|
|
35
|
+
)
|
|
36
|
+
ON CONFLICT DO NOTHING;
|
|
37
|
+
|
|
38
|
+
-- ---------------------------------------------------------------------------
|
|
39
|
+
-- 2. Spine Core App
|
|
40
|
+
-- ---------------------------------------------------------------------------
|
|
41
|
+
INSERT INTO public.apps (slug, name, description, version, app_type, source, is_active, is_system, min_role, config, nav_items, route_prefix, renderer, integration_deps, metadata)
|
|
42
|
+
VALUES (
|
|
43
|
+
'spine-core',
|
|
44
|
+
'Spine Core',
|
|
45
|
+
'Core Spine runtime — provides accounts, people, items, threads, messages, links, attachments, watchers',
|
|
46
|
+
'1.0.0',
|
|
47
|
+
'system',
|
|
48
|
+
'builtin',
|
|
49
|
+
true,
|
|
50
|
+
true,
|
|
51
|
+
NULL,
|
|
52
|
+
'{}'::jsonb,
|
|
53
|
+
'[]'::jsonb,
|
|
54
|
+
NULL,
|
|
55
|
+
'none',
|
|
56
|
+
'[]'::jsonb,
|
|
57
|
+
'{}'::jsonb
|
|
58
|
+
)
|
|
59
|
+
ON CONFLICT (slug) DO NOTHING;
|
|
60
|
+
|
|
61
|
+
-- ---------------------------------------------------------------------------
|
|
62
|
+
-- 3. Core Types
|
|
63
|
+
-- accounts.type_id and people.type_id are both NOT NULL, so these must exist
|
|
64
|
+
-- before any account or person can be created.
|
|
65
|
+
-- ---------------------------------------------------------------------------
|
|
66
|
+
INSERT INTO public.types (app_id, kind, slug, name, description, ownership, is_active)
|
|
67
|
+
VALUES
|
|
68
|
+
((SELECT id FROM public.apps WHERE slug = 'spine-core'), 'account', 'account', 'Account', 'Organization or tenant account', 'pack', true),
|
|
69
|
+
((SELECT id FROM public.apps WHERE slug = 'spine-core'), 'person', 'person', 'Person', 'User or contact person', 'pack', true)
|
|
70
|
+
ON CONFLICT (app_id, kind, slug) DO NOTHING;
|
|
71
|
+
|
|
72
|
+
-- ---------------------------------------------------------------------------
|
|
73
|
+
-- 4. Root System Account
|
|
74
|
+
-- The first system_admin person must belong to an account. This is the
|
|
75
|
+
-- bootstrap account for the Spine system itself.
|
|
76
|
+
-- NOTE: The system_admin person record is NOT seeded here — it is created
|
|
77
|
+
-- via Supabase Auth invite + the onboarding flow, which stamps type_id and
|
|
78
|
+
-- role_id onto the resulting people row.
|
|
79
|
+
-- ---------------------------------------------------------------------------
|
|
80
|
+
INSERT INTO public.accounts (type_id, slug, display_name, description, is_active)
|
|
81
|
+
VALUES (
|
|
82
|
+
(SELECT id FROM public.types WHERE kind = 'account' AND slug = 'account'),
|
|
83
|
+
'spine-system',
|
|
84
|
+
'Spine System',
|
|
85
|
+
'Root system account for the Spine framework',
|
|
86
|
+
true
|
|
87
|
+
)
|
|
88
|
+
ON CONFLICT (slug) DO NOTHING;
|
|
89
|
+
|
|
90
|
+
-- NOTE: No link types are seeded here. All link types are app-owned.
|
|
91
|
+
-- Cortex seeds: account_signals, account_opportunities, analyzed_by, tagged_with
|
|
92
|
+
-- See custom/apps/cortex/seed/link-types.json
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- =============================================================================
|
|
2
|
+
-- 002_seed_constraints.sql
|
|
3
|
+
-- Adds unique constraints required for idempotent seed upserts
|
|
4
|
+
-- =============================================================================
|
|
5
|
+
|
|
6
|
+
-- link_types needs unique on (app_id, slug) for seed upserts
|
|
7
|
+
ALTER TABLE public.link_types ADD CONSTRAINT IF NOT EXISTS link_types_app_id_slug_key UNIQUE (app_id, slug);
|
|
8
|
+
|
|
9
|
+
-- triggers needs unique on (app_id, name) for seed upserts
|
|
10
|
+
ALTER TABLE public.triggers ADD CONSTRAINT IF NOT EXISTS triggers_app_id_name_key UNIQUE (app_id, name);
|
|
11
|
+
|
|
12
|
+
-- pipelines needs unique on (app_id, name) for seed upserts
|
|
13
|
+
ALTER TABLE public.pipelines ADD CONSTRAINT IF NOT EXISTS pipelines_app_id_name_key UNIQUE (app_id, name);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
-- =============================================================================
|
|
2
|
+
-- 003_auth_user_trigger.sql
|
|
3
|
+
-- Creates a people record whenever a new Supabase auth user signs up.
|
|
4
|
+
-- Run AFTER 001_seed.sql (requires spine-system account and person type).
|
|
5
|
+
--
|
|
6
|
+
-- First user ever → system_admin role (bootstrap)
|
|
7
|
+
-- Subsequent users → no role (assigned later via admin invite flow)
|
|
8
|
+
-- =============================================================================
|
|
9
|
+
|
|
10
|
+
CREATE OR REPLACE FUNCTION public.handle_new_auth_user()
|
|
11
|
+
RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$
|
|
12
|
+
DECLARE
|
|
13
|
+
v_account_id uuid;
|
|
14
|
+
v_type_id uuid;
|
|
15
|
+
v_role_id uuid;
|
|
16
|
+
v_is_first boolean;
|
|
17
|
+
BEGIN
|
|
18
|
+
SELECT id INTO v_account_id FROM public.accounts WHERE slug = 'spine-system' LIMIT 1;
|
|
19
|
+
SELECT id INTO v_type_id FROM public.types WHERE kind = 'person' AND slug = 'person' LIMIT 1;
|
|
20
|
+
|
|
21
|
+
IF v_account_id IS NULL OR v_type_id IS NULL THEN
|
|
22
|
+
RAISE WARNING 'handle_new_auth_user: spine-system account or person type not found — skipping people insert';
|
|
23
|
+
RETURN NEW;
|
|
24
|
+
END IF;
|
|
25
|
+
|
|
26
|
+
-- First person ever gets system_admin; subsequent users get no role
|
|
27
|
+
SELECT NOT EXISTS (SELECT 1 FROM public.people LIMIT 1) INTO v_is_first;
|
|
28
|
+
IF v_is_first THEN
|
|
29
|
+
SELECT id INTO v_role_id FROM public.roles WHERE slug = 'system_admin' LIMIT 1;
|
|
30
|
+
END IF;
|
|
31
|
+
|
|
32
|
+
INSERT INTO public.people (
|
|
33
|
+
id,
|
|
34
|
+
auth_uid,
|
|
35
|
+
email,
|
|
36
|
+
full_name,
|
|
37
|
+
account_id,
|
|
38
|
+
type_id,
|
|
39
|
+
role_id,
|
|
40
|
+
is_active
|
|
41
|
+
) VALUES (
|
|
42
|
+
NEW.id,
|
|
43
|
+
NEW.id,
|
|
44
|
+
NEW.email,
|
|
45
|
+
COALESCE(NEW.raw_user_meta_data->>'full_name', split_part(NEW.email, '@', 1)),
|
|
46
|
+
v_account_id,
|
|
47
|
+
v_type_id,
|
|
48
|
+
v_role_id,
|
|
49
|
+
true
|
|
50
|
+
)
|
|
51
|
+
ON CONFLICT (id) DO NOTHING;
|
|
52
|
+
|
|
53
|
+
RETURN NEW;
|
|
54
|
+
END;
|
|
55
|
+
$$;
|
|
56
|
+
|
|
57
|
+
CREATE OR REPLACE TRIGGER on_auth_user_created
|
|
58
|
+
AFTER INSERT ON auth.users
|
|
59
|
+
FOR EACH ROW EXECUTE FUNCTION public.handle_new_auth_user();
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Suspense, lazy } from 'react'
|
|
2
|
+
import { Routes, Route, Navigate } from 'react-router-dom'
|
|
3
|
+
import { useAuth } from './contexts/AuthContext'
|
|
4
|
+
import { LoadingSpinner } from './components/ui/LoadingSpinner'
|
|
5
|
+
import { LoginPage } from './pages/auth/LoginPage'
|
|
6
|
+
import { RegisterPage } from './pages/auth/RegisterPage'
|
|
7
|
+
import { NotFoundPage } from './pages/NotFoundPage'
|
|
8
|
+
import { DashboardPage } from './pages/DashboardPage'
|
|
9
|
+
import { AppWrapper } from './components/AppWrapper'
|
|
10
|
+
import { AppsRegistryProvider, useAppsRegistry } from './contexts/AppContext'
|
|
11
|
+
import { CustomAppLoader } from './components/CustomAppLoader'
|
|
12
|
+
import { GenericAppShell } from './components/app-shell/GenericAppShell'
|
|
13
|
+
import { APIPage } from './pages/spine-framework/APIPage'
|
|
14
|
+
import { CLIPage } from './pages/spine-framework/CLIPage'
|
|
15
|
+
|
|
16
|
+
const AdminApp = lazy(() => import('./apps/admin/index'))
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
const { user, isLoading } = useAuth()
|
|
20
|
+
|
|
21
|
+
if (isLoading) {
|
|
22
|
+
return (
|
|
23
|
+
<div className="min-h-screen flex items-center justify-center bg-slate-50">
|
|
24
|
+
<div className="text-center">
|
|
25
|
+
<LoadingSpinner className="w-8 h-8 mx-auto mb-4" />
|
|
26
|
+
<p className="text-slate-600">Loading Spine...</p>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<AppsRegistryProvider>
|
|
34
|
+
{!user ? (
|
|
35
|
+
<Routes>
|
|
36
|
+
<Route path="/login" element={<LoginPage />} />
|
|
37
|
+
<Route path="/register" element={<RegisterPage />} />
|
|
38
|
+
<Route path="*" element={<Navigate to="/login" replace />} />
|
|
39
|
+
</Routes>
|
|
40
|
+
) : (
|
|
41
|
+
<AuthenticatedRouter />
|
|
42
|
+
)}
|
|
43
|
+
</AppsRegistryProvider>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function AuthenticatedRouter() {
|
|
48
|
+
const { routableApps: apps, loading } = useAppsRegistry()
|
|
49
|
+
|
|
50
|
+
if (loading) {
|
|
51
|
+
return (
|
|
52
|
+
<div className="min-h-screen flex items-center justify-center bg-slate-50">
|
|
53
|
+
<div className="text-center">
|
|
54
|
+
<LoadingSpinner className="w-8 h-8 mx-auto mb-4" />
|
|
55
|
+
<p className="text-slate-600">Loading apps...</p>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Sort: explicit prefixes first, root (/) last
|
|
62
|
+
const sorted = [...apps]
|
|
63
|
+
.filter(app => app.slug !== 'spine-framework' && !app.route_prefix?.startsWith('/spine-framework'))
|
|
64
|
+
.sort((a, b) => {
|
|
65
|
+
if (a.route_prefix === '/') return 1
|
|
66
|
+
if (b.route_prefix === '/') return -1
|
|
67
|
+
return (b.route_prefix?.length || 0) - (a.route_prefix?.length || 0)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Suspense fallback={<div className="min-h-screen flex items-center justify-center"><LoadingSpinner /></div>}>
|
|
72
|
+
<Routes>
|
|
73
|
+
{/* Default redirect */}
|
|
74
|
+
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
|
75
|
+
<Route path="/dashboard" element={<DashboardPage />} />
|
|
76
|
+
|
|
77
|
+
{/* Login route for authenticated users (redirects to qualified app) */}
|
|
78
|
+
<Route path="/login" element={<LoginPage />} />
|
|
79
|
+
|
|
80
|
+
{/* ── Spine Framework namespace (reserved — no custom app may use this path) ── */}
|
|
81
|
+
<Route path="/spine-framework/admin/*" element={<AdminApp />} />
|
|
82
|
+
<Route path="/spine-framework/api" element={<APIPage />} />
|
|
83
|
+
<Route path="/spine-framework/cli" element={<CLIPage />} />
|
|
84
|
+
|
|
85
|
+
{/* Dynamic app routes */}
|
|
86
|
+
{sorted.map(app => {
|
|
87
|
+
const prefix = app.route_prefix!
|
|
88
|
+
|
|
89
|
+
if (app.renderer === 'custom') {
|
|
90
|
+
return (
|
|
91
|
+
<Route
|
|
92
|
+
key={app.slug}
|
|
93
|
+
path={`${prefix}/*`}
|
|
94
|
+
element={
|
|
95
|
+
<AppWrapper app={app}>
|
|
96
|
+
<CustomAppLoader slug={app.slug} />
|
|
97
|
+
</AppWrapper>
|
|
98
|
+
}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (app.renderer === 'generic') {
|
|
104
|
+
return (
|
|
105
|
+
<Route
|
|
106
|
+
key={app.slug}
|
|
107
|
+
path={`${prefix}/*`}
|
|
108
|
+
element={
|
|
109
|
+
<AppWrapper app={app}>
|
|
110
|
+
<GenericAppShell app={app} />
|
|
111
|
+
</AppWrapper>
|
|
112
|
+
}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return null
|
|
118
|
+
})}
|
|
119
|
+
|
|
120
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
121
|
+
</Routes>
|
|
122
|
+
</Suspense>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default App
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { lazy, Suspense } from 'react'
|
|
2
|
+
import { Routes, Route, Navigate, useLocation } from 'react-router-dom'
|
|
3
|
+
import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
|
|
4
|
+
import { NotFoundPage } from '../../pages/NotFoundPage'
|
|
5
|
+
import { useAuth } from '../../contexts/AuthContext'
|
|
6
|
+
import { AppShell } from '../../components/layout/AppShell'
|
|
7
|
+
import { AdminSidebar } from '../../components/admin/AdminSidebar'
|
|
8
|
+
import { TooltipProvider } from '../../components/ui/tooltip'
|
|
9
|
+
|
|
10
|
+
// Config list pages
|
|
11
|
+
const TypesPage = lazy(() => import('../../pages/admin/TypesPage').then(m => ({ default: m.TypesPage })))
|
|
12
|
+
const AppsPage = lazy(() => import('../../pages/admin/AppsPage').then(m => ({ default: m.AppsPage })))
|
|
13
|
+
const TypeDetailPage = lazy(() => import('../../pages/admin/TypeDetailPage').then(m => ({ default: m.TypeDetailPage })))
|
|
14
|
+
const PipelinesPage = lazy(() => import('../../pages/admin/PipelinesPage').then(m => ({ default: m.PipelinesPage })))
|
|
15
|
+
const TriggersPage = lazy(() => import('../../pages/admin/TriggersPage').then(m => ({ default: m.TriggersPage })))
|
|
16
|
+
const AIAgentsPage = lazy(() => import('../../pages/admin/AIAgentsPage').then(m => ({ default: m.AIAgentsPage })))
|
|
17
|
+
const EmbeddingsPage = lazy(() => import('../../pages/admin/EmbeddingsPage').then(m => ({ default: m.EmbeddingsPage })))
|
|
18
|
+
const TimersPage = lazy(() => import('../../pages/admin/TimersPage').then(m => ({ default: m.TimersPage })))
|
|
19
|
+
const IntegrationsPage = lazy(() => import('../../pages/admin/IntegrationsPage').then(m => ({ default: m.IntegrationsPage })))
|
|
20
|
+
|
|
21
|
+
// Config detail pages
|
|
22
|
+
const AppDetailPage = lazy(() => import('../../pages/admin/AppDetailPage').then(m => ({ default: m.AppDetailPage })))
|
|
23
|
+
const PipelineDetailPage = lazy(() => import('../../pages/admin/PipelineDetailPage').then(m => ({ default: m.PipelineDetailPage })))
|
|
24
|
+
const TriggerDetailPage = lazy(() => import('../../pages/admin/TriggerDetailPage').then(m => ({ default: m.TriggerDetailPage })))
|
|
25
|
+
const AIAgentDetailPage = lazy(() => import('../../pages/admin/AIAgentDetailPage').then(m => ({ default: m.AIAgentDetailPage })))
|
|
26
|
+
const EmbeddingDetailPage = lazy(() => import('../../pages/admin/EmbeddingDetailPage').then(m => ({ default: m.EmbeddingDetailPage })))
|
|
27
|
+
const TimerDetailPage = lazy(() => import('../../pages/admin/TimerDetailPage').then(m => ({ default: m.TimerDetailPage })))
|
|
28
|
+
const IntegrationDetailPage = lazy(() => import('../../pages/admin/IntegrationDetailPage').then(m => ({ default: m.IntegrationDetailPage })))
|
|
29
|
+
const RolesPage = lazy(() => import('../../pages/admin/RolesPage').then(m => ({ default: m.RolesPage })))
|
|
30
|
+
const RoleDetailPage = lazy(() => import('../../pages/admin/RoleDetailPage').then(m => ({ default: m.RoleDetailPage })))
|
|
31
|
+
const PromptConfigsPage = lazy(() => import('../../pages/admin/PromptConfigsPage').then(m => ({ default: m.PromptConfigsPage })))
|
|
32
|
+
const PromptConfigDetailPage = lazy(() => import('../../pages/admin/PromptConfigDetailPage').then(m => ({ default: m.PromptConfigDetailPage })))
|
|
33
|
+
const APIKeysPage = lazy(() => import('../../pages/admin/APIKeysPage').then(m => ({ default: m.APIKeysPage })))
|
|
34
|
+
const APIKeyDetailPage = lazy(() => import('../../pages/admin/APIKeyDetailPage').then(m => ({ default: m.APIKeyDetailPage })))
|
|
35
|
+
|
|
36
|
+
// Observability pages
|
|
37
|
+
const ObservabilityDashboard = lazy(() => import('../../pages/admin/ObservabilityDashboard').then(m => ({ default: m.ObservabilityDashboard })))
|
|
38
|
+
const PipelineExecutionsPage = lazy(() => import('../../pages/admin/PipelineExecutionsPage').then(m => ({ default: m.PipelineExecutionsPage })))
|
|
39
|
+
const LogsPage = lazy(() => import('../../pages/admin/LogsPage').then(m => ({ default: m.LogsPage })))
|
|
40
|
+
const AlertsConfigPage = lazy(() => import('../../pages/admin/AlertsConfigPage').then(m => ({ default: m.AlertsConfigPage })))
|
|
41
|
+
|
|
42
|
+
// Runtime data pages (unified)
|
|
43
|
+
const DataListPage = lazy(() => import('../../components/runtime/DataListPage').then(m => ({ default: m.DataListPage })))
|
|
44
|
+
const DataDetailPage = lazy(() => import('../../components/runtime/DataDetailPage').then(m => ({ default: m.DataDetailPage })))
|
|
45
|
+
|
|
46
|
+
// Testing pages
|
|
47
|
+
const TestingDashboard = lazy(() => import('../../pages/admin/TestingDashboard'))
|
|
48
|
+
const TestRunDetailPage = lazy(() => import('../../pages/admin/TestRunDetailPage').then(m => ({ default: m.TestRunDetailPage })))
|
|
49
|
+
const ShadcnTestPage = lazy(() => import('../../pages/admin/ShadcnTestPage').then(m => ({ default: m.ShadcnTestPage })))
|
|
50
|
+
const MinimalShadcnTestPage = lazy(() => import('../../pages/admin/MinimalShadcnTestPage').then(m => ({ default: m.MinimalShadcnTestPage })))
|
|
51
|
+
const IncrementalShadcnTestPage = lazy(() => import('../../pages/admin/IncrementalShadcnTestPage').then(m => ({ default: m.IncrementalShadcnTestPage })))
|
|
52
|
+
const ExtendedShadcnTestPage = lazy(() => import('../../pages/admin/ExtendedShadcnTestPage').then(m => ({ default: m.ExtendedShadcnTestPage })))
|
|
53
|
+
const SelectTestPage = lazy(() => import('../../pages/admin/SelectTestPage').then(m => ({ default: m.SelectTestPage })))
|
|
54
|
+
const DesignedPage = lazy(() => import('../../pages/admin/DesignedPage').then(m => ({ default: m.DesignedPage })))
|
|
55
|
+
const ProperlyDesignedPage = lazy(() => import('../../pages/admin/ProperlyDesignedPage').then(m => ({ default: m.ProperlyDesignedPage })))
|
|
56
|
+
const IntegratedDashboard = lazy(() => import('../../pages/admin/IntegratedDashboard').then(m => ({ default: m.IntegratedDashboard })))
|
|
57
|
+
const SimpleDashboard = lazy(() => import('../../pages/admin/SimpleDashboard').then(m => ({ default: m.SimpleDashboard })))
|
|
58
|
+
|
|
59
|
+
function AdminRoutes() {
|
|
60
|
+
const location = useLocation()
|
|
61
|
+
|
|
62
|
+
// Build breadcrumbs from current path
|
|
63
|
+
const pathSegments = location.pathname.split('/').filter(Boolean)
|
|
64
|
+
const breadcrumbs: { title: string; url?: string }[] = [{ title: 'Spine', url: '/dashboard' }]
|
|
65
|
+
|
|
66
|
+
// Add admin section
|
|
67
|
+
if (pathSegments.includes('admin')) {
|
|
68
|
+
breadcrumbs.push({ title: 'Admin', url: '/spine-framework/admin' })
|
|
69
|
+
|
|
70
|
+
// Add subsection
|
|
71
|
+
const section = pathSegments[2]
|
|
72
|
+
if (section) {
|
|
73
|
+
breadcrumbs.push({ title: section.charAt(0).toUpperCase() + section.slice(1) })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<AppShell
|
|
79
|
+
sidebar={<AdminSidebar />}
|
|
80
|
+
breadcrumbs={breadcrumbs}
|
|
81
|
+
>
|
|
82
|
+
<Suspense fallback={<div className="min-h-[400px] flex items-center justify-center"><LoadingSpinner /></div>}>
|
|
83
|
+
<Routes>
|
|
84
|
+
{/* Default redirect → configs */}
|
|
85
|
+
<Route index element={<Navigate to="configs/types" replace />} />
|
|
86
|
+
<Route path="dashboard" element={<Navigate to="configs/types" replace />} />
|
|
87
|
+
|
|
88
|
+
{/* Configs section */}
|
|
89
|
+
<Route path="configs/types/new" element={<TypeDetailPage />} />
|
|
90
|
+
<Route path="configs/types/:id" element={<TypeDetailPage />} />
|
|
91
|
+
<Route path="configs/types" element={<TypesPage />} />
|
|
92
|
+
<Route path="configs/apps/new" element={<AppDetailPage />} />
|
|
93
|
+
<Route path="configs/apps/:id" element={<AppDetailPage />} />
|
|
94
|
+
<Route path="configs/apps" element={<AppsPage />} />
|
|
95
|
+
<Route path="configs/pipelines/new" element={<PipelineDetailPage />} />
|
|
96
|
+
<Route path="configs/pipelines/:id" element={<PipelineDetailPage />} />
|
|
97
|
+
<Route path="configs/pipelines" element={<PipelinesPage />} />
|
|
98
|
+
<Route path="configs/triggers/new" element={<TriggerDetailPage />} />
|
|
99
|
+
<Route path="configs/triggers/:id" element={<TriggerDetailPage />} />
|
|
100
|
+
<Route path="configs/triggers" element={<TriggersPage />} />
|
|
101
|
+
<Route path="configs/ai-agents/new" element={<AIAgentDetailPage />} />
|
|
102
|
+
<Route path="configs/ai-agents/:id" element={<AIAgentDetailPage />} />
|
|
103
|
+
<Route path="configs/ai-agents" element={<AIAgentsPage />} />
|
|
104
|
+
<Route path="configs/embeddings/new" element={<EmbeddingDetailPage />} />
|
|
105
|
+
<Route path="configs/embeddings/:id" element={<EmbeddingDetailPage />} />
|
|
106
|
+
<Route path="configs/embeddings" element={<EmbeddingsPage />} />
|
|
107
|
+
<Route path="configs/timers/new" element={<TimerDetailPage />} />
|
|
108
|
+
<Route path="configs/timers/:id" element={<TimerDetailPage />} />
|
|
109
|
+
<Route path="configs/timers" element={<TimersPage />} />
|
|
110
|
+
<Route path="configs/integrations/new" element={<IntegrationDetailPage />} />
|
|
111
|
+
<Route path="configs/integrations/:id" element={<IntegrationDetailPage />} />
|
|
112
|
+
<Route path="configs/integrations" element={<IntegrationsPage />} />
|
|
113
|
+
<Route path="configs/roles/new" element={<RoleDetailPage />} />
|
|
114
|
+
<Route path="configs/roles/:id" element={<RoleDetailPage />} />
|
|
115
|
+
<Route path="configs/roles" element={<RolesPage />} />
|
|
116
|
+
<Route path="configs/prompts/new" element={<PromptConfigDetailPage />} />
|
|
117
|
+
<Route path="configs/prompts/:id" element={<PromptConfigDetailPage />} />
|
|
118
|
+
<Route path="configs/prompts" element={<PromptConfigsPage />} />
|
|
119
|
+
<Route path="configs/api-keys/new" element={<APIKeyDetailPage />} />
|
|
120
|
+
<Route path="configs/api-keys/:id" element={<APIKeyDetailPage />} />
|
|
121
|
+
<Route path="configs/api-keys" element={<APIKeysPage />} />
|
|
122
|
+
|
|
123
|
+
{/* Observability section */}
|
|
124
|
+
<Route path="observability" element={<ObservabilityDashboard />} />
|
|
125
|
+
<Route path="observability/dashboard" element={<ObservabilityDashboard />} />
|
|
126
|
+
<Route path="observability/alerts" element={<AlertsConfigPage />} />
|
|
127
|
+
<Route path="observability/executions/:id" element={<PipelineExecutionsPage />} />
|
|
128
|
+
<Route path="observability/executions" element={<PipelineExecutionsPage />} />
|
|
129
|
+
<Route path="observability/logs" element={<LogsPage />} />
|
|
130
|
+
|
|
131
|
+
{/* Testing section */}
|
|
132
|
+
<Route path="testing/:run_id" element={<TestRunDetailPage />} />
|
|
133
|
+
<Route path="testing" element={<TestingDashboard />} />
|
|
134
|
+
<Route path="test/ui" element={<ShadcnTestPage />} />
|
|
135
|
+
<Route path="test/minimal" element={<MinimalShadcnTestPage />} />
|
|
136
|
+
<Route path="test/incremental" element={<IncrementalShadcnTestPage />} />
|
|
137
|
+
<Route path="test/extended" element={<ExtendedShadcnTestPage />} />
|
|
138
|
+
<Route path="test/select" element={<SelectTestPage />} />
|
|
139
|
+
<Route path="test/designed" element={<DesignedPage />} />
|
|
140
|
+
<Route path="test/properly-designed" element={<ProperlyDesignedPage />} />
|
|
141
|
+
<Route path="test/integrated" element={<IntegratedDashboard />} />
|
|
142
|
+
<Route path="test/simple" element={<SimpleDashboard />} />
|
|
143
|
+
|
|
144
|
+
{/* Runtime Data - Unified entity management */}
|
|
145
|
+
<Route path="runtime/:entity" element={<DataListPage />} />
|
|
146
|
+
<Route path="runtime/:entity/new" element={<DataDetailPage />} />
|
|
147
|
+
<Route path="runtime/:entity/:id" element={<DataDetailPage />} />
|
|
148
|
+
|
|
149
|
+
{/* Legacy data routes - redirect to runtime routes */}
|
|
150
|
+
<Route path="data/:entity" element={<Navigate to="../runtime/:entity" replace />} />
|
|
151
|
+
<Route path="data/:entity/create" element={<Navigate to="../runtime/:entity/new" replace />} />
|
|
152
|
+
<Route path="data/:entity/:id" element={<Navigate to="../runtime/:entity/:id" replace />} />
|
|
153
|
+
|
|
154
|
+
<Route path="*" element={<NotFoundPage />} />
|
|
155
|
+
</Routes>
|
|
156
|
+
</Suspense>
|
|
157
|
+
</AppShell>
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default function AdminApp() {
|
|
162
|
+
const { user } = useAuth()
|
|
163
|
+
|
|
164
|
+
if (!user?.is_system_admin) {
|
|
165
|
+
return <Navigate to="/dashboard" replace />
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<TooltipProvider>
|
|
170
|
+
<AdminRoutes />
|
|
171
|
+
</TooltipProvider>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Navigate } from 'react-router-dom'
|
|
3
|
+
import { useAuth } from '../contexts/AuthContext'
|
|
4
|
+
import { AppProvider } from '../contexts/AppContext'
|
|
5
|
+
import { AppRecord } from '../hooks/useApps'
|
|
6
|
+
|
|
7
|
+
interface AppWrapperProps {
|
|
8
|
+
app: AppRecord
|
|
9
|
+
children: React.ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Shared wrapper for all apps. Provides:
|
|
14
|
+
* - Auth gate: redirects to /login if unauthenticated
|
|
15
|
+
* - Role gate: shows 403 if user lacks app's min_role
|
|
16
|
+
* - App context: makes app record available via useCurrentApp()
|
|
17
|
+
*/
|
|
18
|
+
export function AppWrapper({ app, children }: AppWrapperProps) {
|
|
19
|
+
const { user } = useAuth()
|
|
20
|
+
|
|
21
|
+
// Auth gate
|
|
22
|
+
if (!user) {
|
|
23
|
+
return <Navigate to="/login" replace />
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Role gate
|
|
27
|
+
if (app.min_role) {
|
|
28
|
+
const hasRole = user.roles?.includes('system_admin') || user.roles?.includes(app.min_role)
|
|
29
|
+
if (!hasRole) {
|
|
30
|
+
return (
|
|
31
|
+
<div className="min-h-screen flex items-center justify-center bg-slate-50">
|
|
32
|
+
<div className="text-center p-8">
|
|
33
|
+
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
34
|
+
<svg className="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
35
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
|
36
|
+
</svg>
|
|
37
|
+
</div>
|
|
38
|
+
<h1 className="text-2xl font-bold text-slate-900 mb-2">Access Denied</h1>
|
|
39
|
+
<p className="text-slate-600 mb-6">You don't have permission to access {app.name}.</p>
|
|
40
|
+
<div className="bg-slate-100 rounded-lg p-4 text-left">
|
|
41
|
+
<p className="text-sm text-slate-600 mb-2"><strong>Your roles:</strong> {user.roles?.join(', ') || 'None'}</p>
|
|
42
|
+
<p className="text-sm text-slate-600"><strong>Required:</strong> {app.min_role}</p>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// App context
|
|
51
|
+
return (
|
|
52
|
+
<AppProvider app={app}>
|
|
53
|
+
{children}
|
|
54
|
+
</AppProvider>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React, { lazy, Suspense, Component } from 'react'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { LoadingSpinner } from './ui/LoadingSpinner'
|
|
4
|
+
|
|
5
|
+
// ─── ERROR BOUNDARY ───────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
interface ErrorBoundaryProps {
|
|
8
|
+
slug: string
|
|
9
|
+
children: ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ErrorBoundaryState {
|
|
13
|
+
error: Error | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class AppErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
17
|
+
constructor(props: ErrorBoundaryProps) {
|
|
18
|
+
super(props)
|
|
19
|
+
this.state = { error: null }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
23
|
+
return { error }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
render() {
|
|
27
|
+
if (this.state.error) {
|
|
28
|
+
return (
|
|
29
|
+
<div className="min-h-screen flex items-center justify-center bg-slate-50">
|
|
30
|
+
<div className="text-center p-8 max-w-lg">
|
|
31
|
+
<div className="w-16 h-16 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
32
|
+
<svg className="w-8 h-8 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
33
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
|
34
|
+
</svg>
|
|
35
|
+
</div>
|
|
36
|
+
<h1 className="text-2xl font-bold text-slate-900 mb-2">App Load Error</h1>
|
|
37
|
+
<p className="text-slate-600 mb-4">
|
|
38
|
+
Failed to load the <strong>{this.props.slug}</strong> app.
|
|
39
|
+
</p>
|
|
40
|
+
<pre className="bg-slate-100 rounded-lg p-4 text-left text-sm text-red-600 overflow-auto mb-4">
|
|
41
|
+
{this.state.error.message}
|
|
42
|
+
</pre>
|
|
43
|
+
<p className="text-sm text-slate-500">
|
|
44
|
+
Check that <code>apps/{this.props.slug}/index.tsx</code> exists and exports a default component.
|
|
45
|
+
</p>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return this.props.children
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─── LOADER ───────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
// Static glob maps — Vite analyzes these at build time and builds a complete
|
|
58
|
+
// module map. Runtime lookup is just a map key lookup, no dynamic string eval.
|
|
59
|
+
// Custom apps take precedence over core apps with the same slug.
|
|
60
|
+
const customAppModules = import.meta.glob('../../../custom/apps/*/index.tsx')
|
|
61
|
+
const coreAppModules = import.meta.glob('../apps/*/index.tsx')
|
|
62
|
+
|
|
63
|
+
// Module-level cache for lazy components to avoid re-creating on each render
|
|
64
|
+
const appModuleCache = new Map<string, React.LazyExoticComponent<React.ComponentType>>()
|
|
65
|
+
|
|
66
|
+
// Resolve the glob key for a given slug from the custom or core map.
|
|
67
|
+
function resolveAppLoader(slug: string): (() => Promise<{ default: React.ComponentType }>) | null {
|
|
68
|
+
// Check custom first (custom/apps/{slug}/index.tsx)
|
|
69
|
+
const customKey = `../../../custom/apps/${slug}/index.tsx`
|
|
70
|
+
if (customAppModules[customKey]) {
|
|
71
|
+
return customAppModules[customKey] as () => Promise<{ default: React.ComponentType }>
|
|
72
|
+
}
|
|
73
|
+
// Fall back to core (../apps/{slug}/index.tsx relative to this file)
|
|
74
|
+
const coreKey = `../apps/${slug}/index.tsx`
|
|
75
|
+
if (coreAppModules[coreKey]) {
|
|
76
|
+
return coreAppModules[coreKey] as () => Promise<{ default: React.ComponentType }>
|
|
77
|
+
}
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getAppComponent(slug: string): React.LazyExoticComponent<React.ComponentType> {
|
|
82
|
+
if (!appModuleCache.has(slug)) {
|
|
83
|
+
const loader = resolveAppLoader(slug)
|
|
84
|
+
const LazyComponent = lazy(
|
|
85
|
+
loader
|
|
86
|
+
? loader
|
|
87
|
+
: () => Promise.reject(new Error(`App "${slug}" not found. Expected apps/${slug}/index.tsx in custom/apps or .framework/src/apps.`))
|
|
88
|
+
)
|
|
89
|
+
appModuleCache.set(slug, LazyComponent)
|
|
90
|
+
}
|
|
91
|
+
return appModuleCache.get(slug)!
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface CustomAppLoaderProps {
|
|
95
|
+
slug: string
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Lazy-loads a custom app component from apps/{slug}/index.tsx.
|
|
100
|
+
* Wraps in an error boundary so a broken app doesn't crash the entire site.
|
|
101
|
+
*/
|
|
102
|
+
export function CustomAppLoader({ slug }: CustomAppLoaderProps) {
|
|
103
|
+
const AppComponent = getAppComponent(slug)
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<AppErrorBoundary slug={slug}>
|
|
107
|
+
<Suspense fallback={
|
|
108
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
109
|
+
<LoadingSpinner />
|
|
110
|
+
</div>
|
|
111
|
+
}>
|
|
112
|
+
<AppComponent />
|
|
113
|
+
</Suspense>
|
|
114
|
+
</AppErrorBoundary>
|
|
115
|
+
)
|
|
116
|
+
}
|