@hotmeshio/long-tail 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api/escalations/create.js +45 -0
- package/build/api/escalations/index.d.ts +1 -1
- package/build/api/escalations/index.js +2 -1
- package/build/api/escalations/list.d.ts +3 -0
- package/build/api/escalations/list.js +14 -0
- package/build/api/roles.d.ts +17 -0
- package/build/api/roles.js +46 -0
- package/build/bin/ltc.js +1 -0
- package/build/lib/cli/commands/escalations.d.ts +1 -0
- package/build/lib/cli/commands/escalations.js +2 -0
- package/build/lib/db/migrate.js +22 -2
- package/build/lib/db/schemas/014_roles_v2.sql +19 -0
- package/build/lib/db/schemas/015_role_ops.sql +50 -0
- package/build/lib/db/schemas/016_station_metrics_indexes.sql +13 -0
- package/build/lib/events/system-events.js +3 -0
- package/build/modules/auth.d.ts +9 -0
- package/build/modules/auth.js +38 -1
- package/build/routes/escalations/list.js +10 -0
- package/build/routes/roles.js +16 -5
- package/build/scripts/ortho-populate.d.ts +37 -0
- package/build/scripts/ortho-populate.js +228 -0
- package/build/sdk/index.d.ts +4 -0
- package/build/sdk/index.js +2 -0
- package/build/services/escalation/client.js +93 -2
- package/build/services/escalation/crud.js +18 -9
- package/build/services/escalation/metrics-cache.d.ts +14 -0
- package/build/services/escalation/metrics-cache.js +53 -0
- package/build/services/escalation/queries.d.ts +14 -2
- package/build/services/escalation/queries.js +121 -11
- package/build/services/escalation/sql.d.ts +29 -2
- package/build/services/escalation/sql.js +156 -27
- package/build/services/escalation/types.d.ts +17 -0
- package/build/services/escalation/types.js +1 -0
- package/build/services/mcp/external-server.js +5 -1
- package/build/services/orchestrator/condition.d.ts +20 -0
- package/build/services/orchestrator/condition.js +20 -0
- package/build/services/role/index.d.ts +22 -3
- package/build/services/role/index.js +50 -2
- package/build/services/role/sql.d.ts +11 -1
- package/build/services/role/sql.js +38 -1
- package/build/services/role/types.d.ts +32 -0
- package/build/system/mcp-servers/admin/escalations.js +8 -7
- package/build/system/mcp-servers/admin/index.js +5 -3
- package/build/system/mcp-servers/admin/ortho.d.ts +10 -0
- package/build/system/mcp-servers/admin/ortho.js +242 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +40 -3
- package/build/system/mcp-servers/admin/schemas.js +17 -4
- package/build/system/mcp-servers/admin/users.js +31 -0
- package/build/system/seed/tool-manifests-admin.d.ts +4016 -712
- package/build/system/seed/tool-manifests-admin.js +14 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/escalation.d.ts +3 -1
- package/dashboard/dist/assets/{AdminDashboard-C5gfVEeL.js → AdminDashboard-Bg1HgukI.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-C5gfVEeL.js.map → AdminDashboard-Bg1HgukI.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-CWEnuiGN.js → AgentConfigPage-HBe_YDU_.js} +6 -6
- package/dashboard/dist/assets/{AgentConfigPage-CWEnuiGN.js.map → AgentConfigPage-HBe_YDU_.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-B6IE27iK.js → AgentDetailPage-BsaiYbwL.js} +4 -4
- package/dashboard/dist/assets/{AgentDetailPage-B6IE27iK.js.map → AgentDetailPage-BsaiYbwL.js.map} +1 -1
- package/dashboard/dist/assets/AgentsPage-DhN2lIBh.js +2 -0
- package/dashboard/dist/assets/{AgentsPage-Blhh2RsK.js.map → AgentsPage-DhN2lIBh.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-_xcWvlIz.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-_xcWvlIz.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-DSozotGk.js → BotPicker-DY6iUVC9.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DSozotGk.js.map → BotPicker-DY6iUVC9.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-CrS_DTA1.js → CapabilitiesPage-w1owqsmN.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-CrS_DTA1.js.map → CapabilitiesPage-w1owqsmN.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-1i6bOQ1L.js → CollapsibleSection-DIMGjlJ-.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-1i6bOQ1L.js.map → CollapsibleSection-DIMGjlJ-.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-BxJio06E.js → CredentialsPage-D1OmMHux.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-BxJio06E.js.map → CredentialsPage-D1OmMHux.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-BAfIHHz4.js → CronLabel-CQy3DrUQ.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-BAfIHHz4.js.map → CronLabel-CQy3DrUQ.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-BLF0NFJK.js → CustomDurationPicker-BoBkl9tw.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-BLF0NFJK.js.map → CustomDurationPicker-BoBkl9tw.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-CEx_wk-g.js → DropZone-QXbiuU0b.js} +2 -2
- package/dashboard/dist/assets/{DropZone-CEx_wk-g.js.map → DropZone-QXbiuU0b.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-nKGJvznH.js → ElapsedCell-Donjm9gT.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-nKGJvznH.js.map → ElapsedCell-Donjm9gT.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-bNNVA6R2.js → EscalationsOverview-CvHIJ_GG.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-bNNVA6R2.js.map → EscalationsOverview-CvHIJ_GG.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-BCuDdqlU.js → EventTable-DpAXMmT3.js} +2 -2
- package/dashboard/dist/assets/{EventTable-BCuDdqlU.js.map → EventTable-DpAXMmT3.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-BPAvqHls.js → GraphInvokePage-De1XVx5d.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-BPAvqHls.js.map → GraphInvokePage-De1XVx5d.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-BYR5VXXw.js +2 -0
- package/dashboard/dist/assets/HomePage-BYR5VXXw.js.map +1 -0
- package/dashboard/dist/assets/{ListToolbar-DIJZfEgu.js → ListToolbar-Bx7jGzPA.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-DIJZfEgu.js.map → ListToolbar-Bx7jGzPA.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-DCfSCfPg.js → McpOverview-DXRKcJN0.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-DCfSCfPg.js.map → McpOverview-DXRKcJN0.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-mb7rmtNe.js → McpQueryDetailPage-CmW15w5e.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-mb7rmtNe.js.map → McpQueryDetailPage-CmW15w5e.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-D9b1T0I3.js → McpQueryPage-BE6I011z.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-D9b1T0I3.js.map → McpQueryPage-BE6I011z.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-Bz2UyuNm.js → McpRunDetailPage-Dsa3G3XQ.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-Bz2UyuNm.js.map → McpRunDetailPage-Dsa3G3XQ.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-Dw7w_ZO-.js → McpRunsPage-uTbym66Y.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-Dw7w_ZO-.js.map → McpRunsPage-uTbym66Y.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-D27vqDXy.js → NamespacePill-BGf0jHwE.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-D27vqDXy.js.map → NamespacePill-BGf0jHwE.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-CAmiFHI6.js +2 -0
- package/dashboard/dist/assets/OperationsPage-CAmiFHI6.js.map +1 -0
- package/dashboard/dist/assets/{OperatorDashboard-CTzSDW3N.js → OperatorDashboard-i8cxaDlk.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-CTzSDW3N.js.map → OperatorDashboard-i8cxaDlk.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-B_1zTx2q.js → PageHeader-BZJsEVOC.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-B_1zTx2q.js.map → PageHeader-BZJsEVOC.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-D-cPg7UD.js → PageHeaderWithStats-BWuD7s9c.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-D-cPg7UD.js.map → PageHeaderWithStats-BWuD7s9c.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-olG2CS-h.js → ProcessDetailPage-CP7GZhgs.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-olG2CS-h.js.map → ProcessDetailPage-CP7GZhgs.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-CUZSL06Y.js → ProcessesListPage-btsU6hBR.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-CUZSL06Y.js.map → ProcessesListPage-btsU6hBR.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-BB_e330f.js +16 -0
- package/dashboard/dist/assets/RoleDetailPage-BB_e330f.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-B-V-31lC.js → RolePill-Pb9TKSQN.js} +2 -2
- package/dashboard/dist/assets/{RolePill-B-V-31lC.js.map → RolePill-Pb9TKSQN.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-1DBoEMsb.js +2 -0
- package/dashboard/dist/assets/RolesPage-1DBoEMsb.js.map +1 -0
- package/dashboard/dist/assets/{RunAsSelector-BoLHF4fy.js → RunAsSelector-Cp6j3FEY.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-BoLHF4fy.js.map → RunAsSelector-Cp6j3FEY.js.map} +1 -1
- package/dashboard/dist/assets/StatusBadge-D9MeQHrh.js +2 -0
- package/dashboard/dist/assets/StatusBadge-D9MeQHrh.js.map +1 -0
- package/dashboard/dist/assets/{StickyPagination-BEMY1L-H.js → StickyPagination-Pr8kawv8.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-BEMY1L-H.js.map → StickyPagination-Pr8kawv8.js.map} +1 -1
- package/dashboard/dist/assets/StreamMessageDetail-BqmQ-N3y.js +2 -0
- package/dashboard/dist/assets/{StreamMessageDetail-CVRVWXcU.js.map → StreamMessageDetail-BqmQ-N3y.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-DDpU0-Jn.js → SwimlaneTimeline-y1YmXrsj.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-DDpU0-Jn.js.map → SwimlaneTimeline-y1YmXrsj.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-BHVfG307.js → TagInput-Df4myuYF.js} +2 -2
- package/dashboard/dist/assets/{TagInput-BHVfG307.js.map → TagInput-Df4myuYF.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-B_xQtJMg.js → TaskDetailPage-CT5rXHK5.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-B_xQtJMg.js.map → TaskDetailPage-CT5rXHK5.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-CGlxXqWt.js → TaskQueuePill-YalYdCNo.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-CGlxXqWt.js.map → TaskQueuePill-YalYdCNo.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-CsrdIOKk.js → TasksListPage-DOy6JlKS.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-CsrdIOKk.js.map → TasksListPage-DOy6JlKS.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-DmicaxjT.js → TimeAgo-BAQwZSpq.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-DmicaxjT.js.map → TimeAgo-BAQwZSpq.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-DeK89eRV.js → TimestampCell-BK_jufns.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-DeK89eRV.js.map → TimestampCell-BK_jufns.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-NV7PxSpA.js → ToolPill-VGpT6Quy.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-NV7PxSpA.js.map → ToolPill-VGpT6Quy.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-D3s2n9-9.js → ToolTestPanel-5PMQyUCe.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-D3s2n9-9.js.map → ToolTestPanel-5PMQyUCe.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-qyv7UWT_.js → TopicDetailPage-Cn1ShfR8.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-qyv7UWT_.js.map → TopicDetailPage-Cn1ShfR8.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-CEUfMoed.js → TopicsPage-D67tjS8L.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-CEUfMoed.js.map → TopicsPage-D67tjS8L.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-Bn_BxI8K.js → UserName-Dsu1Jk4N.js} +2 -2
- package/dashboard/dist/assets/{UserName-Bn_BxI8K.js.map → UserName-Dsu1Jk4N.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-DAk2RqpB.js → WorkflowExecutionPage-BwQoOmUE.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-DAk2RqpB.js.map → WorkflowExecutionPage-BwQoOmUE.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-DDR0B6DN.js → WorkflowPill-pPUqmx6Q.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-DDR0B6DN.js.map → WorkflowPill-pPUqmx6Q.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-DfBgM80t.js → WorkflowsDashboard-D3z7COgH.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-DfBgM80t.js.map → WorkflowsDashboard-D3z7COgH.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview--Dkoo9Jq.js → WorkflowsOverview-C15JcxyY.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview--Dkoo9Jq.js.map → WorkflowsOverview-C15JcxyY.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-B6HYbfFj.js → YamlWorkflowDetailPage-BYq3t7E-.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-B6HYbfFj.js.map → YamlWorkflowDetailPage-BYq3t7E-.js.map} +1 -1
- package/dashboard/dist/assets/YamlWorkflowsPage-B44WgwE_.js +2 -0
- package/dashboard/dist/assets/{YamlWorkflowsPage-CJE-PxC0.js.map → YamlWorkflowsPage-B44WgwE_.js.map} +1 -1
- package/dashboard/dist/assets/{agents-DUciUZpt.js → agents-BRmmK6Y9.js} +2 -2
- package/dashboard/dist/assets/{agents-DUciUZpt.js.map → agents-BRmmK6Y9.js.map} +1 -1
- package/dashboard/dist/assets/{bots-BnyqCfuC.js → bots-BD75mzGG.js} +2 -2
- package/dashboard/dist/assets/{bots-BnyqCfuC.js.map → bots-BD75mzGG.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-CkTWWT0q.js → capabilities-DDgC_Kds.js} +2 -2
- package/dashboard/dist/assets/{capabilities-CkTWWT0q.js.map → capabilities-DDgC_Kds.js.map} +1 -1
- package/dashboard/dist/assets/controlplane-BJh12lMv.js +2 -0
- package/dashboard/dist/assets/controlplane-BJh12lMv.js.map +1 -0
- package/dashboard/dist/assets/{escalation-De9JnTKI.js → escalation-DIJt2sgn.js} +2 -2
- package/dashboard/dist/assets/{escalation-De9JnTKI.js.map → escalation-DIJt2sgn.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-Df7Pluzg.js → escalation-columns-zl6E94Up.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-Df7Pluzg.js.map → escalation-columns-zl6E94Up.js.map} +1 -1
- package/dashboard/dist/assets/{index-Cqjf-yVP.js → index-B0kmiSmt.js} +2 -2
- package/dashboard/dist/assets/{index-Cqjf-yVP.js.map → index-B0kmiSmt.js.map} +1 -1
- package/dashboard/dist/assets/index-BRtKQtQZ.css +1 -0
- package/dashboard/dist/assets/{index-BxdiKgnH.js → index-BUbzfEA9.js} +2 -2
- package/dashboard/dist/assets/{index-BxdiKgnH.js.map → index-BUbzfEA9.js.map} +1 -1
- package/dashboard/dist/assets/{index-DpAS4-DA.js → index-BhznDv-4.js} +2 -2
- package/dashboard/dist/assets/{index-DpAS4-DA.js.map → index-BhznDv-4.js.map} +1 -1
- package/dashboard/dist/assets/{index-BAhkWcOq.js → index-CDPBXB4e.js} +4 -4
- package/dashboard/dist/assets/{index-BAhkWcOq.js.map → index-CDPBXB4e.js.map} +1 -1
- package/dashboard/dist/assets/{index-CgD9fA9P.js → index-CV4lEd4I.js} +2 -2
- package/dashboard/dist/assets/{index-CgD9fA9P.js.map → index-CV4lEd4I.js.map} +1 -1
- package/dashboard/dist/assets/{index-DjxTV3cn.js → index-CknKy4RA.js} +2 -2
- package/dashboard/dist/assets/{index-DjxTV3cn.js.map → index-CknKy4RA.js.map} +1 -1
- package/dashboard/dist/assets/{index-DNugmBs7.js → index-CpgV4Kkp.js} +2 -2
- package/dashboard/dist/assets/{index-DNugmBs7.js.map → index-CpgV4Kkp.js.map} +1 -1
- package/dashboard/dist/assets/{index-B1kc6itM.js → index-CtDf8vvQ.js} +24 -24
- package/dashboard/dist/assets/index-CtDf8vvQ.js.map +1 -0
- package/dashboard/dist/assets/{index-BzMsh6Ui.js → index-DU7YumVJ.js} +2 -2
- package/dashboard/dist/assets/{index-BzMsh6Ui.js.map → index-DU7YumVJ.js.map} +1 -1
- package/dashboard/dist/assets/{index-BsIaUbKD.js → index-DVVIeBBk.js} +2 -2
- package/dashboard/dist/assets/{index-BsIaUbKD.js.map → index-DVVIeBBk.js.map} +1 -1
- package/dashboard/dist/assets/index-DccCwyZl.js +6 -0
- package/dashboard/dist/assets/{index-CN0efCE9.js.map → index-DccCwyZl.js.map} +1 -1
- package/dashboard/dist/assets/{index-7hqtQI43.js → index-DrUkTcn-.js} +2 -2
- package/dashboard/dist/assets/{index-7hqtQI43.js.map → index-DrUkTcn-.js.map} +1 -1
- package/dashboard/dist/assets/{index-B07xLlyz.js → index-j-C7g_Ej.js} +2 -2
- package/dashboard/dist/assets/{index-B07xLlyz.js.map → index-j-C7g_Ej.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-D4SvdNv5.js → knowledge-pNDWACXB.js} +2 -2
- package/dashboard/dist/assets/{knowledge-D4SvdNv5.js.map → knowledge-pNDWACXB.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-CLP9EnnU.js → mcp-query-CmNjvxTG.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-CLP9EnnU.js.map → mcp-query-CmNjvxTG.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-DVxt4T9J.js → mcp-xddLBwUW.js} +2 -2
- package/dashboard/dist/assets/{mcp-DVxt4T9J.js.map → mcp-xddLBwUW.js.map} +1 -1
- package/dashboard/dist/assets/pipelines-B05sd2nB.js +2 -0
- package/dashboard/dist/assets/pipelines-B05sd2nB.js.map +1 -0
- package/dashboard/dist/assets/roles-bRgaMcdy.js +2 -0
- package/dashboard/dist/assets/roles-bRgaMcdy.js.map +1 -0
- package/dashboard/dist/assets/tasks-Bm1OKrJA.js +2 -0
- package/dashboard/dist/assets/{tasks-1eTuP02d.js.map → tasks-Bm1OKrJA.js.map} +1 -1
- package/dashboard/dist/assets/{topics-B3p8K0PW.js → topics-BVIqtmd4.js} +2 -2
- package/dashboard/dist/assets/{topics-B3p8K0PW.js.map → topics-BVIqtmd4.js.map} +1 -1
- package/dashboard/dist/assets/useEventHooks-_qCzU74s.js +2 -0
- package/dashboard/dist/assets/useEventHooks-_qCzU74s.js.map +1 -0
- package/dashboard/dist/assets/{useNamespace-Dx81_JM0.js → useNamespace-CDnCSFpG.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-Dx81_JM0.js.map → useNamespace-CDnCSFpG.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-D5T3TbE7.js → useYamlActivityEvents-zrHMhaFM.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-D5T3TbE7.js.map → useYamlActivityEvents-zrHMhaFM.js.map} +1 -1
- package/dashboard/dist/assets/{users-BEMysNdR.js → users-BPBMZg27.js} +2 -2
- package/dashboard/dist/assets/{users-BEMysNdR.js.map → users-BPBMZg27.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons--Ivmp-RX.js → vendor-icons-C1zVP_dv.js} +125 -115
- package/dashboard/dist/assets/vendor-icons-C1zVP_dv.js.map +1 -0
- package/dashboard/dist/assets/workflows-CT6nTL1I.js +2 -0
- package/dashboard/dist/assets/workflows-CT6nTL1I.js.map +1 -0
- package/dashboard/dist/assets/{yaml-workflows-4rJ5v5rJ.js → yaml-workflows-CdfjkOKU.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-4rJ5v5rJ.js.map → yaml-workflows-CdfjkOKU.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +82 -6
- package/docs/api/http/roles.md +82 -3
- package/docs/api/mcp/admin.md +127 -1
- package/docs/api/sdk/escalations.md +65 -4
- package/docs/api/sdk/roles.md +81 -0
- package/docs/cli.md +2 -0
- package/docs/dashboard.md +17 -2
- package/docs/hitl-guide.md +12 -3
- package/docs/operations.md +113 -0
- package/package.json +3 -2
- package/dashboard/dist/assets/AgentsPage-Blhh2RsK.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-ClwAle76.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-ClwAle76.js.map +0 -1
- package/dashboard/dist/assets/HomePage-BZ1YTrjR.js +0 -2
- package/dashboard/dist/assets/HomePage-BZ1YTrjR.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-DLnaW6iD.js +0 -2
- package/dashboard/dist/assets/RolesPage-DLnaW6iD.js.map +0 -1
- package/dashboard/dist/assets/StatusBadge-Bl3FXVmF.js +0 -2
- package/dashboard/dist/assets/StatusBadge-Bl3FXVmF.js.map +0 -1
- package/dashboard/dist/assets/StreamMessageDetail-CVRVWXcU.js +0 -2
- package/dashboard/dist/assets/YamlWorkflowsPage-CJE-PxC0.js +0 -2
- package/dashboard/dist/assets/controlplane-D9HaFZsC.js +0 -2
- package/dashboard/dist/assets/controlplane-D9HaFZsC.js.map +0 -1
- package/dashboard/dist/assets/index-B1kc6itM.js.map +0 -1
- package/dashboard/dist/assets/index-CN0efCE9.js +0 -6
- package/dashboard/dist/assets/index-nO0cim3_.css +0 -1
- package/dashboard/dist/assets/pipelines-C_x0LTBN.js +0 -2
- package/dashboard/dist/assets/pipelines-C_x0LTBN.js.map +0 -1
- package/dashboard/dist/assets/roles-QaGFZQ58.js +0 -2
- package/dashboard/dist/assets/roles-QaGFZQ58.js.map +0 -1
- package/dashboard/dist/assets/tasks-1eTuP02d.js +0 -2
- package/dashboard/dist/assets/useEventHooks-BNZky4xM.js +0 -2
- package/dashboard/dist/assets/useEventHooks-BNZky4xM.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons--Ivmp-RX.js.map +0 -1
- package/dashboard/dist/assets/workflows-C4rjvQbI.js +0 -2
- package/dashboard/dist/assets/workflows-C4rjvQbI.js.map +0 -1
|
@@ -32,10 +32,32 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.createEscalation = createEscalation;
|
|
40
|
+
const ajv_1 = __importDefault(require("ajv"));
|
|
37
41
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
42
|
+
const roleService = __importStar(require("../../services/role"));
|
|
38
43
|
const helpers_1 = require("./helpers");
|
|
44
|
+
// strict: false — role metadata_schemas use the project's house extension
|
|
45
|
+
// keywords (x-lt-widget etc., the same style as seeded form_schemas); Ajv 8's
|
|
46
|
+
// strict mode throws at compile on any unknown keyword, which would turn every
|
|
47
|
+
// metadata-bearing create for that role into a 500.
|
|
48
|
+
const ajv = new ajv_1.default({ allErrors: true, strict: false });
|
|
49
|
+
// Compiled validators cached per role, keyed by the schema's serialized form
|
|
50
|
+
// so an admin's schema edit invalidates the stale validator on the next create.
|
|
51
|
+
const validatorCache = new Map();
|
|
52
|
+
function compileRoleValidator(role, schema) {
|
|
53
|
+
const key = JSON.stringify(schema);
|
|
54
|
+
const cached = validatorCache.get(role);
|
|
55
|
+
if (cached && cached.key === key)
|
|
56
|
+
return cached.validate;
|
|
57
|
+
const validate = ajv.compile(schema);
|
|
58
|
+
validatorCache.set(role, { key, validate });
|
|
59
|
+
return validate;
|
|
60
|
+
}
|
|
39
61
|
// ── Create ────────────────────────────────────────────────────────────────
|
|
40
62
|
/**
|
|
41
63
|
* Create a standalone escalation (not tied to a workflow).
|
|
@@ -70,6 +92,29 @@ async function createEscalation(input, auth) {
|
|
|
70
92
|
if (denied) {
|
|
71
93
|
return { status: 403, error: `You must have write access to the "${role}" role or be a superadmin to create escalations for it` };
|
|
72
94
|
}
|
|
95
|
+
// Validate metadata against the role's declared schema (if any).
|
|
96
|
+
if (input.metadata) {
|
|
97
|
+
const schema = await roleService.getRoleMetadataSchema(role);
|
|
98
|
+
if (schema) {
|
|
99
|
+
let validate;
|
|
100
|
+
try {
|
|
101
|
+
validate = compileRoleValidator(role, schema);
|
|
102
|
+
}
|
|
103
|
+
catch (compileErr) {
|
|
104
|
+
// A broken stored schema is the role admin's bug, not the creator's —
|
|
105
|
+
// name it explicitly instead of surfacing an opaque 500.
|
|
106
|
+
return {
|
|
107
|
+
status: 422,
|
|
108
|
+
error: `metadata_schema for role "${role}" is not a valid JSON Schema: ${compileErr.message}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const valid = validate(input.metadata);
|
|
112
|
+
if (!valid) {
|
|
113
|
+
const msgs = (validate.errors ?? []).map((e) => `${e.instancePath || '(root)'} ${e.message}`).join('; ');
|
|
114
|
+
return { status: 400, error: `metadata does not match the role schema: ${msgs}` };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
73
118
|
const escalation = await escalationService.createEscalation({
|
|
74
119
|
type,
|
|
75
120
|
subtype: input.subtype ?? type,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createEscalation } from './create';
|
|
2
|
-
export { listEscalations, listAvailableEscalations, listDistinctTypes, listFacetKeys, getEscalationStats } from './list';
|
|
2
|
+
export { listEscalations, listAvailableEscalations, listDistinctTypes, listFacetKeys, getEscalationStats, getStationMetrics } from './list';
|
|
3
3
|
export { getEscalation, getEscalationsByWorkflowId, escalateToRole } from './single';
|
|
4
4
|
export { claimEscalation, releaseEscalation } from './claim';
|
|
5
5
|
export { releaseExpiredClaims, updatePriority, bulkClaim, bulkAssign, bulkEscalate, bulkTriage } from './bulk';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.claimByFacets = exports.claimGroups = exports.searchByFacets = exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveByIds = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getEscalationStats = exports.listFacetKeys = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
|
|
3
|
+
exports.claimByFacets = exports.claimGroups = exports.searchByFacets = exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveByIds = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getStationMetrics = exports.getEscalationStats = exports.listFacetKeys = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
|
|
4
4
|
var create_1 = require("./create");
|
|
5
5
|
Object.defineProperty(exports, "createEscalation", { enumerable: true, get: function () { return create_1.createEscalation; } });
|
|
6
6
|
var list_1 = require("./list");
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "listAvailableEscalations", { enumerable: true, g
|
|
|
9
9
|
Object.defineProperty(exports, "listDistinctTypes", { enumerable: true, get: function () { return list_1.listDistinctTypes; } });
|
|
10
10
|
Object.defineProperty(exports, "listFacetKeys", { enumerable: true, get: function () { return list_1.listFacetKeys; } });
|
|
11
11
|
Object.defineProperty(exports, "getEscalationStats", { enumerable: true, get: function () { return list_1.getEscalationStats; } });
|
|
12
|
+
Object.defineProperty(exports, "getStationMetrics", { enumerable: true, get: function () { return list_1.getStationMetrics; } });
|
|
12
13
|
var single_1 = require("./single");
|
|
13
14
|
Object.defineProperty(exports, "getEscalation", { enumerable: true, get: function () { return single_1.getEscalation; } });
|
|
14
15
|
Object.defineProperty(exports, "getEscalationsByWorkflowId", { enumerable: true, get: function () { return single_1.getEscalationsByWorkflowId; } });
|
|
@@ -95,3 +95,6 @@ export declare function listDistinctTypes(): Promise<LTApiResult>;
|
|
|
95
95
|
export declare function getEscalationStats(input: {
|
|
96
96
|
period?: string;
|
|
97
97
|
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
98
|
+
export declare function getStationMetrics(input: {
|
|
99
|
+
period?: string;
|
|
100
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
@@ -38,6 +38,7 @@ exports.listAvailableEscalations = listAvailableEscalations;
|
|
|
38
38
|
exports.listFacetKeys = listFacetKeys;
|
|
39
39
|
exports.listDistinctTypes = listDistinctTypes;
|
|
40
40
|
exports.getEscalationStats = getEscalationStats;
|
|
41
|
+
exports.getStationMetrics = getStationMetrics;
|
|
41
42
|
const escalationService = __importStar(require("../../services/escalation"));
|
|
42
43
|
const helpers_1 = require("./helpers");
|
|
43
44
|
/** True when the caller asked for anything the plain list path can't express. */
|
|
@@ -270,3 +271,16 @@ async function getEscalationStats(input, auth) {
|
|
|
270
271
|
return { status: 500, error: err.message };
|
|
271
272
|
}
|
|
272
273
|
}
|
|
274
|
+
async function getStationMetrics(input, auth) {
|
|
275
|
+
try {
|
|
276
|
+
const scope = await (0, helpers_1.getEscalationReadScope)(auth.userId);
|
|
277
|
+
if (!scope.global && scope.allRoles.length === 0) {
|
|
278
|
+
return { status: 200, data: { stations: [] } };
|
|
279
|
+
}
|
|
280
|
+
const stations = await escalationService.getStationMetrics(scope.global ? undefined : scope.allRoles, input.period);
|
|
281
|
+
return { status: 200, data: { stations } };
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
return { status: 500, error: err.message };
|
|
285
|
+
}
|
|
286
|
+
}
|
package/build/api/roles.d.ts
CHANGED
|
@@ -75,6 +75,23 @@ export declare function replaceEscalationTargets(input: {
|
|
|
75
75
|
role: string;
|
|
76
76
|
targets: string[];
|
|
77
77
|
}): Promise<LTApiResult>;
|
|
78
|
+
/**
|
|
79
|
+
* Update role metadata. Fields omitted from input are left unchanged.
|
|
80
|
+
* form_schema, metadata_schema, and parent_role can be set to null to clear them.
|
|
81
|
+
*/
|
|
82
|
+
export declare function updateRole(input: {
|
|
83
|
+
role: string;
|
|
84
|
+
title?: string | null;
|
|
85
|
+
description?: string | null;
|
|
86
|
+
form_schema?: Record<string, any> | null;
|
|
87
|
+
metadata_schema?: Record<string, any> | null;
|
|
88
|
+
properties?: Record<string, any> | null;
|
|
89
|
+
ops_visible?: boolean;
|
|
90
|
+
parent_role?: string | null;
|
|
91
|
+
sla_minutes?: number | null;
|
|
92
|
+
target_per_hour?: number | null;
|
|
93
|
+
worker_count?: number | null;
|
|
94
|
+
}): Promise<LTApiResult>;
|
|
78
95
|
/**
|
|
79
96
|
* Delete a role from the system. Requires admin privileges.
|
|
80
97
|
*
|
package/build/api/roles.js
CHANGED
|
@@ -41,6 +41,7 @@ exports.addEscalationChain = addEscalationChain;
|
|
|
41
41
|
exports.removeEscalationChain = removeEscalationChain;
|
|
42
42
|
exports.getEscalationTargets = getEscalationTargets;
|
|
43
43
|
exports.replaceEscalationTargets = replaceEscalationTargets;
|
|
44
|
+
exports.updateRole = updateRole;
|
|
44
45
|
exports.deleteRole = deleteRole;
|
|
45
46
|
const roleService = __importStar(require("../services/role"));
|
|
46
47
|
/**
|
|
@@ -195,6 +196,51 @@ async function replaceEscalationTargets(input) {
|
|
|
195
196
|
return { status: 500, error: err.message };
|
|
196
197
|
}
|
|
197
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Update role metadata. Fields omitted from input are left unchanged.
|
|
201
|
+
* form_schema, metadata_schema, and parent_role can be set to null to clear them.
|
|
202
|
+
*/
|
|
203
|
+
async function updateRole(input) {
|
|
204
|
+
try {
|
|
205
|
+
if (!input.role) {
|
|
206
|
+
return { status: 400, error: 'role is required' };
|
|
207
|
+
}
|
|
208
|
+
for (const field of ['sla_minutes', 'target_per_hour', 'worker_count']) {
|
|
209
|
+
const value = input[field];
|
|
210
|
+
if (value !== undefined && value !== null && (typeof value !== 'number' || !Number.isFinite(value) || value < 0)) {
|
|
211
|
+
return { status: 400, error: `${field} must be a non-negative number or null` };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (input.worker_count != null && !Number.isInteger(input.worker_count)) {
|
|
215
|
+
return { status: 400, error: 'worker_count must be a whole number' };
|
|
216
|
+
}
|
|
217
|
+
if (input.ops_visible !== undefined && typeof input.ops_visible !== 'boolean') {
|
|
218
|
+
return { status: 400, error: 'ops_visible must be a boolean' };
|
|
219
|
+
}
|
|
220
|
+
if (input.parent_role != null && input.parent_role === input.role) {
|
|
221
|
+
return { status: 400, error: 'parent_role must reference a different role' };
|
|
222
|
+
}
|
|
223
|
+
const updated = await roleService.updateRoleMetadata(input.role, {
|
|
224
|
+
title: input.title,
|
|
225
|
+
description: input.description,
|
|
226
|
+
form_schema: input.form_schema,
|
|
227
|
+
metadata_schema: input.metadata_schema,
|
|
228
|
+
properties: input.properties,
|
|
229
|
+
ops_visible: input.ops_visible,
|
|
230
|
+
parent_role: input.parent_role,
|
|
231
|
+
sla_minutes: input.sla_minutes,
|
|
232
|
+
target_per_hour: input.target_per_hour,
|
|
233
|
+
worker_count: input.worker_count,
|
|
234
|
+
});
|
|
235
|
+
if (!updated) {
|
|
236
|
+
return { status: 404, error: `Role '${input.role}' not found` };
|
|
237
|
+
}
|
|
238
|
+
return { status: 200, data: updated };
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
return { status: 500, error: err.message };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
198
244
|
/**
|
|
199
245
|
* Delete a role from the system. Requires admin privileges.
|
|
200
246
|
*
|
package/build/bin/ltc.js
CHANGED
|
@@ -119,6 +119,7 @@ const escCmd = commander_1.program.command('escalations').alias('esc').descripti
|
|
|
119
119
|
escCmd.command('list')
|
|
120
120
|
.option('--status <status>', 'Filter by status (pending, resolved, cancelled)')
|
|
121
121
|
.option('--role <role>', 'Filter by role')
|
|
122
|
+
.option('--search <term>', 'Exact-match by correlation id (escalation id, workflow id, or origin id). For a value inside metadata, use --facets')
|
|
122
123
|
.option('--limit <n>', 'Max results')
|
|
123
124
|
.option('--facets <json>', 'Required metadata facets, JSON object (metadata @>), e.g. \'{"filament":"pla"}\'')
|
|
124
125
|
.option('--block <json>', 'Exclude rows containing ANY of these facet sets, JSON array')
|
|
@@ -32,6 +32,8 @@ async function listEscalations(opts) {
|
|
|
32
32
|
params.set('status', opts.status);
|
|
33
33
|
if (opts.role)
|
|
34
34
|
params.set('role', opts.role);
|
|
35
|
+
if (opts.search)
|
|
36
|
+
params.set('search', opts.search);
|
|
35
37
|
if (opts.limit)
|
|
36
38
|
params.set('limit', opts.limit);
|
|
37
39
|
// Faceted query elements travel as JSON-encoded query params (the route JSON-parses
|
package/build/lib/db/migrate.js
CHANGED
|
@@ -64,8 +64,28 @@ async function migrate() {
|
|
|
64
64
|
const { rows } = await client.query('SELECT 1 FROM lt_migrations WHERE name = $1', [file]);
|
|
65
65
|
if (rows.length === 0) {
|
|
66
66
|
const sql = fs.readFileSync(path.join(SCHEMAS_DIR, file), 'utf-8');
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
// Apply the file and record it in ONE transaction. Without this, a crash
|
|
68
|
+
// between applying the SQL and inserting the tracking row leaves the
|
|
69
|
+
// migration applied-but-untracked, so the next boot re-runs it. That is
|
|
70
|
+
// only safe if every migration is idempotent — this wrapper removes that
|
|
71
|
+
// hidden requirement and makes apply+track atomic across containers
|
|
72
|
+
// (the advisory lock already serializes them; this closes the gap within
|
|
73
|
+
// a single file's application).
|
|
74
|
+
//
|
|
75
|
+
// Constraint: migration files must NOT use CREATE INDEX CONCURRENTLY —
|
|
76
|
+
// it is illegal inside a transaction block. Concurrent index builds on
|
|
77
|
+
// large runtime tables belong in the post-boot path (see
|
|
78
|
+
// services/escalation/client.ts), not in migrate().
|
|
79
|
+
try {
|
|
80
|
+
await client.query('BEGIN');
|
|
81
|
+
await client.query(sql);
|
|
82
|
+
await client.query('INSERT INTO lt_migrations (name) VALUES ($1)', [file]);
|
|
83
|
+
await client.query('COMMIT');
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
await client.query('ROLLBACK').catch(() => { });
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
69
89
|
logger_1.loggerRegistry.info(`[migrate] applied: ${file}`);
|
|
70
90
|
}
|
|
71
91
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- Migration 014: Elevate lt_roles with rich metadata and process dependency graph
|
|
2
|
+
--
|
|
3
|
+
-- New columns:
|
|
4
|
+
-- title — display name separate from the technical role key
|
|
5
|
+
-- description — human-readable summary for card layout
|
|
6
|
+
-- form_schema — role-level template for rendering/validating escalation data
|
|
7
|
+
-- (used as default; workflow-level resolver_schema overrides)
|
|
8
|
+
-- properties — open bag for SLA targets, icon, color, station location, etc.
|
|
9
|
+
-- ops_visible — flag controlling COO /operations view visibility
|
|
10
|
+
-- parent_role — nullable self-referencing FK for the process dependency graph
|
|
11
|
+
-- (child references parent; roots have NULL)
|
|
12
|
+
|
|
13
|
+
ALTER TABLE lt_roles
|
|
14
|
+
ADD COLUMN IF NOT EXISTS title TEXT,
|
|
15
|
+
ADD COLUMN IF NOT EXISTS description TEXT,
|
|
16
|
+
ADD COLUMN IF NOT EXISTS form_schema JSONB,
|
|
17
|
+
ADD COLUMN IF NOT EXISTS properties JSONB NOT NULL DEFAULT '{}',
|
|
18
|
+
ADD COLUMN IF NOT EXISTS ops_visible BOOLEAN NOT NULL DEFAULT false,
|
|
19
|
+
ADD COLUMN IF NOT EXISTS parent_role TEXT REFERENCES lt_roles(role);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- Migration 015: Role operational columns
|
|
2
|
+
--
|
|
3
|
+
-- Promotes the COO operational triangle to first-class typed columns on lt_roles
|
|
4
|
+
-- instead of loose keys inside the properties JSONB bag.
|
|
5
|
+
--
|
|
6
|
+
-- Triangle: if you know any two, the third is derived.
|
|
7
|
+
-- throughput = worker_count / (sla_minutes / 60)
|
|
8
|
+
-- worker_count = target_per_hour × (sla_minutes / 60)
|
|
9
|
+
-- sla_minutes = worker_count / target_per_hour × 60
|
|
10
|
+
--
|
|
11
|
+
-- Also formalises metadata_schema: a JSON Schema document that declares the
|
|
12
|
+
-- expected shape of lt_escalations.metadata for escalations created under
|
|
13
|
+
-- this role. Drives faceted-query key autocomplete and creation-time validation.
|
|
14
|
+
--
|
|
15
|
+
-- properties remains a free user-owned JSONB bag — no reserved keys.
|
|
16
|
+
|
|
17
|
+
ALTER TABLE lt_roles
|
|
18
|
+
ADD COLUMN IF NOT EXISTS sla_minutes NUMERIC,
|
|
19
|
+
ADD COLUMN IF NOT EXISTS target_per_hour NUMERIC,
|
|
20
|
+
ADD COLUMN IF NOT EXISTS worker_count NUMERIC,
|
|
21
|
+
ADD COLUMN IF NOT EXISTS metadata_schema JSONB;
|
|
22
|
+
|
|
23
|
+
-- Migrate values that lived in the properties bag (common key names).
|
|
24
|
+
-- The bag is user-owned free text, so every cast is guarded: only JSON numbers
|
|
25
|
+
-- and numeric strings are promoted. An unparseable value (e.g. "30 min") is
|
|
26
|
+
-- left in the bag rather than aborting the migration transaction and
|
|
27
|
+
-- crash-looping every container at boot.
|
|
28
|
+
UPDATE lt_roles
|
|
29
|
+
SET sla_minutes = (properties->>'sla_minutes')::numeric
|
|
30
|
+
WHERE sla_minutes IS NULL
|
|
31
|
+
AND (jsonb_typeof(properties->'sla_minutes') = 'number'
|
|
32
|
+
OR properties->>'sla_minutes' ~ '^[0-9]+(\.[0-9]+)?$');
|
|
33
|
+
|
|
34
|
+
UPDATE lt_roles
|
|
35
|
+
SET sla_minutes = (properties->>'tat_minutes')::numeric
|
|
36
|
+
WHERE sla_minutes IS NULL
|
|
37
|
+
AND (jsonb_typeof(properties->'tat_minutes') = 'number'
|
|
38
|
+
OR properties->>'tat_minutes' ~ '^[0-9]+(\.[0-9]+)?$');
|
|
39
|
+
|
|
40
|
+
UPDATE lt_roles
|
|
41
|
+
SET target_per_hour = (properties->>'target_per_hour')::numeric
|
|
42
|
+
WHERE target_per_hour IS NULL
|
|
43
|
+
AND (jsonb_typeof(properties->'target_per_hour') = 'number'
|
|
44
|
+
OR properties->>'target_per_hour' ~ '^[0-9]+(\.[0-9]+)?$');
|
|
45
|
+
|
|
46
|
+
UPDATE lt_roles
|
|
47
|
+
SET worker_count = (properties->>'worker_count')::numeric
|
|
48
|
+
WHERE worker_count IS NULL
|
|
49
|
+
AND (jsonb_typeof(properties->'worker_count') = 'number'
|
|
50
|
+
OR properties->>'worker_count' ~ '^[0-9]+(\.[0-9]+)?$');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- Station-metrics indexes on hmsh_escalations
|
|
2
|
+
--
|
|
3
|
+
-- hmsh_escalations is created by HotMesh at runtime, AFTER migrate() runs.
|
|
4
|
+
-- These indexes cannot be created here because the table does not yet exist
|
|
5
|
+
-- when this migration file executes.
|
|
6
|
+
--
|
|
7
|
+
-- The indexes are created in services/escalation/client.ts:installEscalationCompatView()
|
|
8
|
+
-- which runs after HotMesh has bootstrapped, under advisory lock 8675310.
|
|
9
|
+
-- That is the single authoritative creation path.
|
|
10
|
+
--
|
|
11
|
+
-- This migration file exists only to claim the 016 slot in the sequence
|
|
12
|
+
-- so the numbering stays stable. It is intentionally a no-op.
|
|
13
|
+
SELECT 1;
|
|
@@ -12,6 +12,9 @@ const ESCALATION_STATUS_BY_VERB = {
|
|
|
12
12
|
reassigned: 'pending',
|
|
13
13
|
resolved: 'resolved',
|
|
14
14
|
cancelled: 'cancelled',
|
|
15
|
+
// hotmesh 0.25.1: the SLA timer on a condition(config + timeout) wait fired
|
|
16
|
+
// first — the engine transitioned the row and emitted .expired.
|
|
17
|
+
expired: 'expired',
|
|
15
18
|
};
|
|
16
19
|
/**
|
|
17
20
|
* Translate a HotMesh `SystemEvent` into long-tail's `LTEvent`. Escalation
|
package/build/modules/auth.d.ts
CHANGED
|
@@ -59,6 +59,15 @@ export declare const requireAdmin: RequestHandler;
|
|
|
59
59
|
* This is the backend equivalent of the dashboard's `isBuilder` check.
|
|
60
60
|
*/
|
|
61
61
|
export declare const requireBuilder: RequestHandler;
|
|
62
|
+
/**
|
|
63
|
+
* Middleware that requires role-management access. Must be placed AFTER requireAuth.
|
|
64
|
+
*
|
|
65
|
+
* Grants access to superadmins, admin-type users, and engineers — the backend
|
|
66
|
+
* equivalent of the dashboard's `isBuilder || isOps`. Ops (admin type) manage
|
|
67
|
+
* users and roles; builders (superadmin, engineer) are a superset. This is the
|
|
68
|
+
* gate for reading and writing role definitions.
|
|
69
|
+
*/
|
|
70
|
+
export declare const requireRoleManager: RequestHandler;
|
|
62
71
|
/**
|
|
63
72
|
* Generate a JWT token. Utility for tests and token provisioning.
|
|
64
73
|
*/
|
package/build/modules/auth.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.requireBuilder = exports.requireAdmin = exports.requireAuth = exports.JwtAuthAdapter = void 0;
|
|
39
|
+
exports.requireRoleManager = exports.requireBuilder = exports.requireAdmin = exports.requireAuth = exports.JwtAuthAdapter = void 0;
|
|
40
40
|
exports.createAuthMiddleware = createAuthMiddleware;
|
|
41
41
|
exports.setAuthAdapter = setAuthAdapter;
|
|
42
42
|
exports.signToken = signToken;
|
|
@@ -257,6 +257,43 @@ const requireBuilder = async (req, res, next) => {
|
|
|
257
257
|
}
|
|
258
258
|
};
|
|
259
259
|
exports.requireBuilder = requireBuilder;
|
|
260
|
+
/**
|
|
261
|
+
* Middleware that requires role-management access. Must be placed AFTER requireAuth.
|
|
262
|
+
*
|
|
263
|
+
* Grants access to superadmins, admin-type users, and engineers — the backend
|
|
264
|
+
* equivalent of the dashboard's `isBuilder || isOps`. Ops (admin type) manage
|
|
265
|
+
* users and roles; builders (superadmin, engineer) are a superset. This is the
|
|
266
|
+
* gate for reading and writing role definitions.
|
|
267
|
+
*/
|
|
268
|
+
const requireRoleManager = async (req, res, next) => {
|
|
269
|
+
try {
|
|
270
|
+
if (!req.auth?.userId) {
|
|
271
|
+
res.status(403).json({ error: 'Forbidden' });
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// Fast path: trust the JWT role claim for admin/superadmin
|
|
275
|
+
if (req.auth.role === 'admin' || req.auth.role === 'superadmin') {
|
|
276
|
+
next();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
// Slow path: check database for superadmin role type
|
|
280
|
+
if (await (0, user_1.isSuperAdmin)(req.auth.userId)) {
|
|
281
|
+
next();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
// Check database for engineer role (builder)
|
|
285
|
+
const { hasRole } = await Promise.resolve().then(() => __importStar(require('../services/user/roles')));
|
|
286
|
+
if (await hasRole(req.auth.userId, 'engineer')) {
|
|
287
|
+
next();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
res.status(403).json({ error: 'Forbidden: role-management access required' });
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
res.status(403).json({ error: 'Forbidden' });
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
exports.requireRoleManager = requireRoleManager;
|
|
260
297
|
/**
|
|
261
298
|
* Generate a JWT token. Utility for tests and token provisioning.
|
|
262
299
|
*/
|
|
@@ -153,6 +153,16 @@ function registerListRoutes(router) {
|
|
|
153
153
|
const result = await api.listFacetKeys(undefined, req.auth);
|
|
154
154
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
155
155
|
});
|
|
156
|
+
/**
|
|
157
|
+
* GET /api/escalations/station-metrics
|
|
158
|
+
* Per-role P99/P50/avg/max wait + work times, resolved count, and in-arrears
|
|
159
|
+
* count. Drives the Operations station pace chart.
|
|
160
|
+
* RBAC: superadmin sees all; others scoped to their roles.
|
|
161
|
+
*/
|
|
162
|
+
router.get('/station-metrics', async (req, res) => {
|
|
163
|
+
const result = await api.getStationMetrics({ period: req.query.period || undefined }, req.auth);
|
|
164
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
165
|
+
});
|
|
156
166
|
/**
|
|
157
167
|
* GET /api/escalations/stats
|
|
158
168
|
* Aggregated escalation statistics.
|
package/build/routes/roles.js
CHANGED
|
@@ -58,7 +58,7 @@ router.get('/details', async (_req, res) => {
|
|
|
58
58
|
* Create a standalone role. Requires admin.
|
|
59
59
|
* Body: { role: string }
|
|
60
60
|
*/
|
|
61
|
-
router.post('/', auth_1.
|
|
61
|
+
router.post('/', auth_1.requireRoleManager, async (req, res) => {
|
|
62
62
|
const { role } = req.body || {};
|
|
63
63
|
const result = await api.createRole({ role });
|
|
64
64
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
@@ -76,7 +76,7 @@ router.get('/escalation-chains', async (_req, res) => {
|
|
|
76
76
|
* Add a single escalation chain entry. Requires admin.
|
|
77
77
|
* Body: { source_role: string, target_role: string }
|
|
78
78
|
*/
|
|
79
|
-
router.post('/escalation-chains', auth_1.
|
|
79
|
+
router.post('/escalation-chains', auth_1.requireRoleManager, async (req, res) => {
|
|
80
80
|
const { source_role, target_role } = req.body || {};
|
|
81
81
|
const result = await api.addEscalationChain({ source_role, target_role });
|
|
82
82
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
@@ -86,12 +86,23 @@ router.post('/escalation-chains', auth_1.requireBuilder, async (req, res) => {
|
|
|
86
86
|
* Remove a single escalation chain entry. Requires admin.
|
|
87
87
|
* Body: { source_role: string, target_role: string }
|
|
88
88
|
*/
|
|
89
|
-
router.delete('/escalation-chains', auth_1.
|
|
89
|
+
router.delete('/escalation-chains', auth_1.requireRoleManager, async (req, res) => {
|
|
90
90
|
const { source_role, target_role } = req.body || {};
|
|
91
91
|
const result = await api.removeEscalationChain({ source_role, target_role });
|
|
92
92
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
93
93
|
});
|
|
94
94
|
// ── Parameterized routes (must come AFTER named routes) ───────────────────
|
|
95
|
+
/**
|
|
96
|
+
* PATCH /api/roles/:role
|
|
97
|
+
* Update role metadata. Requires admin.
|
|
98
|
+
* Body: { title?, description?, form_schema?, metadata_schema?, properties?, ops_visible?, parent_role?, sla_minutes?, target_per_hour?, worker_count? }
|
|
99
|
+
*/
|
|
100
|
+
router.patch('/:role', auth_1.requireRoleManager, async (req, res) => {
|
|
101
|
+
// URL is the resource identity — a `role` key in the body must not redirect
|
|
102
|
+
// the write to a different row.
|
|
103
|
+
const result = await api.updateRole({ ...req.body, role: req.params.role });
|
|
104
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
105
|
+
});
|
|
95
106
|
/**
|
|
96
107
|
* GET /api/roles/:role/escalation-targets
|
|
97
108
|
* Get allowed escalation targets for a specific role.
|
|
@@ -105,7 +116,7 @@ router.get('/:role/escalation-targets', async (req, res) => {
|
|
|
105
116
|
* Replace escalation targets for a role. Requires admin.
|
|
106
117
|
* Body: { targets: string[] }
|
|
107
118
|
*/
|
|
108
|
-
router.put('/:role/escalation-targets', auth_1.
|
|
119
|
+
router.put('/:role/escalation-targets', auth_1.requireRoleManager, async (req, res) => {
|
|
109
120
|
const { targets } = req.body || {};
|
|
110
121
|
const result = await api.replaceEscalationTargets({ role: req.params.role, targets });
|
|
111
122
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
@@ -114,7 +125,7 @@ router.put('/:role/escalation-targets', auth_1.requireBuilder, async (req, res)
|
|
|
114
125
|
* DELETE /api/roles/:role
|
|
115
126
|
* Delete a role if it has no references. Requires admin.
|
|
116
127
|
*/
|
|
117
|
-
router.delete('/:role', auth_1.
|
|
128
|
+
router.delete('/:role', auth_1.requireRoleManager, async (req, res) => {
|
|
118
129
|
const result = await api.deleteRole({ role: req.params.role });
|
|
119
130
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
120
131
|
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ortho-populate — Populate the Operations dashboard with ortho pipeline history.
|
|
3
|
+
*
|
|
4
|
+
* Adapted from the boilerplate's 08-day-resolver.ts pattern:
|
|
5
|
+
* - concurrent claim loop + resolve loop + watchdog via Promise.all
|
|
6
|
+
* - hold time read from esc.claimed_at (API), not tracked in memory
|
|
7
|
+
* - simple resolve payload { approved: true, station: role }
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npx ts-node scripts/ortho-populate.ts
|
|
11
|
+
* ORDERS=50 HOLD_S=3 npx ts-node scripts/ortho-populate.ts
|
|
12
|
+
* ORDERS=5 HOLD_S=0 POLL_MS=500 npx ts-node scripts/ortho-populate.ts
|
|
13
|
+
*
|
|
14
|
+
* Env vars:
|
|
15
|
+
* ORDERS — number of orders to run (default 20)
|
|
16
|
+
* HOLD_S — seconds to hold each escalation before resolving (default 2)
|
|
17
|
+
* POLL_MS — poll interval ms (default 1000)
|
|
18
|
+
* BASE_URL — server URL (default http://localhost:3000)
|
|
19
|
+
*/
|
|
20
|
+
declare const BASE_URL: string;
|
|
21
|
+
declare const ORDERS: number;
|
|
22
|
+
declare const HOLD_S: number;
|
|
23
|
+
declare const POLL_MS: number;
|
|
24
|
+
declare const ORTHO_ROLES: string[];
|
|
25
|
+
declare let token: string;
|
|
26
|
+
declare let userId: string;
|
|
27
|
+
declare let totalClaimed: number;
|
|
28
|
+
declare let totalResolved: number;
|
|
29
|
+
declare function ts(): string;
|
|
30
|
+
declare function sleep(ms: number): Promise<unknown>;
|
|
31
|
+
declare function ageSeconds(iso: string): number;
|
|
32
|
+
declare function api(method: string, path: string, body?: unknown): Promise<any>;
|
|
33
|
+
declare function login(): Promise<void>;
|
|
34
|
+
declare function claimBatch(batchTag: string): Promise<number>;
|
|
35
|
+
declare function resolveBatch(batchTag: string): Promise<number>;
|
|
36
|
+
declare function runOrders(batchTag: string, target: number): Promise<void>;
|
|
37
|
+
declare function main(): Promise<void>;
|