spine-framework 0.3.124 → 0.3.126
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/cli/commands/agents.ts +35 -37
- package/.framework/cli/commands/auth.ts +35 -34
- package/.framework/cli/commands/create-app.ts +104 -40
- package/.framework/cli/commands/dev.ts +104 -69
- package/.framework/cli/commands/doctor.ts +179 -69
- package/.framework/cli/commands/generate.ts +83 -143
- package/.framework/cli/commands/init.ts +72 -34
- package/.framework/cli/commands/install-app.ts +260 -125
- package/.framework/cli/commands/items.ts +33 -44
- package/.framework/cli/commands/migrate.ts +122 -40
- package/.framework/cli/commands/migrations.ts +33 -40
- package/.framework/cli/commands/pipelines.ts +31 -39
- package/.framework/cli/commands/status.ts +47 -23
- package/.framework/cli/commands/system.ts +47 -37
- package/.framework/cli/commands/test.ts +112 -68
- package/.framework/cli/commands/validate-app.ts +215 -121
- package/.framework/cli/context.ts +112 -130
- package/.framework/cli/env-loader.ts +47 -18
- package/.framework/cli/index.ts +81 -42
- package/.framework/docs/runtime-types.md +1024 -0
- package/.framework/functions/_shared/agent-runner.ts +1296 -961
- package/.framework/functions/_shared/app-manifest.ts +82 -68
- package/.framework/functions/_shared/audit.ts +135 -83
- package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
- package/.framework/functions/_shared/db.ts +80 -126
- package/.framework/functions/_shared/hash-api-key.ts +63 -0
- package/.framework/functions/_shared/index.ts +224 -335
- package/.framework/functions/_shared/kb.ts +319 -0
- package/.framework/functions/_shared/llm.ts +92 -0
- package/.framework/functions/_shared/logging.ts +288 -0
- package/.framework/functions/_shared/middleware.ts +329 -365
- package/.framework/functions/_shared/outcomes.ts +36 -0
- package/.framework/functions/_shared/permissions.ts +359 -643
- package/.framework/functions/_shared/pipeline-runner.ts +243 -316
- package/.framework/functions/_shared/principal.ts +376 -468
- package/.framework/functions/_shared/resolve-credentials.ts +102 -0
- package/.framework/functions/_shared/resolve-ids.ts +188 -16
- package/.framework/functions/_shared/retry-deliveries.ts +104 -0
- package/.framework/functions/_shared/schema-utils.ts +611 -373
- package/.framework/functions/_shared/testing.ts +207 -137
- package/.framework/functions/_shared/tracing.ts +276 -0
- package/.framework/functions/_shared/trigger-engine.ts +159 -211
- package/.framework/functions/_shared/webhook-registration.ts +95 -81
- package/.framework/functions/_shared/webhook-registry.ts +153 -75
- package/.framework/functions/account-nodes.ts +31 -53
- package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
- package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
- package/.framework/functions/adapters/openai-outbound.ts +40 -0
- package/.framework/functions/admin-data.ts +685 -537
- package/.framework/functions/admin-funnel.ts +31 -20
- package/.framework/functions/admin-install-funnel.ts +47 -28
- package/.framework/functions/admin-signals.ts +47 -29
- package/.framework/functions/ai-agents.ts +122 -147
- package/.framework/functions/ai-interactions.ts +180 -90
- package/.framework/functions/api-keys.ts +250 -192
- package/.framework/functions/apps.ts +203 -197
- package/.framework/functions/auth.ts +98 -91
- package/.framework/functions/cil-agent-ingest.ts +773 -0
- package/.framework/functions/cil-agent.ts +592 -0
- package/.framework/functions/cil-callmap.ts +1227 -0
- package/.framework/functions/cil-config.ts +583 -0
- package/.framework/functions/cil-context.ts +445 -0
- package/.framework/functions/cil-doc.ts +1752 -0
- package/.framework/functions/cil-embeddings.ts +1202 -0
- package/.framework/functions/cil-graph.ts +585 -0
- package/.framework/functions/cil-ingest.ts +2416 -0
- package/.framework/functions/cil-pipeline.ts +318 -0
- package/.framework/functions/cil-project.ts +499 -0
- package/.framework/functions/cil-validate.ts +469 -0
- package/.framework/functions/debug-auth.ts +73 -53
- package/.framework/functions/deliveries.ts +117 -0
- package/.framework/functions/embeddings.ts +528 -516
- package/.framework/functions/eval-harness.ts +158 -106
- package/.framework/functions/integration-outbound.ts +311 -0
- package/.framework/functions/integration-routes.ts +240 -466
- package/.framework/functions/integrations.ts +132 -140
- package/.framework/functions/invites.ts +401 -201
- package/.framework/functions/item-progress.ts +113 -53
- package/.framework/functions/kb.ts +28 -0
- package/.framework/functions/logs.ts +239 -222
- package/.framework/functions/observability.ts +308 -259
- package/.framework/functions/pipeline-executions.ts +179 -235
- package/.framework/functions/pipelines.ts +163 -205
- package/.framework/functions/prompt-configs.ts +109 -156
- package/.framework/functions/roles.ts +97 -147
- package/.framework/functions/system-cron.ts +132 -218
- package/.framework/functions/system.ts +112 -52
- package/.framework/functions/tests.ts +32 -28
- package/.framework/functions/timers.ts +162 -210
- package/.framework/functions/triggers.ts +282 -346
- package/.framework/functions/types.ts +283 -400
- package/.framework/migrations/001_core_schema.sql +630 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
- package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
- package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
- package/.framework/migrations/005_core_observability.sql +598 -0
- package/.framework/migrations/006_security_hardening_rls.sql +97 -0
- package/.framework/migrations/007_kb_article_list.sql +237 -0
- package/.framework/migrations/008_items_target.sql +8 -0
- package/.framework/migrations/009_roles_slug_unique.sql +11 -0
- package/.framework/public/spine.config.json +91 -0
- package/.framework/scripts/app-install-cli.ts +112 -98
- package/.framework/src/App.tsx +67 -20
- package/.framework/src/apps/admin/index.tsx +97 -27
- package/.framework/src/components/AppWrapper.tsx +50 -11
- package/.framework/src/components/CustomAppLoader.tsx +131 -22
- package/.framework/src/components/admin/AdminListPage.tsx +75 -8
- package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
- package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
- package/.framework/src/components/admin/PageInstructions.tsx +49 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
- package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
- package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
- package/.framework/src/components/app-sidebar.tsx +50 -3
- package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
- package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
- package/.framework/src/components/kb/KBArticleList.tsx +557 -0
- package/.framework/src/components/layout/AppShell.tsx +54 -16
- package/.framework/src/components/layout/Header.tsx +50 -38
- package/.framework/src/components/layout/Layout.tsx +48 -32
- package/.framework/src/components/layout/Sidebar.tsx +167 -16
- package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
- package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
- package/.framework/src/components/runtime/DataFilters.tsx +78 -1
- package/.framework/src/components/runtime/DataHeader.tsx +32 -29
- package/.framework/src/components/runtime/DataListPage.tsx +161 -2
- package/.framework/src/components/runtime/DataStats.tsx +48 -1
- package/.framework/src/components/runtime/DataTable.tsx +153 -1
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
- package/.framework/src/components/runtime/index.ts +16 -9
- package/.framework/src/components/search-form.tsx +33 -1
- package/.framework/src/components/shared/AgentView.tsx +48 -51
- package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
- package/.framework/src/components/shared/InviteManager.tsx +113 -52
- package/.framework/src/components/shared/MemberList.tsx +182 -0
- package/.framework/src/components/shared/SchemaFields.tsx +96 -78
- package/.framework/src/components/ui/DataTable.tsx +86 -56
- package/.framework/src/components/ui/Form.tsx +188 -109
- package/.framework/src/components/ui/ItemCard.tsx +86 -58
- package/.framework/src/components/ui/ItemListView.tsx +88 -61
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
- package/.framework/src/components/ui/Modal.tsx +81 -4
- package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
- package/.framework/src/components/ui/accordion.tsx +97 -5
- package/.framework/src/components/ui/alert-dialog.tsx +225 -13
- package/.framework/src/components/ui/alert.tsx +113 -6
- package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
- package/.framework/src/components/ui/avatar.tsx +129 -7
- package/.framework/src/components/ui/badge.tsx +65 -3
- package/.framework/src/components/ui/breadcrumb.tsx +145 -8
- package/.framework/src/components/ui/button-group.tsx +81 -4
- package/.framework/src/components/ui/button.tsx +49 -2
- package/.framework/src/components/ui/calendar.tsx +65 -3
- package/.framework/src/components/ui/card.tsx +145 -8
- package/.framework/src/components/ui/carousel.tsx +129 -7
- package/.framework/src/components/ui/chart.tsx +161 -10
- package/.framework/src/components/ui/checkbox.tsx +49 -2
- package/.framework/src/components/ui/collapsible.tsx +81 -5
- package/.framework/src/components/ui/combobox.tsx +305 -20
- package/.framework/src/components/ui/command.tsx +177 -11
- package/.framework/src/components/ui/context-menu.tsx +273 -17
- package/.framework/src/components/ui/dialog.tsx +209 -27
- package/.framework/src/components/ui/direction.tsx +64 -4
- package/.framework/src/components/ui/drawer.tsx +193 -12
- package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
- package/.framework/src/components/ui/empty.tsx +145 -9
- package/.framework/src/components/ui/field.tsx +209 -14
- package/.framework/src/components/ui/hover-card.tsx +81 -4
- package/.framework/src/components/ui/input-group.tsx +162 -10
- package/.framework/src/components/ui/input-otp.tsx +98 -5
- package/.framework/src/components/ui/input.tsx +49 -2
- package/.framework/src/components/ui/item.tsx +225 -13
- package/.framework/src/components/ui/kbd.tsx +65 -3
- package/.framework/src/components/ui/label.tsx +49 -2
- package/.framework/src/components/ui/menubar.tsx +289 -17
- package/.framework/src/components/ui/native-select.tsx +97 -5
- package/.framework/src/components/ui/navigation-menu.tsx +177 -10
- package/.framework/src/components/ui/pagination.tsx +161 -9
- package/.framework/src/components/ui/popover.tsx +145 -8
- package/.framework/src/components/ui/progress.tsx +49 -2
- package/.framework/src/components/ui/radio-group.tsx +65 -3
- package/.framework/src/components/ui/resizable.tsx +82 -4
- package/.framework/src/components/ui/scroll-area.tsx +65 -3
- package/.framework/src/components/ui/select.tsx +193 -11
- package/.framework/src/components/ui/separator.tsx +49 -2
- package/.framework/src/components/ui/sheet.tsx +193 -11
- package/.framework/src/components/ui/sidebar.tsx +481 -38
- package/.framework/src/components/ui/skeleton.tsx +49 -2
- package/.framework/src/components/ui/slider.tsx +49 -2
- package/.framework/src/components/ui/sonner.tsx +49 -2
- package/.framework/src/components/ui/spinner.tsx +49 -2
- package/.framework/src/components/ui/switch.tsx +49 -2
- package/.framework/src/components/ui/table-primitives.tsx +160 -33
- package/.framework/src/components/ui/table.tsx +161 -9
- package/.framework/src/components/ui/tabs.tsx +113 -6
- package/.framework/src/components/ui/textarea.tsx +49 -2
- package/.framework/src/components/ui/toggle-group.tsx +81 -4
- package/.framework/src/components/ui/toggle.tsx +65 -3
- package/.framework/src/components/ui/tooltip.tsx +97 -5
- package/.framework/src/contexts/AppContext.tsx +129 -45
- package/.framework/src/contexts/AuthContext.tsx +187 -144
- package/.framework/src/hooks/use-mobile.ts +48 -2
- package/.framework/src/hooks/useApi.ts +208 -159
- package/.framework/src/hooks/useAppPath.ts +32 -23
- package/.framework/src/hooks/useApps.ts +51 -20
- package/.framework/src/hooks/useEntityList.ts +32 -78
- package/.framework/src/hooks/useEntityRecord.ts +35 -96
- package/.framework/src/hooks/useForm.ts +32 -92
- package/.framework/src/hooks/useListSchema.ts +49 -78
- package/.framework/src/hooks/useSchemaRecord.ts +114 -105
- package/.framework/src/lib/api.ts +195 -116
- package/.framework/src/lib/supabase.ts +98 -64
- package/.framework/src/lib/utils.ts +287 -151
- package/.framework/src/main.tsx +49 -3
- package/.framework/src/pages/DashboardPage.tsx +52 -29
- package/.framework/src/pages/NotFoundPage.tsx +32 -10
- package/.framework/src/pages/SetupPage.tsx +32 -12
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
- package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
- package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
- package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
- package/.framework/src/pages/admin/AppsPage.tsx +49 -2
- package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
- package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
- package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
- package/.framework/src/pages/admin/LogsPage.tsx +422 -75
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
- package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
- package/.framework/src/pages/admin/RolesPage.tsx +171 -18
- package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
- package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
- package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
- package/.framework/src/pages/admin/TimersPage.tsx +48 -20
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
- package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
- package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
- package/.framework/src/pages/admin/TypesPage.tsx +48 -20
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
- package/.framework/src/pages/auth/LoginPage.tsx +50 -23
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
- package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
- package/.framework/src/types/auth.ts +128 -71
- package/.framework/src/types/types.ts +373 -157
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
- package/docs/README.md +9 -130
- package/netlify.toml +5 -2
- package/package.json +13 -25
- package/scripts/kb-ingest-docs.ts +203 -0
- package/spine.config.json +65 -4
- package/.framework/migrations/001_schema.sql +0 -425
- package/.framework/migrations/002_rls_and_functions.sql +0 -763
- package/.framework/migrations/004_enhancements.sql +0 -43
- package/.framework/migrations/005_auth_user_trigger.sql +0 -105
- package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
- package/.framework/migrations/016_invites.sql +0 -27
- package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
- package/.framework/migrations/018_apps_required_roles.sql +0 -17
- package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
- package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
- package/.framework/migrations/021_app_domains.sql +0 -112
- package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
- package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
- package/.framework/migrations/024_messages_account_id.sql +0 -9
- package/.framework/migrations/025_retype_threads_messages.sql +0 -240
- package/.framework/migrations/026_fix_rls_policies.sql +0 -47
- package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
- package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
- package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
- package/.framework/migrations/030_messages_updated_by.sql +0 -6
- package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
- package/docs/APPS.md +0 -232
- package/docs/ARCHITECTURE.md +0 -246
- package/docs/INSTALL.md +0 -155
- package/docs/design-schema-spec.md +0 -1837
- package/docs/dev-guides/01-create-an-app.md +0 -183
- package/docs/dev-guides/02-create-a-function.md +0 -178
- package/docs/dev-guides/03-create-a-component.md +0 -178
- package/docs/dev-guides/04-webhook-handlers.md +0 -129
- package/docs/dev-guides/05-testing.md +0 -168
- package/docs/dev-guides/README.md +0 -40
- package/docs/npm-packages-plan.md +0 -288
- package/docs/spine-core--api-reference.md +0 -822
|
@@ -1,22 +1,85 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
/**
|
|
3
|
-
* @module cli/commands/install-app
|
|
4
|
-
* @audience installer
|
|
5
|
-
* @layer cli
|
|
6
|
-
* @stability stable
|
|
7
|
-
*
|
|
8
|
-
* `spine-framework install-app` — installs a Spine Framework app from npm.
|
|
9
|
-
* Copies app files into custom/apps/, seeds types/roles/link-types into the DB.
|
|
10
|
-
*
|
|
11
|
-
* **Usage:**
|
|
12
|
-
* ```bash
|
|
13
|
-
* npx spine-framework install-app spine-framework-portal@latest
|
|
14
|
-
* npx spine-framework install-app spine-framework-cortex
|
|
15
|
-
* npx spine-framework uninstall-app spine-framework-portal
|
|
16
|
-
* npx spine-framework update-app spine-framework-portal
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
1
|
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
// CIL[BEGIN chunk_id="install-app_registrationconfig_1784069813" type="interface" identity="RegistrationConfig" parent="none"]
|
|
12
|
+
// CIL[DOC-MACRO purpose="Define registration configuration for an installed app"]
|
|
13
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Own the RegistrationConfig interface definition"]
|
|
14
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[]"]
|
|
15
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="interface definition" errors="none" side_effects="none"]
|
|
16
|
+
// CIL[DOC-MICRO logic_summary="Defines registration settings including default role, redirect path, and account strategy" notable_decisions="[]"]
|
|
17
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.95" flags="none"]
|
|
18
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
19
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
20
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
21
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
22
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
23
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
24
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
25
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
26
|
+
interface RegistrationConfig {
|
|
27
|
+
enabled: boolean
|
|
28
|
+
default_role: string
|
|
29
|
+
redirect_path: string
|
|
30
|
+
account_strategy?: 'existing' | 'new' | 'choice'
|
|
31
|
+
target_account?: string
|
|
32
|
+
}
|
|
33
|
+
// CIL[END chunk_id="install-app_registrationconfig_1784069813"]
|
|
34
|
+
|
|
35
|
+
// CIL[BEGIN chunk_id="install-app_spineconfig_1784069813" type="interface" identity="SpineConfig" parent="none"]
|
|
36
|
+
// CIL[DOC-MACRO purpose="Define spine.config.json structure for registration"]
|
|
37
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Own the SpineConfig interface definition"]
|
|
38
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[]"]
|
|
39
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="interface definition" errors="none" side_effects="none"]
|
|
40
|
+
// CIL[DOC-MICRO logic_summary="Defines registration config with default app/role, URL override flag, and apps map" notable_decisions="[]"]
|
|
41
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.95" flags="none"]
|
|
42
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
43
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
44
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
45
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
46
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
47
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
48
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
49
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
50
|
+
interface SpineConfig {
|
|
51
|
+
registration?: {
|
|
52
|
+
default_app?: string
|
|
53
|
+
default_role?: string
|
|
54
|
+
allow_url_override?: boolean
|
|
55
|
+
default_app_by_role?: Record<string, string>
|
|
56
|
+
apps?: Record<string, {
|
|
57
|
+
roles: string[]
|
|
58
|
+
default_role: string
|
|
59
|
+
path: string
|
|
60
|
+
enabled: boolean
|
|
61
|
+
account_strategy: 'existing' | 'new' | 'choice'
|
|
62
|
+
target_account?: string
|
|
63
|
+
}>
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// CIL[END chunk_id="install-app_spineconfig_1784069813"]
|
|
67
|
+
|
|
68
|
+
// CIL[BEGIN chunk_id="install-app_moduleheader_1784069813" type="module_header" identity="module_header" parent="none"]
|
|
69
|
+
// CIL[DOC-MACRO purpose="Module imports for app installation CLI"]
|
|
70
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide filesystem, database, and CLI utilities"]
|
|
71
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[\".framework/migrations/015_simplify_apps_table.sql\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
|
|
72
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="imports" errors="none" side_effects="none"]
|
|
73
|
+
// CIL[DOC-MICRO logic_summary="Imports Node.js modules, Supabase client, and migration utilities" notable_decisions="[]"]
|
|
74
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
75
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
76
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
77
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: .framework/migrations/015_simplify_apps_table.sql, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
|
|
78
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
79
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
80
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
81
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
82
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
20
83
|
import type { Command } from 'commander'
|
|
21
84
|
import { existsSync, readFileSync, mkdirSync, cpSync, writeFileSync } from 'fs'
|
|
22
85
|
import { resolve, dirname, join, basename } from 'path'
|
|
@@ -29,9 +92,26 @@ import { runMigrations } from './migrate.js'
|
|
|
29
92
|
const __filename = fileURLToPath(import.meta.url)
|
|
30
93
|
const __dirname = dirname(__filename)
|
|
31
94
|
const PROJECT_ROOT = process.cwd()
|
|
32
|
-
|
|
95
|
+
const SPINE_CONFIG_PATH = join(PROJECT_ROOT, 'spine.config.json')
|
|
96
|
+
// CIL[END chunk_id="install-app_moduleheader_1784069813"]
|
|
97
|
+
// CIL[BEGIN chunk_id="install-app_registerinstallappcommands_1784069813" type="function" identity="registerInstallAppCommands" parent="none"]
|
|
98
|
+
// CIL[DOC-LEARNING topic="install-app:two_phase_install" content="install-app is two phases: npm copy then DB seed. If seed fails or is skipped, custom/apps/<slug> files may exist while the app is absent from public.apps, so it will not appear in the app registry."]
|
|
99
|
+
// CIL[DOC-MACRO purpose="Register app management CLI commands"]
|
|
100
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide install, uninstall, update, and sync operations"]
|
|
101
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[\".framework/migrations/015_simplify_apps_table.sql\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\"\\\"]"]
|
|
102
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="CLI commands" errors="npm, filesystem, database errors" side_effects="filesystem writes, database writes"]
|
|
103
|
+
// CIL[DOC-NOTE type="manifest_config_sync" content="install-app and update-db-app now persist manifest.config and manifest.metadata into the apps table's config/metadata JSONB columns."]
|
|
104
|
+
// CIL[DOC-MICRO logic_summary="Registers install-app, uninstall-app, update-app, and update-db-app commands; syncs manifest config/metadata into apps table" notable_decisions="[]"]
|
|
105
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
106
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
107
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: .framework/migrations/015_simplify_apps_table.sql, custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/cortex/functions/custom_cil-embeddings.ts, \."]
|
|
108
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
109
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
110
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
111
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
112
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
33
113
|
export function registerInstallAppCommands(program: Command) {
|
|
34
|
-
|
|
114
|
+
|
|
35
115
|
program
|
|
36
116
|
.command('install-app <package>')
|
|
37
117
|
.description('Install a Spine Framework app from npm')
|
|
@@ -42,7 +122,7 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
42
122
|
loadEnv()
|
|
43
123
|
|
|
44
124
|
try {
|
|
45
|
-
|
|
125
|
+
|
|
46
126
|
console.log(` Installing ${packageName} via npm...`)
|
|
47
127
|
execSync(`npm install ${packageName} --save`, {
|
|
48
128
|
cwd: PROJECT_ROOT,
|
|
@@ -50,7 +130,7 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
50
130
|
})
|
|
51
131
|
console.log(' ✓ npm install complete')
|
|
52
132
|
|
|
53
|
-
|
|
133
|
+
|
|
54
134
|
const pkgSlug = packageName.split('@')[0] // strip version
|
|
55
135
|
const pkgPath = join(PROJECT_ROOT, 'node_modules', pkgSlug)
|
|
56
136
|
|
|
@@ -60,20 +140,18 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
60
140
|
|
|
61
141
|
console.log(` Package path: ${pkgPath}`)
|
|
62
142
|
|
|
63
|
-
|
|
143
|
+
|
|
64
144
|
const appSlug = resolveAppSlug(pkgPath, pkgSlug)
|
|
65
145
|
console.log(` App slug: ${appSlug}`)
|
|
66
146
|
|
|
67
|
-
|
|
147
|
+
|
|
68
148
|
const appDest = join(PROJECT_ROOT, 'custom/apps', appSlug)
|
|
69
149
|
const isReinstall = existsSync(appDest)
|
|
70
150
|
if (!isReinstall) {
|
|
71
151
|
mkdirSync(appDest, { recursive: true })
|
|
72
152
|
}
|
|
73
153
|
|
|
74
|
-
|
|
75
|
-
// the source of truth for route_prefix and other project-level config.
|
|
76
|
-
// All other files are updated from npm as normal.
|
|
154
|
+
|
|
77
155
|
const allowedEntries = [
|
|
78
156
|
'index.tsx', 'manifest.json', 'package.json',
|
|
79
157
|
'pages', 'components', 'hooks', 'utils', 'functions', 'seed', 'public'
|
|
@@ -85,7 +163,7 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
85
163
|
const src = join(pkgPath, entry)
|
|
86
164
|
if (!existsSync(src)) continue
|
|
87
165
|
|
|
88
|
-
|
|
166
|
+
|
|
89
167
|
if (entry === 'manifest.json' && isReinstall) {
|
|
90
168
|
console.log(` Preserving local manifest.json (use update-db-app to sync changes)`)
|
|
91
169
|
continue
|
|
@@ -96,7 +174,7 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
96
174
|
}
|
|
97
175
|
console.log(` ✓ App files copied to custom/apps/${appSlug}/`)
|
|
98
176
|
|
|
99
|
-
|
|
177
|
+
|
|
100
178
|
console.log(' Running migrations...')
|
|
101
179
|
try {
|
|
102
180
|
const migrateResult = await runMigrations({ frameworkOnly: false })
|
|
@@ -114,17 +192,17 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
114
192
|
console.warn(` ⚠ Migration error: ${migrateErr.message} — continuing with seed`)
|
|
115
193
|
}
|
|
116
194
|
|
|
117
|
-
|
|
195
|
+
|
|
118
196
|
if (!opts.seed === false) {
|
|
119
197
|
await seedApp(appDest, appSlug)
|
|
120
198
|
}
|
|
121
199
|
|
|
122
|
-
|
|
200
|
+
|
|
123
201
|
const manifestPath = join(appDest, 'manifest.json')
|
|
124
202
|
let manifest: Record<string, any> = {}
|
|
125
203
|
try { manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) } catch {}
|
|
126
204
|
if (manifest.registration?.enabled) {
|
|
127
|
-
updateRegistrationConfig(appSlug, manifest.registration)
|
|
205
|
+
updateRegistrationConfig(appSlug, manifest.registration, manifest.required_roles ?? [])
|
|
128
206
|
}
|
|
129
207
|
|
|
130
208
|
let installedVersion = 'unknown'
|
|
@@ -140,7 +218,7 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
140
218
|
}
|
|
141
219
|
})
|
|
142
220
|
|
|
143
|
-
|
|
221
|
+
|
|
144
222
|
program
|
|
145
223
|
.command('uninstall-app <package>')
|
|
146
224
|
.description('Remove an installed Spine Framework app')
|
|
@@ -173,7 +251,7 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
173
251
|
}
|
|
174
252
|
})
|
|
175
253
|
|
|
176
|
-
|
|
254
|
+
|
|
177
255
|
program
|
|
178
256
|
.command('update-app <package>')
|
|
179
257
|
.description('Update an installed Spine Framework app to a newer version')
|
|
@@ -203,12 +281,12 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
203
281
|
}
|
|
204
282
|
}
|
|
205
283
|
|
|
206
|
-
|
|
284
|
+
|
|
207
285
|
const manifestPath = join(appDest, 'manifest.json')
|
|
208
286
|
let manifest: Record<string, any> = {}
|
|
209
287
|
try { manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) } catch {}
|
|
210
288
|
if (manifest.registration?.enabled) {
|
|
211
|
-
updateRegistrationConfig(appSlug, manifest.registration)
|
|
289
|
+
updateRegistrationConfig(appSlug, manifest.registration, manifest.required_roles ?? [])
|
|
212
290
|
}
|
|
213
291
|
|
|
214
292
|
let updatedVersion = 'unknown'
|
|
@@ -221,7 +299,7 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
221
299
|
}
|
|
222
300
|
})
|
|
223
301
|
|
|
224
|
-
|
|
302
|
+
|
|
225
303
|
program
|
|
226
304
|
.command('update-db-app <slug>')
|
|
227
305
|
.description('Sync local manifest.json changes to the database (no npm, no file changes)')
|
|
@@ -274,6 +352,8 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
274
352
|
min_role: manifest.required_roles?.[0] ?? manifest.min_role ?? null,
|
|
275
353
|
preview_domain: manifest.preview_domain ?? null,
|
|
276
354
|
prod_domain: manifest.prod_domain ?? null,
|
|
355
|
+
config: manifest.config ?? undefined,
|
|
356
|
+
metadata: manifest.metadata ?? undefined,
|
|
277
357
|
},
|
|
278
358
|
{ onConflict: 'slug' }
|
|
279
359
|
)
|
|
@@ -285,30 +365,56 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
285
365
|
process.exit(1)
|
|
286
366
|
}
|
|
287
367
|
|
|
288
|
-
console.log(` ✓ apps row updated (route_prefix: ${manifest.route_prefix ?? '/' + slug})`)
|
|
368
|
+
console.log(` ✓ apps row updated (route_prefix: ${manifest.route_prefix ?? '/' + slug}, config: ${manifest.config ? 'synced' : 'none'})`)
|
|
289
369
|
|
|
290
|
-
|
|
370
|
+
|
|
291
371
|
if (manifest.registration?.enabled) {
|
|
292
|
-
updateRegistrationConfig(slug, manifest.registration)
|
|
372
|
+
updateRegistrationConfig(slug, manifest.registration, manifest.required_roles ?? [])
|
|
293
373
|
}
|
|
294
374
|
|
|
295
375
|
console.log(`\n✅ ${slug} DB synced from local manifest.\n`)
|
|
296
376
|
console.log(`Next: npm run assemble && netlify dev\n`)
|
|
297
377
|
})
|
|
298
378
|
}
|
|
299
|
-
|
|
300
|
-
//
|
|
301
|
-
|
|
379
|
+
// CIL[END chunk_id="install-app_registerinstallappcommands_1784069813"]
|
|
380
|
+
// CIL[BEGIN chunk_id="install-app_resolveappslug_1784069813" type="function" identity="resolveAppSlug" parent="none"]
|
|
381
|
+
// CIL[DOC-MACRO purpose="Resolve app slug from package name or manifest"]
|
|
382
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Extract app identifier from package metadata"]
|
|
383
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[\".framework/migrations/015_simplify_apps_table.sql\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\"\\\"]"]
|
|
384
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="app slug" errors="file read errors" side_effects="none"]
|
|
385
|
+
// CIL[DOC-MICRO logic_summary="Reads manifest.json slug field or strips spine-framework- prefix" notable_decisions="[]"]
|
|
386
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
387
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
388
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 1. Direct calls: 0. Related files: .framework/migrations/015_simplify_apps_table.sql, custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/cortex/functions/custom_cil-embeddings.ts, \."]
|
|
389
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 1. Total affected: 1."]
|
|
390
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
391
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
392
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
393
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
302
394
|
function resolveAppSlug(pkgPath: string, pkgName: string): string {
|
|
303
395
|
try {
|
|
304
396
|
const manifest = JSON.parse(readFileSync(join(pkgPath, 'manifest.json'), 'utf8'))
|
|
305
397
|
if (manifest.slug) return manifest.slug
|
|
306
398
|
} catch {}
|
|
307
|
-
|
|
399
|
+
|
|
308
400
|
const slug = pkgName.replace(/^spine-framework-/, '')
|
|
309
401
|
return slug || pkgName // fallback to full name if replacement results in empty
|
|
310
402
|
}
|
|
311
|
-
|
|
403
|
+
// CIL[END chunk_id="install-app_resolveappslug_1784069813"]
|
|
404
|
+
// CIL[BEGIN chunk_id="install-app_loadenv_1784069813" type="function" identity="loadEnv" parent="none"]
|
|
405
|
+
// CIL[DOC-MACRO purpose="Load environment variables from .env file"]
|
|
406
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Parse and set environment variables"]
|
|
407
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[\"\\\"]"]
|
|
408
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="process.env updates" errors="file read errors" side_effects="process.env mutation"]
|
|
409
|
+
// CIL[DOC-MICRO logic_summary="Reads .env file, parses key=value pairs, sets process.env" notable_decisions="[]"]
|
|
410
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
411
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
412
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 1. Direct calls: 0. Related files: \."]
|
|
413
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 1. Total affected: 1."]
|
|
414
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
415
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
416
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
417
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
312
418
|
function loadEnv() {
|
|
313
419
|
const envPath = join(PROJECT_ROOT, '.env')
|
|
314
420
|
if (!existsSync(envPath)) return
|
|
@@ -323,7 +429,25 @@ function loadEnv() {
|
|
|
323
429
|
if (key && !(key in process.env)) process.env[key] = val
|
|
324
430
|
}
|
|
325
431
|
}
|
|
326
|
-
|
|
432
|
+
// CIL[END chunk_id="install-app_loadenv_1784069813"]
|
|
433
|
+
// CIL[BEGIN chunk_id="install-app_seedapp_1784069813" type="function" identity="seedApp" parent="none"]
|
|
434
|
+
// CIL[DOC-LEARNING topic="install-app:seed_requires_env" content="DB seed requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY in .env; missing env vars cause install-app to copy files without registering the app in public.apps."]
|
|
435
|
+
// CIL[DOC-LEARNING topic="install-app:types_unique_slug" content="public.types must enforce a unique slug. Seeding types per app (inject_app_id with conflict 'app_id,kind,slug') created duplicate funnel_signal, support_ticket, etc. rows, breaking resolveTypeId and .single() lookups. Types are now seeded with conflict 'kind,slug', inject_app_id: false, and ignoreDuplicates to keep them global."]
|
|
436
|
+
// CIL[DOC-NOTE type="caution" content="The apps upsert uses onConflict: 'slug' and can still fail on the active route_prefix unique index if another active app already owns the same route_prefix."]
|
|
437
|
+
// CIL[DOC-NOTE type="caution" content="types seeds must not inject app_id. The types table uses slug as the global type identifier; app-scoped duplicates cause lookup ambiguity."]
|
|
438
|
+
// CIL[DOC-MACRO purpose="Seed database with app types, roles, and permissions"]
|
|
439
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Populate database with app-specific seed data"]
|
|
440
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[]"]
|
|
441
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="database records" errors="database errors" side_effects="database writes, app config sync from manifest"]
|
|
442
|
+
// CIL[DOC-MICRO logic_summary="Registers app (including manifest.config/metadata), seeds roles/types, handles permission patches with user prompts" notable_decisions="[]"]
|
|
443
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="complex_database_operations"]
|
|
444
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
445
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
446
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
447
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
448
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
449
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
450
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
327
451
|
async function seedApp(appDir: string, appSlug: string) {
|
|
328
452
|
const supabaseUrl = process.env.SUPABASE_URL
|
|
329
453
|
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY
|
|
@@ -337,9 +461,7 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
337
461
|
auth: { persistSession: false },
|
|
338
462
|
})
|
|
339
463
|
|
|
340
|
-
|
|
341
|
-
// apps.slug is UNIQUE NOT NULL, making it the only reliable conflict target
|
|
342
|
-
// at install time (we have no UUID yet).
|
|
464
|
+
|
|
343
465
|
const manifestPath = join(appDir, 'manifest.json')
|
|
344
466
|
let manifest: Record<string, any> = {}
|
|
345
467
|
try { manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) } catch {}
|
|
@@ -358,12 +480,14 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
358
480
|
is_active: true,
|
|
359
481
|
route_prefix: manifest.route_prefix ?? ('/' + appSlug),
|
|
360
482
|
renderer: manifest.renderer ?? 'custom',
|
|
361
|
-
|
|
483
|
+
|
|
362
484
|
required_roles: manifest.required_roles ?? (manifest.min_role ? [manifest.min_role] : []),
|
|
363
|
-
|
|
485
|
+
|
|
364
486
|
min_role: manifest.required_roles?.[0] ?? manifest.min_role ?? null,
|
|
365
487
|
preview_domain: manifest.preview_domain ?? null,
|
|
366
488
|
prod_domain: manifest.prod_domain ?? null,
|
|
489
|
+
config: manifest.config ?? undefined,
|
|
490
|
+
metadata: manifest.metadata ?? undefined,
|
|
367
491
|
},
|
|
368
492
|
{ onConflict: 'slug' }
|
|
369
493
|
)
|
|
@@ -377,37 +501,29 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
377
501
|
}
|
|
378
502
|
|
|
379
503
|
const appId = appRow.id
|
|
380
|
-
console.log(` ✓ App registered (id: ${appId})`)
|
|
504
|
+
console.log(` ✓ App registered (id: ${appId}, config: ${manifest.config ? 'synced' : 'none'})`)
|
|
381
505
|
|
|
382
|
-
|
|
383
|
-
// If manifest declares a `seed` array, use it. Each entry:
|
|
384
|
-
// { file, table, conflict, inject_app_id? }
|
|
385
|
-
// inject_app_id defaults to true — set false for tables with no app_id column.
|
|
386
|
-
//
|
|
387
|
-
// Fallback: if no manifest.seed, use legacy hardcoded behavior so existing
|
|
388
|
-
// apps without a seed block continue to work.
|
|
389
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
506
|
+
|
|
390
507
|
|
|
391
508
|
interface SeedEntry {
|
|
392
509
|
file: string
|
|
393
510
|
table: string
|
|
394
511
|
conflict: string
|
|
395
512
|
inject_app_id?: boolean
|
|
396
|
-
|
|
397
|
-
// The record must have { kind, slug, record_permissions } — no app_id injection.
|
|
513
|
+
ignore_duplicates?: boolean
|
|
398
514
|
permissions_patch?: boolean
|
|
399
515
|
}
|
|
400
516
|
|
|
401
517
|
const seedEntries: SeedEntry[] = Array.isArray(manifest.seed)
|
|
402
518
|
? manifest.seed
|
|
403
519
|
: [
|
|
404
|
-
|
|
405
|
-
{ file: 'seed/roles.json', table: 'roles', conflict: '
|
|
406
|
-
{ file: 'seed/types.json', table: 'types', conflict: '
|
|
520
|
+
|
|
521
|
+
{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' },
|
|
522
|
+
{ file: 'seed/types.json', table: 'types', conflict: 'kind,slug', inject_app_id: false },
|
|
407
523
|
{ file: 'seed/link-types.json', table: 'link_types', conflict: 'app_id,slug' },
|
|
408
524
|
]
|
|
409
525
|
|
|
410
|
-
|
|
526
|
+
|
|
411
527
|
const hasPermissionsPatch = seedEntries.some(e => e.permissions_patch)
|
|
412
528
|
if (!hasPermissionsPatch) {
|
|
413
529
|
seedEntries.push({
|
|
@@ -439,7 +555,7 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
439
555
|
continue
|
|
440
556
|
}
|
|
441
557
|
|
|
442
|
-
|
|
558
|
+
|
|
443
559
|
if (entry.permissions_patch) {
|
|
444
560
|
console.log(` Patching record_permissions for ${records.length} type(s) from ${entry.file}...`)
|
|
445
561
|
for (const record of records) {
|
|
@@ -449,7 +565,7 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
449
565
|
continue
|
|
450
566
|
}
|
|
451
567
|
|
|
452
|
-
|
|
568
|
+
|
|
453
569
|
const { data: existing } = await db
|
|
454
570
|
.from('types')
|
|
455
571
|
.select('id, design_schema')
|
|
@@ -465,13 +581,13 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
465
581
|
const existingSchema = (existing.design_schema ?? {}) as Record<string, any>
|
|
466
582
|
const existingPerms = existingSchema.record_permissions ?? null
|
|
467
583
|
|
|
468
|
-
|
|
584
|
+
|
|
469
585
|
if (JSON.stringify(existingPerms) === JSON.stringify(newPerms)) {
|
|
470
586
|
console.log(` ✓ ${kind}/${slug} record_permissions unchanged — skipped`)
|
|
471
587
|
continue
|
|
472
588
|
}
|
|
473
589
|
|
|
474
|
-
|
|
590
|
+
|
|
475
591
|
console.log(`\n Type: ${kind}/${slug}`)
|
|
476
592
|
console.log(` Existing record_permissions:`)
|
|
477
593
|
console.log(' ' + JSON.stringify(existingPerms, null, 2).replace(/\n/g, '\n '))
|
|
@@ -484,7 +600,7 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
484
600
|
continue
|
|
485
601
|
}
|
|
486
602
|
|
|
487
|
-
|
|
603
|
+
|
|
488
604
|
const { error: patchErr } = await db
|
|
489
605
|
.from('types')
|
|
490
606
|
.update({
|
|
@@ -502,7 +618,7 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
502
618
|
continue
|
|
503
619
|
}
|
|
504
620
|
|
|
505
|
-
|
|
621
|
+
|
|
506
622
|
const injectAppId = entry.inject_app_id !== false // default true
|
|
507
623
|
console.log(` Seeding ${records.length} record(s) from ${entry.file} → ${entry.table}...`)
|
|
508
624
|
|
|
@@ -510,7 +626,10 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
510
626
|
const row = injectAppId ? { ...record, app_id: appId } : { ...record }
|
|
511
627
|
const { error } = await db
|
|
512
628
|
.from(entry.table)
|
|
513
|
-
.upsert(row, {
|
|
629
|
+
.upsert(row, {
|
|
630
|
+
onConflict: entry.conflict,
|
|
631
|
+
ignoreDuplicates: entry.table === 'types' || entry.ignore_duplicates === true,
|
|
632
|
+
})
|
|
514
633
|
if (error) {
|
|
515
634
|
const label = record.slug ?? record.name ?? JSON.stringify(record).slice(0, 40)
|
|
516
635
|
console.warn(` ⚠ ${entry.table} "${label}": ${error.message}`)
|
|
@@ -520,9 +639,21 @@ async function seedApp(appDir: string, appSlug: string) {
|
|
|
520
639
|
console.log(` ✓ ${entry.table} seeded`)
|
|
521
640
|
}
|
|
522
641
|
}
|
|
523
|
-
|
|
524
|
-
//
|
|
525
|
-
|
|
642
|
+
// CIL[END chunk_id="install-app_seedapp_1784069813"]
|
|
643
|
+
// CIL[BEGIN chunk_id="install-app_prompt_1784069813" type="function" identity="prompt" parent="none"]
|
|
644
|
+
// CIL[DOC-MACRO purpose="Prompt user for input via command line"]
|
|
645
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle interactive user input"]
|
|
646
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[]"]
|
|
647
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="user answer" errors="readline errors" side_effects="stdin interaction"]
|
|
648
|
+
// CIL[DOC-MICRO logic_summary="Creates readline interface and returns user input as Promise" notable_decisions="[]"]
|
|
649
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
650
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
651
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
652
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
653
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
654
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
655
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
656
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
526
657
|
function prompt(question: string): Promise<string> {
|
|
527
658
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
528
659
|
return new Promise(resolve => {
|
|
@@ -532,35 +663,21 @@ function prompt(question: string): Promise<string> {
|
|
|
532
663
|
})
|
|
533
664
|
})
|
|
534
665
|
}
|
|
535
|
-
|
|
536
|
-
//
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
allow_url_override?: boolean
|
|
551
|
-
apps?: Record<string, {
|
|
552
|
-
roles: string[]
|
|
553
|
-
default_role: string
|
|
554
|
-
path: string
|
|
555
|
-
enabled: boolean
|
|
556
|
-
account_strategy: 'existing' | 'new' | 'choice'
|
|
557
|
-
target_account?: string
|
|
558
|
-
}>
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const SPINE_CONFIG_PATH = join(PROJECT_ROOT, 'spine.config.json')
|
|
563
|
-
|
|
666
|
+
// CIL[END chunk_id="install-app_prompt_1784069813"]
|
|
667
|
+
// CIL[BEGIN chunk_id="install-app_loadspineconfig_1784069813" type="function" identity="loadSpineConfig" parent="none"]
|
|
668
|
+
// CIL[DOC-MACRO purpose="Load spine.config.json configuration file"]
|
|
669
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Read and parse Spine configuration"]
|
|
670
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[]"]
|
|
671
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="SpineConfig object" errors="file read errors" side_effects="none"]
|
|
672
|
+
// CIL[DOC-MICRO logic_summary="Reads spine.config.json with fallback to empty config" notable_decisions="[]"]
|
|
673
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
674
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
675
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
676
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
677
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
678
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
679
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
680
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
564
681
|
function loadSpineConfig(): SpineConfig {
|
|
565
682
|
if (!existsSync(SPINE_CONFIG_PATH)) {
|
|
566
683
|
return { registration: { apps: {} } }
|
|
@@ -571,15 +688,44 @@ function loadSpineConfig(): SpineConfig {
|
|
|
571
688
|
return { registration: { apps: {} } }
|
|
572
689
|
}
|
|
573
690
|
}
|
|
574
|
-
|
|
691
|
+
// CIL[END chunk_id="install-app_loadspineconfig_1784069813"]
|
|
692
|
+
// CIL[BEGIN chunk_id="install-app_savespineconfig_1784069813" type="function" identity="saveSpineConfig" parent="none"]
|
|
693
|
+
// CIL[DOC-MACRO purpose="Save configuration to spine.config.json"]
|
|
694
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Write Spine configuration to file"]
|
|
695
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[]"]
|
|
696
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="file write" errors="file write errors" side_effects="filesystem write"]
|
|
697
|
+
// CIL[DOC-MICRO logic_summary="Writes configuration as formatted JSON to spine.config.json" notable_decisions="[]"]
|
|
698
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
699
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
700
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
701
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
702
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
703
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
704
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
705
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
575
706
|
function saveSpineConfig(config: SpineConfig): void {
|
|
576
707
|
writeFileSync(SPINE_CONFIG_PATH, JSON.stringify(config, null, 2) + '\n')
|
|
577
708
|
}
|
|
578
|
-
|
|
579
|
-
|
|
709
|
+
// CIL[END chunk_id="install-app_savespineconfig_1784069813"]
|
|
710
|
+
// CIL[BEGIN chunk_id="install-app_updateregistrationconfig_1784069813" type="function" identity="updateRegistrationConfig" parent="none"]
|
|
711
|
+
// CIL[DOC-LEARNING topic="spine.config:registration_slug_match" content="spine.config.json registration.apps is keyed by app slug and default_app points to that key; the slug must match the public.apps row or post-login redirects will point to a non-routable app."]
|
|
712
|
+
// CIL[DOC-MACRO purpose="Update registration config for app in spine.config.json"]
|
|
713
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Configure app registration and role settings"]
|
|
714
|
+
// CIL[DOC-CONTEXT module="cli.commands.install-app" layer="cli" related_files="[]"]
|
|
715
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="config file update" errors="file errors" side_effects="config file write"]
|
|
716
|
+
// CIL[DOC-MICRO logic_summary="Updates spine.config.json with app registration settings and available roles" notable_decisions="[]"]
|
|
717
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
718
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
719
|
+
// CIL[DOC-VECTOR type="context" content="Layer: cli. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
720
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
721
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
722
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
723
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
724
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
725
|
+
function updateRegistrationConfig(appSlug: string, regConfig: RegistrationConfig, requiredRoles: string[] = []): void {
|
|
580
726
|
const config = loadSpineConfig()
|
|
581
727
|
|
|
582
|
-
|
|
728
|
+
|
|
583
729
|
if (!config.registration) {
|
|
584
730
|
config.registration = { apps: {} }
|
|
585
731
|
}
|
|
@@ -587,22 +733,10 @@ function updateRegistrationConfig(appSlug: string, regConfig: RegistrationConfig
|
|
|
587
733
|
config.registration.apps = {}
|
|
588
734
|
}
|
|
589
735
|
|
|
590
|
-
// Try to load all roles from the app's seed data
|
|
591
|
-
let availableRoles: string[] = [regConfig.default_role] // Start with default role from manifest
|
|
592
736
|
|
|
593
|
-
|
|
594
|
-
const rolesPath = join(PROJECT_ROOT, 'custom/apps', appSlug, 'seed/roles.json')
|
|
595
|
-
if (existsSync(rolesPath)) {
|
|
596
|
-
const rolesData = JSON.parse(readFileSync(rolesPath, 'utf8'))
|
|
597
|
-
if (Array.isArray(rolesData)) {
|
|
598
|
-
availableRoles = rolesData.map((role: any) => role.slug).filter(Boolean)
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
} catch (error) {
|
|
602
|
-
console.log(` ⚠️ Could not load roles from seed data, using default role only`)
|
|
603
|
-
}
|
|
737
|
+
let availableRoles: string[] = requiredRoles.length ? requiredRoles : [regConfig.default_role]
|
|
604
738
|
|
|
605
|
-
|
|
739
|
+
|
|
606
740
|
config.registration.apps[appSlug] = {
|
|
607
741
|
roles: availableRoles,
|
|
608
742
|
default_role: regConfig.default_role,
|
|
@@ -612,14 +746,14 @@ function updateRegistrationConfig(appSlug: string, regConfig: RegistrationConfig
|
|
|
612
746
|
target_account: regConfig.target_account,
|
|
613
747
|
}
|
|
614
748
|
|
|
615
|
-
|
|
749
|
+
|
|
616
750
|
const enabledApps = Object.entries(config.registration.apps).filter(([, cfg]) => cfg.enabled)
|
|
617
751
|
if (enabledApps.length === 1 || !config.registration.default_app) {
|
|
618
752
|
config.registration.default_app = appSlug
|
|
619
753
|
config.registration.default_role = regConfig.default_role
|
|
620
754
|
}
|
|
621
755
|
|
|
622
|
-
|
|
756
|
+
|
|
623
757
|
if (config.registration.allow_url_override === undefined) {
|
|
624
758
|
config.registration.allow_url_override = true
|
|
625
759
|
}
|
|
@@ -628,3 +762,4 @@ function updateRegistrationConfig(appSlug: string, regConfig: RegistrationConfig
|
|
|
628
762
|
console.log(` ✓ Updated spine.config.json registration for ${appSlug}`)
|
|
629
763
|
console.log(` ✓ Available roles: ${availableRoles.join(', ')}`)
|
|
630
764
|
}
|
|
765
|
+
// CIL[END chunk_id="install-app_updateregistrationconfig_1784069813"]
|