spine-framework 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.framework/README.md +129 -0
- package/.framework/cli/bin.cjs +14 -0
- package/.framework/cli/commands/agents.ts +153 -0
- package/.framework/cli/commands/auth.ts +94 -0
- package/.framework/cli/commands/create-app.ts +185 -0
- package/.framework/cli/commands/dev.ts +295 -0
- package/.framework/cli/commands/doctor.ts +442 -0
- package/.framework/cli/commands/generate.ts +332 -0
- package/.framework/cli/commands/init.ts +272 -0
- package/.framework/cli/commands/install-app.ts +391 -0
- package/.framework/cli/commands/items.ts +253 -0
- package/.framework/cli/commands/migrations.ts +141 -0
- package/.framework/cli/commands/pipelines.ts +166 -0
- package/.framework/cli/commands/status.ts +197 -0
- package/.framework/cli/commands/system.ts +184 -0
- package/.framework/cli/commands/test.ts +227 -0
- package/.framework/cli/commands/uninstall-app.ts +166 -0
- package/.framework/cli/context.ts +268 -0
- package/.framework/cli/env-loader.ts +36 -0
- package/.framework/cli/index.ts +106 -0
- package/.framework/cli/welcome.cjs +45 -0
- package/.framework/docs/API.md +384 -0
- package/.framework/docs/STABILITY.md +52 -0
- package/.framework/docs/admin-routes.md +76 -0
- package/.framework/docs/api-docs-progress.md +38 -0
- package/.framework/docs/api-governance.md +146 -0
- package/.framework/docs/api-testing-results.md +212 -0
- package/.framework/docs/apis/admin-configs.md +567 -0
- package/.framework/docs/apis/admin-data.md +272 -0
- package/.framework/docs/apis/index.md +231 -0
- package/.framework/docs/apis/internal.md +295 -0
- package/.framework/docs/apis/runtime.md +537 -0
- package/.framework/docs/assembly-launch-guide.md +138 -0
- package/.framework/docs/audit-results.md +590 -0
- package/.framework/docs/authorization-model.md +170 -0
- package/.framework/docs/db-api-inventory.md +95 -0
- package/.framework/docs/examples/custom-app/README.md +77 -0
- package/.framework/docs/examples/custom-function/README.md +27 -0
- package/.framework/docs/examples/custom-function/handler.ts +48 -0
- package/.framework/docs/examples/custom-webhook/README.md +68 -0
- package/.framework/docs/gap-remediation-backlog.md +103 -0
- package/.framework/docs/guides/cli-guide.md +224 -0
- package/.framework/docs/guides/getting-started.md +103 -0
- package/.framework/docs/guides/import-guide.md +193 -0
- package/.framework/docs/guides/testing-guide.md +229 -0
- package/.framework/docs/permission-examples.md +326 -0
- package/.framework/docs/ui-adoption-verification.md +111 -0
- package/.framework/docs/ui-api-coverage.md +84 -0
- package/.framework/docs/v2-compatibility-audit.md +228 -0
- package/.framework/functions/.gitkeep +1 -0
- package/.framework/functions/_shared/agent-runner.ts +1097 -0
- package/.framework/functions/_shared/app-manifest.ts +184 -0
- package/.framework/functions/_shared/audit.ts +150 -0
- package/.framework/functions/_shared/db.ts +174 -0
- package/.framework/functions/_shared/index.ts +382 -0
- package/.framework/functions/_shared/middleware.ts +490 -0
- package/.framework/functions/_shared/permissions.ts +1325 -0
- package/.framework/functions/_shared/pipeline-runner.ts +731 -0
- package/.framework/functions/_shared/principal.ts +760 -0
- package/.framework/functions/_shared/schema-utils.ts +967 -0
- package/.framework/functions/_shared/testing.ts +258 -0
- package/.framework/functions/_shared/trigger-engine.ts +425 -0
- package/.framework/functions/_shared/webhook-registration.ts +168 -0
- package/.framework/functions/_shared/webhook-registry.ts +129 -0
- package/.framework/functions/account-nodes.ts +111 -0
- package/.framework/functions/admin-data.ts +606 -0
- package/.framework/functions/ai-agents.ts +323 -0
- package/.framework/functions/api-keys.ts +376 -0
- package/.framework/functions/apps.ts +483 -0
- package/.framework/functions/auth.ts +196 -0
- package/.framework/functions/debug-auth.ts +107 -0
- package/.framework/functions/embeddings.ts +556 -0
- package/.framework/functions/integration-routes.ts +523 -0
- package/.framework/functions/integrations.ts +319 -0
- package/.framework/functions/item-progress.ts +272 -0
- package/.framework/functions/logs.ts +438 -0
- package/.framework/functions/observability.ts +275 -0
- package/.framework/functions/pipeline-executions.ts +494 -0
- package/.framework/functions/pipelines.ts +485 -0
- package/.framework/functions/prompt-configs.ts +339 -0
- package/.framework/functions/roles.ts +387 -0
- package/.framework/functions/system-cron.ts +742 -0
- package/.framework/functions/system.ts +323 -0
- package/.framework/functions/tests.ts +119 -0
- package/.framework/functions/timers.ts +357 -0
- package/.framework/functions/triggers.ts +563 -0
- package/.framework/functions/types.ts +604 -0
- package/.framework/migrations/000_foundation.sql +1256 -0
- package/.framework/migrations/001_seed.sql +92 -0
- package/.framework/migrations/002_seed_constraints.sql +13 -0
- package/.framework/migrations/003_auth_user_trigger.sql +59 -0
- package/.framework/src/App.tsx +126 -0
- package/.framework/src/apps/admin/index.tsx +173 -0
- package/.framework/src/components/AppWrapper.tsx +56 -0
- package/.framework/src/components/CustomAppLoader.tsx +116 -0
- package/.framework/src/components/admin/AdminListPage.tsx +151 -0
- package/.framework/src/components/admin/AdminSidebar.tsx +166 -0
- package/.framework/src/components/admin/AdminStatsCard.tsx +62 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +42 -0
- package/.framework/src/components/app-shell/GenericAppShell.tsx +181 -0
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +200 -0
- package/.framework/src/components/app-shell/GenericListPage.tsx +116 -0
- package/.framework/src/components/app-sidebar.tsx +228 -0
- package/.framework/src/components/auth/ProtectedRoute.tsx +88 -0
- package/.framework/src/components/layout/AppShell.tsx +91 -0
- package/.framework/src/components/layout/Header.tsx +88 -0
- package/.framework/src/components/layout/Layout.tsx +95 -0
- package/.framework/src/components/layout/Sidebar.tsx +329 -0
- package/.framework/src/components/runtime/DataDetailHeader.tsx +77 -0
- package/.framework/src/components/runtime/DataDetailPage.tsx +171 -0
- package/.framework/src/components/runtime/DataFilters.tsx +91 -0
- package/.framework/src/components/runtime/DataHeader.tsx +68 -0
- package/.framework/src/components/runtime/DataListPage.tsx +124 -0
- package/.framework/src/components/runtime/DataStats.tsx +70 -0
- package/.framework/src/components/runtime/DataTable.tsx +174 -0
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +134 -0
- package/.framework/src/components/runtime/index.ts +18 -0
- package/.framework/src/components/search-form.tsx +29 -0
- package/.framework/src/components/shared/AgentView.tsx +213 -0
- package/.framework/src/components/shared/FieldRenderer.tsx +478 -0
- package/.framework/src/components/shared/SchemaFields.tsx +226 -0
- package/.framework/src/components/ui/DataTable.tsx +343 -0
- package/.framework/src/components/ui/Form.tsx +281 -0
- package/.framework/src/components/ui/ItemCard.tsx +296 -0
- package/.framework/src/components/ui/ItemListView.tsx +308 -0
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -0
- package/.framework/src/components/ui/Modal.tsx +61 -0
- package/.framework/src/components/ui/RichTextEditor.tsx +210 -0
- package/.framework/src/components/ui/accordion.tsx +82 -0
- package/.framework/src/components/ui/alert-dialog.tsx +197 -0
- package/.framework/src/components/ui/alert.tsx +76 -0
- package/.framework/src/components/ui/aspect-ratio.tsx +11 -0
- package/.framework/src/components/ui/avatar.tsx +110 -0
- package/.framework/src/components/ui/badge.tsx +49 -0
- package/.framework/src/components/ui/breadcrumb.tsx +122 -0
- package/.framework/src/components/ui/button-group.tsx +83 -0
- package/.framework/src/components/ui/button.tsx +65 -0
- package/.framework/src/components/ui/calendar.tsx +222 -0
- package/.framework/src/components/ui/card.tsx +100 -0
- package/.framework/src/components/ui/carousel.tsx +240 -0
- package/.framework/src/components/ui/chart.tsx +373 -0
- package/.framework/src/components/ui/checkbox.tsx +31 -0
- package/.framework/src/components/ui/collapsible.tsx +33 -0
- package/.framework/src/components/ui/combobox.tsx +299 -0
- package/.framework/src/components/ui/command.tsx +193 -0
- package/.framework/src/components/ui/context-menu.tsx +261 -0
- package/.framework/src/components/ui/dialog.tsx +165 -0
- package/.framework/src/components/ui/direction.tsx +22 -0
- package/.framework/src/components/ui/drawer.tsx +132 -0
- package/.framework/src/components/ui/dropdown-menu.tsx +269 -0
- package/.framework/src/components/ui/empty.tsx +104 -0
- package/.framework/src/components/ui/field.tsx +238 -0
- package/.framework/src/components/ui/hover-card.tsx +42 -0
- package/.framework/src/components/ui/input-group.tsx +153 -0
- package/.framework/src/components/ui/input-otp.tsx +87 -0
- package/.framework/src/components/ui/input.tsx +19 -0
- package/.framework/src/components/ui/item.tsx +196 -0
- package/.framework/src/components/ui/kbd.tsx +26 -0
- package/.framework/src/components/ui/label.tsx +22 -0
- package/.framework/src/components/ui/menubar.tsx +277 -0
- package/.framework/src/components/ui/native-select.tsx +61 -0
- package/.framework/src/components/ui/navigation-menu.tsx +164 -0
- package/.framework/src/components/ui/pagination.tsx +129 -0
- package/.framework/src/components/ui/popover.tsx +87 -0
- package/.framework/src/components/ui/progress.tsx +31 -0
- package/.framework/src/components/ui/radio-group.tsx +42 -0
- package/.framework/src/components/ui/resizable.tsx +50 -0
- package/.framework/src/components/ui/scroll-area.tsx +53 -0
- package/.framework/src/components/ui/select.tsx +195 -0
- package/.framework/src/components/ui/separator.tsx +26 -0
- package/.framework/src/components/ui/sheet.tsx +145 -0
- package/.framework/src/components/ui/sidebar.tsx +706 -0
- package/.framework/src/components/ui/skeleton.tsx +13 -0
- package/.framework/src/components/ui/slider.tsx +59 -0
- package/.framework/src/components/ui/sonner.tsx +47 -0
- package/.framework/src/components/ui/spinner.tsx +10 -0
- package/.framework/src/components/ui/switch.tsx +33 -0
- package/.framework/src/components/ui/table-primitives.tsx +141 -0
- package/.framework/src/components/ui/table.tsx +114 -0
- package/.framework/src/components/ui/tabs.tsx +90 -0
- package/.framework/src/components/ui/textarea.tsx +18 -0
- package/.framework/src/components/ui/toggle-group.tsx +89 -0
- package/.framework/src/components/ui/toggle.tsx +45 -0
- package/.framework/src/components/ui/tooltip.tsx +57 -0
- package/.framework/src/contexts/AppContext.tsx +133 -0
- package/.framework/src/contexts/AuthContext.tsx +371 -0
- package/.framework/src/hooks/use-mobile.ts +19 -0
- package/.framework/src/hooks/useApi.ts +526 -0
- package/.framework/src/hooks/useApps.ts +114 -0
- package/.framework/src/hooks/useEntityList.ts +190 -0
- package/.framework/src/hooks/useEntityRecord.ts +308 -0
- package/.framework/src/hooks/useForm.ts +307 -0
- package/.framework/src/hooks/useListSchema.ts +264 -0
- package/.framework/src/hooks/useSchemaRecord.ts +223 -0
- package/.framework/src/index.css +128 -0
- package/.framework/src/lib/api.ts +156 -0
- package/.framework/src/lib/supabase.ts +94 -0
- package/.framework/src/lib/utils.ts +317 -0
- package/.framework/src/main.tsx +27 -0
- package/.framework/src/pages/DashboardPage.tsx +181 -0
- package/.framework/src/pages/NotFoundPage.tsx +39 -0
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +161 -0
- package/.framework/src/pages/admin/AIAgentsPage.tsx +318 -0
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +199 -0
- package/.framework/src/pages/admin/APIKeysPage.tsx +303 -0
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +523 -0
- package/.framework/src/pages/admin/AppDetailPage.tsx +493 -0
- package/.framework/src/pages/admin/AppsPage.tsx +355 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +491 -0
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +534 -0
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +424 -0
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +176 -0
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +109 -0
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +402 -0
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +187 -0
- package/.framework/src/pages/admin/IntegrationsPage.tsx +301 -0
- package/.framework/src/pages/admin/LogsPage.tsx +283 -0
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +85 -0
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +470 -0
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +183 -0
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +279 -0
- package/.framework/src/pages/admin/PipelinesPage.tsx +390 -0
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +299 -0
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +292 -0
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +434 -0
- package/.framework/src/pages/admin/RoleDetailPage.tsx +273 -0
- package/.framework/src/pages/admin/RolesPage.tsx +292 -0
- package/.framework/src/pages/admin/SelectTestPage.tsx +61 -0
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +588 -0
- package/.framework/src/pages/admin/SimpleDashboard.tsx +387 -0
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +172 -0
- package/.framework/src/pages/admin/TestingDashboard.tsx +257 -0
- package/.framework/src/pages/admin/TimerDetailPage.tsx +151 -0
- package/.framework/src/pages/admin/TimersPage.tsx +376 -0
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +149 -0
- package/.framework/src/pages/admin/TriggersPage.tsx +381 -0
- package/.framework/src/pages/admin/TypeDetailPage.tsx +694 -0
- package/.framework/src/pages/admin/TypesPage.tsx +295 -0
- package/.framework/src/pages/auth/LoginPage.tsx +188 -0
- package/.framework/src/pages/auth/RegisterPage.tsx +163 -0
- package/.framework/src/pages/spine-framework/APIPage.tsx +17 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +25 -0
- package/.framework/src/types/auth.ts +125 -0
- package/.framework/src/types/types.ts +407 -0
- package/STRUCTURE.md +150 -0
- package/config/components.json +25 -0
- package/config/deno.lock +108 -0
- package/config/package-lock.json +17183 -0
- package/config/postcss.config.cjs +10 -0
- package/config/tailwind.config.cjs +78 -0
- package/config/tsconfig.build.json +32 -0
- package/config/tsconfig.cli.json +18 -0
- package/config/tsconfig.json +41 -0
- package/config/tsconfig.node.json +17 -0
- package/config/tsconfig.node.tsbuildinfo +1 -0
- package/config/tsconfig.tsbuildinfo +1 -0
- package/config/typedoc.json +16 -0
- package/config/vite.config.d.ts +2 -0
- package/config/vite.config.ts +72 -0
- package/dist/cli/commands/agents.d.ts +39 -0
- package/dist/cli/commands/agents.d.ts.map +1 -0
- package/dist/cli/commands/auth.d.ts +36 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/create-app.d.ts +23 -0
- package/dist/cli/commands/create-app.d.ts.map +1 -0
- package/dist/cli/commands/dev.d.ts +39 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/doctor.d.ts +42 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/generate.d.ts +36 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts +30 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/install-app.d.ts +30 -0
- package/dist/cli/commands/install-app.d.ts.map +1 -0
- package/dist/cli/commands/items.d.ts +45 -0
- package/dist/cli/commands/items.d.ts.map +1 -0
- package/dist/cli/commands/migrations.d.ts +41 -0
- package/dist/cli/commands/migrations.d.ts.map +1 -0
- package/dist/cli/commands/pipelines.d.ts +40 -0
- package/dist/cli/commands/pipelines.d.ts.map +1 -0
- package/dist/cli/commands/status.d.ts +23 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/system.d.ts +29 -0
- package/dist/cli/commands/system.d.ts.map +1 -0
- package/dist/cli/commands/test.d.ts +46 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/uninstall-app.d.ts +23 -0
- package/dist/cli/commands/uninstall-app.d.ts.map +1 -0
- package/dist/cli/context.d.ts +88 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/env-loader.d.ts +14 -0
- package/dist/cli/env-loader.d.ts.map +1 -0
- package/dist/cli/index.d.ts +41 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/functions/_shared/agent-runner.d.ts +156 -0
- package/dist/functions/_shared/agent-runner.d.ts.map +1 -0
- package/dist/functions/_shared/app-manifest.d.ts +68 -0
- package/dist/functions/_shared/app-manifest.d.ts.map +1 -0
- package/dist/functions/_shared/audit.d.ts +91 -0
- package/dist/functions/_shared/audit.d.ts.map +1 -0
- package/dist/functions/_shared/db.d.ts +125 -0
- package/dist/functions/_shared/db.d.ts.map +1 -0
- package/dist/functions/_shared/index.d.ts +298 -0
- package/dist/functions/_shared/index.d.ts.map +1 -0
- package/dist/functions/_shared/middleware.d.ts +315 -0
- package/dist/functions/_shared/middleware.d.ts.map +1 -0
- package/dist/functions/_shared/permissions.d.ts +626 -0
- package/dist/functions/_shared/permissions.d.ts.map +1 -0
- package/dist/functions/_shared/pipeline-runner.d.ts +124 -0
- package/dist/functions/_shared/pipeline-runner.d.ts.map +1 -0
- package/dist/functions/_shared/principal.d.ts +284 -0
- package/dist/functions/_shared/principal.d.ts.map +1 -0
- package/dist/functions/_shared/schema-utils.d.ts +181 -0
- package/dist/functions/_shared/schema-utils.d.ts.map +1 -0
- package/dist/functions/_shared/testing.d.ts +172 -0
- package/dist/functions/_shared/testing.d.ts.map +1 -0
- package/dist/functions/_shared/trigger-engine.d.ts +140 -0
- package/dist/functions/_shared/trigger-engine.d.ts.map +1 -0
- package/dist/functions/_shared/webhook-registration.d.ts +81 -0
- package/dist/functions/_shared/webhook-registration.d.ts.map +1 -0
- package/dist/functions/_shared/webhook-registry.d.ts +57 -0
- package/dist/functions/_shared/webhook-registry.d.ts.map +1 -0
- package/dist/functions/account-nodes.d.ts +48 -0
- package/dist/functions/account-nodes.d.ts.map +1 -0
- package/dist/functions/admin-data.d.ts +178 -0
- package/dist/functions/admin-data.d.ts.map +1 -0
- package/dist/functions/ai-agents.d.ts +125 -0
- package/dist/functions/ai-agents.d.ts.map +1 -0
- package/dist/functions/api-keys.d.ts +140 -0
- package/dist/functions/api-keys.d.ts.map +1 -0
- package/dist/functions/apps.d.ts +163 -0
- package/dist/functions/apps.d.ts.map +1 -0
- package/dist/functions/auth.d.ts +74 -0
- package/dist/functions/auth.d.ts.map +1 -0
- package/dist/functions/debug-auth.d.ts +33 -0
- package/dist/functions/debug-auth.d.ts.map +1 -0
- package/dist/functions/embeddings.d.ts +205 -0
- package/dist/functions/embeddings.d.ts.map +1 -0
- package/dist/functions/integration-routes.d.ts +45 -0
- package/dist/functions/integration-routes.d.ts.map +1 -0
- package/dist/functions/integrations.d.ts +124 -0
- package/dist/functions/integrations.d.ts.map +1 -0
- package/dist/functions/item-progress.d.ts +41 -0
- package/dist/functions/item-progress.d.ts.map +1 -0
- package/dist/functions/logs.d.ts +162 -0
- package/dist/functions/logs.d.ts.map +1 -0
- package/dist/functions/observability.d.ts +123 -0
- package/dist/functions/observability.d.ts.map +1 -0
- package/dist/functions/pipeline-executions.d.ts +190 -0
- package/dist/functions/pipeline-executions.d.ts.map +1 -0
- package/dist/functions/pipelines.d.ts +171 -0
- package/dist/functions/pipelines.d.ts.map +1 -0
- package/dist/functions/prompt-configs.d.ts +125 -0
- package/dist/functions/prompt-configs.d.ts.map +1 -0
- package/dist/functions/roles.d.ts +118 -0
- package/dist/functions/roles.d.ts.map +1 -0
- package/dist/functions/system-cron.d.ts +65 -0
- package/dist/functions/system-cron.d.ts.map +1 -0
- package/dist/functions/system.d.ts +29 -0
- package/dist/functions/system.d.ts.map +1 -0
- package/dist/functions/tests.d.ts +28 -0
- package/dist/functions/tests.d.ts.map +1 -0
- package/dist/functions/timers.d.ts +139 -0
- package/dist/functions/timers.d.ts.map +1 -0
- package/dist/functions/triggers.d.ts +203 -0
- package/dist/functions/triggers.d.ts.map +1 -0
- package/dist/functions/types.d.ts +151 -0
- package/dist/functions/types.d.ts.map +1 -0
- package/dist/src/types/types.d.ts +364 -0
- package/dist/src/types/types.d.ts.map +1 -0
- package/package.json +192 -0
- package/scripts/app-install-cli.ts +286 -0
- package/scripts/assemble-frontend.sh +79 -0
- package/scripts/assemble-functions.sh +62 -0
- package/scripts/assemble.sh +35 -0
- package/scripts/boundary-check.sh +106 -0
- package/scripts/build-manifest.sh +80 -0
- package/scripts/check-core-integrity.sh +82 -0
- package/scripts/ingest-chunks.cjs +202 -0
- package/scripts/kb-chunk-parser.cjs +312 -0
- package/scripts/kb-chunk-parser.ts +330 -0
- package/scripts/load-test-app-install.ts +484 -0
- package/scripts/netlify-dev-wrapper.sh +22 -0
- package/scripts/verify-integrity.sh +69 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ai-agents
|
|
3
|
+
* @audience core-contributor
|
|
4
|
+
* @layer api-handler
|
|
5
|
+
* @stability stable
|
|
6
|
+
*
|
|
7
|
+
* CRUD API for the `ai_agents` table. AI agent records define the configuration
|
|
8
|
+
* for agentic inference workloads: model settings, system prompts, available
|
|
9
|
+
* tools, capabilities, and constraints. The runtime execution is handled by
|
|
10
|
+
* `_shared/agent-runner.ts`.
|
|
11
|
+
*
|
|
12
|
+
* **Routed by:** `GET/POST/PATCH/DELETE /.netlify/functions/ai-agents`
|
|
13
|
+
*
|
|
14
|
+
* **Actions (standard CRUD only):**
|
|
15
|
+
* | method | condition | handler |
|
|
16
|
+
* |--------|-----------|---------|
|
|
17
|
+
* | GET | ?id | get |
|
|
18
|
+
* | GET | (default) | list |
|
|
19
|
+
* | POST | — | create |
|
|
20
|
+
* | PATCH | — | update |
|
|
21
|
+
* | DELETE | — | remove |
|
|
22
|
+
*
|
|
23
|
+
* **Authorization:** All operations use `ctx.db` (RLS-scoped). Authenticated
|
|
24
|
+
* principal required for writes.
|
|
25
|
+
*
|
|
26
|
+
* INVARIANT: `remove` is a hard delete.
|
|
27
|
+
* INVARIANT: `update` only patches allowed fields: name, description,
|
|
28
|
+
* model_config, system_prompt, tools, capabilities, constraints, metadata,
|
|
29
|
+
* is_active.
|
|
30
|
+
*
|
|
31
|
+
* @seeAlso agent-runner.ts (runAgent — runtime execution using these configs)
|
|
32
|
+
* @seeAlso prompt-configs.ts (prompt_configs referenced by agent configs)
|
|
33
|
+
* @seeAlso audit.ts (emitLog for ai_agent.* events)
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { createHandler } from './_shared/middleware'
|
|
37
|
+
import { joins } from './_shared/db'
|
|
38
|
+
import { emitLog } from './_shared/audit'
|
|
39
|
+
import { PermissionEngine, sanitizeRecordData } from './_shared/permissions'
|
|
40
|
+
|
|
41
|
+
const permissions = PermissionEngine as any
|
|
42
|
+
|
|
43
|
+
// ─── HANDLERS ─────────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
// ─── CHUNK_START: AI_AGENTS_LIST ──────────────────────────────────────────────
|
|
46
|
+
/**
|
|
47
|
+
* @chunk-id AI_AGENTS_LIST_1_0_0
|
|
48
|
+
* @version 1.0.0
|
|
49
|
+
* @hash 237972d65e86f5d432f55b602ac5e6f2dcfaefa83c662c10b7d3437ac906ff0d
|
|
50
|
+
* @macro AI Agents List Handler
|
|
51
|
+
* @micro Lists AI agents with filtering and pagination
|
|
52
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
53
|
+
* @inputs _body: any — Request body (unused for GET)
|
|
54
|
+
* @outputs Array of sanitized AI agent records
|
|
55
|
+
* @depends-on [createHandler, joins, sanitizeRecordData]
|
|
56
|
+
* @depended-by [Netlify function routing]
|
|
57
|
+
* @side-effects [DB queries, permission sanitization]
|
|
58
|
+
* @tags ai-agents, list, crud, pagination
|
|
59
|
+
*/
|
|
60
|
+
export const list = createHandler(async (ctx, _body) => {
|
|
61
|
+
const { agent_type, is_active, limit = 100, offset = 0 } = ctx.query || {}
|
|
62
|
+
|
|
63
|
+
if (!ctx.accountId) {
|
|
64
|
+
throw new Error('Account context required')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// RLS automatically filters to accessible accounts
|
|
68
|
+
let query = ctx.db
|
|
69
|
+
.from('ai_agents')
|
|
70
|
+
.select(`*, ${joins.app}, ${joins.createdBy}`)
|
|
71
|
+
.order('name')
|
|
72
|
+
|
|
73
|
+
if (agent_type) {
|
|
74
|
+
query = query.eq('agent_type', agent_type)
|
|
75
|
+
}
|
|
76
|
+
if (is_active !== undefined) {
|
|
77
|
+
query = query.eq('is_active', is_active === 'true')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const { data, error: err } = await query.range(
|
|
81
|
+
parseInt(offset.toString()),
|
|
82
|
+
parseInt(offset.toString()) + parseInt(limit.toString()) - 1
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if (err) throw err
|
|
86
|
+
|
|
87
|
+
const sanitized = []
|
|
88
|
+
for (const agent of data || []) {
|
|
89
|
+
sanitized.push(await sanitizeRecordData(ctx, agent, 'ai_agent'))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return sanitized
|
|
93
|
+
})
|
|
94
|
+
// ─── CHUNK_END: AI_AGENTS_LIST ────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
// ─── CHUNK_START: AI_AGENTS_GET ──────────────────────────────────────────────
|
|
97
|
+
/**
|
|
98
|
+
* @chunk-id AI_AGENTS_GET_1_0_0
|
|
99
|
+
* @version 1.0.0
|
|
100
|
+
* @hash 62f283668d9a90ff81e2cbf8c437298206b355d2dd96fb559b2848fc4ffbc5cd
|
|
101
|
+
* @macro AI Agent Get Handler
|
|
102
|
+
* @micro Returns a single AI agent by UUID with joins
|
|
103
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
104
|
+
* @inputs _body: any — Request body (unused for GET)
|
|
105
|
+
* @outputs Sanitized AI agent record with app and createdBy joins
|
|
106
|
+
* @depends-on [createHandler, joins, sanitizeRecordData]
|
|
107
|
+
* @depended-by [Netlify function routing]
|
|
108
|
+
* @side-effects [DB single row query, permission sanitization]
|
|
109
|
+
* @tags ai-agents, get, crud, single-record
|
|
110
|
+
*/
|
|
111
|
+
export const get = createHandler(async (ctx, _body) => {
|
|
112
|
+
const { id } = ctx.query || {}
|
|
113
|
+
|
|
114
|
+
if (!id) {
|
|
115
|
+
throw new Error('Agent ID is required')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const { data, error: err } = await ctx.db
|
|
119
|
+
.from('ai_agents')
|
|
120
|
+
.select(`*, ${joins.app}, ${joins.createdBy}`)
|
|
121
|
+
.eq('id', id)
|
|
122
|
+
.single()
|
|
123
|
+
|
|
124
|
+
if (err) throw err
|
|
125
|
+
|
|
126
|
+
return await sanitizeRecordData(ctx, data, 'ai_agent')
|
|
127
|
+
})
|
|
128
|
+
// ─── CHUNK_END: AI_AGENTS_GET ────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
// ─── CHUNK_START: AI_AGENTS_CREATE ──────────────────────────────────────────────
|
|
131
|
+
/**
|
|
132
|
+
* @chunk-id AI_AGENTS_CREATE_1_0_0
|
|
133
|
+
* @version 1.0.0
|
|
134
|
+
* @hash 355c8ceb1f33e033616480827141a2f33cde8e18fa4e9f9d94f4866af6fe66f6
|
|
135
|
+
* @macro AI Agent Create Handler
|
|
136
|
+
* @micro Creates new AI agent configuration with validation and audit logging
|
|
137
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
138
|
+
* @inputs body: object — Agent configuration data
|
|
139
|
+
* @outputs Inserted AI agent record
|
|
140
|
+
* @depends-on [createHandler, emitLog]
|
|
141
|
+
* @depended-by [Netlify function routing]
|
|
142
|
+
* @side-effects [DB insert, audit logging]
|
|
143
|
+
* @tags ai-agents, create, crud, audit
|
|
144
|
+
*/
|
|
145
|
+
export const create = createHandler(async (ctx, body) => {
|
|
146
|
+
const { app_id, name, description, agent_type, model_config, system_prompt, tools, capabilities, constraints, metadata } = body
|
|
147
|
+
|
|
148
|
+
if (!name || !agent_type) {
|
|
149
|
+
throw new Error('name and agent_type are required')
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
|
|
153
|
+
throw new Error('User context (person and account) required')
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const { data, error: err } = await ctx.db
|
|
157
|
+
.from('ai_agents')
|
|
158
|
+
.insert({
|
|
159
|
+
app_id: app_id || null,
|
|
160
|
+
account_id: ctx.accountId,
|
|
161
|
+
name,
|
|
162
|
+
description: description || null,
|
|
163
|
+
agent_type,
|
|
164
|
+
model_config: model_config || {},
|
|
165
|
+
system_prompt: system_prompt || null,
|
|
166
|
+
tools: tools || [],
|
|
167
|
+
capabilities: capabilities || [],
|
|
168
|
+
constraints: constraints || {},
|
|
169
|
+
metadata: metadata || {},
|
|
170
|
+
created_by: ctx.principal.id
|
|
171
|
+
})
|
|
172
|
+
.select()
|
|
173
|
+
.single()
|
|
174
|
+
|
|
175
|
+
if (err) throw err
|
|
176
|
+
|
|
177
|
+
await emitLog(ctx, 'ai_agent.created',
|
|
178
|
+
{ type: 'ai_agent', id: data.id },
|
|
179
|
+
{ after: { name, agent_type } }
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return data
|
|
183
|
+
})
|
|
184
|
+
// ─── CHUNK_END: AI_AGENTS_CREATE ────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
// ─── CHUNK_START: AI_AGENTS_UPDATE ──────────────────────────────────────────────
|
|
187
|
+
/**
|
|
188
|
+
* @chunk-id AI_AGENTS_UPDATE_1_0_0
|
|
189
|
+
* @version 1.0.0
|
|
190
|
+
* @hash 07e173626958a0ef032cc4c80719c49df5990756e08ecdb70a4168ab73544f10
|
|
191
|
+
* @macro AI Agent Update Handler
|
|
192
|
+
* @micro Updates AI agent with field validation and audit logging
|
|
193
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
194
|
+
* @inputs body: object — Agent updates including id
|
|
195
|
+
* @outputs Updated AI agent record
|
|
196
|
+
* @depends-on [createHandler, emitLog]
|
|
197
|
+
* @depended-by [Netlify function routing]
|
|
198
|
+
* @side-effects [DB update, audit logging]
|
|
199
|
+
* @tags ai-agents, update, crud, audit
|
|
200
|
+
*/
|
|
201
|
+
export const update = createHandler(async (ctx, body) => {
|
|
202
|
+
const id = body?.id || ctx.query?.id
|
|
203
|
+
const { id: _bodyId, ...updates } = body || {}
|
|
204
|
+
|
|
205
|
+
if (!id) {
|
|
206
|
+
throw new Error('Agent ID is required')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
|
|
210
|
+
throw new Error('User context (person and account) required')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const allowed = ['name', 'description', 'model_config', 'system_prompt', 'tools', 'capabilities', 'constraints', 'metadata', 'is_active']
|
|
214
|
+
const updateData: Record<string, any> = { updated_at: new Date().toISOString() }
|
|
215
|
+
for (const key of allowed) {
|
|
216
|
+
if (updates[key] !== undefined) updateData[key] = updates[key]
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const { data, error: err } = await ctx.db
|
|
220
|
+
.from('ai_agents')
|
|
221
|
+
.update(updateData)
|
|
222
|
+
.eq('id', id)
|
|
223
|
+
.select()
|
|
224
|
+
.single()
|
|
225
|
+
|
|
226
|
+
if (err) throw err
|
|
227
|
+
|
|
228
|
+
await emitLog(ctx, 'ai_agent.updated',
|
|
229
|
+
{ type: 'ai_agent', id },
|
|
230
|
+
{ after: updateData }
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return data
|
|
234
|
+
})
|
|
235
|
+
// ─── CHUNK_END: AI_AGENTS_UPDATE ────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
// ─── CHUNK_START: AI_AGENTS_REMOVE ──────────────────────────────────────────────
|
|
238
|
+
/**
|
|
239
|
+
* @chunk-id AI_AGENTS_REMOVE_1_0_0
|
|
240
|
+
* @version 1.0.0
|
|
241
|
+
* @hash 7d990350b55b85945cc2148908618d6cd1dad27d1fc8343eab6966bedd9d84fa
|
|
242
|
+
* @macro AI Agent Remove Handler
|
|
243
|
+
* @micro Hard-deletes AI agent with validation and audit logging
|
|
244
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
245
|
+
* @inputs _body: any — Request body (unused for DELETE)
|
|
246
|
+
* @outputs {success: true} — Success confirmation
|
|
247
|
+
* @depends-on [createHandler, emitLog]
|
|
248
|
+
* @depended-by [Netlify function routing]
|
|
249
|
+
* @side-effects [DB delete, audit logging]
|
|
250
|
+
* @tags ai-agents, remove, crud, audit
|
|
251
|
+
*/
|
|
252
|
+
export const remove = createHandler(async (ctx, _body) => {
|
|
253
|
+
const id = ctx.query?.id
|
|
254
|
+
|
|
255
|
+
if (!id) {
|
|
256
|
+
throw new Error('Agent ID is required')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const { data: current } = await ctx.db
|
|
260
|
+
.from('ai_agents')
|
|
261
|
+
.select('id, name')
|
|
262
|
+
.eq('id', id)
|
|
263
|
+
.single()
|
|
264
|
+
|
|
265
|
+
if (!current) throw new Error('Agent not found')
|
|
266
|
+
|
|
267
|
+
const { error: err } = await ctx.db
|
|
268
|
+
.from('ai_agents')
|
|
269
|
+
.delete()
|
|
270
|
+
.eq('id', id)
|
|
271
|
+
|
|
272
|
+
if (err) throw err
|
|
273
|
+
|
|
274
|
+
await emitLog(ctx, 'ai_agent.deleted',
|
|
275
|
+
{ type: 'ai_agent', id },
|
|
276
|
+
{ before: current }
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
return { success: true }
|
|
280
|
+
})
|
|
281
|
+
// ─── CHUNK_END: AI_AGENTS_REMOVE ────────────────────────────────────────────────
|
|
282
|
+
|
|
283
|
+
// ─── MAIN HANDLER ────────────────────────────────────────────────────────────
|
|
284
|
+
|
|
285
|
+
// ─── CHUNK_START: AI_AGENTS_HANDLER ──────────────────────────────────────────────
|
|
286
|
+
/**
|
|
287
|
+
* @chunk-id AI_AGENTS_HANDLER_1_0_0
|
|
288
|
+
* @version 1.0.0
|
|
289
|
+
* @hash a49f4917dd07295e0ba9815bf929cccc5cffcbe4dcf6852aa4bd84d6cba1b690
|
|
290
|
+
* @macro AI Agents Router
|
|
291
|
+
* @micro Routes HTTP methods to appropriate CRUD handlers
|
|
292
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
293
|
+
* @inputs body: any — Request body for POST/PATCH operations
|
|
294
|
+
* @outputs Varies — Depends on routed handler (list/get/create/update/remove)
|
|
295
|
+
* @depends-on [createHandler, list, get, create, update, remove]
|
|
296
|
+
* @depended-by [Netlify function routing]
|
|
297
|
+
* @side-effects [Delegates to appropriate handler]
|
|
298
|
+
* @tags ai-agents, router, crud, netlify-function
|
|
299
|
+
*/
|
|
300
|
+
export const handler = createHandler(async (ctx, body) => {
|
|
301
|
+
const { action } = ctx.query || {}
|
|
302
|
+
const method = ctx.query?.method || 'GET'
|
|
303
|
+
|
|
304
|
+
switch (action) {
|
|
305
|
+
default:
|
|
306
|
+
if (method === 'GET') {
|
|
307
|
+
if (ctx.query?.id) {
|
|
308
|
+
return await get(ctx, body)
|
|
309
|
+
} else {
|
|
310
|
+
return await list(ctx, body)
|
|
311
|
+
}
|
|
312
|
+
} else if (method === 'POST') {
|
|
313
|
+
return await create(ctx, body)
|
|
314
|
+
} else if (method === 'PATCH') {
|
|
315
|
+
return await update(ctx, body)
|
|
316
|
+
} else if (method === 'DELETE') {
|
|
317
|
+
return await remove(ctx, body)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
throw new Error('Invalid action or method')
|
|
322
|
+
})
|
|
323
|
+
// ─── CHUNK_END: AI_AGENTS_HANDLER ────────────────────────────────────────────────
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module api-keys
|
|
3
|
+
* @audience core-contributor
|
|
4
|
+
* @layer api-handler
|
|
5
|
+
* @stability stable
|
|
6
|
+
*
|
|
7
|
+
* Management and validation API for the `api_keys` table. API keys are
|
|
8
|
+
* issued per-integration and optionally scoped to specific permissions.
|
|
9
|
+
* Key material is generated and stored by the `create_api_key` Postgres
|
|
10
|
+
* RPC, which handles hashing internally. Validation also delegates to the
|
|
11
|
+
* `validate_api_key` RPC.
|
|
12
|
+
*
|
|
13
|
+
* **Routed by:** `GET/POST /.netlify/functions/api-keys`
|
|
14
|
+
*
|
|
15
|
+
* **Actions:**
|
|
16
|
+
* | method | ?action | handler |
|
|
17
|
+
* |--------|------------|---------------|
|
|
18
|
+
* | POST | validate | validate |
|
|
19
|
+
* | POST | revoke | revoke |
|
|
20
|
+
* | GET | usage-logs | listUsageLogs |
|
|
21
|
+
* | GET | ?id | get |
|
|
22
|
+
* | GET | (default) | list |
|
|
23
|
+
* | POST | — | create |
|
|
24
|
+
*
|
|
25
|
+
* **Authorization:** All operations use `ctx.db` (RLS-scoped). Account context
|
|
26
|
+
* required for creates. No PATCH/DELETE — use `revoke` for deactivation.
|
|
27
|
+
*
|
|
28
|
+
* INVARIANT: Raw key material is never stored. Only the hash is persisted.
|
|
29
|
+
* `create` returns the plaintext key once via RPC response.
|
|
30
|
+
* INVARIANT: `revoke` soft-deactivates by setting `is_active = false`.
|
|
31
|
+
*
|
|
32
|
+
* @seeAlso integrations.ts (integration_id FK on api_keys)
|
|
33
|
+
* @seeAlso audit.ts (emitLog for api_key.* events)
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { createHandler } from './_shared/middleware'
|
|
37
|
+
import { emitLog } from './_shared/audit'
|
|
38
|
+
import { sanitizeRecordData } from './_shared/permissions'
|
|
39
|
+
|
|
40
|
+
// ─── HANDLERS ─────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
// ─── CHUNK_START: API_KEYS_LIST ──────────────────────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* @chunk-id API_KEYS_LIST_1_0_0
|
|
45
|
+
* @version 1.0.0
|
|
46
|
+
* @hash f825d8f4bbabe21071b61fe41e5e4e1b87482d501bf4078fab70906b7a5f5e78
|
|
47
|
+
* @macro API Keys List Handler
|
|
48
|
+
* @micro Lists API keys with filtering, pagination, and joins
|
|
49
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
50
|
+
* @inputs _body: any — Request body (unused for GET)
|
|
51
|
+
* @outputs Array of sanitized API key records with integration joins
|
|
52
|
+
* @depends-on [createHandler, sanitizeRecordData]
|
|
53
|
+
* @depended-by [Netlify function routing]
|
|
54
|
+
* @side-effects [DB queries, permission sanitization]
|
|
55
|
+
* @tags api-keys, list, crud, pagination
|
|
56
|
+
*/
|
|
57
|
+
export const list = createHandler(async (ctx, _body) => {
|
|
58
|
+
const { integration_id, key_type, is_active, expires_before, expires_after, limit = 100, offset = 0 } = ctx.query || {}
|
|
59
|
+
|
|
60
|
+
if (!ctx.accountId) {
|
|
61
|
+
throw new Error('Account context required')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let query = ctx.db
|
|
65
|
+
.from('api_keys')
|
|
66
|
+
.select(`
|
|
67
|
+
*,
|
|
68
|
+
integration:integrations(id, name, provider, integration_type),
|
|
69
|
+
created_by_person:people(id, full_name, email)
|
|
70
|
+
`)
|
|
71
|
+
.order('created_at', { ascending: false })
|
|
72
|
+
|
|
73
|
+
if (integration_id) {
|
|
74
|
+
query = query.eq('integration_id', integration_id)
|
|
75
|
+
}
|
|
76
|
+
if (key_type) {
|
|
77
|
+
query = query.eq('key_type', key_type)
|
|
78
|
+
}
|
|
79
|
+
if (is_active !== undefined) {
|
|
80
|
+
query = query.eq('is_active', is_active === 'true')
|
|
81
|
+
}
|
|
82
|
+
if (expires_before) {
|
|
83
|
+
query = query.lte('expires_at', expires_before)
|
|
84
|
+
}
|
|
85
|
+
if (expires_after) {
|
|
86
|
+
query = query.gte('expires_at', expires_after)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const { data, error: err } = await query.range(
|
|
90
|
+
parseInt(offset.toString()),
|
|
91
|
+
parseInt(offset.toString()) + parseInt(limit.toString()) - 1
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if (err) throw err
|
|
95
|
+
|
|
96
|
+
const sanitized = []
|
|
97
|
+
for (const key of data || []) {
|
|
98
|
+
sanitized.push(await sanitizeRecordData(ctx, key, 'api_key'))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return sanitized
|
|
102
|
+
})
|
|
103
|
+
// ─── CHUNK_END: API_KEYS_LIST ────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
// ─── CHUNK_START: API_KEYS_GET ──────────────────────────────────────────────
|
|
106
|
+
/**
|
|
107
|
+
* @chunk-id API_KEYS_GET_1_0_0
|
|
108
|
+
* @version 1.0.0
|
|
109
|
+
* @hash 357f926aeb833ad0ca755e48350534771f491890645134a1b43d2f2a8e4be037
|
|
110
|
+
* @macro API Key Get Handler
|
|
111
|
+
* @micro Returns a single API key record with joins (no raw key material)
|
|
112
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
113
|
+
* @inputs _body: any — Request body (unused for GET)
|
|
114
|
+
* @outputs Sanitized API key record with integration and createdBy joins
|
|
115
|
+
* @depends-on [createHandler, sanitizeRecordData]
|
|
116
|
+
* @depended-by [Netlify function routing]
|
|
117
|
+
* @side-effects [DB single row query, permission sanitization]
|
|
118
|
+
* @tags api-keys, get, crud, single-record
|
|
119
|
+
*/
|
|
120
|
+
export const get = createHandler(async (ctx, _body) => {
|
|
121
|
+
const { id } = ctx.query || {}
|
|
122
|
+
|
|
123
|
+
if (!id) {
|
|
124
|
+
throw new Error('API key ID is required')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const { data, error: err } = await ctx.db
|
|
128
|
+
.from('api_keys')
|
|
129
|
+
.select(`
|
|
130
|
+
*,
|
|
131
|
+
integration:integrations(id, name, provider, integration_type),
|
|
132
|
+
created_by_person:people(id, full_name, email)
|
|
133
|
+
`)
|
|
134
|
+
.eq('id', id)
|
|
135
|
+
.single()
|
|
136
|
+
|
|
137
|
+
if (err) throw err
|
|
138
|
+
|
|
139
|
+
return await sanitizeRecordData(ctx, data, 'api_key')
|
|
140
|
+
})
|
|
141
|
+
// ─── CHUNK_END: API_KEYS_GET ────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
// ─── CHUNK_START: API_KEYS_CREATE ──────────────────────────────────────────────
|
|
144
|
+
/**
|
|
145
|
+
* @chunk-id API_KEYS_CREATE_1_0_0
|
|
146
|
+
* @version 1.0.0
|
|
147
|
+
* @hash 8d00c97111cddebc2b3821a1fee3e6cf4c6b7c55fc20cd19d70865d728c3387c
|
|
148
|
+
* @macro API Key Create Handler
|
|
149
|
+
* @micro Creates new API key via RPC with validation and audit logging
|
|
150
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
151
|
+
* @inputs body: object — API key configuration data
|
|
152
|
+
* @outputs RPC result containing api_key_id and plaintext key_value
|
|
153
|
+
* @depends-on [createHandler, emitLog]
|
|
154
|
+
* @depended-by [Netlify function routing]
|
|
155
|
+
* @side-effects [RPC call, audit logging]
|
|
156
|
+
* @tags api-keys, create, crud, rpc, audit
|
|
157
|
+
*/
|
|
158
|
+
export const create = createHandler(async (ctx, body) => {
|
|
159
|
+
const { integration_id, name, key_type, key_prefix, permissions, rate_limit, expires_at, metadata } = body
|
|
160
|
+
|
|
161
|
+
if (!name) {
|
|
162
|
+
throw new Error('name is required')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!ctx.accountId) {
|
|
166
|
+
throw new Error('Account context required')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const { data, error: err } = await ctx.db
|
|
170
|
+
.rpc('create_api_key', {
|
|
171
|
+
integration_id,
|
|
172
|
+
name,
|
|
173
|
+
key_type: key_type || 'private',
|
|
174
|
+
key_prefix: key_prefix || 'sk_',
|
|
175
|
+
permissions: permissions || {},
|
|
176
|
+
rate_limit: rate_limit || 1000,
|
|
177
|
+
expires_at,
|
|
178
|
+
metadata: metadata || {},
|
|
179
|
+
created_by: ctx.principal?.id,
|
|
180
|
+
account_id: ctx.accountId
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
if (err) throw err
|
|
184
|
+
|
|
185
|
+
await emitLog(ctx, 'api_key.created',
|
|
186
|
+
{ type: 'api_key', id: data[0]?.api_key_id },
|
|
187
|
+
{ after: { name, key_type, rate_limit } }
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
return data
|
|
191
|
+
})
|
|
192
|
+
// ─── CHUNK_END: API_KEYS_CREATE ────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
// ─── CHUNK_START: API_KEYS_VALIDATE ──────────────────────────────────────────────
|
|
195
|
+
/**
|
|
196
|
+
* @chunk-id API_KEYS_VALIDATE_1_0_0
|
|
197
|
+
* @version 1.0.0
|
|
198
|
+
* @hash 05791f9b2c23bfebed01dffaa1430e896b89d311c5a51409704d731200eb621a
|
|
199
|
+
* @macro API Key Validation Handler
|
|
200
|
+
* @micro Validates API key and checks permissions via RPC
|
|
201
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
202
|
+
* @inputs body: object — key_value and optional required_permissions
|
|
203
|
+
* @outputs RPC validation result with is_valid, account_id, permissions
|
|
204
|
+
* @depends-on [createHandler]
|
|
205
|
+
* @depended-by [Netlify function routing, middleware]
|
|
206
|
+
* @side-effects [RPC validation call]
|
|
207
|
+
* @tags api-keys, validate, rpc, authentication
|
|
208
|
+
*/
|
|
209
|
+
export const validate = createHandler(async (ctx, body) => {
|
|
210
|
+
const { key_value, required_permissions } = body
|
|
211
|
+
|
|
212
|
+
if (!key_value) {
|
|
213
|
+
throw new Error('key_value is required')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const { data, error: err } = await ctx.db
|
|
217
|
+
.rpc('validate_api_key', {
|
|
218
|
+
key_value,
|
|
219
|
+
required_permissions: required_permissions || {}
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
if (err) throw err
|
|
223
|
+
|
|
224
|
+
return data
|
|
225
|
+
})
|
|
226
|
+
// ─── CHUNK_END: API_KEYS_VALIDATE ────────────────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
// ─── CHUNK_START: API_KEYS_REVOKE ──────────────────────────────────────────────
|
|
229
|
+
/**
|
|
230
|
+
* @chunk-id API_KEYS_REVOKE_1_0_0
|
|
231
|
+
* @version 1.0.0
|
|
232
|
+
* @hash 3302286a12c1315616470fcfbd3cbdc8dcf63dd00211a71159f3325bab95607a
|
|
233
|
+
* @macro API Key Revoke Handler
|
|
234
|
+
* @micro Soft-deactivates API key with validation and audit logging
|
|
235
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
236
|
+
* @inputs body: object — API key ID to revoke
|
|
237
|
+
* @outputs Updated API key record with is_active: false
|
|
238
|
+
* @depends-on [createHandler, emitLog]
|
|
239
|
+
* @depended-by [Netlify function routing]
|
|
240
|
+
* @side-effects [DB update, audit logging]
|
|
241
|
+
* @tags api-keys, revoke, crud, audit
|
|
242
|
+
*/
|
|
243
|
+
export const revoke = createHandler(async (ctx, body) => {
|
|
244
|
+
const { id } = body
|
|
245
|
+
|
|
246
|
+
if (!id) {
|
|
247
|
+
throw new Error('API key ID is required')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const { data, error: err } = await ctx.db
|
|
251
|
+
.from('api_keys')
|
|
252
|
+
.update({ is_active: false, updated_at: new Date().toISOString() })
|
|
253
|
+
.eq('id', id)
|
|
254
|
+
.select()
|
|
255
|
+
.single()
|
|
256
|
+
|
|
257
|
+
if (err) throw err
|
|
258
|
+
|
|
259
|
+
await emitLog(ctx, 'api_key.revoked',
|
|
260
|
+
{ type: 'api_key', id },
|
|
261
|
+
{ after: { revoked_by: ctx.principal?.id } }
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
return data
|
|
265
|
+
})
|
|
266
|
+
// ─── CHUNK_END: API_KEYS_REVOKE ────────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
// ─── CHUNK_START: API_KEYS_LIST_USAGE_LOGS ──────────────────────────────────────────────
|
|
269
|
+
/**
|
|
270
|
+
* @chunk-id API_KEYS_LIST_USAGE_LOGS_1_0_0
|
|
271
|
+
* @version 1.0.0
|
|
272
|
+
* @hash f21e004490dd983c8f20dc1d1f4db33e158334d19e38a4ace5e99389a8a96ded
|
|
273
|
+
* @macro API Key Usage Logs Handler
|
|
274
|
+
* @micro Lists paginated API key usage logs with filtering and joins
|
|
275
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
276
|
+
* @inputs _body: any — Request body (unused for GET)
|
|
277
|
+
* @outputs Array of usage log records with api_key joins
|
|
278
|
+
* @depends-on [createHandler]
|
|
279
|
+
* @depended-by [Netlify function routing]
|
|
280
|
+
* @side-effects [DB queries with filtering]
|
|
281
|
+
* @tags api-keys, usage-logs, pagination, monitoring
|
|
282
|
+
*/
|
|
283
|
+
export const listUsageLogs = createHandler(async (ctx, _body) => {
|
|
284
|
+
const { api_key_id, response_status, success, date_from, date_to, limit = 100, offset = 0 } = ctx.query || {}
|
|
285
|
+
|
|
286
|
+
if (!ctx.accountId) {
|
|
287
|
+
throw new Error('Account context required')
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let query = ctx.db
|
|
291
|
+
.from('api_key_usage_logs')
|
|
292
|
+
.select(`
|
|
293
|
+
*,
|
|
294
|
+
api_key:api_keys(id, name, key_type)
|
|
295
|
+
`)
|
|
296
|
+
.order('created_at', { ascending: false })
|
|
297
|
+
|
|
298
|
+
if (api_key_id) {
|
|
299
|
+
query = query.eq('api_key_id', api_key_id)
|
|
300
|
+
}
|
|
301
|
+
if (response_status) {
|
|
302
|
+
query = query.eq('response_status', parseInt(response_status.toString()))
|
|
303
|
+
}
|
|
304
|
+
if (success !== undefined) {
|
|
305
|
+
query = query.eq('success', success === 'true')
|
|
306
|
+
}
|
|
307
|
+
if (date_from) {
|
|
308
|
+
query = query.gte('created_at', date_from)
|
|
309
|
+
}
|
|
310
|
+
if (date_to) {
|
|
311
|
+
query = query.lte('created_at', date_to)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const { data, error: err } = await query.range(
|
|
315
|
+
parseInt(offset.toString()),
|
|
316
|
+
parseInt(offset.toString()) + parseInt(limit.toString()) - 1
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
if (err) throw err
|
|
320
|
+
|
|
321
|
+
return data
|
|
322
|
+
})
|
|
323
|
+
// ─── CHUNK_END: API_KEYS_LIST_USAGE_LOGS ────────────────────────────────────────────────
|
|
324
|
+
|
|
325
|
+
// ─── MAIN HANDLER ────────────────────────────────────────────────────────────
|
|
326
|
+
|
|
327
|
+
// ─── CHUNK_START: API_KEYS_HANDLER ──────────────────────────────────────────────
|
|
328
|
+
/**
|
|
329
|
+
* @chunk-id API_KEYS_HANDLER_1_0_0
|
|
330
|
+
* @version 1.0.0
|
|
331
|
+
* @hash 6c2da419bea6f12b0fe654e2aad6c5ebac3d5993c2547a47d239f44197d90a68
|
|
332
|
+
* @macro API Keys Router
|
|
333
|
+
* @micro Routes HTTP methods and actions to appropriate handlers
|
|
334
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
335
|
+
* @inputs body: any — Request body for POST operations
|
|
336
|
+
* @outputs Varies — Depends on routed handler (list/get/create/validate/revoke/usage-logs)
|
|
337
|
+
* @depends-on [createHandler, list, get, create, validate, revoke, listUsageLogs]
|
|
338
|
+
* @depended-by [Netlify function routing]
|
|
339
|
+
* @side-effects [Delegates to appropriate handler]
|
|
340
|
+
* @tags api-keys, router, crud, netlify-function
|
|
341
|
+
*/
|
|
342
|
+
export const handler = createHandler(async (ctx, body) => {
|
|
343
|
+
const { action } = ctx.query || {}
|
|
344
|
+
const method = ctx.query?.method || 'GET'
|
|
345
|
+
|
|
346
|
+
switch (action) {
|
|
347
|
+
case 'validate':
|
|
348
|
+
if (method === 'POST') {
|
|
349
|
+
return await validate(ctx, body)
|
|
350
|
+
}
|
|
351
|
+
break
|
|
352
|
+
case 'revoke':
|
|
353
|
+
if (method === 'POST') {
|
|
354
|
+
return await revoke(ctx, body)
|
|
355
|
+
}
|
|
356
|
+
break
|
|
357
|
+
case 'usage-logs':
|
|
358
|
+
if (method === 'GET') {
|
|
359
|
+
return await listUsageLogs(ctx, body)
|
|
360
|
+
}
|
|
361
|
+
break
|
|
362
|
+
default:
|
|
363
|
+
if (method === 'GET') {
|
|
364
|
+
if (ctx.query?.id) {
|
|
365
|
+
return await get(ctx, body)
|
|
366
|
+
} else {
|
|
367
|
+
return await list(ctx, body)
|
|
368
|
+
}
|
|
369
|
+
} else if (method === 'POST') {
|
|
370
|
+
return await create(ctx, body)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
throw new Error('Invalid action or method')
|
|
375
|
+
})
|
|
376
|
+
// ─── CHUNK_END: API_KEYS_HANDLER ────────────────────────────────────────────────
|