spine-framework 0.3.144 → 0.4.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/.env.example +76 -0
- package/.framework/.spine-manifest.json +19 -0
- package/.framework/cli/commands/agents.ts +61 -33
- package/.framework/cli/commands/auth.ts +60 -28
- package/.framework/cli/commands/create-app.ts +212 -80
- package/.framework/cli/commands/dev.ts +184 -68
- package/.framework/cli/commands/doctor.ts +324 -152
- package/.framework/cli/commands/generate.ts +119 -68
- package/.framework/cli/commands/init.ts +141 -242
- package/.framework/cli/commands/install-app.ts +1336 -314
- package/.framework/cli/commands/items.ts +31 -29
- package/.framework/cli/commands/migrate.ts +299 -156
- package/.framework/cli/commands/migrations.ts +62 -30
- package/.framework/cli/commands/pipelines.ts +31 -29
- package/.framework/cli/commands/status.ts +105 -45
- package/.framework/cli/commands/system.ts +47 -42
- package/.framework/cli/commands/test.ts +161 -102
- package/.framework/cli/commands/upgrade.ts +260 -0
- package/.framework/cli/commands/validate-app.ts +517 -161
- package/.framework/cli/context.ts +117 -85
- package/.framework/cli/env-loader.ts +74 -43
- package/.framework/cli/index.ts +70 -46
- package/.framework/cli/lib/project-files.ts +404 -0
- package/.framework/docs/guides/testing-guide.md +1 -1
- package/.framework/docs/runtime-types.md +72 -72
- package/.framework/functions/_shared/access.ts +84 -0
- package/.framework/functions/_shared/agent-runner.ts +5051 -1026
- package/.framework/functions/_shared/app-manifest.ts +214 -94
- package/.framework/functions/_shared/ast.ts +214 -0
- package/.framework/functions/_shared/audit.ts +91 -55
- package/.framework/functions/_shared/cil.ts +368 -127
- package/.framework/functions/_shared/db.ts +337 -68
- package/.framework/functions/_shared/funnel.ts +228 -0
- package/.framework/functions/_shared/geo.ts +105 -0
- package/.framework/functions/_shared/hash-api-key.ts +94 -17
- package/.framework/functions/_shared/index.ts +22 -224
- package/.framework/functions/_shared/kb.ts +832 -58
- package/.framework/functions/_shared/llm.ts +118 -18
- package/.framework/functions/_shared/logging.ts +317 -19
- package/.framework/functions/_shared/middleware.ts +701 -131
- package/.framework/functions/_shared/notify.ts +70 -0
- package/.framework/functions/_shared/outcomes.ts +34 -19
- package/.framework/functions/_shared/permissions.ts +501 -315
- package/.framework/functions/_shared/pipeline-runner.ts +704 -155
- package/.framework/functions/_shared/principal.ts +436 -263
- package/.framework/functions/_shared/rate-limit.ts +233 -0
- package/.framework/functions/_shared/resolve-credentials.ts +86 -19
- package/.framework/functions/_shared/resolve-ids.ts +203 -139
- package/.framework/functions/_shared/retry-deliveries.ts +51 -16
- package/.framework/functions/_shared/schema-utils.ts +602 -521
- package/.framework/functions/_shared/security.ts +345 -0
- package/.framework/functions/_shared/testing.ts +197 -172
- package/.framework/functions/_shared/tracing.ts +351 -21
- package/.framework/functions/_shared/trigger-engine.ts +425 -136
- package/.framework/functions/_shared/webhook-registration.ts +71 -70
- package/.framework/functions/_shared/webhook-registry.ts +126 -83
- package/.framework/functions/account-nodes.ts +30 -30
- package/.framework/functions/adapters/generic-webhook-inbound.ts +49 -16
- package/.framework/functions/adapters/generic-webhook-outbound.ts +17 -17
- package/.framework/functions/adapters/openai-outbound.ts +15 -16
- package/.framework/functions/admin-data.ts +596 -173
- package/.framework/functions/admin-funnel.ts +64 -60
- package/.framework/functions/admin-install-funnel.ts +84 -66
- package/.framework/functions/admin-signals.ts +69 -51
- package/.framework/functions/ai-agents.ts +431 -106
- package/.framework/functions/ai-interactions.ts +186 -112
- package/.framework/functions/api-keys.ts +107 -107
- package/.framework/functions/apps.ts +154 -136
- package/.framework/functions/auth.ts +83 -65
- package/.framework/functions/cil-agent-ingest.ts +189 -232
- package/.framework/functions/cil-agent.ts +433 -177
- package/.framework/functions/cil-callmap.ts +642 -430
- package/.framework/functions/cil-config.ts +139 -135
- package/.framework/functions/cil-context.ts +214 -127
- package/.framework/functions/cil-doc.ts +587 -458
- package/.framework/functions/cil-embeddings.ts +453 -364
- package/.framework/functions/cil-graph.ts +427 -142
- package/.framework/functions/cil-ingest.ts +1418 -1154
- package/.framework/functions/cil-pipeline.ts +276 -21
- package/.framework/functions/cil-project.ts +659 -132
- package/.framework/functions/cil-validate.ts +198 -164
- package/.framework/functions/debug-auth.ts +57 -55
- package/.framework/functions/deliveries.ts +80 -21
- package/.framework/functions/embeddings.ts +1023 -318
- package/.framework/functions/eval-harness.ts +146 -108
- package/.framework/functions/integration-outbound.ts +205 -75
- package/.framework/functions/integration-routes.ts +128 -23
- package/.framework/functions/integrations.ts +125 -100
- package/.framework/functions/invites.ts +1095 -460
- package/.framework/functions/item-progress.ts +158 -102
- package/.framework/functions/kb.ts +34 -10
- package/.framework/functions/logs.ts +133 -133
- package/.framework/functions/observability.ts +152 -122
- package/.framework/functions/pipeline-executions.ts +147 -132
- package/.framework/functions/pipelines.ts +134 -119
- package/.framework/functions/prompt-configs.ts +96 -80
- package/.framework/functions/roles.ts +112 -82
- package/.framework/functions/security.ts +450 -0
- package/.framework/functions/system-cron.ts +117 -80
- package/.framework/functions/system.ts +158 -98
- package/.framework/functions/tests.ts +29 -29
- package/.framework/functions/timers.ts +94 -95
- package/.framework/functions/trigger-scheduler.ts +10 -0
- package/.framework/functions/triggers.ts +179 -149
- package/.framework/functions/types.ts +137 -125
- package/.framework/mcp/index.ts +133 -0
- package/.framework/mcp/load-env.ts +60 -0
- package/.framework/mcp/shared/app-manifest.ts +275 -0
- package/.framework/mcp/shared/db.ts +477 -0
- package/.framework/mcp/shared/kb.ts +118 -0
- package/.framework/mcp/tools/architect.ts +431 -0
- package/.framework/mcp/tools/cil/context.ts +345 -0
- package/.framework/mcp/tools/cil/graph.ts +242 -0
- package/.framework/mcp/tools/cil/ingest.ts +1217 -0
- package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +693 -0
- package/.framework/mcp/tools/cil/pipeline.ts +767 -0
- package/.framework/mcp/tools/cil/project.ts +791 -0
- package/.framework/mcp/tools/cil/retrieval.ts +336 -0
- package/.framework/mcp/tools/cil/search-core.ts +454 -0
- package/.framework/mcp/tools/cil/search.ts +97 -0
- package/.framework/mcp/tools/cil/suggest.ts +186 -0
- package/.framework/mcp/tools/cil/validate.ts +1369 -0
- package/.framework/mcp/tools/kb/delete.ts +139 -0
- package/.framework/mcp/tools/kb/index.ts +52 -0
- package/.framework/mcp/tools/kb/ingest.ts +84 -0
- package/.framework/mcp/tools/kb/list-types.ts +102 -0
- package/.framework/mcp/tools/kb/publish.ts +100 -0
- package/.framework/mcp/tools/kb/reindex.ts +86 -0
- package/.framework/mcp/tools/kb/revoke.ts +85 -0
- package/.framework/mcp/tools/kb/search.ts +101 -0
- package/.framework/mcp/tools/kb/upsert.ts +125 -0
- package/.framework/mcp/tools/observability.ts +510 -0
- package/.framework/migrations/001_core_schema.sql +15 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +15 -0
- package/.framework/migrations/003_core_system_data.sql +16 -1
- package/.framework/migrations/004_core_rpc_and_broker.sql +17 -1
- package/.framework/migrations/005_core_observability.sql +17 -1
- package/.framework/migrations/006_security_hardening_rls.sql +15 -0
- package/.framework/migrations/007_kb_article_list.sql +17 -1
- package/.framework/migrations/008_items_target.sql +15 -0
- package/.framework/migrations/009_roles_slug_unique.sql +15 -0
- package/.framework/migrations/010_auth_user_trigger.sql +15 -0
- package/.framework/migrations/011_spine_system_account.sql +16 -0
- package/.framework/migrations/012_service_role_grants.sql +16 -0
- package/.framework/migrations/013_get_account_apps_role_filter.sql +15 -0
- package/.framework/migrations/014_apps_required_roles.sql +16 -0
- package/.framework/migrations/015_types_kind_slug_unique.sql +20 -0
- package/.framework/migrations/016_cil_types.sql +281 -0
- package/.framework/migrations/017_cil_stable_ids.sql +37 -0
- package/.framework/migrations/018_runtime_tables.sql +129 -0
- package/.framework/migrations/019_agent_prompt_columns.sql +56 -0
- package/.framework/migrations/020_schema_reconciliation.sql +67 -0
- package/.framework/migrations/021_kb_embedding_lifecycle.sql +31 -0
- package/.framework/migrations/032_cil_status_migration.sql +73 -0
- package/.framework/migrations/033_cil_versioning.sql +73 -0
- package/.framework/migrations/034_match_embeddings_knn.sql +115 -0
- package/.framework/migrations/035_rate_limits.sql +55 -0
- package/.framework/migrations/036_demo_record_flags.sql +72 -0
- package/.framework/migrations/037_kb_types_canonical.sql +244 -0
- package/.framework/migrations/038_drop_demo_record_flags.sql +38 -0
- package/.framework/migrations/042_registration_hierarchy.sql +309 -0
- package/.framework/migrations/043_policy_actor_cleanup.sql +39 -0
- package/.framework/migrations/044_match_embeddings_filtered_knn.sql +151 -0
- package/.framework/migrations/045_security_critical_rls.sql +139 -0
- package/.framework/migrations/046_security_missing_rls.sql +79 -0
- package/.framework/migrations/047_people_updated_by.sql +27 -0
- package/.framework/migrations/048_watcher_last_seen_at.sql +24 -0
- package/.framework/migrations/049_security_blocklist.sql +148 -0
- package/.framework/migrations/050_notifications.sql +49 -0
- package/.framework/migrations/051_message_agent_fields.sql +74 -0
- package/.framework/migrations/052_registration_security.sql +421 -0
- package/.framework/migrations/053_builtin_action_seeds.sql +66 -0
- package/.framework/migrations/054_kb_ingest_lease.sql +157 -0
- package/.framework/migrations/055_type_id_drift_reconciliation.sql +33 -0
- package/.framework/migrations/056_slug_column_reconciliation.sql +38 -0
- package/.framework/migrations/057_pipelines_slug_identity.sql +14 -0
- package/.framework/migrations/058_account_data_field_declarations.sql +53 -0
- package/.framework/migrations/059_registration_placement_fixes.sql +269 -0
- package/.framework/migrations/060_account_person_record_permissions.sql +38 -0
- package/.framework/migrations/061_fk_dedup.sql +32 -0
- package/.framework/public/spine-logo.svg +10 -3
- package/.framework/scripts/app-install-cli.ts +190 -93
- package/.framework/scripts/assemble-frontend.sh +127 -44
- package/.framework/scripts/assemble-functions.sh +128 -30
- package/.framework/scripts/boundary-check.sh +6 -2
- package/.framework/scripts/build-manifest.sh +24 -68
- package/.framework/scripts/check-core-integrity.sh +15 -76
- package/.framework/scripts/checksum-manifest.mts +343 -0
- package/.framework/scripts/cil-migrate-markers.ts +403 -0
- package/.framework/scripts/fix-cil-markers.mjs +176 -0
- package/.framework/scripts/verify-integrity.sh +21 -57
- package/.framework/scripts/verify-manifest.mts +305 -0
- package/.framework/seeds/types/funnel_signal_use.json +74 -21
- package/.framework/seeds/types/spine_instance.json +73 -21
- package/.framework/src/App.tsx +118 -72
- package/.framework/src/apps/admin/index.tsx +855 -58
- package/.framework/src/components/AppWrapper.tsx +43 -45
- package/.framework/src/components/CustomAppLoader.tsx +160 -116
- package/.framework/src/components/admin/AdminListPage.tsx +46 -44
- package/.framework/src/components/admin/AdminSidebar.tsx +57 -47
- package/.framework/src/components/admin/AdminStatsCard.tsx +46 -44
- package/.framework/src/components/admin/PageInstructions.tsx +98 -2
- package/.framework/src/components/admin/SortableTableHeader.tsx +44 -45
- package/.framework/src/components/app-shell/GenericAppShell.tsx +64 -65
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +44 -47
- package/.framework/src/components/app-shell/GenericListPage.tsx +45 -49
- package/.framework/src/components/app-sidebar.tsx +42 -45
- package/.framework/src/components/auth/ProtectedRoute.tsx +48 -41
- package/.framework/src/components/auth/TurnstileWidget.tsx +27 -0
- package/.framework/src/components/kb/KBArticleEditor.tsx +231 -22
- package/.framework/src/components/kb/KBArticleList.tsx +251 -10
- package/.framework/src/components/layout/AppShell.tsx +51 -45
- package/.framework/src/components/layout/Header.tsx +42 -43
- package/.framework/src/components/layout/Layout.tsx +46 -46
- package/.framework/src/components/layout/Sidebar.tsx +196 -96
- package/.framework/src/components/runtime/DataDetailHeader.tsx +55 -30
- package/.framework/src/components/runtime/DataDetailPage.tsx +31 -269
- package/.framework/src/components/runtime/DataFilters.tsx +49 -74
- package/.framework/src/components/runtime/DataHeader.tsx +53 -32
- package/.framework/src/components/runtime/DataListPage.tsx +51 -155
- package/.framework/src/components/runtime/DataStats.tsx +66 -44
- package/.framework/src/components/runtime/DataTable.tsx +49 -149
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +45 -75
- package/.framework/src/components/runtime/index.ts +119 -16
- package/.framework/src/components/search-form.tsx +46 -29
- package/.framework/src/components/shared/AgentView.tsx +65 -48
- package/.framework/src/components/shared/FieldRenderer.tsx +144 -50
- package/.framework/src/components/shared/InviteManager.tsx +207 -97
- package/.framework/src/components/shared/MemberList.tsx +97 -0
- package/.framework/src/components/shared/SchemaFields.tsx +107 -86
- package/.framework/src/components/ui/DataTable.tsx +80 -68
- package/.framework/src/components/ui/Form.tsx +157 -146
- package/.framework/src/components/ui/ItemCard.tsx +74 -67
- package/.framework/src/components/ui/ItemListView.tsx +73 -68
- package/.framework/src/components/ui/LoadingSpinner.tsx +43 -48
- package/.framework/src/components/ui/Modal.tsx +78 -68
- package/.framework/src/components/ui/RichTextEditor.tsx +89 -69
- package/.framework/src/components/ui/accordion.tsx +85 -86
- package/.framework/src/components/ui/alert-dialog.tsx +197 -184
- package/.framework/src/components/ui/alert.tsx +105 -94
- package/.framework/src/components/ui/aspect-ratio.tsx +57 -43
- package/.framework/src/components/ui/avatar.tsx +122 -107
- package/.framework/src/components/ui/badge.tsx +60 -55
- package/.framework/src/components/ui/breadcrumb.tsx +127 -128
- package/.framework/src/components/ui/button-group.tsx +86 -72
- package/.framework/src/components/ui/button.tsx +71 -50
- package/.framework/src/components/ui/calendar.tsx +72 -57
- package/.framework/src/components/ui/card.tsx +136 -129
- package/.framework/src/components/ui/carousel.tsx +226 -107
- package/.framework/src/components/ui/chart.tsx +252 -147
- package/.framework/src/components/ui/checkbox.tsx +43 -44
- package/.framework/src/components/ui/collapsible.tsx +86 -71
- package/.framework/src/components/ui/combobox.tsx +282 -249
- package/.framework/src/components/ui/command.tsx +158 -146
- package/.framework/src/components/ui/context-menu.tsx +239 -240
- package/.framework/src/components/ui/dialog.tsx +174 -157
- package/.framework/src/components/ui/direction.tsx +72 -56
- package/.framework/src/components/ui/drawer.tsx +168 -169
- package/.framework/src/components/ui/dropdown-menu.tsx +254 -223
- package/.framework/src/components/ui/empty.tsx +131 -120
- package/.framework/src/components/ui/field.tsx +205 -173
- package/.framework/src/components/ui/hover-card.tsx +71 -72
- package/.framework/src/components/ui/input-group.tsx +148 -133
- package/.framework/src/components/ui/input-otp.tsx +99 -86
- package/.framework/src/components/ui/input.tsx +43 -41
- package/.framework/src/components/ui/item.tsx +207 -185
- package/.framework/src/components/ui/kbd.tsx +57 -56
- package/.framework/src/components/ui/label.tsx +45 -41
- package/.framework/src/components/ui/menubar.tsx +268 -236
- package/.framework/src/components/ui/native-select.tsx +85 -86
- package/.framework/src/components/ui/navigation-menu.tsx +166 -156
- package/.framework/src/components/ui/pagination.tsx +131 -141
- package/.framework/src/components/ui/popover.tsx +125 -128
- package/.framework/src/components/ui/progress.tsx +47 -44
- package/.framework/src/components/ui/radio-group.tsx +60 -55
- package/.framework/src/components/ui/resizable.tsx +85 -72
- package/.framework/src/components/ui/scroll-area.tsx +57 -58
- package/.framework/src/components/ui/select.tsx +182 -169
- package/.framework/src/components/ui/separator.tsx +42 -43
- package/.framework/src/components/ui/sheet.tsx +170 -159
- package/.framework/src/components/ui/sidebar.tsx +395 -398
- package/.framework/src/components/ui/skeleton.tsx +42 -43
- package/.framework/src/components/ui/slider.tsx +61 -44
- package/.framework/src/components/ui/sonner.tsx +44 -44
- package/.framework/src/components/ui/spinner.tsx +47 -42
- package/.framework/src/components/ui/switch.tsx +56 -42
- package/.framework/src/components/ui/table-primitives.tsx +272 -132
- package/.framework/src/components/ui/table.tsx +140 -142
- package/.framework/src/components/ui/tabs.tsx +110 -93
- package/.framework/src/components/ui/textarea.tsx +43 -44
- package/.framework/src/components/ui/toggle-group.tsx +87 -71
- package/.framework/src/components/ui/toggle.tsx +56 -56
- package/.framework/src/components/ui/tooltip.tsx +99 -85
- package/.framework/src/contexts/AppContext.tsx +166 -114
- package/.framework/src/contexts/AuthContext.tsx +232 -137
- package/.framework/src/hooks/use-mobile.ts +43 -44
- package/.framework/src/hooks/useApi.ts +185 -172
- package/.framework/src/hooks/useAppPath.ts +31 -29
- package/.framework/src/hooks/useApps.ts +77 -42
- package/.framework/src/hooks/useEntityList.ts +66 -29
- package/.framework/src/hooks/useEntityRecord.ts +76 -32
- package/.framework/src/hooks/useForm.ts +62 -30
- package/.framework/src/hooks/useListSchema.ts +60 -43
- package/.framework/src/hooks/useSchemaRecord.ts +115 -95
- package/.framework/src/lib/api.ts +258 -134
- package/.framework/src/lib/registration.ts +181 -0
- package/.framework/src/lib/supabase.ts +118 -81
- package/.framework/src/lib/utils.ts +266 -266
- package/.framework/src/main.tsx +43 -44
- package/.framework/src/pages/DashboardPage.tsx +44 -43
- package/.framework/src/pages/NoAppPage.tsx +50 -0
- package/.framework/src/pages/NotFoundPage.tsx +32 -29
- package/.framework/src/pages/SetupPage.tsx +34 -33
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +69 -58
- package/.framework/src/pages/admin/AIAgentsPage.tsx +43 -40
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +63 -55
- package/.framework/src/pages/admin/APIKeysPage.tsx +44 -42
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +117 -94
- package/.framework/src/pages/admin/AppDetailPage.tsx +48 -42
- package/.framework/src/pages/admin/AppsPage.tsx +45 -42
- package/.framework/src/pages/admin/DeliveriesPage.tsx +49 -17
- package/.framework/src/pages/admin/DesignedPage.tsx +31 -30
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +58 -0
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +49 -0
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +31 -29
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +46 -29
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -29
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +68 -55
- package/.framework/src/pages/admin/IntegrationsPage.tsx +45 -42
- package/.framework/src/pages/admin/InvitesPage.tsx +60 -44
- package/.framework/src/pages/admin/LogsPage.tsx +87 -137
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +31 -29
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +150 -89
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +77 -104
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +44 -43
- package/.framework/src/pages/admin/PipelinesPage.tsx +59 -145
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +53 -42
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +47 -117
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +31 -44
- package/.framework/src/pages/admin/RoleDetailPage.tsx +49 -92
- package/.framework/src/pages/admin/RolesPage.tsx +47 -164
- package/.framework/src/pages/admin/SecurityPage.tsx +310 -0
- package/.framework/src/pages/admin/SelectTestPage.tsx +30 -32
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +37 -44
- package/.framework/src/pages/admin/SimpleDashboard.tsx +32 -44
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +97 -44
- package/.framework/src/pages/admin/TestingDashboard.tsx +126 -71
- package/.framework/src/pages/admin/TimerDetailPage.tsx +67 -55
- package/.framework/src/pages/admin/TimersPage.tsx +44 -42
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +47 -42
- package/.framework/src/pages/admin/TriggersPage.tsx +58 -57
- package/.framework/src/pages/admin/TypeDetailPage.tsx +91 -55
- package/.framework/src/pages/admin/TypesPage.tsx +45 -42
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +258 -128
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +127 -123
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +308 -235
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +159 -110
- package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +432 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +200 -178
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +286 -239
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +211 -90
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +156 -143
- package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +379 -0
- package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +337 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +219 -214
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +135 -65
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +39 -31
- package/.framework/src/pages/auth/LoginPage.tsx +38 -32
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -28
- package/.framework/src/pages/auth/RegisterPage.tsx +429 -155
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -30
- package/.framework/src/pages/spine-framework/CLIPage.tsx +32 -30
- package/.framework/src/types/auth.ts +101 -93
- package/.framework/src/types/types.ts +283 -262
- package/.framework/templates/project.json +87 -0
- package/.framework/templates/spine.config.neutral.json +7 -0
- package/.framework/tests/api/api-surface.test.ts +381 -0
- package/.framework/tests/fixtures/seed.ts +427 -0
- package/.framework/tests/fixtures/teardown.ts +228 -0
- package/.framework/tests/reporter.ts +351 -0
- package/.framework/tests/setup.ts +135 -0
- package/.framework/tests/ui/ui-sweep.ts +278 -0
- package/.framework/tests/unit/admin-data-logger.test.ts +310 -0
- package/.framework/tests/unit/agent-runner.test.ts +2389 -0
- package/.framework/tests/unit/ai-interactions.test.ts +81 -0
- package/.framework/tests/unit/app-manifest.test.ts +359 -0
- package/.framework/tests/unit/assembly-scripts.test.ts +172 -0
- package/.framework/tests/unit/cil-determinism.test.ts +85 -0
- package/.framework/tests/unit/core-isolation.test.ts +153 -0
- package/.framework/tests/unit/funnel-signals.test.ts +135 -0
- package/.framework/tests/unit/install-app.test.ts +1759 -0
- package/.framework/tests/unit/invites-security.test.ts +572 -0
- package/.framework/tests/unit/kb-ingest-lease.test.ts +287 -0
- package/.framework/tests/unit/kb-search.test.ts +119 -0
- package/.framework/tests/unit/migrate.test.ts +509 -0
- package/.framework/tests/unit/notify.test.ts +136 -0
- package/.framework/tests/unit/permissions.test.ts +269 -0
- package/.framework/tests/unit/pipeline-runner.test.ts +755 -0
- package/.framework/tests/unit/principal-cache.test.ts +138 -0
- package/.framework/tests/unit/retrieval-golden.test.ts +520 -0
- package/.framework/tests/unit/runtime-isolation.test.ts +642 -0
- package/.framework/tests/unit/schema-utils.test.ts +225 -0
- package/.framework/tests/unit/trigger-engine.test.ts +513 -0
- package/.framework/tests/unit/validate-app.test.ts +527 -0
- package/README.md +34 -3
- package/bin/spine-framework.cjs +1 -0
- package/config/tailwind.config.cjs +7 -0
- package/config/tsconfig.functions.json +42 -0
- package/config/tsconfig.json +37 -9
- package/config/vite.config.ts +31 -2
- package/netlify.toml +31 -3
- package/package.json +46 -29
- package/scripts/assemble-frontend.sh +127 -44
- package/scripts/assemble-functions.sh +127 -29
- package/scripts/boundary-check.sh +6 -2
- package/scripts/build-manifest.sh +24 -68
- package/scripts/checksum-manifest.mts +345 -0
- package/scripts/dev-clean.sh +86 -0
- package/scripts/dev-netlify.sh +49 -0
- package/scripts/netlify-dev-wrapper.sh +2 -0
- package/scripts/verify-install.ts +358 -0
- package/scripts/verify-integrity.sh +21 -57
- package/scripts/verify-manifest.mts +305 -0
- package/templates/project/gitignore +45 -0
- package/{config → templates/project}/package-lock.json +22335 -9366
- package/vitest.config.ts +46 -0
- package/.framework/public/spine.config.json +0 -91
- package/config/deno.lock +0 -108
- package/config/tsconfig.node.tsbuildinfo +0 -1
- package/config/tsconfig.tsbuildinfo +0 -1
- package/config/vite.config.d.ts +0 -2
- package/custom/.devin/AGENT.md +0 -357
- package/custom/.devin/skills/install-app/SKILL.md +0 -39
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +0 -39
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +0 -36
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +0 -46
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +0 -28
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +0 -38
- package/custom/.devin/workflows/app-install.yaml +0 -83
- package/custom/README.md +0 -79
- package/docs/README.md +0 -15
- package/scripts/app-install-cli.ts +0 -286
- package/scripts/check-core-integrity.sh +0 -82
- package/scripts/ingest-chunks.cjs +0 -202
- package/scripts/kb-chunk-parser.cjs +0 -312
- package/scripts/kb-chunk-parser.ts +0 -330
- package/scripts/kb-ingest-docs.ts +0 -203
- package/scripts/load-test-app-install.ts +0 -484
- package/scripts/test-login.js +0 -33
- package/spine.config.json +0 -91
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
// CIL[BEGIN stable_id="~/scripts/verify-install.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/scripts/verify-install.ts"]
|
|
3
|
+
// CIL[DOC-MACRO purpose="Read-only post-install contract check for a standalone repository against a target Supabase project"]
|
|
4
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Verify required tables exist, required RPCs are callable, the app row + registration config are present, and every seed-declared row (types, roles, link types, agents, prompt configs, pipelines, triggers) materialized with resolvable pipeline/stage references — plus optional emptiness assertions for fresh-database acceptance"]
|
|
5
|
+
// CIL[DOC-CONTEXT module="export" layer="tooling" related_files="scripts/export-standalone.ts,custom/apps/demo/manifest.json,scripts/check-db-schema.ts"]
|
|
6
|
+
// CIL[DOC-CONTRACT inputs="--app <slug> [--expect-empty-people] [--expect-empty-accounts] [--expect-empty-runtime] [--expected-project-ref <ref>]; .env with SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY" outputs="stdout check report; exit 0 all pass" errors="exit 2 usage/missing env; exit 1 any failed check" side_effects="READ-ONLY: table probes use HEAD selects; RPC probes use invalid argument values so the function signature resolves but execution fails before any write; auth admin listUsers reads only"]
|
|
7
|
+
// CIL[DOC-MICRO logic_summary="RPC existence is probed by passing a malformed value to a typed parameter — PG rejects coercion (22P02) before executing the body, distinguishing 'exists but errors' from PostgREST PGRST202 'function not found'. Seed inventory is derived from the app manifest + seed JSONs, nothing is hardcoded" notable_decisions="no repair mode — this tool never writes; failures are reported for the operator"]
|
|
8
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
9
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
10
|
+
import { createClient } from '@supabase/supabase-js'
|
|
11
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs'
|
|
12
|
+
import { join, resolve, dirname } from 'node:path'
|
|
13
|
+
import { fileURLToPath } from 'node:url'
|
|
14
|
+
// CIL[END stable_id="~/scripts/verify-install.ts:module_header:module_header"]
|
|
15
|
+
|
|
16
|
+
// CIL[BEGIN stable_id="~/scripts/verify-install.ts:setup:block" type="block" identity="setup" parent="~/scripts/verify-install.ts"]
|
|
17
|
+
// CIL[DOC-MACRO purpose="Env loading, arg parsing, result tracking"]
|
|
18
|
+
function loadEnv(): void {
|
|
19
|
+
const envPath = join(process.cwd(), '.env')
|
|
20
|
+
if (!existsSync(envPath)) return
|
|
21
|
+
for (const line of readFileSync(envPath, 'utf8').split('\n')) {
|
|
22
|
+
const t = line.trim()
|
|
23
|
+
if (!t || t.startsWith('#')) continue
|
|
24
|
+
const eq = t.indexOf('=')
|
|
25
|
+
if (eq === -1) continue
|
|
26
|
+
const key = t.slice(0, eq).trim()
|
|
27
|
+
const val = t.slice(eq + 1).trim()
|
|
28
|
+
if (key && !(key in process.env)) process.env[key] = val
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const args = process.argv.slice(2)
|
|
33
|
+
const appIdx = args.indexOf('--app')
|
|
34
|
+
const appSlug = appIdx >= 0 ? args[appIdx + 1] : undefined
|
|
35
|
+
const expectEmptyPeople = args.includes('--expect-empty-people')
|
|
36
|
+
const expectEmptyAccounts = args.includes('--expect-empty-accounts')
|
|
37
|
+
const expectEmptyRuntime = args.includes('--expect-empty-runtime')
|
|
38
|
+
const refIdx = args.indexOf('--expected-project-ref')
|
|
39
|
+
const expectedProjectRef = refIdx >= 0 ? args[refIdx + 1] : undefined
|
|
40
|
+
if (!appSlug) {
|
|
41
|
+
console.error('usage: tsx scripts/verify-install.ts --app <slug> [--expect-empty-people] [--expect-empty-accounts] [--expect-empty-runtime] [--expected-project-ref <ref>]')
|
|
42
|
+
process.exit(2)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
loadEnv()
|
|
46
|
+
const SUPABASE_URL = process.env.SUPABASE_URL
|
|
47
|
+
const SERVICE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY
|
|
48
|
+
if (!SUPABASE_URL || !SERVICE_KEY) {
|
|
49
|
+
console.error('❌ SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are required (.env or environment)')
|
|
50
|
+
process.exit(2)
|
|
51
|
+
}
|
|
52
|
+
const db = createClient(SUPABASE_URL, SERVICE_KEY, { auth: { persistSession: false } })
|
|
53
|
+
|
|
54
|
+
let passed = 0
|
|
55
|
+
const failures: string[] = []
|
|
56
|
+
function check(name: string, ok: boolean, detail = ''): void {
|
|
57
|
+
if (ok) { passed++; console.log(` ✓ ${name}`) }
|
|
58
|
+
else { failures.push(name); console.log(` ✗ ${name}${detail ? ` — ${detail}` : ''}`) }
|
|
59
|
+
}
|
|
60
|
+
// CIL[END stable_id="~/scripts/verify-install.ts:setup:block"]
|
|
61
|
+
|
|
62
|
+
// CIL[BEGIN stable_id="~/scripts/verify-install.ts:required:block" type="block" identity="required" parent="~/scripts/verify-install.ts"]
|
|
63
|
+
// CIL[DOC-MACRO purpose="Required tables and RPC probes"]
|
|
64
|
+
const REQUIRED_TABLES = [
|
|
65
|
+
'accounts', 'actions', 'ai_agents', 'apps', 'attachments',
|
|
66
|
+
'document_leases', 'embeddings', 'invites', 'items', 'link_types',
|
|
67
|
+
'links', 'messages', 'notifications', 'people', 'pipeline_executions',
|
|
68
|
+
'pipelines', 'prompt_configs', 'roles', 'security_blocklist',
|
|
69
|
+
'system_settings', 'threads', 'trigger_executions', 'triggers',
|
|
70
|
+
'types', 'watchers',
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
/** RPC name → args that resolve the signature but fail inside execution (no writes). */
|
|
74
|
+
const REQUIRED_RPCS: Record<string, Record<string, unknown>> = {
|
|
75
|
+
complete_registration_placement: {
|
|
76
|
+
p_person_id: '__probe__',
|
|
77
|
+
p_app_slug: '__probe__',
|
|
78
|
+
p_path: '__probe__',
|
|
79
|
+
p_account_name: '__probe__',
|
|
80
|
+
},
|
|
81
|
+
acquire_document_lease: {
|
|
82
|
+
p_document_id: '__probe__',
|
|
83
|
+
p_account_id: '__probe__',
|
|
84
|
+
p_content_hash: '__probe__',
|
|
85
|
+
p_ttl_seconds: 1,
|
|
86
|
+
},
|
|
87
|
+
release_document_lease: {
|
|
88
|
+
p_document_id: '__probe__',
|
|
89
|
+
p_lease_id: '__probe__',
|
|
90
|
+
p_status: '__probe__',
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
// CIL[END stable_id="~/scripts/verify-install.ts:required:block"]
|
|
94
|
+
|
|
95
|
+
// CIL[BEGIN stable_id="~/scripts/verify-install.ts:probes:block" type="block" identity="probes" parent="~/scripts/verify-install.ts"]
|
|
96
|
+
// CIL[DOC-MACRO purpose="Read-only DB probes"]
|
|
97
|
+
async function tableExists(table: string): Promise<boolean> {
|
|
98
|
+
const { error } = await db.from(table).select('*', { count: 'exact', head: true })
|
|
99
|
+
return !error
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function rowCount(table: string): Promise<number> {
|
|
103
|
+
const { count } = await db.from(table).select('*', { count: 'exact', head: true })
|
|
104
|
+
return count ?? -1
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function rpcExists(name: string, probeArgs: Record<string, unknown>): Promise<boolean> {
|
|
108
|
+
const { error } = await db.rpc(name, probeArgs)
|
|
109
|
+
if (!error) return true
|
|
110
|
+
return error.code !== 'PGRST202'
|
|
111
|
+
}
|
|
112
|
+
// CIL[END stable_id="~/scripts/verify-install.ts:probes:block"]
|
|
113
|
+
|
|
114
|
+
// CIL[BEGIN stable_id="~/scripts/verify-install.ts:seed_check:block" type="block" identity="seed_inventory" parent="~/scripts/verify-install.ts"]
|
|
115
|
+
// CIL[DOC-MACRO purpose="Compare the app's declared seed inventory against DB state"]
|
|
116
|
+
// CIL[DOC-MICRO logic_summary="Each seed table gets its own key extractor: types match kind+slug, most others match slug, prompt_configs match name (the table has no slug)" notable_decisions="DB is the oracle — seed rows are the contract the installer promised to materialize"]
|
|
117
|
+
interface SeedCheck { table: string; label: string; key: (row: any) => string }
|
|
118
|
+
|
|
119
|
+
const SEED_KEYS: Record<string, SeedCheck> = {
|
|
120
|
+
'seed/roles.json': { table: 'roles', label: 'role', key: (r) => r.slug },
|
|
121
|
+
'seed/types.json': { table: 'types', label: 'type', key: (r) => `${r.kind}/${r.slug}` },
|
|
122
|
+
'seed/link-types.json': { table: 'link_types', label: 'link_type', key: (r) => r.slug },
|
|
123
|
+
'seed/ai-agents.json': { table: 'ai_agents', label: 'ai_agent', key: (r) => r.slug ?? r.name },
|
|
124
|
+
'seed/prompt-configs.json': { table: 'prompt_configs', label: 'prompt_config', key: (r) => r.name },
|
|
125
|
+
'seed/pipelines.json': { table: 'pipelines', label: 'pipeline', key: (r) => r.slug },
|
|
126
|
+
'seed/triggers.json': { table: 'triggers', label: 'trigger', key: (r) => r.slug },
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function checkSeeds(appDir: string, seedEntries: any[]): Promise<void> {
|
|
130
|
+
for (const entry of seedEntries) {
|
|
131
|
+
const spec = SEED_KEYS[entry.file]
|
|
132
|
+
if (!spec) { console.log(` ○ no verifier for seed file ${entry.file} — skipped`); continue }
|
|
133
|
+
const file = join(appDir, entry.file)
|
|
134
|
+
if (!existsSync(file)) { check(`seed file ${entry.file} exists`, false); continue }
|
|
135
|
+
const rows: any[] = JSON.parse(readFileSync(file, 'utf8'))
|
|
136
|
+
for (const row of rows) {
|
|
137
|
+
const key = spec.key(row)
|
|
138
|
+
let ok = false
|
|
139
|
+
if (spec.table === 'types') {
|
|
140
|
+
const [kind, slug] = key.split('/')
|
|
141
|
+
const { data } = await db.from('types').select('slug, validation_schema').eq('kind', kind).eq('slug', slug).limit(1)
|
|
142
|
+
ok = (data?.length ?? 0) > 0
|
|
143
|
+
check(`${spec.label} seeded: ${key}`, ok)
|
|
144
|
+
if (ok) {
|
|
145
|
+
const vs = data![0].validation_schema
|
|
146
|
+
check(`${spec.label} validation_schema generated: ${key}`,
|
|
147
|
+
!!vs && typeof vs === 'object' && !Array.isArray(vs) && Object.keys(vs).length > 0)
|
|
148
|
+
}
|
|
149
|
+
continue
|
|
150
|
+
} else if (spec.table === 'prompt_configs') {
|
|
151
|
+
const { data } = await db.from(spec.table).select('name').eq('name', key).limit(1)
|
|
152
|
+
ok = (data?.length ?? 0) > 0
|
|
153
|
+
} else if (spec.table === 'ai_agents') {
|
|
154
|
+
// Seed rows may identify by slug (canonical) or name — query whichever
|
|
155
|
+
// identity the seed row actually carries.
|
|
156
|
+
const col = row.slug ? 'slug' : 'name'
|
|
157
|
+
const { data } = await db.from(spec.table).select(col).eq(col, key).limit(1)
|
|
158
|
+
ok = (data?.length ?? 0) > 0
|
|
159
|
+
} else {
|
|
160
|
+
const { data } = await db.from(spec.table).select('slug').eq('slug', key).limit(1)
|
|
161
|
+
ok = (data?.length ?? 0) > 0
|
|
162
|
+
}
|
|
163
|
+
check(`${spec.label} seeded: ${key}`, ok)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// CIL[END stable_id="~/scripts/verify-install.ts:seed_check:block"]
|
|
168
|
+
|
|
169
|
+
// CIL[BEGIN stable_id="~/scripts/verify-install.ts:main:function" type="function" identity="main" parent="~/scripts/verify-install.ts"]
|
|
170
|
+
// CIL[DOC-MACRO purpose="CLI entry — run all contract checks and report"]
|
|
171
|
+
async function main(): Promise<void> {
|
|
172
|
+
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
173
|
+
const appDir = join(repoRoot, 'custom/apps', appSlug)
|
|
174
|
+
const manifestPath = join(appDir, 'manifest.json')
|
|
175
|
+
if (!existsSync(manifestPath)) {
|
|
176
|
+
console.error(`❌ app manifest not found: ${manifestPath}`)
|
|
177
|
+
process.exit(2)
|
|
178
|
+
}
|
|
179
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
|
|
180
|
+
|
|
181
|
+
console.log(`\n🔍 verify-install: ${appSlug} → ${SUPABASE_URL}\n`)
|
|
182
|
+
|
|
183
|
+
// 1. Connectivity + target identity
|
|
184
|
+
const { error: pingErr } = await db.from('apps').select('slug', { head: true, count: 'exact' })
|
|
185
|
+
check('database connectivity', !pingErr, pingErr?.message ?? '')
|
|
186
|
+
if (pingErr) { summarize(); return }
|
|
187
|
+
if (expectedProjectRef) {
|
|
188
|
+
check(`target project is ${expectedProjectRef}`,
|
|
189
|
+
SUPABASE_URL === `https://${expectedProjectRef}.supabase.co`,
|
|
190
|
+
`SUPABASE_URL is ${SUPABASE_URL}`)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 2. Required tables
|
|
194
|
+
for (const t of REQUIRED_TABLES) check(`table exists: ${t}`, await tableExists(t))
|
|
195
|
+
|
|
196
|
+
// 3. Required RPCs
|
|
197
|
+
for (const [name, probeArgs] of Object.entries(REQUIRED_RPCS)) {
|
|
198
|
+
check(`rpc exists: ${name}`, await rpcExists(name, probeArgs))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 4. App row + registration config
|
|
202
|
+
const { data: appRows } = await db.from('apps').select('id, slug, config').eq('slug', appSlug).limit(1)
|
|
203
|
+
const app = appRows?.[0]
|
|
204
|
+
check(`app installed: ${appSlug}`, !!app)
|
|
205
|
+
check('app registration config present', !!app?.config?.registration,
|
|
206
|
+
app ? 'apps.config.registration missing' : '')
|
|
207
|
+
|
|
208
|
+
// 5. Seed inventory vs DB
|
|
209
|
+
if (Array.isArray(manifest.seed)) await checkSeeds(appDir, manifest.seed)
|
|
210
|
+
|
|
211
|
+
// 5b. App config materialization — the seeded apps.config must carry the
|
|
212
|
+
// manifest's declared knowledge envelope (runtime KB scope binding).
|
|
213
|
+
if (app && manifest.config?.knowledge !== undefined) {
|
|
214
|
+
const expected = JSON.stringify(manifest.config.knowledge)
|
|
215
|
+
const actual = JSON.stringify((app.config as any)?.knowledge)
|
|
216
|
+
check('app knowledge envelope materialized', actual === expected,
|
|
217
|
+
actual === undefined ? 'apps.config.knowledge missing' : '')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 5c. Migration inventory — every filename the export shipped must appear
|
|
221
|
+
// in the _spine_migrations tracking table (no silent skips on a virgin DB).
|
|
222
|
+
const exportManifestPath = join(repoRoot, '.spine-export-manifest.json')
|
|
223
|
+
// npm-created projects have no export manifest — the inventory falls back to
|
|
224
|
+
// the migration files on disk (.framework + custom).
|
|
225
|
+
let expectedMigrations: string[] | null = null
|
|
226
|
+
if (existsSync(exportManifestPath)) {
|
|
227
|
+
try {
|
|
228
|
+
const em = JSON.parse(readFileSync(exportManifestPath, 'utf8'))
|
|
229
|
+
expectedMigrations = [
|
|
230
|
+
...(em.migrations?.framework ?? []),
|
|
231
|
+
...(em.migrations?.custom ?? []),
|
|
232
|
+
]
|
|
233
|
+
} catch (e: any) {
|
|
234
|
+
check('export manifest readable', false, e?.message ?? '')
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
const listDir = (rel: string): string[] => {
|
|
238
|
+
const dir = join(repoRoot, rel)
|
|
239
|
+
return existsSync(dir)
|
|
240
|
+
? readdirSync(dir).filter((n) => n.endsWith('.sql')).sort()
|
|
241
|
+
: []
|
|
242
|
+
}
|
|
243
|
+
expectedMigrations = [...listDir('.framework/migrations'), ...listDir('custom/migrations')]
|
|
244
|
+
}
|
|
245
|
+
if (expectedMigrations) {
|
|
246
|
+
const { data: applied, error: migErr } = await db
|
|
247
|
+
.from('_spine_migrations')
|
|
248
|
+
.select('filename')
|
|
249
|
+
if (migErr) {
|
|
250
|
+
check('migration tracking table readable', false, migErr.message)
|
|
251
|
+
} else {
|
|
252
|
+
const appliedSet = new Set((applied ?? []).map((r: any) => r.filename))
|
|
253
|
+
check('migration inventory complete', expectedMigrations.every((f) => appliedSet.has(f)),
|
|
254
|
+
`${expectedMigrations.filter((f) => !appliedSet.has(f)).length} of ${expectedMigrations.length} not applied`)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 5d. No unexpected installed apps — builtin framework apps plus exactly
|
|
259
|
+
// this app; anything else is a seed/scope defect.
|
|
260
|
+
{
|
|
261
|
+
const { data: appRows2 } = await db.from('apps').select('slug, source')
|
|
262
|
+
const nonBuiltin = (appRows2 ?? []).filter((a: any) => a.source !== 'builtin').map((a: any) => a.slug)
|
|
263
|
+
check('only expected app installed', nonBuiltin.length === 1 && nonBuiltin[0] === appSlug,
|
|
264
|
+
`found: ${nonBuiltin.join(', ') || 'none'}`)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 5e. openai integration credentials must be an env-var reference, never a
|
|
268
|
+
// materialized secret. Report the source field only — never the value.
|
|
269
|
+
{
|
|
270
|
+
const { data: integ } = await db.from('integrations').select('slug, credentials')
|
|
271
|
+
.eq('slug', 'openai').limit(1)
|
|
272
|
+
const creds = (integ?.[0]?.credentials ?? {}) as any
|
|
273
|
+
const src = creds?.api_token?.source ?? creds?.api_key?.source
|
|
274
|
+
check('openai integration uses env-var credential reference', src === 'env_var',
|
|
275
|
+
src ? `source=${src}` : 'integration or credential block missing')
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 6. Pipeline stage → action resolution (app pipelines only)
|
|
279
|
+
if (app) {
|
|
280
|
+
const { data: pipelineRows } = await db.from('pipelines')
|
|
281
|
+
.select('slug, stages').eq('app_id', app.id)
|
|
282
|
+
const { data: actionRows } = await db.from('actions').select('slug')
|
|
283
|
+
const actionSlugs = new Set((actionRows ?? []).map((a: any) => a.slug))
|
|
284
|
+
for (const p of pipelineRows ?? []) {
|
|
285
|
+
const stages = Array.isArray(p.stages) ? p.stages : []
|
|
286
|
+
check(`pipeline has stages: ${p.slug}`, stages.length > 0)
|
|
287
|
+
for (const [i, st] of stages.entries()) {
|
|
288
|
+
check(`pipeline ${p.slug} stage[${i}] action "${st.stage_type}" exists`, actionSlugs.has(st.stage_type))
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// 7. Trigger → pipeline resolution
|
|
293
|
+
const { data: triggerRows } = await db.from('triggers')
|
|
294
|
+
.select('slug, pipeline_id').eq('app_id', app.id)
|
|
295
|
+
for (const tr of triggerRows ?? []) {
|
|
296
|
+
check(`trigger ${tr.slug} resolved to pipeline`, !!tr.pipeline_id)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 8. Fresh-database assertions
|
|
301
|
+
if (expectEmptyPeople) check('people table empty (fresh install)', (await rowCount('people')) === 0)
|
|
302
|
+
if (expectEmptyAccounts) check('accounts table empty (fresh install)', (await rowCount('accounts')) === 0)
|
|
303
|
+
|
|
304
|
+
// 8b. Empty runtime — no records of this app's seeded types may exist on a
|
|
305
|
+
// virgin install (config seeds materialize types, not runtime content).
|
|
306
|
+
if (expectEmptyRuntime && app) {
|
|
307
|
+
const kinds: Record<string, string> = { item: 'items', thread: 'threads', message: 'messages' }
|
|
308
|
+
const typesFile = join(appDir, 'seed/types.json')
|
|
309
|
+
if (existsSync(typesFile)) {
|
|
310
|
+
const typeRows: any[] = JSON.parse(readFileSync(typesFile, 'utf8'))
|
|
311
|
+
for (const t of typeRows) {
|
|
312
|
+
const table = kinds[t.kind]
|
|
313
|
+
if (!table) continue
|
|
314
|
+
const { data: tRow } = await db.from('types').select('id')
|
|
315
|
+
.eq('kind', t.kind).eq('slug', t.slug).limit(1)
|
|
316
|
+
if (!tRow?.[0]?.id) continue // covered by the seed checks above
|
|
317
|
+
const { count } = await db.from(table).select('*', { count: 'exact', head: true })
|
|
318
|
+
.eq('type_id', tRow[0].id)
|
|
319
|
+
check(`no runtime records: ${t.kind}/${t.slug}`, (count ?? 0) === 0, `found ${count}`)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 8c. Auth/people consistency — every auth user must resolve to a people
|
|
325
|
+
// row; a stale auth user on an empty people table is the classic
|
|
326
|
+
// post-purge "Loading…" failure. Read-only via the admin API.
|
|
327
|
+
if (expectEmptyPeople) {
|
|
328
|
+
const { data: authData, error: authErr } = await db.auth.admin.listUsers({ perPage: 1000 })
|
|
329
|
+
if (authErr) {
|
|
330
|
+
console.log(` ○ auth user check skipped: ${authErr.message}`)
|
|
331
|
+
} else {
|
|
332
|
+
const authIds = new Set((authData?.users ?? []).map((u: any) => u.id))
|
|
333
|
+
const { data: peopleRows } = await db.from('people').select('auth_uid')
|
|
334
|
+
const linked = new Set((peopleRows ?? []).map((p: any) => p.auth_uid).filter(Boolean))
|
|
335
|
+
const orphans = [...authIds].filter((id) => !linked.has(id))
|
|
336
|
+
check('no auth users without matching people', orphans.length === 0,
|
|
337
|
+
`${orphans.length} orphan auth user(s)`)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
summarize()
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function summarize(): void {
|
|
345
|
+
console.log('')
|
|
346
|
+
if (failures.length === 0) {
|
|
347
|
+
console.log(`✅ verify-install: all ${passed} checks passed`)
|
|
348
|
+
process.exit(0)
|
|
349
|
+
}
|
|
350
|
+
console.log(`❌ verify-install: ${failures.length} failed, ${passed} passed`)
|
|
351
|
+
process.exit(1)
|
|
352
|
+
}
|
|
353
|
+
// CIL[END stable_id="~/scripts/verify-install.ts:main:function"]
|
|
354
|
+
|
|
355
|
+
main().catch((err) => {
|
|
356
|
+
console.error('❌ verify-install crashed:', err)
|
|
357
|
+
process.exit(1)
|
|
358
|
+
})
|
|
@@ -1,69 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
2
|
+
# CIL[BEGIN stable_id="~/scripts/verify-integrity.sh:module:script" type="script" identity="verify-integrity" parent="~/scripts/verify-integrity.sh"]
|
|
3
|
+
# CIL[DOC-MACRO purpose="Thin wrapper that verifies protected trees against .spine-manifest.json via scripts/verify-manifest.mts"]
|
|
4
|
+
# CIL[DOC-RESPONSIBILITY responsibility="Map SPINE_INTEGRITY to verifier flags and invoke the Node verifier; fail closed by default"]
|
|
5
|
+
# CIL[DOC-CONTEXT module="spine-integrity" layer="build" related_files="scripts/verify-manifest.mts,scripts/build-manifest.sh,scripts/check-core-integrity.sh,package.json"]
|
|
6
|
+
# CIL[DOC-CONTRACT inputs="SPINE_INTEGRITY=warn|enforce (default enforce); passthrough args" outputs="exit 0 pass / exit 1 integrity failure" errors="exit 1 on missing tsx or verifier failure" side_effects="stdout/stderr report"]
|
|
7
|
+
# CIL[DOC-MICRO logic_summary="SPINE_INTEGRITY=warn passes --warn; the verifier itself ignores --warn when the manifest is frozen" notable_decisions="default is enforce — fail-closed — unlike the legacy warn-by-default script"]
|
|
8
|
+
# CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
9
|
+
# CIL[DOC-STABILITY stability="stable"]
|
|
10
|
+
# CIL[DOC-AUDIENCE audience="system-admin"]
|
|
11
|
+
# CIL[DOC-VECTOR type="context" content="Invoked by npm run verify and the retired check-core-integrity.sh shim"]
|
|
12
|
+
# CIL[DOC-VECTOR type="implementation_pattern" content="Sibling thin wrapper of build-manifest.sh; real logic in scripts/verify-manifest.mts" status="ready"]
|
|
13
|
+
# CIL[END stable_id="~/scripts/verify-integrity.sh:module:script"]
|
|
6
14
|
|
|
7
15
|
set -euo pipefail
|
|
8
16
|
|
|
9
17
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
18
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
11
|
-
MANIFEST_FILE="$PROJECT_ROOT/.spine-manifest.json"
|
|
12
19
|
|
|
13
|
-
INTEGRITY_MODE="${SPINE_INTEGRITY:-
|
|
20
|
+
INTEGRITY_MODE="${SPINE_INTEGRITY:-enforce}"
|
|
21
|
+
echo "🔒 Verifying spine integrity (mode: $INTEGRITY_MODE)"
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
exit
|
|
19
|
-
fi
|
|
20
|
-
|
|
21
|
-
echo "🔒 Verifying core integrity (mode: $INTEGRITY_MODE)"
|
|
22
|
-
|
|
23
|
-
MISMATCHES=0
|
|
24
|
-
MISSING=0
|
|
25
|
-
|
|
26
|
-
# Parse each file:hash pair from manifest
|
|
27
|
-
while IFS=: read -r file_path expected_hash; do
|
|
28
|
-
# Strip quotes and whitespace
|
|
29
|
-
file_path=$(echo "$file_path" | tr -d '"' | xargs)
|
|
30
|
-
expected_hash=$(echo "$expected_hash" | tr -d '"' | xargs)
|
|
31
|
-
|
|
32
|
-
FULL_PATH="$PROJECT_ROOT/$file_path"
|
|
33
|
-
|
|
34
|
-
if [ ! -f "$FULL_PATH" ]; then
|
|
35
|
-
echo " ✗ MISSING: $file_path"
|
|
36
|
-
MISSING=$((MISSING + 1))
|
|
37
|
-
continue
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
ACTUAL_HASH=$(shasum -a 256 "$FULL_PATH" | cut -d' ' -f1)
|
|
41
|
-
|
|
42
|
-
if [ "$ACTUAL_HASH" != "$expected_hash" ]; then
|
|
43
|
-
echo " ✗ MODIFIED: $file_path"
|
|
44
|
-
MISMATCHES=$((MISMATCHES + 1))
|
|
45
|
-
fi
|
|
46
|
-
done < <(cat "$MANIFEST_FILE" | python3 -c "
|
|
47
|
-
import json, sys
|
|
48
|
-
data = json.load(sys.stdin)
|
|
49
|
-
for k, v in data.get('hashes', {}).items():
|
|
50
|
-
print(f'{k}:{v}')
|
|
51
|
-
" 2>/dev/null || echo "")
|
|
52
|
-
|
|
53
|
-
TOTAL=$((MISMATCHES + MISSING))
|
|
54
|
-
|
|
55
|
-
if [ "$TOTAL" -eq 0 ]; then
|
|
56
|
-
echo "✅ All core files match manifest"
|
|
57
|
-
exit 0
|
|
23
|
+
TSX="$PROJECT_ROOT/node_modules/.bin/tsx"
|
|
24
|
+
if [ ! -x "$TSX" ]; then
|
|
25
|
+
echo "❌ tsx not found at $TSX — run 'npm install' first" >&2
|
|
26
|
+
exit 1
|
|
58
27
|
fi
|
|
59
28
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if [ "$INTEGRITY_MODE" = "enforce" ]; then
|
|
64
|
-
echo "❌ Build failed: core integrity check failed (SPINE_INTEGRITY=enforce)"
|
|
65
|
-
exit 1
|
|
29
|
+
if [ "$INTEGRITY_MODE" = "warn" ]; then
|
|
30
|
+
"$TSX" "$SCRIPT_DIR/verify-manifest.mts" "$PROJECT_ROOT" --warn "$@"
|
|
66
31
|
else
|
|
67
|
-
|
|
68
|
-
exit 0
|
|
32
|
+
"$TSX" "$SCRIPT_DIR/verify-manifest.mts" "$PROJECT_ROOT" "$@"
|
|
69
33
|
fi
|