spine-framework 0.3.125 → 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.
Files changed (319) hide show
  1. package/.framework/cli/commands/agents.ts +35 -37
  2. package/.framework/cli/commands/auth.ts +35 -34
  3. package/.framework/cli/commands/create-app.ts +104 -40
  4. package/.framework/cli/commands/dev.ts +104 -69
  5. package/.framework/cli/commands/doctor.ts +169 -97
  6. package/.framework/cli/commands/generate.ts +82 -160
  7. package/.framework/cli/commands/init.ts +72 -34
  8. package/.framework/cli/commands/install-app.ts +260 -125
  9. package/.framework/cli/commands/items.ts +33 -44
  10. package/.framework/cli/commands/migrate.ts +122 -40
  11. package/.framework/cli/commands/migrations.ts +33 -40
  12. package/.framework/cli/commands/pipelines.ts +31 -39
  13. package/.framework/cli/commands/status.ts +47 -23
  14. package/.framework/cli/commands/system.ts +47 -37
  15. package/.framework/cli/commands/test.ts +112 -68
  16. package/.framework/cli/commands/validate-app.ts +215 -121
  17. package/.framework/cli/context.ts +112 -130
  18. package/.framework/cli/env-loader.ts +47 -18
  19. package/.framework/cli/index.ts +81 -42
  20. package/.framework/docs/runtime-types.md +1024 -0
  21. package/.framework/functions/_shared/agent-runner.ts +1296 -961
  22. package/.framework/functions/_shared/app-manifest.ts +82 -68
  23. package/.framework/functions/_shared/audit.ts +135 -83
  24. package/.framework/functions/_shared/{cil-state.ts → cil.ts} +147 -54
  25. package/.framework/functions/_shared/db.ts +80 -126
  26. package/.framework/functions/_shared/hash-api-key.ts +63 -0
  27. package/.framework/functions/_shared/index.ts +222 -347
  28. package/.framework/functions/_shared/kb.ts +319 -0
  29. package/.framework/functions/_shared/llm.ts +92 -0
  30. package/.framework/functions/_shared/logging.ts +288 -0
  31. package/.framework/functions/_shared/middleware.ts +329 -365
  32. package/.framework/functions/_shared/outcomes.ts +36 -0
  33. package/.framework/functions/_shared/permissions.ts +359 -643
  34. package/.framework/functions/_shared/pipeline-runner.ts +243 -316
  35. package/.framework/functions/_shared/principal.ts +376 -468
  36. package/.framework/functions/_shared/resolve-credentials.ts +102 -0
  37. package/.framework/functions/_shared/resolve-ids.ts +162 -14
  38. package/.framework/functions/_shared/retry-deliveries.ts +104 -0
  39. package/.framework/functions/_shared/schema-utils.ts +611 -373
  40. package/.framework/functions/_shared/testing.ts +207 -137
  41. package/.framework/functions/_shared/tracing.ts +276 -0
  42. package/.framework/functions/_shared/trigger-engine.ts +159 -211
  43. package/.framework/functions/_shared/webhook-registration.ts +95 -81
  44. package/.framework/functions/_shared/webhook-registry.ts +153 -75
  45. package/.framework/functions/account-nodes.ts +31 -53
  46. package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
  47. package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
  48. package/.framework/functions/adapters/openai-outbound.ts +40 -0
  49. package/.framework/functions/admin-data.ts +687 -533
  50. package/.framework/functions/admin-funnel.ts +31 -20
  51. package/.framework/functions/admin-install-funnel.ts +47 -28
  52. package/.framework/functions/admin-signals.ts +47 -29
  53. package/.framework/functions/ai-agents.ts +122 -147
  54. package/.framework/functions/ai-interactions.ts +180 -90
  55. package/.framework/functions/api-keys.ts +250 -192
  56. package/.framework/functions/apps.ts +203 -197
  57. package/.framework/functions/auth.ts +98 -91
  58. package/.framework/functions/cil-agent-ingest.ts +773 -0
  59. package/.framework/functions/cil-agent.ts +592 -0
  60. package/.framework/functions/cil-callmap.ts +1227 -0
  61. package/.framework/functions/cil-config.ts +583 -0
  62. package/.framework/functions/cil-context.ts +445 -0
  63. package/.framework/functions/cil-doc.ts +1752 -0
  64. package/.framework/functions/cil-embeddings.ts +1202 -0
  65. package/.framework/functions/cil-graph.ts +585 -0
  66. package/.framework/functions/cil-ingest.ts +2416 -0
  67. package/.framework/functions/cil-pipeline.ts +318 -0
  68. package/.framework/functions/cil-project.ts +499 -0
  69. package/.framework/functions/cil-validate.ts +469 -0
  70. package/.framework/functions/debug-auth.ts +73 -53
  71. package/.framework/functions/deliveries.ts +117 -0
  72. package/.framework/functions/embeddings.ts +528 -516
  73. package/.framework/functions/eval-harness.ts +158 -106
  74. package/.framework/functions/integration-outbound.ts +311 -0
  75. package/.framework/functions/integration-routes.ts +240 -466
  76. package/.framework/functions/integrations.ts +132 -140
  77. package/.framework/functions/invites.ts +401 -201
  78. package/.framework/functions/item-progress.ts +113 -53
  79. package/.framework/functions/kb.ts +28 -0
  80. package/.framework/functions/logs.ts +239 -222
  81. package/.framework/functions/observability.ts +308 -259
  82. package/.framework/functions/pipeline-executions.ts +179 -235
  83. package/.framework/functions/pipelines.ts +163 -205
  84. package/.framework/functions/prompt-configs.ts +109 -156
  85. package/.framework/functions/roles.ts +97 -147
  86. package/.framework/functions/system-cron.ts +132 -218
  87. package/.framework/functions/system.ts +112 -52
  88. package/.framework/functions/tests.ts +32 -28
  89. package/.framework/functions/timers.ts +162 -210
  90. package/.framework/functions/triggers.ts +282 -346
  91. package/.framework/functions/types.ts +283 -400
  92. package/.framework/migrations/001_core_schema.sql +630 -0
  93. package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
  94. package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
  95. package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
  96. package/.framework/migrations/005_core_observability.sql +598 -0
  97. package/.framework/migrations/006_security_hardening_rls.sql +97 -0
  98. package/.framework/migrations/007_kb_article_list.sql +237 -0
  99. package/.framework/migrations/008_items_target.sql +8 -0
  100. package/.framework/migrations/009_roles_slug_unique.sql +11 -0
  101. package/.framework/public/spine.config.json +91 -0
  102. package/.framework/scripts/app-install-cli.ts +112 -98
  103. package/.framework/src/App.tsx +67 -20
  104. package/.framework/src/apps/admin/index.tsx +97 -27
  105. package/.framework/src/components/AppWrapper.tsx +50 -11
  106. package/.framework/src/components/CustomAppLoader.tsx +131 -22
  107. package/.framework/src/components/admin/AdminListPage.tsx +75 -8
  108. package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
  109. package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
  110. package/.framework/src/components/admin/PageInstructions.tsx +49 -0
  111. package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
  112. package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
  113. package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
  114. package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
  115. package/.framework/src/components/app-sidebar.tsx +50 -3
  116. package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
  117. package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
  118. package/.framework/src/components/kb/KBArticleList.tsx +557 -0
  119. package/.framework/src/components/layout/AppShell.tsx +54 -16
  120. package/.framework/src/components/layout/Header.tsx +50 -38
  121. package/.framework/src/components/layout/Layout.tsx +48 -32
  122. package/.framework/src/components/layout/Sidebar.tsx +167 -16
  123. package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
  124. package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
  125. package/.framework/src/components/runtime/DataFilters.tsx +78 -1
  126. package/.framework/src/components/runtime/DataHeader.tsx +32 -29
  127. package/.framework/src/components/runtime/DataListPage.tsx +161 -2
  128. package/.framework/src/components/runtime/DataStats.tsx +48 -1
  129. package/.framework/src/components/runtime/DataTable.tsx +153 -1
  130. package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
  131. package/.framework/src/components/runtime/index.ts +16 -9
  132. package/.framework/src/components/search-form.tsx +33 -1
  133. package/.framework/src/components/shared/AgentView.tsx +48 -51
  134. package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
  135. package/.framework/src/components/shared/InviteManager.tsx +113 -52
  136. package/.framework/src/components/shared/MemberList.tsx +182 -0
  137. package/.framework/src/components/shared/SchemaFields.tsx +96 -78
  138. package/.framework/src/components/ui/DataTable.tsx +86 -56
  139. package/.framework/src/components/ui/Form.tsx +188 -109
  140. package/.framework/src/components/ui/ItemCard.tsx +86 -58
  141. package/.framework/src/components/ui/ItemListView.tsx +88 -61
  142. package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
  143. package/.framework/src/components/ui/Modal.tsx +81 -4
  144. package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
  145. package/.framework/src/components/ui/accordion.tsx +97 -5
  146. package/.framework/src/components/ui/alert-dialog.tsx +225 -13
  147. package/.framework/src/components/ui/alert.tsx +113 -6
  148. package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
  149. package/.framework/src/components/ui/avatar.tsx +129 -7
  150. package/.framework/src/components/ui/badge.tsx +65 -3
  151. package/.framework/src/components/ui/breadcrumb.tsx +145 -8
  152. package/.framework/src/components/ui/button-group.tsx +81 -4
  153. package/.framework/src/components/ui/button.tsx +49 -2
  154. package/.framework/src/components/ui/calendar.tsx +65 -3
  155. package/.framework/src/components/ui/card.tsx +145 -8
  156. package/.framework/src/components/ui/carousel.tsx +129 -7
  157. package/.framework/src/components/ui/chart.tsx +161 -10
  158. package/.framework/src/components/ui/checkbox.tsx +49 -2
  159. package/.framework/src/components/ui/collapsible.tsx +81 -5
  160. package/.framework/src/components/ui/combobox.tsx +305 -20
  161. package/.framework/src/components/ui/command.tsx +177 -11
  162. package/.framework/src/components/ui/context-menu.tsx +273 -17
  163. package/.framework/src/components/ui/dialog.tsx +209 -27
  164. package/.framework/src/components/ui/direction.tsx +64 -4
  165. package/.framework/src/components/ui/drawer.tsx +193 -12
  166. package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
  167. package/.framework/src/components/ui/empty.tsx +145 -9
  168. package/.framework/src/components/ui/field.tsx +209 -14
  169. package/.framework/src/components/ui/hover-card.tsx +81 -4
  170. package/.framework/src/components/ui/input-group.tsx +162 -10
  171. package/.framework/src/components/ui/input-otp.tsx +98 -5
  172. package/.framework/src/components/ui/input.tsx +49 -2
  173. package/.framework/src/components/ui/item.tsx +225 -13
  174. package/.framework/src/components/ui/kbd.tsx +65 -3
  175. package/.framework/src/components/ui/label.tsx +49 -2
  176. package/.framework/src/components/ui/menubar.tsx +289 -17
  177. package/.framework/src/components/ui/native-select.tsx +97 -5
  178. package/.framework/src/components/ui/navigation-menu.tsx +177 -10
  179. package/.framework/src/components/ui/pagination.tsx +161 -9
  180. package/.framework/src/components/ui/popover.tsx +145 -8
  181. package/.framework/src/components/ui/progress.tsx +49 -2
  182. package/.framework/src/components/ui/radio-group.tsx +65 -3
  183. package/.framework/src/components/ui/resizable.tsx +82 -4
  184. package/.framework/src/components/ui/scroll-area.tsx +65 -3
  185. package/.framework/src/components/ui/select.tsx +193 -11
  186. package/.framework/src/components/ui/separator.tsx +49 -2
  187. package/.framework/src/components/ui/sheet.tsx +193 -11
  188. package/.framework/src/components/ui/sidebar.tsx +481 -38
  189. package/.framework/src/components/ui/skeleton.tsx +49 -2
  190. package/.framework/src/components/ui/slider.tsx +49 -2
  191. package/.framework/src/components/ui/sonner.tsx +49 -2
  192. package/.framework/src/components/ui/spinner.tsx +49 -2
  193. package/.framework/src/components/ui/switch.tsx +49 -2
  194. package/.framework/src/components/ui/table-primitives.tsx +160 -33
  195. package/.framework/src/components/ui/table.tsx +161 -9
  196. package/.framework/src/components/ui/tabs.tsx +113 -6
  197. package/.framework/src/components/ui/textarea.tsx +49 -2
  198. package/.framework/src/components/ui/toggle-group.tsx +81 -4
  199. package/.framework/src/components/ui/toggle.tsx +65 -3
  200. package/.framework/src/components/ui/tooltip.tsx +97 -5
  201. package/.framework/src/contexts/AppContext.tsx +129 -45
  202. package/.framework/src/contexts/AuthContext.tsx +173 -145
  203. package/.framework/src/hooks/use-mobile.ts +48 -2
  204. package/.framework/src/hooks/useApi.ts +208 -159
  205. package/.framework/src/hooks/useAppPath.ts +32 -23
  206. package/.framework/src/hooks/useApps.ts +51 -20
  207. package/.framework/src/hooks/useEntityList.ts +32 -78
  208. package/.framework/src/hooks/useEntityRecord.ts +35 -96
  209. package/.framework/src/hooks/useForm.ts +32 -92
  210. package/.framework/src/hooks/useListSchema.ts +49 -78
  211. package/.framework/src/hooks/useSchemaRecord.ts +114 -105
  212. package/.framework/src/lib/api.ts +192 -115
  213. package/.framework/src/lib/supabase.ts +98 -64
  214. package/.framework/src/lib/utils.ts +287 -151
  215. package/.framework/src/main.tsx +49 -3
  216. package/.framework/src/pages/DashboardPage.tsx +52 -29
  217. package/.framework/src/pages/NotFoundPage.tsx +32 -10
  218. package/.framework/src/pages/SetupPage.tsx +32 -12
  219. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
  220. package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
  221. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
  222. package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
  223. package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
  224. package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
  225. package/.framework/src/pages/admin/AppsPage.tsx +49 -2
  226. package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
  227. package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
  228. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
  229. package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
  230. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
  231. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
  232. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
  233. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
  234. package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
  235. package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
  236. package/.framework/src/pages/admin/LogsPage.tsx +422 -75
  237. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
  238. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
  239. package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
  240. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
  241. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
  242. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
  243. package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
  244. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
  245. package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
  246. package/.framework/src/pages/admin/RolesPage.tsx +171 -18
  247. package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
  248. package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
  249. package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
  250. package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
  251. package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
  252. package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
  253. package/.framework/src/pages/admin/TimersPage.tsx +48 -20
  254. package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
  255. package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
  256. package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
  257. package/.framework/src/pages/admin/TypesPage.tsx +48 -20
  258. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
  259. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
  260. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
  261. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
  262. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
  263. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
  264. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
  265. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
  266. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
  267. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  268. package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
  269. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
  270. package/.framework/src/pages/auth/LoginPage.tsx +50 -23
  271. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
  272. package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
  273. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
  274. package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
  275. package/.framework/src/types/auth.ts +128 -71
  276. package/.framework/src/types/types.ts +373 -157
  277. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
  278. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
  279. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
  280. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
  281. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
  282. package/docs/README.md +9 -130
  283. package/netlify.toml +3 -3
  284. package/package.json +13 -25
  285. package/scripts/kb-ingest-docs.ts +203 -0
  286. package/spine.config.json +65 -4
  287. package/.framework/migrations/001_schema.sql +0 -425
  288. package/.framework/migrations/002_rls_and_functions.sql +0 -763
  289. package/.framework/migrations/004_enhancements.sql +0 -43
  290. package/.framework/migrations/005_auth_user_trigger.sql +0 -105
  291. package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
  292. package/.framework/migrations/016_invites.sql +0 -27
  293. package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
  294. package/.framework/migrations/018_apps_required_roles.sql +0 -17
  295. package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
  296. package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
  297. package/.framework/migrations/021_app_domains.sql +0 -112
  298. package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
  299. package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
  300. package/.framework/migrations/024_messages_account_id.sql +0 -9
  301. package/.framework/migrations/025_retype_threads_messages.sql +0 -240
  302. package/.framework/migrations/026_fix_rls_policies.sql +0 -47
  303. package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
  304. package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
  305. package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
  306. package/.framework/migrations/030_messages_updated_by.sql +0 -6
  307. package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
  308. package/docs/APPS.md +0 -232
  309. package/docs/ARCHITECTURE.md +0 -246
  310. package/docs/INSTALL.md +0 -155
  311. package/docs/design-schema-spec.md +0 -1837
  312. package/docs/dev-guides/01-create-an-app.md +0 -183
  313. package/docs/dev-guides/02-create-a-function.md +0 -178
  314. package/docs/dev-guides/03-create-a-component.md +0 -178
  315. package/docs/dev-guides/04-webhook-handlers.md +0 -129
  316. package/docs/dev-guides/05-testing.md +0 -168
  317. package/docs/dev-guides/README.md +0 -40
  318. package/docs/npm-packages-plan.md +0 -288
  319. package/docs/spine-core--api-reference.md +0 -822
@@ -0,0 +1,311 @@
1
+ // CIL[BEGIN chunk_id="integration_outbound_module_header_1784200004" type="module_header" identity="imports" parent="none"]
2
+ // CIL[DOC-MACRO purpose="Outbound integration broker action"]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Execute outbound HTTP calls through configured integrations and log deliveries"]
4
+ // CIL[DOC-CONTEXT module="integration-outbound" layer="backend_functions" related_files="[".framework/functions/_shared/resolve-credentials.ts",".framework/functions/admin-data.ts",".framework/functions/_shared/audit.ts",".framework/functions/_shared/pipeline-runner.ts",".framework/migrations/051_integration_broker.sql"]"]
5
+ // CIL[DOC-CONTRACT inputs="ctx, body with integration_slug and record_id" outputs="{ success, delivery_id, response_status }" errors="integration_not_found, credential_missing, adapter_error, http_error" side_effects="HTTP request, webhook_deliveries insert/update, optional admin-data update"]
6
+ // CIL[DOC-MICRO logic_summary="Loads integration, resolves credential, loads adapter, builds request, performs HTTP call, and records the result in webhook_deliveries. Supports retry by delivery_id for internal callers." notable_decisions="["Outbound uses integrations.credentials, not api_keys (which are for inbound).","Failures are written to webhook_deliveries for retry by system-cron.","Record updates go through admin-data.","When delivery_id is passed by an internal principal, the existing delivery row is reused."]"]
7
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
8
+ // CIL[DOC-STABILITY stability="experimental"]
9
+ // CIL[DOC-AUDIENCE audience="developer"]
10
+ // CIL[DOC-VECTOR type="context" content="Layer: backend_functions. Direct callers: 0. Direct calls: 0. Related files: .framework/functions/_shared/resolve-credentials.ts, .framework/functions/admin-data.ts, .framework/functions/_shared/audit.ts, .framework/functions/_shared/pipeline-runner.ts, .framework/migrations/051_integration_broker.sql."]
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="Requires integration.slug and a credential field."]
14
+ // CIL[DOC-VECTOR type="implementation_pattern" content="Called as a pipeline action or directly via /.netlify/functions/integration-outbound."]
15
+ // CIL[DOC-VECTOR type="user_journey" content="Spine calls an external service through the broker."]
16
+
17
+ import { createHandler, CoreContext } from './_shared/middleware'
18
+ import { adminDb } from './_shared/db'
19
+ import { emitLog } from './_shared/audit'
20
+ import { resolveCredential } from './_shared/resolve-credentials'
21
+ import { get as adminDataGet, update as adminDataUpdate } from './admin-data'
22
+ import * as openaiOutbound from './adapters/openai-outbound'
23
+ import * as genericWebhookOutbound from './adapters/generic-webhook-outbound'
24
+
25
+ interface OutboundRequest {
26
+ url: string
27
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
28
+ headers?: Record<string, string>
29
+ body?: any
30
+ external_id?: string
31
+ }
32
+
33
+ interface OutboundAdapterContext {
34
+ record: any
35
+ integration: any
36
+ apiKey?: any
37
+ accountId: string | null
38
+ appId: string | null
39
+ actionConfig: any
40
+ }
41
+
42
+ function computeNextAttempt(attemptCount: number, baseMs = 30000, maxDelayMs = 3600000): Date {
43
+ const delay = Math.min(baseMs * Math.pow(2, attemptCount), maxDelayMs)
44
+ const jitter = Math.floor(Math.random() * delay * 0.2)
45
+ return new Date(Date.now() + delay + jitter)
46
+ }
47
+
48
+ export async function execute(ctx: CoreContext, body: any = {}): Promise<any> {
49
+ let deliveryId = body?.delivery_id
50
+ let requestBody = body
51
+ let isRetry = false
52
+
53
+ if (deliveryId && ctx.principal?.isInternal) {
54
+ const { data: delivery, error: deliveryError } = await adminDb
55
+ .from('webhook_deliveries')
56
+ .select('*')
57
+ .eq('id', deliveryId)
58
+ .single()
59
+
60
+ if (deliveryError || !delivery) {
61
+ throw new Error(`Delivery not found: ${deliveryId}`)
62
+ }
63
+
64
+ requestBody = delivery.payload || {}
65
+ isRetry = true
66
+
67
+ await adminDb
68
+ .from('webhook_deliveries')
69
+ .update({ status: 'processing', updated_at: new Date().toISOString() })
70
+ .eq('id', deliveryId)
71
+ }
72
+
73
+ const { integration_slug, record_id, action_config = {} } = requestBody
74
+
75
+ if (!integration_slug) {
76
+ throw new Error('integration_slug is required')
77
+ }
78
+
79
+ let integration: any = null
80
+
81
+ if (ctx.accountId) {
82
+ const { data: tenant } = await adminDb
83
+ .from('integrations')
84
+ .select('*')
85
+ .eq('slug', integration_slug)
86
+ .eq('account_id', ctx.accountId)
87
+ .eq('is_active', true)
88
+ .maybeSingle()
89
+ if (tenant) integration = tenant
90
+ }
91
+
92
+ if (!integration) {
93
+ const { data: system } = await adminDb
94
+ .from('integrations')
95
+ .select('*')
96
+ .eq('slug', integration_slug)
97
+ .is('account_id', null)
98
+ .eq('is_active', true)
99
+ .maybeSingle()
100
+ if (system) integration = system
101
+ }
102
+
103
+ if (!integration) {
104
+ throw new Error(`Integration not found: ${integration_slug}`)
105
+ }
106
+
107
+ let record: any = null
108
+ if (record_id) {
109
+ const getCtx = { ...ctx, query: { entity: 'items', id: record_id } } as any
110
+ record = await adminDataGet(getCtx)
111
+ }
112
+
113
+ let deliveryPayload: any
114
+ if (!deliveryId) {
115
+ deliveryPayload = {
116
+ direction: 'outbound',
117
+ integration_id: integration.id,
118
+ record_id: record_id || null,
119
+ action_slug: action_config.action_slug || null,
120
+ payload: requestBody,
121
+ status: 'pending',
122
+ attempt_count: 0,
123
+ max_attempts: action_config.max_attempts ?? integration.config?.retry_policy?.max_attempts ?? 3,
124
+ next_attempt_at: null,
125
+ account_id: ctx.accountId,
126
+ created_at: new Date().toISOString(),
127
+ updated_at: new Date().toISOString()
128
+ }
129
+
130
+ const { data: deliveryRow, error: deliveryInsertError } = await adminDb
131
+ .from('webhook_deliveries')
132
+ .insert(deliveryPayload)
133
+ .select('id')
134
+ .single()
135
+
136
+ if (deliveryInsertError || !deliveryRow) {
137
+ throw new Error('Failed to create outbound delivery record')
138
+ }
139
+
140
+ deliveryId = deliveryRow.id
141
+ } else {
142
+ await adminDb
143
+ .from('webhook_deliveries')
144
+ .update({ status: 'processing', updated_at: new Date().toISOString() })
145
+ .eq('id', deliveryId)
146
+ }
147
+
148
+ await emitLog(ctx, 'integration.outbound.started', { type: 'integration', id: integration.id }, undefined, {
149
+ delivery_id: deliveryId,
150
+ integration_id: integration.id,
151
+ record_id,
152
+ account_id: ctx.accountId,
153
+ is_retry: isRetry
154
+ })
155
+
156
+ try {
157
+ const credentialField = action_config.credential_field || 'api_token'
158
+ const resolved = await resolveCredential(ctx, integration, credentialField)
159
+
160
+ const adapterName = action_config.adapter || integration.config?.outbound_adapter
161
+ if (!adapterName) {
162
+ throw new Error('No outbound adapter configured for integration ' + integration_slug)
163
+ }
164
+
165
+ const adapter = await loadOutboundAdapter(adapterName)
166
+ if (!adapter) {
167
+ throw new Error(`Outbound adapter not found: ${adapterName}`)
168
+ }
169
+
170
+ const adapterCtx: OutboundAdapterContext = {
171
+ record,
172
+ integration,
173
+ apiKey: resolved,
174
+ accountId: ctx.accountId,
175
+ appId: (ctx as any).appId || null,
176
+ actionConfig: action_config
177
+ }
178
+
179
+ const request: OutboundRequest = await adapter.buildRequest(record, adapterCtx)
180
+ if (!request.url || !request.method) {
181
+ throw new Error('Outbound adapter must return url and method')
182
+ }
183
+
184
+ const startMs = Date.now()
185
+ const response = await fetch(request.url, {
186
+ method: request.method,
187
+ headers: {
188
+ 'Content-Type': 'application/json',
189
+ ...(request.headers || {})
190
+ },
191
+ body: request.body ? JSON.stringify(request.body) : undefined
192
+ })
193
+ const durationMs = Date.now() - startMs
194
+ const responseBodyText = await response.text()
195
+ let responseBody: any = null
196
+ try {
197
+ responseBody = JSON.parse(responseBodyText)
198
+ } catch {
199
+ responseBody = responseBodyText
200
+ }
201
+
202
+ const success = response.ok
203
+
204
+ const { data: currentDelivery } = await adminDb
205
+ .from('webhook_deliveries')
206
+ .select('attempt_count')
207
+ .eq('id', deliveryId)
208
+ .single()
209
+
210
+ const attemptCount = (currentDelivery?.attempt_count || 0) + 1
211
+
212
+ await adminDb
213
+ .from('webhook_deliveries')
214
+ .update({
215
+ status: success ? 'delivered' : (attemptCount >= (deliveryPayload?.max_attempts || 3) ? 'failed' : 'pending'),
216
+ attempt_count: attemptCount,
217
+ next_attempt_at: success ? null : computeNextAttempt(attemptCount).toISOString(),
218
+ response_status: response.status,
219
+ response_body: responseBody,
220
+ duration_ms: durationMs,
221
+ updated_at: new Date().toISOString()
222
+ })
223
+ .eq('id', deliveryId)
224
+
225
+ await emitLog(ctx, success ? 'integration.outbound.succeeded' : 'integration.outbound.failed_http', { type: 'integration', id: integration.id }, undefined, {
226
+ delivery_id: deliveryId,
227
+ integration_id: integration.id,
228
+ response_status: response.status,
229
+ duration_ms: durationMs,
230
+ account_id: ctx.accountId,
231
+ is_retry: isRetry
232
+ })
233
+
234
+ if (success && action_config.update_record && record_id) {
235
+ await adminDataUpdate({ ...ctx, query: { entity: 'items', id: record_id } } as any, {
236
+ entity: 'items',
237
+ id: record_id,
238
+ data: {
239
+ ...(action_config.update_fields || {}),
240
+ last_outbound_status: response.status,
241
+ last_outbound_at: new Date().toISOString()
242
+ }
243
+ })
244
+ }
245
+
246
+ if (!success) {
247
+ throw new Error(`Outbound HTTP ${request.method} to ${request.url} failed with status ${response.status}`)
248
+ }
249
+
250
+ return {
251
+ success: true,
252
+ delivery_id: deliveryId,
253
+ response_status: response.status,
254
+ response_body: responseBody,
255
+ is_retry: isRetry
256
+ }
257
+ } catch (err: any) {
258
+ const { data: currentDelivery } = await adminDb
259
+ .from('webhook_deliveries')
260
+ .select('attempt_count, max_attempts')
261
+ .eq('id', deliveryId)
262
+ .single()
263
+
264
+ const attemptCount = (currentDelivery?.attempt_count || 0) + 1
265
+ const shouldRetry = attemptCount < (currentDelivery?.max_attempts || 3)
266
+
267
+ await adminDb
268
+ .from('webhook_deliveries')
269
+ .update({
270
+ status: shouldRetry ? 'pending' : 'failed',
271
+ attempt_count: attemptCount,
272
+ next_attempt_at: shouldRetry ? computeNextAttempt(attemptCount).toISOString() : null,
273
+ last_error: err.message || String(err),
274
+ updated_at: new Date().toISOString()
275
+ })
276
+ .eq('id', deliveryId)
277
+
278
+ await emitLog(ctx, 'integration.outbound.error', { type: 'integration', id: integration.id }, undefined, {
279
+ delivery_id: deliveryId,
280
+ integration_id: integration.id,
281
+ error: err.message || String(err),
282
+ account_id: ctx.accountId,
283
+ is_retry: isRetry
284
+ })
285
+
286
+ throw err
287
+ }
288
+ }
289
+
290
+ const builtInOutboundAdapters: Record<string, any> = {
291
+ 'adapters/openai-outbound': openaiOutbound,
292
+ 'adapters/generic-webhook-outbound': genericWebhookOutbound
293
+ }
294
+
295
+ async function loadOutboundAdapter(functionName: string): Promise<any | null> {
296
+ if (builtInOutboundAdapters[functionName]) {
297
+ return builtInOutboundAdapters[functionName]
298
+ }
299
+ try {
300
+ const module = await import(`./${functionName}`)
301
+ return module.buildRequest ? module : null
302
+ } catch (err) {
303
+ console.error(`[integration-outbound] Failed to load adapter ${functionName}:`, err)
304
+ return null
305
+ }
306
+ }
307
+
308
+ const integrationOutbound = createHandler(execute)
309
+ export default integrationOutbound
310
+
311
+ // CIL[END chunk_id="integration_outbound_module_header_1784200004"]