@hotmeshio/long-tail 0.10.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/build/api/bot-accounts.d.ts +4 -0
- package/build/api/bot-accounts.js +3 -1
- package/build/api/escalations/metadata.js +21 -1
- package/build/api/escalations/resolve.d.ts +8 -0
- package/build/api/escalations/resolve.js +82 -3
- package/build/api/me.d.ts +16 -0
- package/build/api/me.js +79 -0
- package/build/api/roles.d.ts +8 -0
- package/build/api/roles.js +14 -0
- package/build/bin/ltc.js +10 -1
- package/build/lib/cli/client.d.ts +4 -1
- package/build/lib/cli/client.js +4 -2
- package/build/lib/db/schemas/022_user_preferences.sql +18 -0
- package/build/lib/db/schemas/023_role_enforce_schema.sql +18 -0
- package/build/lib/typed-resolution.d.ts +35 -0
- package/build/lib/typed-resolution.js +37 -0
- package/build/modules/config.d.ts +1 -0
- package/build/modules/config.js +5 -0
- package/build/routes/bot-accounts.js +3 -1
- package/build/routes/index.js +2 -0
- package/build/routes/me.d.ts +2 -0
- package/build/routes/me.js +57 -0
- package/build/sdk/index.d.ts +10 -0
- package/build/sdk/index.js +25 -0
- package/build/services/escalation/crud.d.ts +18 -6
- package/build/services/escalation/crud.js +18 -2
- package/build/services/escalation/index.d.ts +1 -0
- package/build/services/escalation/index.js +1 -0
- package/build/services/escalation/resolver-validation.d.ts +28 -0
- package/build/services/escalation/resolver-validation.js +95 -0
- package/build/services/escalation/sql.d.ts +12 -2
- package/build/services/escalation/sql.js +22 -2
- package/build/services/iam/bots.d.ts +4 -1
- package/build/services/iam/bots.js +5 -3
- package/build/services/iam/sql.d.ts +2 -2
- package/build/services/iam/sql.js +15 -1
- package/build/services/role/enforcement-cache.d.ts +21 -0
- package/build/services/role/enforcement-cache.js +0 -0
- package/build/services/role/index.js +6 -0
- package/build/services/role/sql.d.ts +15 -2
- package/build/services/role/sql.js +21 -2
- package/build/services/role/types.d.ts +25 -0
- package/build/services/user/index.d.ts +1 -0
- package/build/services/user/index.js +5 -1
- package/build/services/user/preferences.d.ts +15 -0
- package/build/services/user/preferences.js +38 -0
- package/build/shared/form-validation/field-validator.d.ts +45 -0
- package/build/shared/form-validation/field-validator.js +214 -0
- package/build/shared/form-validation/index.d.ts +11 -0
- package/build/shared/form-validation/index.js +27 -0
- package/build/shared/form-validation/validate-resolver-payload.d.ts +41 -0
- package/build/shared/form-validation/validate-resolver-payload.js +62 -0
- package/build/shared/form-validation/x-lt-bind.d.ts +41 -0
- package/build/shared/form-validation/x-lt-bind.js +110 -0
- package/build/shared/form-validation/x-lt-help.d.ts +21 -0
- package/build/shared/form-validation/x-lt-help.js +91 -0
- package/build/shared/form-validation/x-lt-show-if.d.ts +7 -0
- package/build/shared/form-validation/x-lt-show-if.js +84 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +29 -0
- package/build/system/mcp-servers/admin/schemas.js +7 -0
- package/build/system/mcp-servers/admin/users.js +8 -3
- package/build/system/mcp-servers/human-queue-schemas.d.ts +4 -4
- package/build/system/mcp-servers/human-queue-schemas.js +4 -1
- package/build/system/mcp-servers/human-queue.js +41 -2
- package/build/system/seed/tool-manifests-admin.d.ts +167 -0
- package/build/system/seed/tool-manifests-admin.js +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/sdk.d.ts +6 -1
- package/build/types/validation.d.ts +43 -0
- package/build/types/validation.js +28 -0
- package/dashboard/dist/assets/{AdminDashboard-D7Amh-EJ.js → AdminDashboard-DQLGybZ9.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-D7Amh-EJ.js.map → AdminDashboard-DQLGybZ9.js.map} +1 -1
- package/dashboard/dist/assets/AgentConfigPage-4JgMpuYJ.js +16 -0
- package/dashboard/dist/assets/AgentConfigPage-4JgMpuYJ.js.map +1 -0
- package/dashboard/dist/assets/{AgentDetailPage-Dy3Qvlfk.js → AgentDetailPage-DCgKokgX.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-Dy3Qvlfk.js.map → AgentDetailPage-DCgKokgX.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-D7WePZ0o.js → AgentsPage-BK_8VKZO.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-D7WePZ0o.js.map → AgentsPage-BK_8VKZO.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-Dp1rsIHC.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-Dp1rsIHC.js.map +1 -0
- package/dashboard/dist/assets/BotPicker-DvUnYEJk.js +2 -0
- package/dashboard/dist/assets/BotPicker-DvUnYEJk.js.map +1 -0
- package/dashboard/dist/assets/{CapabilitiesPage-3VTLEPGO.js → CapabilitiesPage-CJ5ldY8w.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-3VTLEPGO.js.map → CapabilitiesPage-CJ5ldY8w.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-BYgeZz9M.js → CollapsibleSection-BXOpfs3z.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-BYgeZz9M.js.map → CollapsibleSection-BXOpfs3z.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-D20FNwS-.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-D20FNwS-.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-CaH3J3SE.js → CredentialsPage-vZ2psNnd.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-CaH3J3SE.js.map → CredentialsPage-vZ2psNnd.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-Bma_4AC2.js → CronLabel-KRkqNSYs.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-Bma_4AC2.js.map → CronLabel-KRkqNSYs.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-BxyjEX27.js → CustomDurationPicker-C-vGsrFl.js} +2 -2
- package/dashboard/dist/assets/CustomDurationPicker-C-vGsrFl.js.map +1 -0
- package/dashboard/dist/assets/{DropZone-zxH-9B8c.js → DropZone-C3NdMtwK.js} +2 -2
- package/dashboard/dist/assets/{DropZone-zxH-9B8c.js.map → DropZone-C3NdMtwK.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-Dr1cEwoi.js → ElapsedCell-DNH-XHOS.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-Dr1cEwoi.js.map → ElapsedCell-DNH-XHOS.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-CLdsR-hK.js → EscalationListSchemaPage-BIW1qxjC.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-CLdsR-hK.js.map → EscalationListSchemaPage-BIW1qxjC.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-DnEnvolM.js → EscalationSchemaPage-Fj2IwQXQ.js} +2 -2
- package/dashboard/dist/assets/{EscalationSchemaPage-DnEnvolM.js.map → EscalationSchemaPage-Fj2IwQXQ.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BH8cxgLI.js → EscalationsOverview-BVUx0vZH.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BH8cxgLI.js.map → EscalationsOverview-BVUx0vZH.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-p-xsfYYg.js → EventTable-Dm4zZaRl.js} +2 -2
- package/dashboard/dist/assets/{EventTable-p-xsfYYg.js.map → EventTable-Dm4zZaRl.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-Cf9c_LJ-.js → GraphInvokePage-D4f074qh.js} +2 -2
- package/dashboard/dist/assets/GraphInvokePage-D4f074qh.js.map +1 -0
- package/dashboard/dist/assets/HomePage-DQ0bb_M_.js +2 -0
- package/dashboard/dist/assets/HomePage-DQ0bb_M_.js.map +1 -0
- package/dashboard/dist/assets/ListToolbar-CItVkLQ-.js +2 -0
- package/dashboard/dist/assets/{ListToolbar-CDlol8XJ.js.map → ListToolbar-CItVkLQ-.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-ChZ36p9t.js → McpOverview-ByYVDldF.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-ChZ36p9t.js.map → McpOverview-ByYVDldF.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-X4j_jm3r.js → McpQueryDetailPage-DYuIdJiH.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-X4j_jm3r.js.map → McpQueryDetailPage-DYuIdJiH.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-C30HsQfZ.js → McpQueryPage-C9C4mPC0.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-C30HsQfZ.js.map → McpQueryPage-C9C4mPC0.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-CWHKhx8n.js +2 -0
- package/dashboard/dist/assets/McpRunDetailPage-CWHKhx8n.js.map +1 -0
- package/dashboard/dist/assets/{McpRunsPage-BHZHCwMM.js → McpRunsPage-DMamEjjr.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-BHZHCwMM.js.map → McpRunsPage-DMamEjjr.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-BQNMfY0m.js → NamespacePill-B3YOa1xo.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-BQNMfY0m.js.map → NamespacePill-B3YOa1xo.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-CVPDugQz.js +2 -0
- package/dashboard/dist/assets/OperationsPage-CVPDugQz.js.map +1 -0
- package/dashboard/dist/assets/{OperatorDashboard-BGJrxLMU.js → OperatorDashboard-CK-4GREp.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-BGJrxLMU.js.map → OperatorDashboard-CK-4GREp.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-BIY1YvKR.js → PageHeader-poyTWcUA.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-BIY1YvKR.js.map → PageHeader-poyTWcUA.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-TUyglVW0.js → PageHeaderWithStats-CAmspbl9.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-TUyglVW0.js.map → PageHeaderWithStats-CAmspbl9.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-Bcp3GKFo.js → ProcessDetailPage-BBCwv1bQ.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-Bcp3GKFo.js.map → ProcessDetailPage-BBCwv1bQ.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-DPrvuLoU.js → ProcessesListPage-m70xmTZx.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-DPrvuLoU.js.map → ProcessesListPage-m70xmTZx.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-l865Cx3n.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-l865Cx3n.js.map +1 -0
- package/dashboard/dist/assets/RolePill-C0SWz9X7.js +2 -0
- package/dashboard/dist/assets/RolePill-C0SWz9X7.js.map +1 -0
- package/dashboard/dist/assets/RolesPage-Wzfjz7Wc.js +2 -0
- package/dashboard/dist/assets/RolesPage-Wzfjz7Wc.js.map +1 -0
- package/dashboard/dist/assets/RunAsSelector-DlnIolTf.js +2 -0
- package/dashboard/dist/assets/{RunAsSelector-Z2Yunqdf.js.map → RunAsSelector-DlnIolTf.js.map} +1 -1
- package/dashboard/dist/assets/{SlidePanel-DK4sWXmp.js → SlidePanel-Bm9B6zIA.js} +2 -2
- package/dashboard/dist/assets/{SlidePanel-DK4sWXmp.js.map → SlidePanel-Bm9B6zIA.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-CeLsnCIS.js → StickyPagination-BHF5fTt5.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-CeLsnCIS.js.map → StickyPagination-BHF5fTt5.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-DWs7XkmH.js → StreamMessageDetail-Dgp1lzay.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-DWs7XkmH.js.map → StreamMessageDetail-Dgp1lzay.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-Dxl9a51q.js → SwimlaneTimeline-BlptZ8lr.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-Dxl9a51q.js.map → SwimlaneTimeline-BlptZ8lr.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-CK-JhBG2.js → TagInput-CM3AV5Ky.js} +2 -2
- package/dashboard/dist/assets/{TagInput-CK-JhBG2.js.map → TagInput-CM3AV5Ky.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-DVr31zDE.js → TaskDetailPage-f5K9dVPG.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-DVr31zDE.js.map → TaskDetailPage-f5K9dVPG.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-C_Vo2Clc.js → TaskQueuePill-BZYmqdcv.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-C_Vo2Clc.js.map → TaskQueuePill-BZYmqdcv.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DNGp5lfi.js → TasksListPage-BPyPG6UY.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DNGp5lfi.js.map → TasksListPage-BPyPG6UY.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-j2Q4w4lY.js → TimeAgo-CBaGbueK.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-j2Q4w4lY.js.map → TimeAgo-CBaGbueK.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-BLEfob2C.js → TimestampCell-Z5kDrR9U.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-BLEfob2C.js.map → TimestampCell-Z5kDrR9U.js.map} +1 -1
- package/dashboard/dist/assets/ToolPill-D_-m3lUO.js +2 -0
- package/dashboard/dist/assets/{ToolPill-BCi101Jh.js.map → ToolPill-D_-m3lUO.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-ZJZT_mDP.js → ToolTestPanel-DFrP_aMx.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-ZJZT_mDP.js.map → ToolTestPanel-DFrP_aMx.js.map} +1 -1
- package/dashboard/dist/assets/TopicDetailPage-Cj9qXnTW.js +9 -0
- package/dashboard/dist/assets/TopicDetailPage-Cj9qXnTW.js.map +1 -0
- package/dashboard/dist/assets/{TopicsPage-Ctb_85Tw.js → TopicsPage-Dyq6niOv.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-Ctb_85Tw.js.map → TopicsPage-Dyq6niOv.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-CkFpUqVK.js → UserName-DOX2P-8M.js} +2 -2
- package/dashboard/dist/assets/{UserName-CkFpUqVK.js.map → UserName-DOX2P-8M.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-Cb_dSIoq.js +2 -0
- package/dashboard/dist/assets/WorkflowExecutionPage-Cb_dSIoq.js.map +1 -0
- package/dashboard/dist/assets/WorkflowPill-CyYYPB7b.js +2 -0
- package/dashboard/dist/assets/{WorkflowPill-DHxrAkp3.js.map → WorkflowPill-CyYYPB7b.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-DOyZbqzf.js → WorkflowsDashboard-DJpMO5IY.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-DOyZbqzf.js.map → WorkflowsDashboard-DJpMO5IY.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CRyHvUBx.js → WorkflowsOverview-CCC_KaXz.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CRyHvUBx.js.map → WorkflowsOverview-CCC_KaXz.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DRBmQXY6.js → YamlWorkflowDetailPage-C3ntUyoZ.js} +11 -11
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DRBmQXY6.js.map → YamlWorkflowDetailPage-C3ntUyoZ.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-HnI-9eBW.js → YamlWorkflowsPage-ju-9JDXg.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-HnI-9eBW.js.map → YamlWorkflowsPage-ju-9JDXg.js.map} +1 -1
- package/dashboard/dist/assets/{agents-BA2CvQO9.js → agents-gs9fea8r.js} +2 -2
- package/dashboard/dist/assets/{agents-BA2CvQO9.js.map → agents-gs9fea8r.js.map} +1 -1
- package/dashboard/dist/assets/bots-DYxAHrob.js +2 -0
- package/dashboard/dist/assets/bots-DYxAHrob.js.map +1 -0
- package/dashboard/dist/assets/{capabilities-BYBKLRRI.js → capabilities-BJoxz6DJ.js} +2 -2
- package/dashboard/dist/assets/{capabilities-BYBKLRRI.js.map → capabilities-BJoxz6DJ.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-H0Ljhb-1.js → controlplane-BeDZgDDf.js} +2 -2
- package/dashboard/dist/assets/{controlplane-H0Ljhb-1.js.map → controlplane-BeDZgDDf.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-CZ4fl0IE.js +2 -0
- package/dashboard/dist/assets/escalation-columns-CZ4fl0IE.js.map +1 -0
- package/dashboard/dist/assets/index-BMM3Xug7.js +2 -0
- package/dashboard/dist/assets/index-BMM3Xug7.js.map +1 -0
- package/dashboard/dist/assets/{index-BXrm76Hm.js → index-BMXkxgnK.js} +2 -2
- package/dashboard/dist/assets/{index-BXrm76Hm.js.map → index-BMXkxgnK.js.map} +1 -1
- package/dashboard/dist/assets/{index-CWkAN6wc.js → index-Bj1Mjjiv.js} +2 -2
- package/dashboard/dist/assets/{index-CWkAN6wc.js.map → index-Bj1Mjjiv.js.map} +1 -1
- package/dashboard/dist/assets/index-BqVb13tE.js +5 -0
- package/dashboard/dist/assets/index-BqVb13tE.js.map +1 -0
- package/dashboard/dist/assets/{index-YWvrMcqh.js → index-CGrtjUdj.js} +2 -2
- package/dashboard/dist/assets/{index-YWvrMcqh.js.map → index-CGrtjUdj.js.map} +1 -1
- package/dashboard/dist/assets/{index-DSjih1iX.js → index-CQ8cd4JZ.js} +2 -2
- package/dashboard/dist/assets/{index-DSjih1iX.js.map → index-CQ8cd4JZ.js.map} +1 -1
- package/dashboard/dist/assets/{index-D1Q8WfGN.js → index-CruL35yU.js} +2 -2
- package/dashboard/dist/assets/{index-D1Q8WfGN.js.map → index-CruL35yU.js.map} +1 -1
- package/dashboard/dist/assets/index-DAM1Dm1F.js +2 -0
- package/dashboard/dist/assets/index-DAM1Dm1F.js.map +1 -0
- package/dashboard/dist/assets/{index-CP6D_Gtw.js → index-DZ7PWRg1.js} +2 -2
- package/dashboard/dist/assets/{index-CP6D_Gtw.js.map → index-DZ7PWRg1.js.map} +1 -1
- package/dashboard/dist/assets/{index-BI-9M7z3.js → index-DfbrPOP3.js} +2 -2
- package/dashboard/dist/assets/{index-BI-9M7z3.js.map → index-DfbrPOP3.js.map} +1 -1
- package/dashboard/dist/assets/index-DwuM7Fka.js +5 -0
- package/dashboard/dist/assets/index-DwuM7Fka.js.map +1 -0
- package/dashboard/dist/assets/index-DycfrrTK.js +63 -0
- package/dashboard/dist/assets/index-DycfrrTK.js.map +1 -0
- package/dashboard/dist/assets/{index-Dx6-4esw.js → index-tbvwCdvQ.js} +2 -2
- package/dashboard/dist/assets/{index-Dx6-4esw.js.map → index-tbvwCdvQ.js.map} +1 -1
- package/dashboard/dist/assets/index-zP2ncT4m.css +1 -0
- package/dashboard/dist/assets/{knowledge-BVbElRHf.js → knowledge-DZnI1Fdk.js} +2 -2
- package/dashboard/dist/assets/{knowledge-BVbElRHf.js.map → knowledge-DZnI1Fdk.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CstDdowJ.js → mcp-BB_m6UyI.js} +2 -2
- package/dashboard/dist/assets/{mcp-CstDdowJ.js.map → mcp-BB_m6UyI.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-DHUs0N8H.js → mcp-query-BTSWn0de.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-DHUs0N8H.js.map → mcp-query-BTSWn0de.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-BDmAzE3U.js → pipelines-CvGQLyUz.js} +2 -2
- package/dashboard/dist/assets/{pipelines-BDmAzE3U.js.map → pipelines-CvGQLyUz.js.map} +1 -1
- package/dashboard/dist/assets/task-queues-7-sHKPsN.js +2 -0
- package/dashboard/dist/assets/task-queues-7-sHKPsN.js.map +1 -0
- package/dashboard/dist/assets/{tasks--qZocuG-.js → tasks-BbgOhCVm.js} +2 -2
- package/dashboard/dist/assets/{tasks--qZocuG-.js.map → tasks-BbgOhCVm.js.map} +1 -1
- package/dashboard/dist/assets/{topics-P3jkjMsx.js → topics-D9usuYHv.js} +2 -2
- package/dashboard/dist/assets/{topics-P3jkjMsx.js.map → topics-D9usuYHv.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-CqH8Pasj.js → useEventHooks-DxnqYVPg.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-CqH8Pasj.js.map → useEventHooks-DxnqYVPg.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-aI3tPfo3.js → useNamespace-CBAn0o5z.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-aI3tPfo3.js.map → useNamespace-CBAn0o5z.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-CPVyuW8i.js → useYamlActivityEvents-blOlKGDO.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-CPVyuW8i.js.map → useYamlActivityEvents-blOlKGDO.js.map} +1 -1
- package/dashboard/dist/assets/{users-BrcmIH7O.js → users-CYVJHgNM.js} +2 -2
- package/dashboard/dist/assets/{users-BrcmIH7O.js.map → users-CYVJHgNM.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-Qk9qLjF_.js → vendor-icons-B98-XxNc.js} +147 -132
- package/dashboard/dist/assets/vendor-icons-B98-XxNc.js.map +1 -0
- package/dashboard/dist/assets/{workflows-DPhX5L5C.js → workflows-csQAJF0G.js} +2 -2
- package/dashboard/dist/assets/{workflows-DPhX5L5C.js.map → workflows-csQAJF0G.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-KZI2cufF.js → yaml-workflows-CctsTDby.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-KZI2cufF.js.map → yaml-workflows-CctsTDby.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +16 -1
- package/docs/api/http/roles.md +4 -0
- package/docs/api/http/users.md +39 -0
- package/docs/api/sdk/roles.md +5 -0
- package/docs/api/sdk/users.md +39 -0
- package/docs/hitl/pinned-views.md +45 -0
- package/docs/hitl/x-lt-list-schema.md +37 -2
- package/docs/hitl/x-lt-show-if.md +11 -0
- package/docs/hitl/x-lt-widget.md +22 -0
- package/docs/hitl-guide.md +9 -5
- package/docs/schema-enforcement.md +149 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/AgentConfigPage-CW2udbSO.js +0 -16
- package/dashboard/dist/assets/AgentConfigPage-CW2udbSO.js.map +0 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-Dx51E6s_.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-Dx51E6s_.js.map +0 -1
- package/dashboard/dist/assets/BotPicker-KebTxTDi.js +0 -2
- package/dashboard/dist/assets/BotPicker-KebTxTDi.js.map +0 -1
- package/dashboard/dist/assets/CountdownTimer-DFqEYmiF.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-DFqEYmiF.js.map +0 -1
- package/dashboard/dist/assets/CustomDurationPicker-BxyjEX27.js.map +0 -1
- package/dashboard/dist/assets/GraphInvokePage-Cf9c_LJ-.js.map +0 -1
- package/dashboard/dist/assets/HomePage-BdCtKXAV.js +0 -2
- package/dashboard/dist/assets/HomePage-BdCtKXAV.js.map +0 -1
- package/dashboard/dist/assets/ListToolbar-CDlol8XJ.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-18d7Iqfy.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-18d7Iqfy.js.map +0 -1
- package/dashboard/dist/assets/OperationsPage-GMojwL3q.js +0 -2
- package/dashboard/dist/assets/OperationsPage-GMojwL3q.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-Ws3umQKQ.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-Ws3umQKQ.js.map +0 -1
- package/dashboard/dist/assets/RolePill-4LO_CuO3.js +0 -2
- package/dashboard/dist/assets/RolePill-4LO_CuO3.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-D3NNU92h.js +0 -2
- package/dashboard/dist/assets/RolesPage-D3NNU92h.js.map +0 -1
- package/dashboard/dist/assets/RunAsSelector-Z2Yunqdf.js +0 -2
- package/dashboard/dist/assets/ToolPill-BCi101Jh.js +0 -2
- package/dashboard/dist/assets/TopicDetailPage-ynUkO1A8.js +0 -9
- package/dashboard/dist/assets/TopicDetailPage-ynUkO1A8.js.map +0 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-CSw2aQJC.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-CSw2aQJC.js.map +0 -1
- package/dashboard/dist/assets/WorkflowPill-DHxrAkp3.js +0 -2
- package/dashboard/dist/assets/bots-DHHnoo8x.js +0 -2
- package/dashboard/dist/assets/bots-DHHnoo8x.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-BV3Ztn7D.js +0 -2
- package/dashboard/dist/assets/escalation-columns-BV3Ztn7D.js.map +0 -1
- package/dashboard/dist/assets/index-B4FaugP1.js +0 -2
- package/dashboard/dist/assets/index-B4FaugP1.js.map +0 -1
- package/dashboard/dist/assets/index-BeFjLCIQ.js +0 -5
- package/dashboard/dist/assets/index-BeFjLCIQ.js.map +0 -1
- package/dashboard/dist/assets/index-BrEcQQ1p.css +0 -1
- package/dashboard/dist/assets/index-CVMA5PVQ.js +0 -2
- package/dashboard/dist/assets/index-CVMA5PVQ.js.map +0 -1
- package/dashboard/dist/assets/index-O2wWcCLo.js +0 -5
- package/dashboard/dist/assets/index-O2wWcCLo.js.map +0 -1
- package/dashboard/dist/assets/index-iX8HLLB9.js +0 -63
- package/dashboard/dist/assets/index-iX8HLLB9.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-Qk9qLjF_.js.map +0 -1
package/README.md
CHANGED
|
@@ -231,6 +231,7 @@ All modes share PostgreSQL and scale independently. See [Cloud Deployment](https
|
|
|
231
231
|
| [Compiler](https://github.com/hotmeshio/long-tail/blob/main/docs/compiler.md) | `ltc compile` — durable TypeScript to YAML DAGs |
|
|
232
232
|
| [CLI](https://github.com/hotmeshio/long-tail/blob/main/docs/cli.md) | `ltc` — terminal access to workflows, escalations, knowledge, MCP |
|
|
233
233
|
| [Escalation Strategies](https://github.com/hotmeshio/long-tail/blob/main/docs/escalation-strategies.md) | Default, MCP triage, custom handlers |
|
|
234
|
+
| [Schema Enforcement](https://github.com/hotmeshio/long-tail/blob/main/docs/schema-enforcement.md) | form_schema as an enforced API contract on every resolve surface |
|
|
234
235
|
| [Faceted Routing](https://github.com/hotmeshio/long-tail/blob/main/docs/faceted-routing.md) | Query and atomically claim the queue by facets; dispatcher pattern |
|
|
235
236
|
| [SDK](https://github.com/hotmeshio/long-tail/blob/main/docs/sdk.md) | Embedded usage, `createClient`, event subscriptions |
|
|
236
237
|
| [Architecture](https://github.com/hotmeshio/long-tail/blob/main/docs/architecture.md) | Project structure, conventions, discovery |
|
|
@@ -4,11 +4,15 @@ import type { LTApiResult, LTApiAuth } from '../types/sdk';
|
|
|
4
4
|
*
|
|
5
5
|
* @param input.limit — maximum number of bots to return (default 50)
|
|
6
6
|
* @param input.offset — number of bots to skip for pagination (default 0)
|
|
7
|
+
* @param input.status — filter by account status
|
|
8
|
+
* @param input.search — free-text match on display name, external id, or description
|
|
7
9
|
* @returns `{ status: 200, data: Bot[] }` paginated list of bots
|
|
8
10
|
*/
|
|
9
11
|
export declare function listBots(input: {
|
|
10
12
|
limit?: number;
|
|
11
13
|
offset?: number;
|
|
14
|
+
status?: string;
|
|
15
|
+
search?: string;
|
|
12
16
|
}): Promise<LTApiResult>;
|
|
13
17
|
/**
|
|
14
18
|
* Retrieve a single bot account by ID.
|
|
@@ -51,13 +51,15 @@ const user_1 = require("../services/user");
|
|
|
51
51
|
*
|
|
52
52
|
* @param input.limit — maximum number of bots to return (default 50)
|
|
53
53
|
* @param input.offset — number of bots to skip for pagination (default 0)
|
|
54
|
+
* @param input.status — filter by account status
|
|
55
|
+
* @param input.search — free-text match on display name, external id, or description
|
|
54
56
|
* @returns `{ status: 200, data: Bot[] }` paginated list of bots
|
|
55
57
|
*/
|
|
56
58
|
async function listBots(input) {
|
|
57
59
|
try {
|
|
58
60
|
const limit = input.limit ?? 50;
|
|
59
61
|
const offset = input.offset ?? 0;
|
|
60
|
-
const result = await iam.listBots(limit, offset);
|
|
62
|
+
const result = await iam.listBots(limit, offset, { status: input.status, search: input.search });
|
|
61
63
|
return { status: 200, data: result };
|
|
62
64
|
}
|
|
63
65
|
catch (err) {
|
|
@@ -38,6 +38,9 @@ exports.claimByMetadata = claimByMetadata;
|
|
|
38
38
|
exports.resolveByMetadata = resolveByMetadata;
|
|
39
39
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
40
40
|
const helpers_1 = require("./helpers");
|
|
41
|
+
const resolver_validation_1 = require("../../services/escalation/resolver-validation");
|
|
42
|
+
const enforcement_cache_1 = require("../../services/role/enforcement-cache");
|
|
43
|
+
const resolve_1 = require("./resolve");
|
|
41
44
|
/**
|
|
42
45
|
* Find escalations by a metadata key-value pair.
|
|
43
46
|
*
|
|
@@ -130,7 +133,24 @@ async function resolveByMetadata(input, auth) {
|
|
|
130
133
|
const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
|
|
131
134
|
const writeAllRoles = writeScope.global ? null : writeScope.allRoles;
|
|
132
135
|
const writeSelfRoles = writeScope.global ? null : writeScope.selfRoles;
|
|
133
|
-
|
|
136
|
+
// Schema enforcement rides the atomic statement: with the cached enforcing
|
|
137
|
+
// set empty (the common case) the single-call behavior is unchanged. When
|
|
138
|
+
// the picked row's role enforces, the statement returns the row WITHOUT
|
|
139
|
+
// writing; the payload validates here, then a second asserted pass claims
|
|
140
|
+
// + resolves the same row with pending re-checked inside the statement.
|
|
141
|
+
const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
|
|
142
|
+
let result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles, enforcing.size > 0 ? [...enforcing] : null);
|
|
143
|
+
if (result.outcome === 'validation_required' && result.row) {
|
|
144
|
+
const report = await (0, resolver_validation_1.checkResolverPayload)(result.row, input.resolverPayload);
|
|
145
|
+
if (report)
|
|
146
|
+
return (0, resolve_1.validationFailure)(report);
|
|
147
|
+
result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles, null, result.row.id);
|
|
148
|
+
// The asserted row left pending between the two passes — a concurrent
|
|
149
|
+
// resolution won the row; surface it as the conflict it is.
|
|
150
|
+
if (result.outcome === 'not_found') {
|
|
151
|
+
return { status: 409, error: 'A concurrent resolution is already in progress for this escalation' };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
134
154
|
if (result.outcome === 'not_found') {
|
|
135
155
|
return { status: 404, error: 'No pending escalation found for this metadata, or insufficient role permissions' };
|
|
136
156
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ResolverSchemaViolationReport } from '../../services/escalation/resolver-validation';
|
|
1
2
|
import type { LTApiResult, LTApiAuth } from '../../types/sdk';
|
|
2
3
|
/**
|
|
3
4
|
* Resolve a pending escalation with a human-provided payload.
|
|
@@ -72,3 +73,10 @@ export declare function resolveAllOrNone(input: {
|
|
|
72
73
|
metadata?: Record<string, any>;
|
|
73
74
|
requireClaimed?: boolean;
|
|
74
75
|
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
76
|
+
/**
|
|
77
|
+
* The canonical 422 for a schema-validation rejection. The full body rides in
|
|
78
|
+
* `data` (routes serialize `data ?? { error }`), so every transport — HTTP,
|
|
79
|
+
* SDK, CLI, dashboard — receives the same shape: error, code, violations,
|
|
80
|
+
* role, schemaVersion. `code` also sits on the envelope for SDK branching.
|
|
81
|
+
*/
|
|
82
|
+
export declare function validationFailure(report: ResolverSchemaViolationReport): LTApiResult;
|
|
@@ -37,6 +37,7 @@ exports.resolveEscalation = resolveEscalation;
|
|
|
37
37
|
exports.resolveBySignalKey = resolveBySignalKey;
|
|
38
38
|
exports.resolveByIds = resolveByIds;
|
|
39
39
|
exports.resolveAllOrNone = resolveAllOrNone;
|
|
40
|
+
exports.validationFailure = validationFailure;
|
|
40
41
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
41
42
|
const taskService = __importStar(require("../../services/task"));
|
|
42
43
|
const escalation_strategy_1 = require("../../services/escalation-strategy");
|
|
@@ -45,6 +46,8 @@ const deployer_1 = require("../../services/yaml-workflow/deployer");
|
|
|
45
46
|
const workers_1 = require("../../workers");
|
|
46
47
|
const defaults_1 = require("../../modules/defaults");
|
|
47
48
|
const helpers_1 = require("./helpers");
|
|
49
|
+
const resolver_validation_1 = require("../../services/escalation/resolver-validation");
|
|
50
|
+
const enforcement_cache_1 = require("../../services/role/enforcement-cache");
|
|
48
51
|
// ── Orchestrator ─────────────────────────────────────────────────────────
|
|
49
52
|
/**
|
|
50
53
|
* Resolve a pending escalation with a human-provided payload.
|
|
@@ -90,6 +93,13 @@ async function resolveEscalation(input, auth) {
|
|
|
90
93
|
const stale = (0, helpers_1.assertLiveClaimant)(auth.userId, escalation);
|
|
91
94
|
if (stale)
|
|
92
95
|
return stale;
|
|
96
|
+
// Schema enforcement (enforce_schema roles): validate the payload against
|
|
97
|
+
// the escalation's resolved form schema BEFORE any resolution path runs.
|
|
98
|
+
// Costs nothing when the role does not enforce (cached role set); the
|
|
99
|
+
// task-fallback envelope read only happens for enforcing roles.
|
|
100
|
+
const violation = await (0, resolver_validation_1.checkResolverPayload)(escalation, resolverPayload, () => reconstructEnvelope(escalation));
|
|
101
|
+
if (violation)
|
|
102
|
+
return validationFailure(violation);
|
|
93
103
|
// The resolver payload is stored exactly as submitted — its shape is the
|
|
94
104
|
// workflow's contract, formed by the caller (the React app maps the form to
|
|
95
105
|
// the payload via x-lt-bind before submitting). No server-side transform.
|
|
@@ -161,6 +171,11 @@ async function resolveBySignalKey(input, auth) {
|
|
|
161
171
|
const denied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
|
|
162
172
|
if (denied)
|
|
163
173
|
return { status: 404, error: 'Escalation not found' };
|
|
174
|
+
// Schema enforcement — the row (envelope included) is already loaded, so
|
|
175
|
+
// this costs zero extra reads beyond the cached schema.
|
|
176
|
+
const violation = await (0, resolver_validation_1.checkResolverPayload)(escalation, resolverPayload);
|
|
177
|
+
if (violation)
|
|
178
|
+
return validationFailure(violation);
|
|
164
179
|
// The payload is delivered as the signal the parked workflow's condition()
|
|
165
180
|
// receives — stored exactly as submitted (the caller formed its shape).
|
|
166
181
|
return resolveViaSignalKey(escalation, resolverPayload, metadata);
|
|
@@ -195,17 +210,39 @@ async function resolveByIds(input, auth) {
|
|
|
195
210
|
// resolve any item in the role; write_self roles may resolve only items assigned
|
|
196
211
|
// to the caller (same rule single-resolve applies via assertWriteAccess). Global
|
|
197
212
|
// bypasses. A missing id or any out-of-scope item → 404 (non-disclosure), and
|
|
198
|
-
// nothing is resolved.
|
|
213
|
+
// nothing is resolved. The scope-row read is shared with the enforcement
|
|
214
|
+
// gate below — one indexed read serves both.
|
|
199
215
|
const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
|
|
216
|
+
const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
|
|
217
|
+
let scopeRows = null;
|
|
200
218
|
if (!writeScope.global) {
|
|
201
219
|
const allSet = new Set(writeScope.allRoles);
|
|
202
220
|
const selfSet = new Set(writeScope.selfRoles);
|
|
203
|
-
|
|
221
|
+
scopeRows = await escalationService.getEscalationScopeRows(ids);
|
|
204
222
|
const writable = (r) => allSet.has(r.role) || (selfSet.has(r.role) && r.assigned_to === auth.userId);
|
|
205
|
-
if (
|
|
223
|
+
if (scopeRows.length !== ids.length || !scopeRows.every(writable)) {
|
|
206
224
|
return { status: 404, error: 'One or more escalations not found' };
|
|
207
225
|
}
|
|
208
226
|
}
|
|
227
|
+
// Schema enforcement (enforce_schema roles): with the cached enforcing set
|
|
228
|
+
// empty this path adds zero reads. Otherwise the scope rows identify which
|
|
229
|
+
// targets enforce, and only those rows load in full for validation — the
|
|
230
|
+
// one payload is validated against each enforcing row's own context.
|
|
231
|
+
if (enforcing.size > 0) {
|
|
232
|
+
const rows = scopeRows ?? await escalationService.getEscalationScopeRows(ids);
|
|
233
|
+
const involved = rows.filter((r) => enforcing.has(r.role)).map((r) => r.id);
|
|
234
|
+
if (involved.length > 0) {
|
|
235
|
+
const fullRows = await escalationService.getEscalationsByIds(involved);
|
|
236
|
+
const failed = [];
|
|
237
|
+
for (const row of fullRows) {
|
|
238
|
+
const report = await (0, resolver_validation_1.checkResolverPayload)(row, resolverPayload);
|
|
239
|
+
if (report)
|
|
240
|
+
failed.push({ id: row.id, report });
|
|
241
|
+
}
|
|
242
|
+
if (failed.length > 0)
|
|
243
|
+
return bulkValidationFailure(failed);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
209
246
|
const resolved = await escalationService.resolveEscalationsByIds(ids, resolverPayload, metadata);
|
|
210
247
|
return { status: 200, data: { resolved: resolved.length, escalationIds: resolved.map((e) => e.id) } };
|
|
211
248
|
}
|
|
@@ -286,6 +323,24 @@ async function resolveAllOrNone(input, auth) {
|
|
|
286
323
|
const error = 'One or more escalations require the single-resolve path (legacy signal routing)';
|
|
287
324
|
return { status: 409, error, data: { error, failedIds: failed.map((f) => f.id), failed } };
|
|
288
325
|
}
|
|
326
|
+
// Schema enforcement (enforce_schema roles): every item validates against
|
|
327
|
+
// its own row's context before ANYTHING resolves — all-or-none extends to
|
|
328
|
+
// validation. Rows are already in hand, so the only added reads are the
|
|
329
|
+
// cached schemas of enforcing roles.
|
|
330
|
+
const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
|
|
331
|
+
if (enforcing.size > 0) {
|
|
332
|
+
const failed = [];
|
|
333
|
+
for (const item of items) {
|
|
334
|
+
const row = rowById.get(item.id);
|
|
335
|
+
if (!row || !row.role || !enforcing.has(row.role))
|
|
336
|
+
continue;
|
|
337
|
+
const report = await (0, resolver_validation_1.checkResolverPayload)(row, item.resolverPayload);
|
|
338
|
+
if (report)
|
|
339
|
+
failed.push({ id: item.id, report });
|
|
340
|
+
}
|
|
341
|
+
if (failed.length > 0)
|
|
342
|
+
return bulkValidationFailure(failed);
|
|
343
|
+
}
|
|
289
344
|
// Per-row password redaction against each row's own form schema — the
|
|
290
345
|
// payload enters the signal store and the audit record; plaintext never.
|
|
291
346
|
const serviceItems = await Promise.all(items.map(async (item) => ({
|
|
@@ -448,6 +503,30 @@ function signaledResult(escalation, workflowId) {
|
|
|
448
503
|
data: { signaled: true, escalationId: escalation.id, workflowId },
|
|
449
504
|
};
|
|
450
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* The canonical 422 for a schema-validation rejection. The full body rides in
|
|
508
|
+
* `data` (routes serialize `data ?? { error }`), so every transport — HTTP,
|
|
509
|
+
* SDK, CLI, dashboard — receives the same shape: error, code, violations,
|
|
510
|
+
* role, schemaVersion. `code` also sits on the envelope for SDK branching.
|
|
511
|
+
*/
|
|
512
|
+
function validationFailure(report) {
|
|
513
|
+
const body = (0, resolver_validation_1.toValidationErrorBody)(report);
|
|
514
|
+
return { status: 422, error: body.error, code: body.code, data: body };
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* The bulk-surface 422: violations from every failing row, each tagged with
|
|
518
|
+
* its escalationId so callers can attribute them. Role/schemaVersion echo the
|
|
519
|
+
* first failing report (bulk resolves target homogeneous role groups).
|
|
520
|
+
*/
|
|
521
|
+
function bulkValidationFailure(failed) {
|
|
522
|
+
const violations = failed.flatMap(({ id, report }) => report.violations.map((v) => ({ ...v, escalationId: id })));
|
|
523
|
+
const body = {
|
|
524
|
+
...(0, resolver_validation_1.toValidationErrorBody)(failed[0].report),
|
|
525
|
+
error: `resolverPayload failed schema validation for ${failed.length} escalation${failed.length === 1 ? '' : 's'} (${violations.length} violation${violations.length === 1 ? '' : 's'})`,
|
|
526
|
+
violations,
|
|
527
|
+
};
|
|
528
|
+
return { status: 422, error: body.error, code: body.code, data: body };
|
|
529
|
+
}
|
|
451
530
|
/** Replace password fields with ephemeral tokens so plaintext never enters the signal store. */
|
|
452
531
|
async function redactPasswords(payload, formSchema) {
|
|
453
532
|
if (!formSchema?.properties)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LTApiResult, LTApiAuth } from '../types/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* The caller's preferences document — a generic per-user JSON store for
|
|
4
|
+
* presentation state (pinned views are the first tenant). Always an object;
|
|
5
|
+
* an unset store reads as {}.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getMyPreferences(auth: LTApiAuth): Promise<LTApiResult>;
|
|
8
|
+
/**
|
|
9
|
+
* Shallow-merge a patch into the caller's preferences. Top-level keys
|
|
10
|
+
* overwrite; a `null` value deletes its key. The patch and the merged
|
|
11
|
+
* document are both size-capped; the merge itself is a single guarded
|
|
12
|
+
* UPDATE (no read-then-write).
|
|
13
|
+
*/
|
|
14
|
+
export declare function patchMyPreferences(input: {
|
|
15
|
+
patch: unknown;
|
|
16
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
package/build/api/me.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getMyPreferences = getMyPreferences;
|
|
37
|
+
exports.patchMyPreferences = patchMyPreferences;
|
|
38
|
+
const userService = __importStar(require("../services/user"));
|
|
39
|
+
// ── Self-service surface (/api/me) ─────────────────────────────────────────
|
|
40
|
+
// Operations on the AUTHENTICATED user only — no ids, no admin gates.
|
|
41
|
+
/**
|
|
42
|
+
* The caller's preferences document — a generic per-user JSON store for
|
|
43
|
+
* presentation state (pinned views are the first tenant). Always an object;
|
|
44
|
+
* an unset store reads as {}.
|
|
45
|
+
*/
|
|
46
|
+
async function getMyPreferences(auth) {
|
|
47
|
+
try {
|
|
48
|
+
const preferences = await userService.getPreferences(auth.userId);
|
|
49
|
+
return { status: 200, data: { preferences } };
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
return { status: 500, error: err.message };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Shallow-merge a patch into the caller's preferences. Top-level keys
|
|
57
|
+
* overwrite; a `null` value deletes its key. The patch and the merged
|
|
58
|
+
* document are both size-capped; the merge itself is a single guarded
|
|
59
|
+
* UPDATE (no read-then-write).
|
|
60
|
+
*/
|
|
61
|
+
async function patchMyPreferences(input, auth) {
|
|
62
|
+
try {
|
|
63
|
+
const { patch } = input;
|
|
64
|
+
if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {
|
|
65
|
+
return { status: 400, error: 'Body must be a JSON object of preference keys' };
|
|
66
|
+
}
|
|
67
|
+
if (JSON.stringify(patch).length > userService.PREFERENCES_MAX_BYTES) {
|
|
68
|
+
return { status: 413, error: `Preferences patch exceeds ${userService.PREFERENCES_MAX_BYTES} bytes` };
|
|
69
|
+
}
|
|
70
|
+
const preferences = await userService.patchPreferences(auth.userId, patch);
|
|
71
|
+
if (preferences === null) {
|
|
72
|
+
return { status: 413, error: `Merged preferences would exceed ${userService.PREFERENCES_MAX_BYTES} bytes` };
|
|
73
|
+
}
|
|
74
|
+
return { status: 200, data: { preferences } };
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
return { status: 500, error: err.message };
|
|
78
|
+
}
|
|
79
|
+
}
|
package/build/api/roles.d.ts
CHANGED
|
@@ -88,6 +88,12 @@ export declare function updateRole(input: {
|
|
|
88
88
|
metadata_schema?: Record<string, any> | null;
|
|
89
89
|
/** The escalation LIST schema — rich formatting for this role's list page. */
|
|
90
90
|
list_schema?: Record<string, any> | null;
|
|
91
|
+
/** Pinned-view seeds for the role's members: [{ label, url, badge? }] or null to clear. */
|
|
92
|
+
default_pins?: {
|
|
93
|
+
label: string;
|
|
94
|
+
url: string;
|
|
95
|
+
badge?: boolean;
|
|
96
|
+
}[] | null;
|
|
91
97
|
properties?: Record<string, any> | null;
|
|
92
98
|
ops_visible?: boolean;
|
|
93
99
|
parent_role?: string | null;
|
|
@@ -98,6 +104,8 @@ export declare function updateRole(input: {
|
|
|
98
104
|
priority_threshold_minutes?: number | null;
|
|
99
105
|
/** Escalation metadata key holding the age origin (ISO 8601 UTC timestamp). Falls back to created_at. */
|
|
100
106
|
priority_facet?: string | null;
|
|
107
|
+
/** Turn server-side resolver schema validation on/off for this role. */
|
|
108
|
+
enforce_schema?: boolean;
|
|
101
109
|
/** Replace the upstream-input set (omitted = preserve; null or [] = clear). */
|
|
102
110
|
upstream_roles?: string[] | null;
|
|
103
111
|
/** Recorded on the schema snapshot when this update changes a schema field. */
|
package/build/api/roles.js
CHANGED
|
@@ -225,9 +225,21 @@ async function updateRole(input) {
|
|
|
225
225
|
if (input.ops_visible !== undefined && typeof input.ops_visible !== 'boolean') {
|
|
226
226
|
return { status: 400, error: 'ops_visible must be a boolean' };
|
|
227
227
|
}
|
|
228
|
+
if (input.enforce_schema !== undefined && typeof input.enforce_schema !== 'boolean') {
|
|
229
|
+
return { status: 400, error: 'enforce_schema must be a boolean' };
|
|
230
|
+
}
|
|
228
231
|
if (input.parent_role != null && input.parent_role === input.role) {
|
|
229
232
|
return { status: 400, error: 'parent_role must reference a different role' };
|
|
230
233
|
}
|
|
234
|
+
if (input.default_pins != null) {
|
|
235
|
+
const pinsValid = Array.isArray(input.default_pins) && input.default_pins.every((p) => p && typeof p === 'object'
|
|
236
|
+
&& typeof p.label === 'string' && p.label.trim() !== ''
|
|
237
|
+
&& typeof p.url === 'string' && p.url.startsWith('/')
|
|
238
|
+
&& (p.badge === undefined || typeof p.badge === 'boolean'));
|
|
239
|
+
if (!pinsValid) {
|
|
240
|
+
return { status: 400, error: 'default_pins must be [{ label, url, badge? }] with dashboard-relative urls' };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
231
243
|
if (input.upstream_roles != null) {
|
|
232
244
|
if (!Array.isArray(input.upstream_roles) || input.upstream_roles.some((u) => typeof u !== 'string' || !u.trim())) {
|
|
233
245
|
return { status: 400, error: 'upstream_roles must be an array of role names' };
|
|
@@ -247,6 +259,7 @@ async function updateRole(input) {
|
|
|
247
259
|
form_schema: input.form_schema,
|
|
248
260
|
metadata_schema: input.metadata_schema,
|
|
249
261
|
list_schema: input.list_schema,
|
|
262
|
+
default_pins: input.default_pins,
|
|
250
263
|
properties: input.properties,
|
|
251
264
|
ops_visible: input.ops_visible,
|
|
252
265
|
parent_role: input.parent_role,
|
|
@@ -255,6 +268,7 @@ async function updateRole(input) {
|
|
|
255
268
|
worker_count: input.worker_count,
|
|
256
269
|
priority_threshold_minutes: input.priority_threshold_minutes,
|
|
257
270
|
priority_facet: input.priority_facet,
|
|
271
|
+
enforce_schema: input.enforce_schema,
|
|
258
272
|
upstream_roles: input.upstream_roles,
|
|
259
273
|
change_summary: input.change_summary,
|
|
260
274
|
});
|
package/build/bin/ltc.js
CHANGED
|
@@ -62,7 +62,16 @@ const envLoaded = fs.existsSync(envPath);
|
|
|
62
62
|
// ── Error handler ────────────────────────────────────────────────────────
|
|
63
63
|
function handleError(err) {
|
|
64
64
|
const msg = err.message || String(err);
|
|
65
|
-
console.error(`\n ${picocolors_1.default.red('✗')} ${msg}
|
|
65
|
+
console.error(`\n ${picocolors_1.default.red('✗')} ${msg}`);
|
|
66
|
+
// Schema-validation rejections carry field-level violations — print each
|
|
67
|
+
// one the way the dashboard's errors panel lists them.
|
|
68
|
+
if (Array.isArray(err.violations)) {
|
|
69
|
+
for (const v of err.violations) {
|
|
70
|
+
const where = v.escalationId ? `${v.escalationId} · ${v.field}` : v.field;
|
|
71
|
+
console.error(` ${picocolors_1.default.dim('·')} ${picocolors_1.default.bold(where)} — ${v.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
console.error('');
|
|
66
75
|
process.exit(1);
|
|
67
76
|
}
|
|
68
77
|
function wrap(fn) {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { type LTFieldViolation } from '../../types/validation';
|
|
1
2
|
export declare class CLIError extends Error {
|
|
2
3
|
status?: number | undefined;
|
|
3
|
-
|
|
4
|
+
/** Field-level violations from a schema_validation 422, when present. */
|
|
5
|
+
violations?: LTFieldViolation[];
|
|
6
|
+
constructor(message: string, status?: number | undefined, violations?: LTFieldViolation[]);
|
|
4
7
|
}
|
|
5
8
|
/** Authenticated fetch against the Long Tail API */
|
|
6
9
|
export declare function apiFetch<T = any>(path: string, options?: RequestInit): Promise<T>;
|
package/build/lib/cli/client.js
CHANGED
|
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CLIError = void 0;
|
|
4
4
|
exports.apiFetch = apiFetch;
|
|
5
5
|
const auth_1 = require("./auth");
|
|
6
|
+
const validation_1 = require("../../types/validation");
|
|
6
7
|
class CLIError extends Error {
|
|
7
|
-
constructor(message, status) {
|
|
8
|
+
constructor(message, status, violations) {
|
|
8
9
|
super(message);
|
|
9
10
|
this.status = status;
|
|
10
11
|
this.name = 'CLIError';
|
|
12
|
+
this.violations = violations;
|
|
11
13
|
}
|
|
12
14
|
}
|
|
13
15
|
exports.CLIError = CLIError;
|
|
@@ -25,7 +27,7 @@ async function apiFetch(path, options) {
|
|
|
25
27
|
});
|
|
26
28
|
const body = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
27
29
|
if (!res.ok) {
|
|
28
|
-
throw new CLIError(body.error || `HTTP ${res.status}`, res.status);
|
|
30
|
+
throw new CLIError(body.error || `HTTP ${res.status}`, res.status, (0, validation_1.isValidationErrorBody)(body) ? body.violations : undefined);
|
|
29
31
|
}
|
|
30
32
|
return body;
|
|
31
33
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Migration 022: Per-user preferences + role default pins
|
|
2
|
+
--
|
|
3
|
+
-- lt_users.preferences is a generic per-user JSON store (pinned views are the
|
|
4
|
+
-- first tenant, not the schema): read via GET /api/me/preferences, written via
|
|
5
|
+
-- PATCH with shallow top-level merge (null deletes a key), size-capped at the
|
|
6
|
+
-- API layer. Preferences carry presentation state only — never data and never
|
|
7
|
+
-- authorization; read-scope enforcement stays with the queries a preference
|
|
8
|
+
-- may point at.
|
|
9
|
+
--
|
|
10
|
+
-- lt_roles.default_pins seeds a role's members with the persona's starting
|
|
11
|
+
-- pinned views ([{ label, url, badge? }]). Users promote, hide, or reorder
|
|
12
|
+
-- them through their own preferences.
|
|
13
|
+
|
|
14
|
+
ALTER TABLE lt_users
|
|
15
|
+
ADD COLUMN IF NOT EXISTS preferences JSONB;
|
|
16
|
+
|
|
17
|
+
ALTER TABLE lt_roles
|
|
18
|
+
ADD COLUMN IF NOT EXISTS default_pins JSONB;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Migration 023: Role-level resolver schema enforcement
|
|
2
|
+
--
|
|
3
|
+
-- enforce_schema makes the role's form_schema an enforced API contract: every
|
|
4
|
+
-- resolve surface (by id, by signal key, by ids, all-or-none, by metadata, and
|
|
5
|
+
-- the MCP resolve tools) validates the submitted resolverPayload against the
|
|
6
|
+
-- escalation's resolved form schema (metadata.form_schema override, else the
|
|
7
|
+
-- pinned lt_role_schemas snapshot, else the role's latest) and rejects
|
|
8
|
+
-- violations with 422 before any state changes. The validation pass is the
|
|
9
|
+
-- same isomorphic code the dashboard runs pre-submission, so a payload that
|
|
10
|
+
-- passes the client panel passes the gate and a 422 carries the identical
|
|
11
|
+
-- field-error list.
|
|
12
|
+
--
|
|
13
|
+
-- Opt-in per role (default false) so surfaces migrate deliberately: flip it on
|
|
14
|
+
-- a role once its submitters (dashboard, sim workforce, webhooks) are known to
|
|
15
|
+
-- send complete payloads. Roles with it off resolve exactly as before.
|
|
16
|
+
|
|
17
|
+
ALTER TABLE lt_roles
|
|
18
|
+
ADD COLUMN IF NOT EXISTS enforce_schema BOOLEAN NOT NULL DEFAULT false;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed resolution parsing — the workflow-side half of the resolver contract.
|
|
3
|
+
*
|
|
4
|
+
* Server-side enforcement (enforce_schema roles) guarantees a resolution
|
|
5
|
+
* matched the role's form schema when it was submitted. This module gives
|
|
6
|
+
* workflow and activity code the consuming half: declare the payload shape
|
|
7
|
+
* once as a zod schema, and parse the resolution through it to get a TYPED
|
|
8
|
+
* value with runtime assurance — a drifted or legacy payload fails loud here
|
|
9
|
+
* instead of propagating `any` into business logic.
|
|
10
|
+
*
|
|
11
|
+
* const IntakeV1 = z.object({ customer: z.object({ name: z.string() }) });
|
|
12
|
+
* const intake = parseResolverPayload(IntakeV1, response);
|
|
13
|
+
* intake.customer.name; // typed AND runtime-checked
|
|
14
|
+
*/
|
|
15
|
+
import type { ZodType } from 'zod';
|
|
16
|
+
import type { LTFieldViolation } from '../types/validation';
|
|
17
|
+
/**
|
|
18
|
+
* A resolution that failed its declared zod schema. Carries the same
|
|
19
|
+
* `{ field, message }` violation list the validation surfaces use, and the
|
|
20
|
+
* original ZodError as `cause`.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ResolverPayloadTypeError extends Error {
|
|
23
|
+
violations: LTFieldViolation[];
|
|
24
|
+
cause?: unknown;
|
|
25
|
+
constructor(violations: LTFieldViolation[], options?: {
|
|
26
|
+
cause?: unknown;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse a resolution payload through its declared zod schema. Returns the
|
|
31
|
+
* typed value; throws {@link ResolverPayloadTypeError} when the payload does
|
|
32
|
+
* not conform. The generic flows from the schema to the return value — no
|
|
33
|
+
* casting at the call site.
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseResolverPayload<T>(schema: ZodType<T>, payload: unknown): T;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResolverPayloadTypeError = void 0;
|
|
4
|
+
exports.parseResolverPayload = parseResolverPayload;
|
|
5
|
+
/**
|
|
6
|
+
* A resolution that failed its declared zod schema. Carries the same
|
|
7
|
+
* `{ field, message }` violation list the validation surfaces use, and the
|
|
8
|
+
* original ZodError as `cause`.
|
|
9
|
+
*/
|
|
10
|
+
class ResolverPayloadTypeError extends Error {
|
|
11
|
+
constructor(violations, options) {
|
|
12
|
+
const n = violations.length;
|
|
13
|
+
super(`resolver payload failed its declared schema (${n} violation${n === 1 ? '' : 's'}): `
|
|
14
|
+
+ violations.map((v) => `${v.field}: ${v.message}`).join('; '));
|
|
15
|
+
this.name = 'ResolverPayloadTypeError';
|
|
16
|
+
this.violations = violations;
|
|
17
|
+
this.cause = options?.cause;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.ResolverPayloadTypeError = ResolverPayloadTypeError;
|
|
21
|
+
/**
|
|
22
|
+
* Parse a resolution payload through its declared zod schema. Returns the
|
|
23
|
+
* typed value; throws {@link ResolverPayloadTypeError} when the payload does
|
|
24
|
+
* not conform. The generic flows from the schema to the return value — no
|
|
25
|
+
* casting at the call site.
|
|
26
|
+
*/
|
|
27
|
+
function parseResolverPayload(schema, payload) {
|
|
28
|
+
const result = schema.safeParse(payload);
|
|
29
|
+
if (!result.success) {
|
|
30
|
+
const violations = result.error.issues.map((issue) => ({
|
|
31
|
+
field: issue.path.length > 0 ? issue.path.join('.') : '(root)',
|
|
32
|
+
message: issue.message,
|
|
33
|
+
}));
|
|
34
|
+
throw new ResolverPayloadTypeError(violations, { cause: result.error });
|
|
35
|
+
}
|
|
36
|
+
return result.data;
|
|
37
|
+
}
|
package/build/modules/config.js
CHANGED
|
@@ -19,6 +19,11 @@ exports.config = {
|
|
|
19
19
|
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
20
20
|
JWT_SECRET: process.env.JWT_SECRET || '',
|
|
21
21
|
HONEYCOMB_API_KEY: process.env.HONEYCOMB_API_KEY || '',
|
|
22
|
+
// Resolver schema enforcement: TTL for the cached enforcing-role set and
|
|
23
|
+
// latest role form schemas. Bounds cross-container staleness after a role
|
|
24
|
+
// admin flips enforce_schema or edits a schema; pinned snapshots are
|
|
25
|
+
// immutable and cache indefinitely.
|
|
26
|
+
ROLE_ENFORCEMENT_CACHE_TTL_MS: parseInt(process.env.ROLE_ENFORCEMENT_CACHE_TTL_MS || '30000', 10),
|
|
22
27
|
};
|
|
23
28
|
exports.postgres_options = {
|
|
24
29
|
host: exports.config.POSTGRES_HOST,
|
|
@@ -43,12 +43,14 @@ router.use(auth_1.requireBuilder);
|
|
|
43
43
|
/**
|
|
44
44
|
* GET /api/bot-accounts
|
|
45
45
|
* List all bot accounts.
|
|
46
|
-
* Query: ?limit=50&offset=0
|
|
46
|
+
* Query: ?limit=50&offset=0&status=active&search=mcp
|
|
47
47
|
*/
|
|
48
48
|
router.get('/', async (req, res) => {
|
|
49
49
|
const result = await api.listBots({
|
|
50
50
|
limit: req.query.limit ? parseInt(req.query.limit, 10) : undefined,
|
|
51
51
|
offset: req.query.offset ? parseInt(req.query.offset, 10) : undefined,
|
|
52
|
+
status: req.query.status || undefined,
|
|
53
|
+
search: req.query.search || undefined,
|
|
52
54
|
});
|
|
53
55
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
54
56
|
});
|
package/build/routes/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const escalations_1 = __importDefault(require("./escalations"));
|
|
|
14
14
|
const workflows_1 = __importDefault(require("./workflows"));
|
|
15
15
|
const exports_1 = __importDefault(require("./exports"));
|
|
16
16
|
const users_1 = __importDefault(require("./users"));
|
|
17
|
+
const me_1 = __importDefault(require("./me"));
|
|
17
18
|
const roles_1 = __importDefault(require("./roles"));
|
|
18
19
|
const dba_1 = __importDefault(require("./dba"));
|
|
19
20
|
const maintenance_1 = __importDefault(require("./maintenance"));
|
|
@@ -51,6 +52,7 @@ router.use('/workflows', workflows_1.default);
|
|
|
51
52
|
router.use('/workflow-states', exports_1.default);
|
|
52
53
|
router.use('/config/maintenance', maintenance_1.default);
|
|
53
54
|
router.use('/users', users_1.default);
|
|
55
|
+
router.use('/me', me_1.default);
|
|
54
56
|
router.use('/roles', roles_1.default);
|
|
55
57
|
router.use('/dba', dba_1.default);
|
|
56
58
|
router.use('/mcp', mcp_1.default);
|