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,15 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
9
15
|
|
|
10
16
|
import { adminDb } from './db'
|
|
11
17
|
|
|
12
|
-
export type FileStatus =
|
|
18
|
+
export type FileStatus =
|
|
19
|
+
| '0-discovered'
|
|
20
|
+
| '1-selected'
|
|
21
|
+
| '2-chunked'
|
|
22
|
+
| '3-first-pass-complete'
|
|
23
|
+
| '4-first-pass-embedded'
|
|
24
|
+
| '5-first-pass-mapped'
|
|
25
|
+
| '6-second-pass-complete'
|
|
26
|
+
| '7-second-pass-embedded'
|
|
27
|
+
| '8-second-pass-mapped'
|
|
13
28
|
|
|
14
29
|
export interface FileTreeNode {
|
|
15
30
|
id?: string | null
|
|
@@ -23,10 +38,20 @@ export interface FileTreeNode {
|
|
|
23
38
|
selected?: boolean
|
|
24
39
|
}
|
|
25
40
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
30
55
|
export function isSelectedPath(filePath: string, selectedPaths: string[]): boolean {
|
|
31
56
|
if (selectedPaths.length === 0) return true
|
|
32
57
|
return selectedPaths.some(sp => {
|
|
@@ -36,32 +61,66 @@ export function isSelectedPath(filePath: string, selectedPaths: string[]): boole
|
|
|
36
61
|
})
|
|
37
62
|
}
|
|
38
63
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
42
78
|
export function getFileStatus(fileItem: any): FileStatus {
|
|
43
|
-
return fileItem.data?.status || 'discovered'
|
|
79
|
+
return fileItem.data?.status || '0-discovered'
|
|
44
80
|
}
|
|
45
81
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
export async function updateFileStatus(_ctx: any, fileItem: any, status: FileStatus, extraData: Record<string, any> = {}): Promise<void> {
|
|
97
|
+
await adminDb
|
|
98
|
+
.from('items')
|
|
99
|
+
.update({
|
|
100
|
+
data: {
|
|
101
|
+
...fileItem.data,
|
|
102
|
+
...extraData,
|
|
103
|
+
status,
|
|
104
|
+
last_status_at: new Date().toISOString(),
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
.eq('id', fileItem.id)
|
|
60
108
|
}
|
|
61
109
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
65
124
|
export function buildFileTree(fileRecords: { file_path: string; id?: string; language: string; status?: FileStatus; extension: string }[]): FileTreeNode {
|
|
66
125
|
const root: FileTreeNode = { name: 'root', path: '', type: 'directory', children: [] }
|
|
67
126
|
|
|
@@ -97,9 +156,20 @@ export function buildFileTree(fileRecords: { file_path: string; id?: string; lan
|
|
|
97
156
|
return root
|
|
98
157
|
}
|
|
99
158
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
103
173
|
export function updateTreeNodeStatus(tree: FileTreeNode, fileId: string, status: FileStatus): void {
|
|
104
174
|
if (tree.id === fileId) {
|
|
105
175
|
tree.status = status
|
|
@@ -112,10 +182,20 @@ export function updateTreeNodeStatus(tree: FileTreeNode, fileId: string, status:
|
|
|
112
182
|
}
|
|
113
183
|
}
|
|
114
184
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
119
199
|
export function updateTreeNodeByPath(tree: FileTreeNode, filePath: string, updates: { id?: string; status?: FileStatus }): void {
|
|
120
200
|
if (tree.type === 'file' && tree.path === filePath) {
|
|
121
201
|
if (updates.id !== undefined) tree.id = updates.id
|
|
@@ -129,20 +209,42 @@ export function updateTreeNodeByPath(tree: FileTreeNode, filePath: string, updat
|
|
|
129
209
|
}
|
|
130
210
|
}
|
|
131
211
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
export async function saveProjectTree(
|
|
227
|
+
_ctx: any,
|
|
228
|
+
projectId: string,
|
|
229
|
+
tree: FileTreeNode,
|
|
230
|
+
extraData: Record<string, any> = {},
|
|
231
|
+
projectItem?: any
|
|
232
|
+
): Promise<void> {
|
|
233
|
+
let projItem = projectItem
|
|
234
|
+
if (!projItem) {
|
|
235
|
+
const { data } = await adminDb.from('items').select('id, data').eq('id', projectId).maybeSingle()
|
|
236
|
+
projItem = data
|
|
237
|
+
}
|
|
138
238
|
if (!projItem) return
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
239
|
+
await adminDb
|
|
240
|
+
.from('items')
|
|
241
|
+
.update({
|
|
242
|
+
data: {
|
|
243
|
+
...projItem.data,
|
|
244
|
+
file_tree: tree,
|
|
245
|
+
...extraData,
|
|
246
|
+
last_tree_update: new Date().toISOString(),
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
.eq('id', projItem.id)
|
|
148
250
|
}
|
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module db
|
|
3
|
-
* @audience both
|
|
4
|
-
* @layer shared-core
|
|
5
|
-
* @stability stable
|
|
6
|
-
*
|
|
7
|
-
* Supabase client factory and PostgREST join helpers. This module owns the
|
|
8
|
-
* two-client pattern that is central to Spine's security model: `adminDb`
|
|
9
|
-
* bypasses RLS for system operations; `getUserDb` enforces RLS for all
|
|
10
|
-
* human-principal requests. Never use `adminDb` for user-scoped queries —
|
|
11
|
-
* doing so silently bypasses account isolation.
|
|
12
|
-
*
|
|
13
|
-
* @seeAlso principal.ts (getPrincipalDb selects between these two clients)
|
|
14
|
-
* @seeAlso middleware.ts (ctx.db is set from getPrincipalDb at request time)
|
|
15
|
-
*/
|
|
16
1
|
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// CIL[BEGIN chunk_id="db_moduleheader_1784066609" type="module_header" identity="module_header" parent="none"]
|
|
7
|
+
// CIL[DOC-MACRO purpose="Database connection setup and configuration"]
|
|
8
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Configure Supabase clients and environment variables"]
|
|
9
|
+
// CIL[DOC-CONTEXT module="_shared/db" layer="infrastructure" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/src/components/ui/dialog.tsx\",\"\\\"]"]
|
|
10
|
+
// CIL[DOC-CONTRACT inputs="url" outputs="configured clients and constants" errors="invalid URLs" side_effects="global configuration"]
|
|
11
|
+
// CIL[DOC-MICRO logic_summary="Validates environment variables and sets up Supabase connection parameters" notable_decisions="[]"]
|
|
12
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
13
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
14
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
15
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/src/components/ui/dialog.tsx, \."]
|
|
16
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
17
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
18
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
19
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
20
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
17
21
|
import { createClient } from '@supabase/supabase-js'
|
|
18
22
|
import WebSocket from 'ws'
|
|
19
23
|
|
|
20
|
-
// ─── ENVIRONMENT RESOLUTION ──────────────────────────────────────────────────
|
|
21
|
-
|
|
22
24
|
const _env = (globalThis as any).process?.env || {}
|
|
23
25
|
|
|
24
|
-
// Use a known-valid placeholder URL so that createClient() doesn't throw at
|
|
25
|
-
// module load time when env vars haven't been set yet (e.g. during CLI init).
|
|
26
|
-
// All real requests validate credentials before hitting the DB.
|
|
27
26
|
const PLACEHOLDER_URL = 'https://placeholder.supabase.co'
|
|
28
27
|
const PLACEHOLDER_KEY = 'placeholder'
|
|
29
28
|
|
|
@@ -36,62 +35,40 @@ const supabaseUrl: string = rawUrl && isValidUrl(rawUrl) ? rawUrl : PLACEHOLDER_
|
|
|
36
35
|
const supabaseServiceKey: string = _env.SUPABASE_SERVICE_ROLE_KEY || PLACEHOLDER_KEY
|
|
37
36
|
const supabaseAnonKey: string = _env.SUPABASE_ANON_KEY || PLACEHOLDER_KEY
|
|
38
37
|
const dbSchema: string = _env.DB_SCHEMA || 'public'
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
* @inputSpec SUPABASE_URL: string — valid Supabase project URL, required
|
|
55
|
-
* @inputSpec SUPABASE_SERVICE_ROLE_KEY: string — service role JWT, required
|
|
56
|
-
* @outputSpec SupabaseClient — PostgREST client scoped to `dbSchema`, RLS disabled
|
|
57
|
-
* @sideEffects none (client construction only)
|
|
58
|
-
* @calledBy principal.ts, middleware.ts, system-cron.ts, permissions.ts,
|
|
59
|
-
* tests/integration/helpers.ts
|
|
60
|
-
* @calls createClient (supabase-js)
|
|
61
|
-
* @testUnit tests/unit/pipeline-runner.test.ts — mocked via vi.mock
|
|
62
|
-
* @testIntegration tests/integration/helpers.ts — used directly as adminDb
|
|
63
|
-
*
|
|
64
|
-
* @example API handler (system operation)
|
|
65
|
-
* ```ts
|
|
66
|
-
* import { adminDb } from './_shared/db'
|
|
67
|
-
* const { data } = await adminDb.from('types').select('*').eq('slug', 'item')
|
|
68
|
-
* ```
|
|
69
|
-
*
|
|
70
|
-
* @example Import usage (v2-custom/ — system-level only)
|
|
71
|
-
* ```ts
|
|
72
|
-
* import { adminDb } from '../_shared/index'
|
|
73
|
-
* // Only use adminDb if your custom code runs as a system/cron principal
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
38
|
+
// CIL[END chunk_id="db_moduleheader_1784066609"]
|
|
39
|
+
// CIL[BEGIN chunk_id="db_admindb_1784066609" type="variable" identity="adminDb" parent="none"]
|
|
40
|
+
// CIL[DOC-MACRO purpose="Admin database client with full permissions"]
|
|
41
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide database access for admin operations"]
|
|
42
|
+
// CIL[DOC-CONTEXT module="_shared/db" layer="infrastructure" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
|
|
43
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="admin Supabase client" errors="invalid credentials" side_effects="database connection"]
|
|
44
|
+
// CIL[DOC-MICRO logic_summary="Creates Supabase client with service role key for admin access" notable_decisions="[]"]
|
|
45
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
46
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
47
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/functions/_shared/principal.ts, \."]
|
|
48
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
49
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
50
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
51
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
52
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
76
53
|
export const adminDb = createClient(supabaseUrl, supabaseServiceKey, {
|
|
77
54
|
db: { schema: dbSchema },
|
|
78
55
|
realtime: { transport: WebSocket as any }
|
|
79
56
|
})
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
57
|
+
// CIL[END chunk_id="db_admindb_1784066609"]
|
|
58
|
+
// CIL[BEGIN chunk_id="db_getuserdb_1784066609" type="function" identity="getUserDb" parent="none"]
|
|
59
|
+
// CIL[DOC-MACRO purpose="Create user-scoped database client"]
|
|
60
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide authenticated database access for users"]
|
|
61
|
+
// CIL[DOC-CONTEXT module="_shared/db" layer="infrastructure" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/components/ui/dialog.tsx\",\"\\\"]"]
|
|
62
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="user Supabase client" errors="invalid JWT" side_effects="database connection"]
|
|
63
|
+
// CIL[DOC-MICRO logic_summary="Creates Supabase client with user JWT for row-level security" notable_decisions="[]"]
|
|
64
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
65
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
66
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/src/components/ui/dialog.tsx, \."]
|
|
67
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
68
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
69
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
70
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
71
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
95
72
|
export function getUserDb(jwt: string) {
|
|
96
73
|
return createClient(supabaseUrl, supabaseAnonKey, {
|
|
97
74
|
db: { schema: dbSchema },
|
|
@@ -99,64 +76,40 @@ export function getUserDb(jwt: string) {
|
|
|
99
76
|
realtime: { transport: WebSocket as any }
|
|
100
77
|
})
|
|
101
78
|
}
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
*
|
|
118
|
-
* @example
|
|
119
|
-
* ```ts
|
|
120
|
-
* const result: DbResult<Item> = await adminDb.from('items').select('*').single()
|
|
121
|
-
* if (result.error) throw result.error
|
|
122
|
-
* return result.data!
|
|
123
|
-
* ```
|
|
124
|
-
*/
|
|
79
|
+
// CIL[END chunk_id="db_getuserdb_1784066609"]
|
|
80
|
+
// CIL[BEGIN chunk_id="db_dbresult_1784066609" type="type" identity="DbResult" parent="none"]
|
|
81
|
+
// CIL[DOC-MACRO purpose="Database operation result type"]
|
|
82
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Define standard database response structure"]
|
|
83
|
+
// CIL[DOC-CONTEXT module="_shared/db" layer="infrastructure" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
|
|
84
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="DbResult type" errors="none" side_effects="type definition"]
|
|
85
|
+
// CIL[DOC-MICRO logic_summary="Generic type for database operations with data and error fields" notable_decisions="[]"]
|
|
86
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
87
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
88
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/cortex/functions/custom_cil-embeddings.ts, .framework/functions/_shared/schema-utils.ts, \."]
|
|
89
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
90
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
91
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
92
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
93
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
125
94
|
export type DbResult<T> = {
|
|
126
95
|
data: T | null
|
|
127
96
|
error: any
|
|
128
97
|
}
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* @inputSpec none — these are static string constants
|
|
145
|
-
* @outputSpec string — valid PostgREST embed expression for use in .select()
|
|
146
|
-
* @sideEffects none
|
|
147
|
-
* @calledBy types.ts, apps.ts, pipelines.ts, triggers.ts, admin-data.ts, and others
|
|
148
|
-
* @testUnit none — these are string constants; incorrect joins fail at runtime
|
|
149
|
-
* @testIntegration tests/integration/admin-data-accounts.test.ts — exercises joins.type
|
|
150
|
-
*
|
|
151
|
-
* @example
|
|
152
|
-
* ```ts
|
|
153
|
-
* import { joins } from './_shared/db'
|
|
154
|
-
* const { data } = await ctx.db
|
|
155
|
-
* .from('items')
|
|
156
|
-
* .select(`*, ${joins.type}, ${joins.app}`)
|
|
157
|
-
* // Returns items with nested type and app objects
|
|
158
|
-
* ```
|
|
159
|
-
*/
|
|
98
|
+
// CIL[END chunk_id="db_dbresult_1784066609"]
|
|
99
|
+
// CIL[BEGIN chunk_id="db_joins_1784066609" type="variable" identity="joins" parent="none"]
|
|
100
|
+
// CIL[DOC-MACRO purpose="Database join configuration for common relationships"]
|
|
101
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Define standard foreign key join patterns"]
|
|
102
|
+
// CIL[DOC-CONTEXT module="_shared/db" layer="infrastructure" related_files="[]"]
|
|
103
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="join configuration object" errors="none" side_effects="query optimization"]
|
|
104
|
+
// CIL[DOC-MICRO logic_summary="Predefined join strings for common table relationships" notable_decisions="[]"]
|
|
105
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="external_symbols_unknown"]
|
|
106
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
107
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
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"]
|
|
160
113
|
export const joins = {
|
|
161
114
|
type: 'type:types!type_id(id, slug, name, icon, color, design_schema)',
|
|
162
115
|
app: 'app:apps!app_id(id, slug, name)',
|
|
@@ -166,3 +119,4 @@ export const joins = {
|
|
|
166
119
|
role: 'role:roles!role_id(id, slug, name)',
|
|
167
120
|
pipeline: 'pipeline:pipelines!pipeline_id(id, name)',
|
|
168
121
|
}
|
|
122
|
+
// CIL[END chunk_id="db_joins_1784066609"]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// CIL[BEGIN chunk_id="hash_api_key_module_header_1784200005" type="module_header" identity="imports" parent="none"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="Hash and verify API keys using PBKDF2"]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Generate secure, salted key hashes and verify raw keys"]
|
|
4
|
+
// CIL[DOC-CONTEXT module="hash_api_key" layer="infrastructure" related_files="[".framework/functions/api-keys.ts",".framework/functions/integration-routes.ts"]"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="raw key" outputs="{ key_prefix, key_hash }" errors="hashing error" side_effects="none"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Uses crypto.pbkdf2Sync to derive a 32-byte key from a random salt and the raw key. Stored format is pbkdf2$<iterations>$<salt base64>$<hash base64>." notable_decisions="["PBKDF2 is built into Node; no extra dependency.","Format is self-describing so iterations can be upgraded later.","key_prefix is the first 8 characters of the raw key for lookup."]"]
|
|
7
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.95" flags="none"]
|
|
8
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
9
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
10
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: .framework/functions/api-keys.ts, .framework/functions/integration-routes.ts."]
|
|
11
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
12
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
13
|
+
// CIL[DOC-VECTOR type="constraint" content="Uses Node crypto only."]
|
|
14
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="Hash on key creation; verify on inbound webhook."]
|
|
15
|
+
// CIL[DOC-VECTOR type="user_journey" content="Machine-principal credential lifecycle."]
|
|
16
|
+
|
|
17
|
+
import { randomBytes, pbkdf2Sync, timingSafeEqual } from 'crypto'
|
|
18
|
+
|
|
19
|
+
const ITERATIONS = 100000
|
|
20
|
+
const KEY_LENGTH = 32
|
|
21
|
+
const DIGEST = 'sha256'
|
|
22
|
+
|
|
23
|
+
export function hashApiKey(rawKey: string, iterations = ITERATIONS): { key_prefix: string; key_hash: string; rawKey: string } {
|
|
24
|
+
const key_prefix = rawKey.slice(0, 8)
|
|
25
|
+
const salt = randomBytes(16)
|
|
26
|
+
const hash = pbkdf2Sync(rawKey, salt, iterations, KEY_LENGTH, DIGEST)
|
|
27
|
+
const key_hash = `pbkdf2$${iterations}$${salt.toString('base64url')}$${hash.toString('base64url')}`
|
|
28
|
+
|
|
29
|
+
return { key_prefix, key_hash, rawKey }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function verifyApiKey(rawKey: string, keyHash: string): boolean {
|
|
33
|
+
if (!keyHash || !keyHash.startsWith('pbkdf2$')) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const parts = keyHash.split('$')
|
|
38
|
+
if (parts.length !== 4) return false
|
|
39
|
+
|
|
40
|
+
const [, iterStr, saltB64, hashB64] = parts
|
|
41
|
+
const iterations = parseInt(iterStr, 10)
|
|
42
|
+
if (Number.isNaN(iterations) || iterations <= 0) return false
|
|
43
|
+
|
|
44
|
+
let salt: Buffer
|
|
45
|
+
let storedHash: Buffer
|
|
46
|
+
try {
|
|
47
|
+
salt = Buffer.from(saltB64, 'base64url')
|
|
48
|
+
storedHash = Buffer.from(hashB64, 'base64url')
|
|
49
|
+
} catch {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const derived = pbkdf2Sync(rawKey, salt, iterations, storedHash.length, DIGEST)
|
|
54
|
+
if (derived.length !== storedHash.length) return false
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
return timingSafeEqual(derived, storedHash)
|
|
58
|
+
} catch {
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// CIL[END chunk_id="hash_api_key_module_header_1784200005"]
|