spine-framework 0.3.125 → 0.3.127

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 -161
  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
@@ -1,18 +1,21 @@
1
- /**
2
- * @module src/pages/admin/AIAgentsPage
3
- * @audience installer
4
- * @layer frontend-page
5
- * @stability stable
6
- *
7
- * Admin list page for AI agents. Fetches all agents via
8
- * `/api/ai-agents?action=list`, applies client-side search, agent-type
9
- * filter (`chat` | `analysis` | `automation` | `custom`), and sort.
10
- * Renders inside `AdminListPage` with stat cards and a sortable table.
11
- * Row clicks navigate to `/spine-framework/admin/configs/ai-agents/:id`.
12
- *
13
- * @seeAlso src/pages/admin/AIAgentDetailPage.tsx
14
- */
15
1
 
2
+
3
+
4
+ // CIL[BEGIN chunk_id="aiagentspage_module_header_1783668265" type="module_header" identity="module_header" parent="none"]
5
+ // CIL[DOC-MACRO purpose="Import dependencies for AIAgentsPage component"]
6
+ // CIL[DOC-RESPONSIBILITY responsibility="Import React hooks, UI components, and utilities"]
7
+ // CIL[DOC-CONTEXT module="AIAgentsPage" layer="presentation" related_files="[\".framework/src/components/layout/Sidebar.tsx\",\".framework/src/components/ui/dialog.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
8
+ // CIL[DOC-CONTRACT inputs="none" outputs="Imported modules" errors="none" side_effects="none"]
9
+ // CIL[DOC-MICRO logic_summary="Imports React state management, routing, API hooks, UI components" notable_decisions="[]"]
10
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
11
+ // CIL[DOC-STABILITY stability="stable"]
12
+ // CIL[DOC-AUDIENCE audience="developer"]
13
+ // CIL[DOC-VECTOR type="context" content="Layer: presentation. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/layout/Sidebar.tsx, .framework/src/components/ui/dialog.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
14
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
15
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
16
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
17
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
18
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
16
19
  import { useState } from 'react'
17
20
  import { useNavigate } from 'react-router-dom'
18
21
  import { Cpu, CheckCircle, Clock } from 'lucide-react';
@@ -22,7 +25,21 @@ import { apiFetch } from '../../lib/api'
22
25
  import { AdminListPage } from '../../components/admin/AdminListPage'
23
26
  import { SortableTableHeader } from '../../components/admin/SortableTableHeader'
24
27
  import { Badge } from '../../components/ui/badge'
25
-
28
+ // CIL[END chunk_id="aiagentspage_module_header_1783668265"]
29
+ // CIL[BEGIN chunk_id="aiagentspage_interface_aiagent_1783668265" type="interface" identity="AIAgent" parent="none"]
30
+ // CIL[DOC-MACRO purpose="Define AIAgent data structure"]
31
+ // CIL[DOC-RESPONSIBILITY responsibility="Type definition for AI agent objects"]
32
+ // CIL[DOC-CONTEXT module="AIAgentsPage" layer="types" related_files="[\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/functions/_shared/principal.ts\",\".framework/src/components/layout/Sidebar.tsx\",\"\\\"]"]
33
+ // CIL[DOC-CONTRACT inputs="none" outputs="AIAgent interface type" errors="none" side_effects="none"]
34
+ // CIL[DOC-MICRO logic_summary="Defines complete AI agent structure with model config and metadata" notable_decisions="[]"]
35
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
36
+ // CIL[DOC-STABILITY stability="stable"]
37
+ // CIL[DOC-VECTOR type="context" content="Layer: types. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/functions/_shared/principal.ts, .framework/src/components/layout/Sidebar.tsx, \."]
38
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
39
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
40
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
41
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
42
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
26
43
  interface AIAgent {
27
44
  id: string
28
45
  name: string
@@ -45,7 +62,21 @@ interface AIAgent {
45
62
  app_id?: string
46
63
  metadata?: Record<string, any>
47
64
  }
48
-
65
+ // CIL[END chunk_id="aiagentspage_interface_aiagent_1783668265"]
66
+ // CIL[BEGIN chunk_id="aiagentspage_function_aiagentspage_1783668265" type="function" identity="AIAgentsPage" parent="none"]
67
+ // CIL[DOC-MACRO purpose="Main AIAgentsPage component implementation"]
68
+ // CIL[DOC-RESPONSIBILITY responsibility="Render AI agent management interface with filtering, sorting, and stats"]
69
+ // CIL[DOC-CONTEXT module="AIAgentsPage" layer="presentation" related_files="[\".framework/src/components/ui/dialog.tsx\",\".framework/src/components/layout/Sidebar.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
70
+ // CIL[DOC-CONTRACT inputs="none" outputs="React component" errors="API failures" side_effects="Navigation, API calls"]
71
+ // CIL[DOC-MICRO logic_summary="Manages state, fetches AI agents, handles filtering/sorting, renders table" notable_decisions="[]"]
72
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
73
+ // CIL[DOC-STABILITY stability="stable"]
74
+ // CIL[DOC-VECTOR type="context" content="Layer: presentation. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/ui/dialog.tsx, .framework/src/components/layout/Sidebar.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
75
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
76
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
77
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
78
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
79
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
49
80
  export function AIAgentsPage() {
50
81
  const navigate = useNavigate()
51
82
 
@@ -56,7 +87,6 @@ export function AIAgentsPage() {
56
87
  const [sortKey, setSortKey] = useState('name')
57
88
  const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc')
58
89
 
59
- // Fetch AI agents from API
60
90
  const { data: agents, loading } = useApi<AIAgent[]>(
61
91
  async (params) => {
62
92
  const response = await apiFetch('/api/ai-agents?action=list', { signal: params?.signal })
@@ -103,7 +133,6 @@ export function AIAgentsPage() {
103
133
  console.log('AIAgentsPage - agents:', agents)
104
134
  console.log('AIAgentsPage - filteredAgents:', filteredAgents)
105
135
 
106
- // Helper functions
107
136
  const getAgentBadgeColor = (agentType: string) => {
108
137
  switch (agentType) {
109
138
  case 'chat':
@@ -136,7 +165,6 @@ export function AIAgentsPage() {
136
165
  navigate(`/spine-framework/admin/configs/ai-agents/${agent.id}`)
137
166
  }
138
167
 
139
- // Sort agents
140
168
  const sortedAgents = [...(filteredAgents || [])].sort((a, b) => {
141
169
  let aValue: any = a[sortKey as keyof AIAgent]
142
170
  let bValue: any = b[sortKey as keyof AIAgent]
@@ -316,3 +344,4 @@ export function AIAgentsPage() {
316
344
  </AdminListPage>
317
345
  )
318
346
  }
347
+ // CIL[END chunk_id="aiagentspage_function_aiagentspage_1783668265"]
@@ -1,6 +1,22 @@
1
- /**
2
- * @module src/pages/admin/APIKeyDetailPage
3
- */
1
+
2
+
3
+
4
+
5
+ // CIL[BEGIN chunk_id="apikeydetailpage_module_header_1783668265" type="module_header" identity="module_header" parent="none"]
6
+ // CIL[DOC-MACRO purpose="Import dependencies for APIKeyDetailPage component"]
7
+ // CIL[DOC-RESPONSIBILITY responsibility="Import React hooks, routing, and UI components"]
8
+ // CIL[DOC-CONTEXT module="APIKeyDetailPage" layer="presentation" related_files="[\".framework/src/components/ui/dialog.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
9
+ // CIL[DOC-CONTRACT inputs="none" outputs="Imported modules" errors="none" side_effects="none"]
10
+ // CIL[DOC-MICRO logic_summary="Imports React state management, routing, UI components, and utilities" notable_decisions="[]"]
11
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
12
+ // CIL[DOC-STABILITY stability="stable"]
13
+ // CIL[DOC-AUDIENCE audience="developer"]
14
+ // CIL[DOC-VECTOR type="context" content="Layer: presentation. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/ui/dialog.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
15
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
16
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
17
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
18
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
19
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
4
20
  import { useState, useEffect } from 'react'
5
21
  import { useParams, useNavigate } from 'react-router-dom'
6
22
  import { Button } from '../../components/ui/button'
@@ -16,31 +32,80 @@ import { ArrowLeft, Key, RefreshCw, Trash2, AlertCircle, Copy, Check } from 'luc
16
32
  import { useApi } from '../../hooks/useApi'
17
33
  import { apiFetch } from '../../lib/api'
18
34
  import { formatDateTime } from '../../lib/utils'
19
-
35
+ import { PageInstructions } from '../../components/admin/PageInstructions'
36
+ // CIL[END chunk_id="apikeydetailpage_module_header_1783668265"]
37
+ // CIL[BEGIN chunk_id="apikeydetailpage_interface_apikey_1783668265" type="interface" identity="APIKey" parent="none"]
38
+ // CIL[DOC-MACRO purpose="Define APIKey data structure"]
39
+ // CIL[DOC-RESPONSIBILITY responsibility="Type definition for API key objects"]
40
+ // CIL[DOC-CONTEXT module="APIKeyDetailPage" layer="types" related_files="[\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/functions/_shared/principal.ts\",\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\"\\\"]"]
41
+ // CIL[DOC-CONTRACT inputs="none" outputs="APIKey interface type" errors="none" side_effects="none"]
42
+ // CIL[DOC-MICRO logic_summary="Defines complete API key structure with permissions and metadata" notable_decisions="[]"]
43
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
44
+ // CIL[DOC-STABILITY stability="stable"]
45
+ // CIL[DOC-VECTOR type="context" content="Layer: types. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/functions/_shared/principal.ts, custom/apps/cortex/functions/custom_cil-embeddings.ts, \."]
46
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
47
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
48
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
49
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
50
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
20
51
  interface APIKey {
21
52
  id: string
22
53
  name: string
23
- key_type: 'public' | 'private'
54
+ integration_id?: string
55
+ app_id?: string
56
+ key_type: 'inbound' | 'outbound' | 'internal' | 'cli'
24
57
  key_value?: string
25
58
  key_prefix: string
26
59
  is_active: boolean
27
60
  permissions?: Record<string, any>
61
+ scopes?: string[]
28
62
  rate_limit: number
29
63
  expires_at?: string
30
64
  last_used_at?: string
31
65
  usage_count: number
32
- integration_id?: string
66
+ machine_type?: string
67
+ is_internal?: boolean
33
68
  metadata?: Record<string, any>
34
69
  created_by_person?: { id: string; full_name: string; email: string }
35
70
  created_at: string
36
71
  updated_at: string
37
72
  }
38
-
73
+ // CIL[END chunk_id="apikeydetailpage_interface_apikey_1783668265"]
74
+ // CIL[BEGIN chunk_id="apikeydetailpage_constant_key_type_options_1783668265" type="constant" identity="KEY_TYPE_OPTIONS" parent="none"]
75
+ // CIL[DOC-MACRO purpose="Define API key type options"]
76
+ // CIL[DOC-RESPONSIBILITY responsibility="Configure available key type choices"]
77
+ // CIL[DOC-CONTEXT module="APIKeyDetailPage" layer="constants" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/migrations/025_retype_threads_messages.sql\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
78
+ // CIL[DOC-CONTRACT inputs="none" outputs="Key type options" errors="none" side_effects="none"]
79
+ // CIL[DOC-MICRO logic_summary="Defines private and public key type options with descriptions" notable_decisions="[]"]
80
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
81
+ // CIL[DOC-STABILITY stability="stable"]
82
+ // CIL[DOC-VECTOR type="context" content="Layer: constants. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/migrations/025_retype_threads_messages.sql, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/functions/_shared/principal.ts, \."]
83
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
84
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
85
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
86
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
87
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
39
88
  const KEY_TYPE_OPTIONS = [
40
- { value: 'private', label: 'Private (Server-side)' },
41
- { value: 'public', label: 'Public (Client-side)' }
89
+ { value: 'inbound', label: 'Inbound (External callers to Spine)' },
90
+ { value: 'outbound', label: 'Outbound (Spine to external services)' },
91
+ { value: 'internal', label: 'Internal (Spine-to-Spine)' },
92
+ { value: 'cli', label: 'CLI / Script' }
42
93
  ]
43
-
94
+ // CIL[END chunk_id="apikeydetailpage_constant_key_type_options_1783668265"]
95
+ // CIL[BEGIN chunk_id="apikeydetailpage_function_apikeydetailpage_1783668265" type="function" identity="APIKeyDetailPage" parent="none"]
96
+ // CIL[DOC-MACRO purpose="Main APIKeyDetailPage component implementation"]
97
+ // CIL[DOC-RESPONSIBILITY responsibility="Render API key detail view with create/edit functionality"]
98
+ // CIL[DOC-CONTEXT module="APIKeyDetailPage" layer="presentation" related_files="[\".framework/src/components/ui/dialog.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
99
+ // CIL[DOC-CONTRACT inputs="none" outputs="React component" errors="API failures" side_effects="Navigation, API calls"]
100
+ // CIL[DOC-MICRO logic_summary="Manages CRUD operations for API keys with form validation and key generation" notable_decisions="[]"]
101
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
102
+ // CIL[DOC-STABILITY stability="stable"]
103
+ // CIL[DOC-VECTOR type="context" content="Layer: presentation. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/ui/dialog.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
104
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
105
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
106
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
107
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
108
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
44
109
  export function APIKeyDetailPage() {
45
110
  const { id } = useParams<{ id: string }>()
46
111
  const navigate = useNavigate()
@@ -48,13 +113,17 @@ export function APIKeyDetailPage() {
48
113
 
49
114
  const [formData, setFormData] = useState({
50
115
  name: '',
51
- key_type: 'private' as 'public' | 'private',
116
+ integration_id: '',
117
+ app_id: '',
118
+ key_type: 'inbound' as 'inbound' | 'outbound' | 'internal' | 'cli',
52
119
  key_prefix: 'sk_',
53
120
  rate_limit: 1000,
54
121
  expires_at: '',
55
- integration_id: '',
56
122
  permissions: {},
123
+ scopes: '[]',
57
124
  metadata: {},
125
+ machine_type: 'integration',
126
+ is_internal: false,
58
127
  is_active: true
59
128
  })
60
129
  const [permissionsJson, setPermissionsJson] = useState('{}')
@@ -79,13 +148,17 @@ export function APIKeyDetailPage() {
79
148
  if (apiKey) {
80
149
  setFormData({
81
150
  name: apiKey.name || '',
82
- key_type: apiKey.key_type || 'private',
151
+ integration_id: apiKey.integration_id || '',
152
+ app_id: apiKey.app_id || '',
153
+ key_type: apiKey.key_type || 'inbound',
83
154
  key_prefix: apiKey.key_prefix || 'sk_',
84
155
  rate_limit: apiKey.rate_limit || 1000,
85
156
  expires_at: apiKey.expires_at ? apiKey.expires_at.split('T')[0] : '',
86
- integration_id: apiKey.integration_id || '',
87
157
  permissions: apiKey.permissions || {},
158
+ scopes: JSON.stringify(apiKey.scopes || [], null, 2),
88
159
  metadata: apiKey.metadata || {},
160
+ machine_type: apiKey.machine_type || 'integration',
161
+ is_internal: apiKey.is_internal || false,
89
162
  is_active: apiKey.is_active
90
163
  })
91
164
  setPermissionsJson(JSON.stringify(apiKey.permissions || {}, null, 2))
@@ -99,9 +172,12 @@ export function APIKeyDetailPage() {
99
172
  try {
100
173
  let permissions = {}
101
174
  let metadata = {}
175
+ let scopes: string[] = []
102
176
  try { permissions = JSON.parse(permissionsJson) } catch {}
103
177
  try { metadata = JSON.parse(metadataJson) } catch {}
104
- const body = { ...formData, permissions, metadata }
178
+ try { scopes = JSON.parse(formData.scopes) } catch {}
179
+ const { key_prefix: _kp, ...rest } = formData
180
+ const body = { ...rest, permissions, scopes, metadata }
105
181
  const response = await apiFetch('/api/api-keys', {
106
182
  method: 'POST',
107
183
  headers: { 'Content-Type': 'application/json' },
@@ -144,6 +220,49 @@ export function APIKeyDetailPage() {
144
220
  <Button variant="ghost" onClick={() => navigate(-1)}><ArrowLeft className="h-5 w-5" /></Button>
145
221
  <h1 className="text-2xl font-bold">{apiKey.name}</h1>
146
222
  </div>
223
+ <PageInstructions
224
+ description="Create an API key and set its permissions, rate limits, and expiration."
225
+ sections={[
226
+ { title: 'What this API key controls', content: 'API keys authenticate callers to Spine. A key controls which callers are accepted, how fast they can call, and which permissions they carry.' },
227
+ { title: 'Fields explained', content: <ul className="list-disc pl-4 space-y-1">
228
+ <li><strong>Name</strong> — string, required. A human-readable label. Missing name blocks creation.</li>
229
+ <li><strong>Integration ID</strong> — string. Optional UUID of the integration this key is for.</li>
230
+ <li><strong>App ID</strong> — string. Optional UUID of the owning app.</li>
231
+ <li><strong>Key Type</strong> — select. inbound, outbound, internal, or cli. Determines which direction the key is used and how it is validated.</li>
232
+ <li><strong>Prefix</strong> — string. The public prefix that appears before the generated secret. Default is "sk_". The create API may override this with the generated prefix.</li>
233
+ <li><strong>Rate Limit (requests/min)</strong> — number 1-10000. Enforces a cap on calls per minute. Missing or 0 may prevent usage.</li>
234
+ <li><strong>Expires At</strong> — date. Optional expiration. After this date the key is rejected.</li>
235
+ <li><strong>Machine Type</strong> — string. The machine principal type, e.g. integration.</li>
236
+ <li><strong>Internal</strong> — checkbox. Marks a key for internal service-to-service use.</li>
237
+ <li><strong>Active</strong> — checkbox. Inactive keys are rejected.</li>
238
+ <li><strong>Permissions (JSON)</strong> — JSON object. Defines allowed actions/resources. Invalid JSON fails creation.</li>
239
+ <li><strong>Scopes (JSON)</strong> — JSON array of scope strings. Invalid JSON fails creation.</li>
240
+ <li><strong>Metadata (JSON)</strong> — JSON object. Optional notes such as environment. Invalid JSON fails creation.</li>
241
+ </ul> },
242
+ { title: 'Options and values', content: <ul className="list-disc pl-4 space-y-1">
243
+ <li><strong>inbound</strong>: External callers into Spine APIs.</li>
244
+ <li><strong>outbound</strong>: Spine calling external services.</li>
245
+ <li><strong>internal</strong>: Service-to-service calls within Spine.</li>
246
+ <li><strong>cli</strong>: Scripts or command-line tooling.</li>
247
+ </ul> },
248
+ { title: 'Downstream impact', content: <ul className="list-disc pl-4 space-y-1">
249
+ <li>API gateway validates the key on every call. An inactive, expired, or missing key is rejected.</li>
250
+ <li>Rate limits are enforced at the edge; a misconfigured limit may throttle or deny callers.</li>
251
+ <li>Permission JSON is parsed and used for authorization checks. Badly shaped permissions may grant too much or too little access.</li>
252
+ </ul> },
253
+ { title: 'How to create, save, and validate', content: <ol className="list-decimal pl-4 space-y-1">
254
+ <li>Only create mode is available; existing keys are shown read-only with the value masked.</li>
255
+ <li>Fill Name, Key Type, and Rate Limit. Provide valid JSON for Permissions and Metadata.</li>
256
+ <li>Submit the form. A one-time key is generated and shown; copy it immediately because it cannot be retrieved again.</li>
257
+ <li>Validation checks required fields, rate limit range, and valid JSON.</li>
258
+ </ol> },
259
+ { title: 'Common mistakes', content: <ul className="list-disc pl-4 space-y-1">
260
+ <li>Forgetting to copy the generated key before leaving the page.</li>
261
+ <li>Entering Permissions as a comma-separated list instead of JSON.</li>
262
+ <li>Setting Rate Limit to 0, which blocks all requests.</li>
263
+ </ul> },
264
+ ]}
265
+ />
147
266
  <Card><CardContent className="p-6">
148
267
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
149
268
  <div className="flex justify-between"><span className="text-muted-foreground">Key Type</span><span>{apiKey.key_type}</span></div>
@@ -178,18 +297,68 @@ export function APIKeyDetailPage() {
178
297
  <Button variant="ghost" onClick={() => navigate(-1)}><ArrowLeft className="h-5 w-5" /></Button>
179
298
  <h1 className="text-2xl font-bold">Create API Key</h1>
180
299
  </div>
300
+ <PageInstructions
301
+ description="Create an API key and set its permissions, rate limits, and expiration."
302
+ sections={[
303
+ { title: 'What this API key controls', content: 'API keys authenticate callers to Spine. A key controls which callers are accepted, how fast they can call, and which permissions they carry.' },
304
+ { title: 'Fields explained', content: <ul className="list-disc pl-4 space-y-1">
305
+ <li><strong>Name</strong> — string, required. A human-readable label. Missing name blocks creation.</li>
306
+ <li><strong>Integration ID</strong> — string. Optional UUID of the integration this key is for.</li>
307
+ <li><strong>App ID</strong> — string. Optional UUID of the owning app.</li>
308
+ <li><strong>Key Type</strong> — select. inbound, outbound, internal, or cli. Determines which direction the key is used and how it is validated.</li>
309
+ <li><strong>Prefix</strong> — string. The public prefix that appears before the generated secret. Default is "sk_". The create API may override this with the generated prefix.</li>
310
+ <li><strong>Rate Limit (requests/min)</strong> — number 1-10000. Enforces a cap on calls per minute. Missing or 0 may prevent usage.</li>
311
+ <li><strong>Expires At</strong> — date. Optional expiration. After this date the key is rejected.</li>
312
+ <li><strong>Machine Type</strong> — string. The machine principal type, e.g. integration.</li>
313
+ <li><strong>Internal</strong> — checkbox. Marks a key for internal service-to-service use.</li>
314
+ <li><strong>Active</strong> — checkbox. Inactive keys are rejected.</li>
315
+ <li><strong>Permissions (JSON)</strong> — JSON object. Defines allowed actions/resources. Invalid JSON fails creation.</li>
316
+ <li><strong>Scopes (JSON)</strong> — JSON array of scope strings. Invalid JSON fails creation.</li>
317
+ <li><strong>Metadata (JSON)</strong> — JSON object. Optional notes such as environment. Invalid JSON fails creation.</li>
318
+ </ul> },
319
+ { title: 'Options and values', content: <ul className="list-disc pl-4 space-y-1">
320
+ <li><strong>inbound</strong>: External callers into Spine APIs.</li>
321
+ <li><strong>outbound</strong>: Spine calling external services.</li>
322
+ <li><strong>internal</strong>: Service-to-service calls within Spine.</li>
323
+ <li><strong>cli</strong>: Scripts or command-line tooling.</li>
324
+ </ul> },
325
+ { title: 'Downstream impact', content: <ul className="list-disc pl-4 space-y-1">
326
+ <li>API gateway validates the key on every call. An inactive, expired, or missing key is rejected.</li>
327
+ <li>Rate limits are enforced at the edge; a misconfigured limit may throttle or deny callers.</li>
328
+ <li>Permission JSON is parsed and used for authorization checks. Badly shaped permissions may grant too much or too little access.</li>
329
+ </ul> },
330
+ { title: 'How to create, save, and validate', content: <ol className="list-decimal pl-4 space-y-1">
331
+ <li>Only create mode is available; existing keys are shown read-only with the value masked.</li>
332
+ <li>Fill Name, Key Type, and Rate Limit. Provide valid JSON for Permissions and Metadata.</li>
333
+ <li>Submit the form. A one-time key is generated and shown; copy it immediately because it cannot be retrieved again.</li>
334
+ <li>Validation checks required fields, rate limit range, and valid JSON.</li>
335
+ </ol> },
336
+ { title: 'Common mistakes', content: <ul className="list-disc pl-4 space-y-1">
337
+ <li>Forgetting to copy the generated key before leaving the page.</li>
338
+ <li>Entering Permissions as a comma-separated list instead of JSON.</li>
339
+ <li>Setting Rate Limit to 0, which blocks all requests.</li>
340
+ </ul> },
341
+ ]}
342
+ />
181
343
  {error && <Alert variant="destructive"><AlertCircle className="h-4 w-4" /><AlertTitle>Error</AlertTitle><AlertDescription>{error}</AlertDescription></Alert>}
182
344
  <form onSubmit={handleSubmit} className="space-y-6">
183
345
  <Card><CardContent className="p-6 space-y-4">
184
346
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
185
347
  <div className="space-y-2"><Label>Name *</Label><Input value={formData.name} onChange={e => setFormData({...formData, name: e.target.value})} required placeholder="e.g., Production API Key" /></div>
186
- <div className="space-y-2"><Label>Key Type</Label><Select value={formData.key_type} onValueChange={v => setFormData({...formData, key_type: v as 'public' | 'private'})}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{KEY_TYPE_OPTIONS.map(opt => <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>)}</SelectContent></Select></div>
348
+ <div className="space-y-2"><Label>Key Type</Label><Select value={formData.key_type} onValueChange={v => setFormData({...formData, key_type: v as 'inbound' | 'outbound' | 'internal' | 'cli'})}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{KEY_TYPE_OPTIONS.map(opt => <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>)}</SelectContent></Select></div>
349
+ <div className="space-y-2"><Label>Integration ID</Label><Input value={formData.integration_id} onChange={e => setFormData({...formData, integration_id: e.target.value})} placeholder="Optional integration UUID" /></div>
350
+ <div className="space-y-2"><Label>App ID</Label><Input value={formData.app_id} onChange={e => setFormData({...formData, app_id: e.target.value})} placeholder="Optional app UUID" /></div>
187
351
  <div className="space-y-2"><Label>Prefix</Label><Input value={formData.key_prefix} onChange={e => setFormData({...formData, key_prefix: e.target.value})} placeholder="e.g., sk_" /></div>
188
352
  <div className="space-y-2"><Label>Rate Limit (requests/min)</Label><Input type="number" value={formData.rate_limit} onChange={e => setFormData({...formData, rate_limit: parseInt(e.target.value)})} min={1} max={10000} /></div>
189
353
  <div className="space-y-2"><Label>Expires At</Label><Input type="date" value={formData.expires_at} onChange={e => setFormData({...formData, expires_at: e.target.value})} /></div>
354
+ <div className="space-y-2"><Label>Machine Type</Label><Input value={formData.machine_type} onChange={e => setFormData({...formData, machine_type: e.target.value})} placeholder="e.g., integration" /></div>
355
+ </div>
356
+ <div className="flex flex-wrap items-center gap-6">
357
+ <div className="flex items-center gap-2"><Checkbox id="active" checked={formData.is_active} onCheckedChange={c => setFormData({...formData, is_active: c === true})} /><Label htmlFor="active">Active</Label></div>
358
+ <div className="flex items-center gap-2"><Checkbox id="is_internal" checked={formData.is_internal} onCheckedChange={c => setFormData({...formData, is_internal: c === true})} /><Label htmlFor="is_internal">Internal</Label></div>
190
359
  </div>
191
- <div className="flex items-center gap-2"><Checkbox id="active" checked={formData.is_active} onCheckedChange={c => setFormData({...formData, is_active: c === true})} /><Label htmlFor="active">Active</Label></div>
192
360
  <div className="space-y-2"><Label>Permissions (JSON)</Label><Textarea value={permissionsJson} onChange={e => setPermissionsJson(e.target.value)} rows={4} className="font-mono text-sm" placeholder='{"resources": ["read", "write"]}' /></div>
361
+ <div className="space-y-2"><Label>Scopes (JSON array)</Label><Textarea value={formData.scopes} onChange={e => setFormData({...formData, scopes: e.target.value})} rows={3} className="font-mono text-sm" placeholder='["read:items"]' /></div>
193
362
  <div className="space-y-2"><Label>Metadata (JSON)</Label><Textarea value={metadataJson} onChange={e => setMetadataJson(e.target.value)} rows={3} className="font-mono text-sm" placeholder='{"env": "production"}' /></div>
194
363
  </CardContent></Card>
195
364
  <div className="flex justify-end gap-3"><Button type="button" variant="outline" onClick={() => navigate(-1)}>Cancel</Button><Button type="submit" disabled={isSubmitting}>{isSubmitting ? 'Creating...' : 'Create API Key'}</Button></div>
@@ -197,3 +366,4 @@ export function APIKeyDetailPage() {
197
366
  </div>
198
367
  )
199
368
  }
369
+ // CIL[END chunk_id="apikeydetailpage_function_apikeydetailpage_1783668265"]
@@ -1,32 +1,51 @@
1
- /**
2
- * @module src/pages/admin/APIKeysPage
3
- * @audience installer
4
- * @layer frontend-page
5
- * @stability stable
6
- *
7
- * Admin list page for API keys. Fetches all keys via
8
- * `/api/api-keys?action=list`, applies client-side search, type filter
9
- * (`public` | `private`), and sort. Key values are never shown in the
10
- * list — only the prefix. Renders inside `AdminListPage`. Row clicks
11
- * navigate to `/spine-framework/admin/configs/api-keys/:id`.
12
- *
13
- * @seeAlso src/pages/admin/APIKeyDetailPage.tsx
14
- */
15
1
 
2
+
3
+
4
+ // CIL[BEGIN chunk_id="apikeyspage_module_header_1783668265" type="module_header" identity="module_header" parent="none"]
5
+ // CIL[DOC-MACRO purpose="Import dependencies for APIKeysPage component"]
6
+ // CIL[DOC-RESPONSIBILITY responsibility="Import React hooks, UI components, and utilities"]
7
+ // CIL[DOC-CONTEXT module="APIKeysPage" layer="presentation" related_files="[\".framework/src/components/layout/Sidebar.tsx\",\".framework/src/components/ui/dialog.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
8
+ // CIL[DOC-CONTRACT inputs="none" outputs="Imported modules" errors="none" side_effects="none"]
9
+ // CIL[DOC-MICRO logic_summary="Imports React state management, UI components, API hooks" notable_decisions="[]"]
10
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
11
+ // CIL[DOC-STABILITY stability="stable"]
12
+ // CIL[DOC-AUDIENCE audience="developer"]
13
+ // CIL[DOC-VECTOR type="context" content="Layer: presentation. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/layout/Sidebar.tsx, .framework/src/components/ui/dialog.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
14
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
15
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
16
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
17
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
18
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
16
19
  import React, { useState } from 'react'
17
20
  import { Plus, Key, CheckCircle, Clock, ShieldCheck, RefreshCw } from 'lucide-react';
18
21
  import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
19
22
  import { Button } from '../../components/ui/button'
20
23
  import { AdminListPage } from '../../components/admin/AdminListPage'
21
24
  import { SortableTableHeader } from '../../components/admin/SortableTableHeader'
25
+
26
+ import { PageInstructions } from '../../components/admin/PageInstructions'
22
27
  import { formatDateTime } from '../../lib/utils'
23
28
  import { useApi } from '../../hooks/useApi'
24
29
  import { apiFetch } from '../../lib/api'
25
-
30
+ // CIL[END chunk_id="apikeyspage_module_header_1783668265"]
31
+ // CIL[BEGIN chunk_id="apikeyspage_interface_apikey_1783668265" type="interface" identity="APIKey" parent="none"]
32
+ // CIL[DOC-MACRO purpose="Define APIKey data structure"]
33
+ // CIL[DOC-RESPONSIBILITY responsibility="Type definition for API key objects"]
34
+ // CIL[DOC-CONTEXT module="APIKeysPage" layer="types" related_files="[\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/functions/_shared/principal.ts\",\".framework/src/components/layout/Sidebar.tsx\",\"\\\"]"]
35
+ // CIL[DOC-CONTRACT inputs="none" outputs="APIKey interface type" errors="none" side_effects="none"]
36
+ // CIL[DOC-MICRO logic_summary="Defines complete API key structure with metadata and usage" notable_decisions="[]"]
37
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
38
+ // CIL[DOC-STABILITY stability="stable"]
39
+ // CIL[DOC-VECTOR type="context" content="Layer: types. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/functions/_shared/principal.ts, .framework/src/components/layout/Sidebar.tsx, \."]
40
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
41
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
42
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
43
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
44
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
26
45
  interface APIKey {
27
46
  id: string
28
47
  name: string
29
- key_type: 'public' | 'private'
48
+ key_type: 'inbound' | 'outbound'
30
49
  is_active: boolean
31
50
  last_used_at?: string
32
51
  expires_at?: string
@@ -44,7 +63,21 @@ interface APIKey {
44
63
  }
45
64
  created_at: string
46
65
  }
47
-
66
+ // CIL[END chunk_id="apikeyspage_interface_apikey_1783668265"]
67
+ // CIL[BEGIN chunk_id="apikeyspage_function_apikeyspage_1783668265" type="function" identity="APIKeysPage" parent="none"]
68
+ // CIL[DOC-MACRO purpose="Main APIKeysPage component implementation"]
69
+ // CIL[DOC-RESPONSIBILITY responsibility="Render API key management interface with filtering, sorting, and stats"]
70
+ // CIL[DOC-CONTEXT module="APIKeysPage" layer="presentation" related_files="[\".framework/src/components/ui/dialog.tsx\",\".framework/src/components/layout/Sidebar.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
71
+ // CIL[DOC-CONTRACT inputs="none" outputs="React component" errors="API failures" side_effects="Navigation, API calls"]
72
+ // CIL[DOC-MICRO logic_summary="Manages state, fetches API keys, handles filtering/sorting, renders table" notable_decisions="[]"]
73
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
74
+ // CIL[DOC-STABILITY stability="stable"]
75
+ // CIL[DOC-VECTOR type="context" content="Layer: presentation. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/ui/dialog.tsx, .framework/src/components/layout/Sidebar.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
76
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
77
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
78
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
79
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
80
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
48
81
  export function APIKeysPage() {
49
82
  const [searchTerm, setSearchTerm] = useState('')
50
83
  const [selectedStatus, setSelectedStatus] = useState('all')
@@ -121,8 +154,8 @@ export function APIKeysPage() {
121
154
  iconColor: 'text-green-500'
122
155
  },
123
156
  {
124
- title: 'Private Keys',
125
- value: (apiKeys || []).filter(k => k.key_type === 'private').length,
157
+ title: 'Inbound Keys',
158
+ value: (apiKeys || []).filter(k => k.key_type === 'inbound').length,
126
159
  icon: ShieldCheck,
127
160
  iconColor: 'text-purple-500'
128
161
  },
@@ -146,8 +179,8 @@ export function APIKeysPage() {
146
179
 
147
180
  const typeOptions = [
148
181
  { value: 'all', label: 'All Types' },
149
- { value: 'public', label: 'Public' },
150
- { value: 'private', label: 'Private' }
182
+ { value: 'inbound', label: 'Inbound' },
183
+ { value: 'outbound', label: 'Outbound' }
151
184
  ]
152
185
 
153
186
  const filters = [
@@ -169,6 +202,7 @@ export function APIKeysPage() {
169
202
  <AdminListPage
170
203
  title="API Keys"
171
204
  description="Manage API keys and access tokens"
205
+ noPageInstructions
172
206
  newButtonText="Generate API Key"
173
207
  newButtonHref="/spine-framework/admin/configs/api-keys/new"
174
208
  statsCards={statsCards}
@@ -181,6 +215,47 @@ export function APIKeysPage() {
181
215
  emptyMessage="No API keys found"
182
216
  emptyIcon={Key}
183
217
  >
218
+ <PageInstructions
219
+ title="API key usage instructions"
220
+ description="API keys are generated tokens that authenticate external callers or service-to-service calls. The full key is shown once after creation. Copy it immediately and store it securely."
221
+ sections={[
222
+ { title: '1. Inbound webhooks (authenticate callers to Spine)', content: (
223
+ <div className="space-y-2">
224
+ <p className="text-sm text-slate-600">
225
+ Create an <code className="text-xs bg-muted px-1 rounded">inbound</code> key, optionally bound to an Integration. Send the key in the request header to <code className="text-xs bg-muted px-1 rounded">/api/integration-routes</code>:
226
+ </p>
227
+ <pre className="text-xs bg-muted p-2 rounded overflow-auto font-mono">{`X-Api-Key: sk_...`}</pre>
228
+ <p className="text-sm text-slate-600">
229
+ The Integration Broker will validate the key and route the event to the adapter defined by the integration.
230
+ </p>
231
+ </div>
232
+ )},
233
+ { title: '2. Outbound / service calls (use a key instead of an env var)', content: (
234
+ <div className="space-y-2">
235
+ <p className="text-sm text-slate-600">
236
+ You can use a literal key value in an integration's <code className="text-xs bg-muted px-1 rounded">credentials</code> instead of <code className="text-xs bg-muted px-1 rounded">env_var</code>. Create a key, copy the value, and paste it into the integration's credentials JSON with <code className="text-xs bg-muted px-1 rounded">source: "value"</code>:
237
+ </p>
238
+ <pre className="text-xs bg-muted p-2 rounded overflow-auto font-mono">{JSON.stringify({ api_token: { source: 'value', value: 'sk_...' } }, null, 2)}</pre>
239
+ </div>
240
+ )},
241
+ { title: '3. Key types', content: (
242
+ <div className="space-y-2">
243
+ <ul className="text-sm text-slate-600 list-disc list-inside space-y-1">
244
+ <li><strong>inbound</strong> — external services calling Spine (webhooks, API clients).</li>
245
+ <li><strong>outbound</strong> — Spine services calling another system, often linked to an integration.</li>
246
+ <li><strong>internal</strong> — internal Spine function-to-function calls.</li>
247
+ <li><strong>cli</strong> — command-line scripts and tooling.</li>
248
+ </ul>
249
+ </div>
250
+ )},
251
+ { title: '4. Rate limits, scopes, and expiration', content: (
252
+ <p className="text-sm text-slate-600">
253
+ Use <code className="text-xs bg-muted px-1 rounded">rate_limit</code> to throttle usage, set <code className="text-xs bg-muted px-1 rounded">expires_at</code> for short-lived keys, and define <code className="text-xs bg-muted px-1 rounded">permissions</code> / <code className="text-xs bg-muted px-1 rounded">scopes</code> as JSON arrays to restrict what the key can do.
254
+ </p>
255
+ )},
256
+ ]}
257
+ />
258
+
184
259
  {sortedKeys.length === 0 ? (
185
260
  <div className="p-8 text-center">
186
261
  <Key className="mx-auto h-12 w-12 text-slate-400" />
@@ -259,7 +334,7 @@ export function APIKeysPage() {
259
334
  </td>
260
335
  <td className="px-6 py-4 whitespace-nowrap">
261
336
  <span className={`inline-flex px-2 py-0.5 text-xs font-medium rounded-md ${
262
- key.key_type === 'private'
337
+ key.key_type === 'inbound'
263
338
  ? 'bg-purple-100 text-purple-700'
264
339
  : 'bg-blue-100 text-blue-700'
265
340
  }`}>
@@ -301,3 +376,4 @@ export function APIKeysPage() {
301
376
  </AdminListPage>
302
377
  )
303
378
  }
379
+ // CIL[END chunk_id="apikeyspage_function_apikeyspage_1783668265"]