@hotmeshio/long-tail 0.5.12 → 0.6.2
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/escalations/bulk.js +9 -1
- package/build/api/escalations/cancel.js +5 -7
- package/build/api/escalations/claim.js +18 -15
- package/build/api/escalations/create.d.ts +6 -0
- package/build/api/escalations/create.js +11 -8
- package/build/api/escalations/facets.d.ts +18 -0
- package/build/api/escalations/facets.js +115 -0
- package/build/api/escalations/helpers.d.ts +70 -1
- package/build/api/escalations/helpers.js +101 -7
- package/build/api/escalations/index.d.ts +3 -2
- package/build/api/escalations/index.js +7 -1
- package/build/api/escalations/list.d.ts +21 -2
- package/build/api/escalations/list.js +109 -10
- package/build/api/escalations/metadata.js +22 -27
- package/build/api/escalations/resolve.d.ts +19 -1
- package/build/api/escalations/resolve.js +111 -26
- package/build/api/escalations/single.js +8 -8
- package/build/api/users.d.ts +6 -0
- package/build/api/users.js +29 -1
- package/build/bin/ltc.js +40 -0
- package/build/lib/cli/commands/escalations.d.ts +36 -0
- package/build/lib/cli/commands/escalations.js +98 -0
- package/build/lib/db/schemas/001_schema.sql +10 -5
- package/build/lib/db/schemas/012_lt_tasks_workflow_id_unique.sql +20 -0
- package/build/lib/db/schemas/013_role_scope.sql +37 -0
- package/build/modules/version.d.ts +10 -5
- package/build/modules/version.js +27 -11
- package/build/routes/escalations/facets.d.ts +7 -0
- package/build/routes/escalations/facets.js +81 -0
- package/build/routes/escalations/index.js +3 -0
- package/build/routes/escalations/list.js +46 -0
- package/build/routes/escalations/resolve.js +22 -4
- package/build/routes/users.js +9 -5
- package/build/sdk/index.d.ts +33 -2
- package/build/sdk/index.js +7 -0
- package/build/services/escalation/crud.d.ts +56 -6
- package/build/services/escalation/crud.js +102 -31
- package/build/services/escalation/facet-sql.d.ts +18 -0
- package/build/services/escalation/facet-sql.js +118 -0
- package/build/services/escalation/facets.d.ts +45 -0
- package/build/services/escalation/facets.js +218 -0
- package/build/services/escalation/index.d.ts +1 -0
- package/build/services/escalation/index.js +8 -1
- package/build/services/escalation/queries.d.ts +45 -0
- package/build/services/escalation/queries.js +107 -6
- package/build/services/escalation/sql.d.ts +10 -4
- package/build/services/escalation/sql.js +36 -8
- package/build/services/interceptor/activities/escalation.d.ts +2 -0
- package/build/services/interceptor/activities/escalation.js +1 -1
- package/build/services/interceptor/lifecycle.js +4 -0
- package/build/services/mcp/server-tools.js +10 -4
- package/build/services/orchestrator/condition.js +6 -2
- package/build/services/role/index.js +2 -3
- package/build/services/role/sql.d.ts +8 -0
- package/build/services/role/sql.js +17 -1
- package/build/services/task/crud.js +22 -14
- package/build/services/task/process.js +3 -21
- package/build/services/task/sql.d.ts +24 -1
- package/build/services/task/sql.js +65 -1
- package/build/services/user/crud.d.ts +1 -0
- package/build/services/user/crud.js +29 -11
- package/build/services/user/index.d.ts +2 -1
- package/build/services/user/index.js +12 -1
- package/build/services/user/rbac.d.ts +18 -0
- package/build/services/user/rbac.js +27 -0
- package/build/services/user/roles.d.ts +7 -2
- package/build/services/user/roles.js +21 -5
- package/build/services/user/scope.d.ts +22 -0
- package/build/services/user/scope.js +50 -0
- package/build/services/user/seed-admin.js +34 -17
- package/build/services/user/sql.d.ts +18 -7
- package/build/services/user/sql.js +38 -13
- package/build/services/user/sso-provision.js +41 -24
- package/build/services/user/types.d.ts +9 -5
- package/build/system/mcp-servers/admin/escalations.js +65 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +325 -0
- package/build/system/mcp-servers/admin/schemas.js +63 -2
- package/build/system/mcp-servers/admin/users.js +28 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/envelope.d.ts +7 -0
- package/build/types/facets.d.ts +60 -0
- package/build/types/facets.js +10 -0
- package/build/types/index.d.ts +2 -1
- package/build/types/user.d.ts +11 -0
- package/dashboard/dist/assets/AdminDashboard-C5gfVEeL.js +2 -0
- package/dashboard/dist/assets/{AdminDashboard-LTiYLuzq.js.map → AdminDashboard-C5gfVEeL.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-nlOudfpZ.js → AgentConfigPage-CWEnuiGN.js} +6 -6
- package/dashboard/dist/assets/{AgentConfigPage-nlOudfpZ.js.map → AgentConfigPage-CWEnuiGN.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-BSUnDvGp.js → AgentDetailPage-B6IE27iK.js} +3 -3
- package/dashboard/dist/assets/AgentDetailPage-B6IE27iK.js.map +1 -0
- package/dashboard/dist/assets/AgentsPage-Blhh2RsK.js +2 -0
- package/dashboard/dist/assets/{AgentsPage-DpS0cCYe.js.map → AgentsPage-Blhh2RsK.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-ClwAle76.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-ClwAle76.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-BhKmq7M7.js → BotPicker-DSozotGk.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-BhKmq7M7.js.map → BotPicker-DSozotGk.js.map} +1 -1
- package/dashboard/dist/assets/CapabilitiesPage-CrS_DTA1.js +2 -0
- package/dashboard/dist/assets/CapabilitiesPage-CrS_DTA1.js.map +1 -0
- package/dashboard/dist/assets/{CollapsibleSection-D7SVwNVk.js → CollapsibleSection-1i6bOQ1L.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-D7SVwNVk.js.map → CollapsibleSection-1i6bOQ1L.js.map} +1 -1
- package/dashboard/dist/assets/CopyableId-blg-gZvT.js +2 -0
- package/dashboard/dist/assets/CopyableId-blg-gZvT.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-fat5Z2U9.js → CredentialsPage-BxJio06E.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-fat5Z2U9.js.map → CredentialsPage-BxJio06E.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-OOP4P2mJ.js → CronLabel-BAfIHHz4.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-OOP4P2mJ.js.map → CronLabel-BAfIHHz4.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-COacSAXO.js → CustomDurationPicker-BLF0NFJK.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-COacSAXO.js.map → CustomDurationPicker-BLF0NFJK.js.map} +1 -1
- package/dashboard/dist/assets/{DataTable-D9yuBv0w.js → DataTable-ZWgVQg7F.js} +2 -2
- package/dashboard/dist/assets/DataTable-ZWgVQg7F.js.map +1 -0
- package/dashboard/dist/assets/{DropZone-Bfw2YLVS.js → DropZone-CEx_wk-g.js} +2 -2
- package/dashboard/dist/assets/{DropZone-Bfw2YLVS.js.map → DropZone-CEx_wk-g.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-CkfCD-m7.js → ElapsedCell-nKGJvznH.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-CkfCD-m7.js.map → ElapsedCell-nKGJvznH.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BmI8jxlh.js → EscalationsOverview-bNNVA6R2.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BmI8jxlh.js.map → EscalationsOverview-bNNVA6R2.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-CeKfFk3m.js → EventTable-BCuDdqlU.js} +2 -2
- package/dashboard/dist/assets/{EventTable-CeKfFk3m.js.map → EventTable-BCuDdqlU.js.map} +1 -1
- package/dashboard/dist/assets/FilterBar-BQ2JGg8U.js +2 -0
- package/dashboard/dist/assets/FilterBar-BQ2JGg8U.js.map +1 -0
- package/dashboard/dist/assets/GraphInvokePage-BPAvqHls.js +2 -0
- package/dashboard/dist/assets/GraphInvokePage-BPAvqHls.js.map +1 -0
- package/dashboard/dist/assets/HomePage-BZ1YTrjR.js +2 -0
- package/dashboard/dist/assets/HomePage-BZ1YTrjR.js.map +1 -0
- package/dashboard/dist/assets/{ListToolbar-BJaCzAXS.js → ListToolbar-DIJZfEgu.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-BJaCzAXS.js.map → ListToolbar-DIJZfEgu.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-CvnDG5OB.js → McpOverview-DCfSCfPg.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-CvnDG5OB.js.map → McpOverview-DCfSCfPg.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-DwmSIcIL.js → McpQueryDetailPage-mb7rmtNe.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-DwmSIcIL.js.map → McpQueryDetailPage-mb7rmtNe.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-CU6FMEEk.js → McpQueryPage-D9b1T0I3.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-CU6FMEEk.js.map → McpQueryPage-D9b1T0I3.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-8a6lsehv.js → McpRunDetailPage-Bz2UyuNm.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-8a6lsehv.js.map → McpRunDetailPage-Bz2UyuNm.js.map} +1 -1
- package/dashboard/dist/assets/McpRunsPage-Dw7w_ZO-.js +2 -0
- package/dashboard/dist/assets/McpRunsPage-Dw7w_ZO-.js.map +1 -0
- package/dashboard/dist/assets/{NamespacePill-D6btr6e8.js → NamespacePill-D27vqDXy.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-D6btr6e8.js.map → NamespacePill-D27vqDXy.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-CTzSDW3N.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-g9o7I925.js.map → OperatorDashboard-CTzSDW3N.js.map} +1 -1
- package/dashboard/dist/assets/PageHeader-B_1zTx2q.js +2 -0
- package/dashboard/dist/assets/PageHeader-B_1zTx2q.js.map +1 -0
- package/dashboard/dist/assets/{PageHeaderWithStats-DJQCZ0FR.js → PageHeaderWithStats-D-cPg7UD.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-DJQCZ0FR.js.map → PageHeaderWithStats-D-cPg7UD.js.map} +1 -1
- package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js +2 -0
- package/dashboard/dist/assets/PriorityBadge-B4ykBH7f.js.map +1 -0
- package/dashboard/dist/assets/{ProcessDetailPage-ByyWCs2A.js → ProcessDetailPage-olG2CS-h.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-ByyWCs2A.js.map → ProcessDetailPage-olG2CS-h.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-BTsXds4l.js → ProcessesListPage-CUZSL06Y.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-BTsXds4l.js.map → ProcessesListPage-CUZSL06Y.js.map} +1 -1
- package/dashboard/dist/assets/RolePill-B-V-31lC.js +2 -0
- package/dashboard/dist/assets/RolePill-B-V-31lC.js.map +1 -0
- package/dashboard/dist/assets/RolesPage-DLnaW6iD.js +2 -0
- package/dashboard/dist/assets/RolesPage-DLnaW6iD.js.map +1 -0
- package/dashboard/dist/assets/RowActions-BA9L9djf.js +2 -0
- package/dashboard/dist/assets/RowActions-BA9L9djf.js.map +1 -0
- package/dashboard/dist/assets/{RunAsSelector-BzkZXpSX.js → RunAsSelector-BoLHF4fy.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-BzkZXpSX.js.map → RunAsSelector-BoLHF4fy.js.map} +1 -1
- package/dashboard/dist/assets/StickyPagination-BEMY1L-H.js +2 -0
- package/dashboard/dist/assets/StickyPagination-BEMY1L-H.js.map +1 -0
- package/dashboard/dist/assets/{StreamMessageDetail-B6ch-6SY.js → StreamMessageDetail-CVRVWXcU.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-B6ch-6SY.js.map → StreamMessageDetail-CVRVWXcU.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-CjvfUkls.js → SwimlaneTimeline-DDpU0-Jn.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-CjvfUkls.js.map → SwimlaneTimeline-DDpU0-Jn.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-CZSeyesi.js → TagInput-BHVfG307.js} +2 -2
- package/dashboard/dist/assets/{TagInput-CZSeyesi.js.map → TagInput-BHVfG307.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-BjHU6opi.js → TaskDetailPage-B_xQtJMg.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-BjHU6opi.js.map → TaskDetailPage-B_xQtJMg.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-CQrXrSxk.js → TaskQueuePill-CGlxXqWt.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-CQrXrSxk.js.map → TaskQueuePill-CGlxXqWt.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-Bem44zd0.js → TasksListPage-CsrdIOKk.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-Bem44zd0.js.map → TasksListPage-CsrdIOKk.js.map} +1 -1
- package/dashboard/dist/assets/TimeAgo-DmicaxjT.js +2 -0
- package/dashboard/dist/assets/{TimeAgo-CUIYqSt_.js.map → TimeAgo-DmicaxjT.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell--tnreI1i.js → TimestampCell-DeK89eRV.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell--tnreI1i.js.map → TimestampCell-DeK89eRV.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-O09jsTQM.js → ToolPill-NV7PxSpA.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-O09jsTQM.js.map → ToolPill-NV7PxSpA.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-OD99H7Pk.js → ToolTestPanel-D3s2n9-9.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-OD99H7Pk.js.map → ToolTestPanel-D3s2n9-9.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-rh_dsTJI.js → TopicDetailPage-qyv7UWT_.js} +3 -3
- package/dashboard/dist/assets/TopicDetailPage-qyv7UWT_.js.map +1 -0
- package/dashboard/dist/assets/TopicsPage-CEUfMoed.js +2 -0
- package/dashboard/dist/assets/TopicsPage-CEUfMoed.js.map +1 -0
- package/dashboard/dist/assets/{UserName-C__c8BN0.js → UserName-Bn_BxI8K.js} +2 -2
- package/dashboard/dist/assets/{UserName-C__c8BN0.js.map → UserName-Bn_BxI8K.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-Bw6QdtUO.js → WorkflowExecutionPage-DAk2RqpB.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-Bw6QdtUO.js.map → WorkflowExecutionPage-DAk2RqpB.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-CS-IsR0U.js → WorkflowPill-DDR0B6DN.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-CS-IsR0U.js.map → WorkflowPill-DDR0B6DN.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowsDashboard-DfBgM80t.js +2 -0
- package/dashboard/dist/assets/WorkflowsDashboard-DfBgM80t.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowsOverview-BRc0QbA7.js → WorkflowsOverview--Dkoo9Jq.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-BRc0QbA7.js.map → WorkflowsOverview--Dkoo9Jq.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DdzspNQR.js → YamlWorkflowDetailPage-B6HYbfFj.js} +25 -25
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DdzspNQR.js.map → YamlWorkflowDetailPage-B6HYbfFj.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-Djw5ZbgQ.js → YamlWorkflowsPage-CJE-PxC0.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-Djw5ZbgQ.js.map → YamlWorkflowsPage-CJE-PxC0.js.map} +1 -1
- package/dashboard/dist/assets/{agents-xstNkF6c.js → agents-DUciUZpt.js} +2 -2
- package/dashboard/dist/assets/{agents-xstNkF6c.js.map → agents-DUciUZpt.js.map} +1 -1
- package/dashboard/dist/assets/{bots-BztAI0s_.js → bots-BnyqCfuC.js} +2 -2
- package/dashboard/dist/assets/{bots-BztAI0s_.js.map → bots-BnyqCfuC.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-CEDwtxNL.js → capabilities-CkTWWT0q.js} +2 -2
- package/dashboard/dist/assets/{capabilities-CEDwtxNL.js.map → capabilities-CkTWWT0q.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-BlGEsRKe.js → controlplane-D9HaFZsC.js} +2 -2
- package/dashboard/dist/assets/{controlplane-BlGEsRKe.js.map → controlplane-D9HaFZsC.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-iI4QKlP-.js → escalation-De9JnTKI.js} +2 -2
- package/dashboard/dist/assets/{escalation-iI4QKlP-.js.map → escalation-De9JnTKI.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-Df7Pluzg.js +2 -0
- package/dashboard/dist/assets/escalation-columns-Df7Pluzg.js.map +1 -0
- package/dashboard/dist/assets/index-7hqtQI43.js +2 -0
- package/dashboard/dist/assets/index-7hqtQI43.js.map +1 -0
- package/dashboard/dist/assets/{index-CAqEy2Db.js → index-B07xLlyz.js} +2 -2
- package/dashboard/dist/assets/{index-CAqEy2Db.js.map → index-B07xLlyz.js.map} +1 -1
- package/dashboard/dist/assets/{index-D1xVY4JR.js → index-B1kc6itM.js} +24 -24
- package/dashboard/dist/assets/index-B1kc6itM.js.map +1 -0
- package/dashboard/dist/assets/index-BAhkWcOq.js +9 -0
- package/dashboard/dist/assets/index-BAhkWcOq.js.map +1 -0
- package/dashboard/dist/assets/{index-Dsi5o7dx.js → index-BsIaUbKD.js} +2 -2
- package/dashboard/dist/assets/{index-Dsi5o7dx.js.map → index-BsIaUbKD.js.map} +1 -1
- package/dashboard/dist/assets/index-BxdiKgnH.js +6 -0
- package/dashboard/dist/assets/index-BxdiKgnH.js.map +1 -0
- package/dashboard/dist/assets/{index-B7VC0vjf.js → index-BzMsh6Ui.js} +2 -2
- package/dashboard/dist/assets/{index-B7VC0vjf.js.map → index-BzMsh6Ui.js.map} +1 -1
- package/dashboard/dist/assets/index-CN0efCE9.js +6 -0
- package/dashboard/dist/assets/index-CN0efCE9.js.map +1 -0
- package/dashboard/dist/assets/{index-B5qsp8lb.js → index-CgD9fA9P.js} +2 -2
- package/dashboard/dist/assets/{index-B5qsp8lb.js.map → index-CgD9fA9P.js.map} +1 -1
- package/dashboard/dist/assets/{index-CUfgt3mV.js → index-Cqjf-yVP.js} +2 -2
- package/dashboard/dist/assets/{index-CUfgt3mV.js.map → index-Cqjf-yVP.js.map} +1 -1
- package/dashboard/dist/assets/{index-DjJ6VCuc.js → index-DNugmBs7.js} +2 -2
- package/dashboard/dist/assets/{index-DjJ6VCuc.js.map → index-DNugmBs7.js.map} +1 -1
- package/dashboard/dist/assets/{index-BUmnJ4eT.js → index-DjxTV3cn.js} +2 -2
- package/dashboard/dist/assets/{index-BUmnJ4eT.js.map → index-DjxTV3cn.js.map} +1 -1
- package/dashboard/dist/assets/index-DpAS4-DA.js +2 -0
- package/dashboard/dist/assets/index-DpAS4-DA.js.map +1 -0
- package/dashboard/dist/assets/index-nO0cim3_.css +1 -0
- package/dashboard/dist/assets/{knowledge-BKg0cr39.js → knowledge-D4SvdNv5.js} +2 -2
- package/dashboard/dist/assets/{knowledge-BKg0cr39.js.map → knowledge-D4SvdNv5.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CVdHpKlz.js → mcp-DVxt4T9J.js} +2 -2
- package/dashboard/dist/assets/{mcp-CVdHpKlz.js.map → mcp-DVxt4T9J.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-CYxZcbwQ.js → mcp-query-CLP9EnnU.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-CYxZcbwQ.js.map → mcp-query-CLP9EnnU.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-BOEv7P6O.js → pipelines-C_x0LTBN.js} +2 -2
- package/dashboard/dist/assets/{pipelines-BOEv7P6O.js.map → pipelines-C_x0LTBN.js.map} +1 -1
- package/dashboard/dist/assets/{roles-c5cV2QYQ.js → roles-QaGFZQ58.js} +2 -2
- package/dashboard/dist/assets/{roles-c5cV2QYQ.js.map → roles-QaGFZQ58.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-JV3BjeEF.js → tasks-1eTuP02d.js} +2 -2
- package/dashboard/dist/assets/{tasks-JV3BjeEF.js.map → tasks-1eTuP02d.js.map} +1 -1
- package/dashboard/dist/assets/{topics-BDaPnhnQ.js → topics-B3p8K0PW.js} +2 -2
- package/dashboard/dist/assets/{topics-BDaPnhnQ.js.map → topics-B3p8K0PW.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-a7Dnh8G4.js → useEventHooks-BNZky4xM.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-a7Dnh8G4.js.map → useEventHooks-BNZky4xM.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-BSwEV0e9.js → useNamespace-Dx81_JM0.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-BSwEV0e9.js.map → useNamespace-Dx81_JM0.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-BuqvL-cn.js → useYamlActivityEvents-D5T3TbE7.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-BuqvL-cn.js.map → useYamlActivityEvents-D5T3TbE7.js.map} +1 -1
- package/dashboard/dist/assets/users-BEMysNdR.js +2 -0
- package/dashboard/dist/assets/users-BEMysNdR.js.map +1 -0
- package/dashboard/dist/assets/{vendor-icons-D5ycKPA2.js → vendor-icons--Ivmp-RX.js} +132 -112
- package/dashboard/dist/assets/vendor-icons--Ivmp-RX.js.map +1 -0
- package/dashboard/dist/assets/{workflows-DF-9Voyi.js → workflows-C4rjvQbI.js} +2 -2
- package/dashboard/dist/assets/{workflows-DF-9Voyi.js.map → workflows-C4rjvQbI.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-2roKaSSK.js → yaml-workflows-4rJ5v5rJ.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-2roKaSSK.js.map → yaml-workflows-4rJ5v5rJ.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +131 -10
- package/docs/api/http/roles.md +44 -9
- package/docs/api/http/users.md +15 -6
- package/docs/api/mcp/admin.md +8 -2
- package/docs/api/mcp/human-queue.md +34 -2
- package/docs/api/sdk/escalations.md +155 -2
- package/docs/api/sdk/roles.md +11 -0
- package/docs/api/sdk/users.md +26 -1
- package/docs/auth.md +1 -1
- package/docs/dashboard.md +5 -4
- package/docs/data.md +9 -2
- package/docs/faceted-routing.md +221 -0
- package/docs/hitl-guide.md +50 -0
- package/docs/iam.md +31 -0
- package/docs/mcp.md +8 -0
- package/package.json +2 -2
- package/dashboard/dist/assets/AdminDashboard-LTiYLuzq.js +0 -2
- package/dashboard/dist/assets/AgentDetailPage-BSUnDvGp.js.map +0 -1
- package/dashboard/dist/assets/AgentsPage-DpS0cCYe.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js.map +0 -1
- package/dashboard/dist/assets/CapabilitiesPage-DLIleX2L.js +0 -2
- package/dashboard/dist/assets/CapabilitiesPage-DLIleX2L.js.map +0 -1
- package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js +0 -2
- package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js.map +0 -1
- package/dashboard/dist/assets/DataTable-D9yuBv0w.js.map +0 -1
- package/dashboard/dist/assets/FilterBar-Ck4K4rzu.js +0 -2
- package/dashboard/dist/assets/FilterBar-Ck4K4rzu.js.map +0 -1
- package/dashboard/dist/assets/GraphInvokePage-BZBPBCSq.js +0 -2
- package/dashboard/dist/assets/GraphInvokePage-BZBPBCSq.js.map +0 -1
- package/dashboard/dist/assets/HomePage-dM7U2tl6.js +0 -2
- package/dashboard/dist/assets/HomePage-dM7U2tl6.js.map +0 -1
- package/dashboard/dist/assets/McpRunsPage-CPMYWgm_.js +0 -2
- package/dashboard/dist/assets/McpRunsPage-CPMYWgm_.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-g9o7I925.js +0 -2
- package/dashboard/dist/assets/PageHeader-Dk2Pe44Q.js +0 -2
- package/dashboard/dist/assets/PageHeader-Dk2Pe44Q.js.map +0 -1
- package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js +0 -2
- package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js.map +0 -1
- package/dashboard/dist/assets/RolePill-Du-aNnjR.js +0 -2
- package/dashboard/dist/assets/RolePill-Du-aNnjR.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-B2Kdf-L9.js +0 -2
- package/dashboard/dist/assets/RolesPage-B2Kdf-L9.js.map +0 -1
- package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js +0 -2
- package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js.map +0 -1
- package/dashboard/dist/assets/StickyPagination-BWhFSr2d.js +0 -2
- package/dashboard/dist/assets/StickyPagination-BWhFSr2d.js.map +0 -1
- package/dashboard/dist/assets/TimeAgo-CUIYqSt_.js +0 -2
- package/dashboard/dist/assets/TopicDetailPage-rh_dsTJI.js.map +0 -1
- package/dashboard/dist/assets/TopicsPage-CD_40_v-.js +0 -2
- package/dashboard/dist/assets/TopicsPage-CD_40_v-.js.map +0 -1
- package/dashboard/dist/assets/WorkflowsDashboard-BgI-iOP6.js +0 -2
- package/dashboard/dist/assets/WorkflowsDashboard-BgI-iOP6.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-nVYVt7Gl.js +0 -2
- package/dashboard/dist/assets/escalation-columns-nVYVt7Gl.js.map +0 -1
- package/dashboard/dist/assets/index-CMviPjXQ.js +0 -9
- package/dashboard/dist/assets/index-CMviPjXQ.js.map +0 -1
- package/dashboard/dist/assets/index-CP0JEQhZ.js +0 -6
- package/dashboard/dist/assets/index-CP0JEQhZ.js.map +0 -1
- package/dashboard/dist/assets/index-ClvYSNZd.css +0 -1
- package/dashboard/dist/assets/index-D1xVY4JR.js.map +0 -1
- package/dashboard/dist/assets/index-DPLy-jwO.js +0 -2
- package/dashboard/dist/assets/index-DPLy-jwO.js.map +0 -1
- package/dashboard/dist/assets/index-DzVYjoex.js +0 -6
- package/dashboard/dist/assets/index-DzVYjoex.js.map +0 -1
- package/dashboard/dist/assets/index-FuhiJuHL.js +0 -2
- package/dashboard/dist/assets/index-FuhiJuHL.js.map +0 -1
- package/dashboard/dist/assets/users-Pn85K_3C.js +0 -2
- package/dashboard/dist/assets/users-Pn85K_3C.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-D5ycKPA2.js.map +0 -1
|
@@ -54,14 +54,20 @@ const getAvailableWorkSchema = zod_1.z.object({
|
|
|
54
54
|
role: zod_1.z.string().describe('Role to filter by'),
|
|
55
55
|
limit: zod_1.z.number().optional().default(10).describe('Max results to return'),
|
|
56
56
|
});
|
|
57
|
+
/** Outcome facets merged onto the row on resolve — see the field describe(). */
|
|
58
|
+
const outcomeMetadata = zod_1.z.record(zod_1.z.any()).optional().describe('Outcome facets to merge into the escalation\'s metadata on resolve — the durable, ' +
|
|
59
|
+
'queryable record of what happened (disposition, reviewer, timing). Distinct from ' +
|
|
60
|
+
'payload, which resumes the waiting workflow and is not indexed.');
|
|
57
61
|
const claimAndResolveSchema = zod_1.z.object({
|
|
58
62
|
escalation_id: zod_1.z.string().describe('The escalation ID to claim and resolve'),
|
|
59
63
|
resolver_id: zod_1.z.string().describe('Identifier for who/what is resolving'),
|
|
60
64
|
payload: zod_1.z.record(zod_1.z.any()).describe('Resolution payload data'),
|
|
65
|
+
metadata: outcomeMetadata,
|
|
61
66
|
});
|
|
62
67
|
const resolveEscalationSchema = zod_1.z.object({
|
|
63
68
|
escalation_id: zod_1.z.string().describe('The escalation ID to resolve'),
|
|
64
69
|
payload: zod_1.z.record(zod_1.z.any()).describe('Resolution payload data'),
|
|
70
|
+
metadata: outcomeMetadata,
|
|
65
71
|
});
|
|
66
72
|
const escalateAndWaitSchema = zod_1.z.object({
|
|
67
73
|
role: zod_1.z.string().describe('Target role for the escalation (e.g., "reviewer")'),
|
|
@@ -176,7 +182,7 @@ function registerHumanQueueTools(server) {
|
|
|
176
182
|
// ── claim_and_resolve ───────────────────────────────────────────────
|
|
177
183
|
server.registerTool('claim_and_resolve', {
|
|
178
184
|
title: 'Claim and Resolve',
|
|
179
|
-
description: 'Claim an escalation and immediately resolve it with a payload. Atomic operation.',
|
|
185
|
+
description: 'Claim an escalation and immediately resolve it with a payload. Atomic operation. Optionally pass `metadata` to record the outcome (disposition, timing) onto the row for the audit trail.',
|
|
180
186
|
inputSchema: claimAndResolveSchema,
|
|
181
187
|
}, async (args) => {
|
|
182
188
|
const claimed = await escalationService.claimEscalation(args.escalation_id, args.resolver_id, 5);
|
|
@@ -189,7 +195,7 @@ function registerHumanQueueTools(server) {
|
|
|
189
195
|
isError: true,
|
|
190
196
|
};
|
|
191
197
|
}
|
|
192
|
-
const resolved = await escalationService.resolveEscalation(args.escalation_id, args.payload);
|
|
198
|
+
const resolved = await escalationService.resolveEscalation(args.escalation_id, args.payload, args.metadata);
|
|
193
199
|
if (!resolved) {
|
|
194
200
|
return {
|
|
195
201
|
content: [{
|
|
@@ -213,10 +219,10 @@ function registerHumanQueueTools(server) {
|
|
|
213
219
|
// ── resolve_escalation ──────────────────────────────────────────────
|
|
214
220
|
server.registerTool('resolve_escalation', {
|
|
215
221
|
title: 'Resolve Escalation',
|
|
216
|
-
description: 'Resolve an already-claimed escalation with a payload. Use when the claim happened externally (e.g. via API).',
|
|
222
|
+
description: 'Resolve an already-claimed escalation with a payload. Use when the claim happened externally (e.g. via API). Optionally pass `metadata` to record the outcome (disposition, timing) onto the row for the audit trail.',
|
|
217
223
|
inputSchema: resolveEscalationSchema,
|
|
218
224
|
}, async (args) => {
|
|
219
|
-
const resolved = await escalationService.resolveEscalation(args.escalation_id, args.payload);
|
|
225
|
+
const resolved = await escalationService.resolveEscalation(args.escalation_id, args.payload, args.metadata);
|
|
220
226
|
if (!resolved) {
|
|
221
227
|
return {
|
|
222
228
|
content: [{
|
|
@@ -87,11 +87,15 @@ async function conditionLT(signalId, escalation) {
|
|
|
87
87
|
taskQueue: LT_ACTIVITY_QUEUE,
|
|
88
88
|
retry: { maximumAttempts: 3 },
|
|
89
89
|
});
|
|
90
|
-
// Strip $escalation_id before
|
|
91
|
-
|
|
90
|
+
// Strip the injected control keys ($escalation_id, $escalation_metadata) before the
|
|
91
|
+
// payload is returned to the caller. The outcome patch ($escalation_metadata, set by
|
|
92
|
+
// the resolve orchestrator's signal paths) rides INTO the single atomic resolve below
|
|
93
|
+
// so it merges in the same guarded UPDATE — never a separate write.
|
|
94
|
+
const { $escalation_id: _id, $escalation_metadata: metadata, ...resolverPayload } = raw;
|
|
92
95
|
await ltResolveEscalation({
|
|
93
96
|
escalationId,
|
|
94
97
|
resolverPayload: resolverPayload,
|
|
98
|
+
metadata: metadata,
|
|
95
99
|
});
|
|
96
100
|
return resolverPayload;
|
|
97
101
|
}
|
|
@@ -34,9 +34,8 @@ async function getAllEscalationChains() {
|
|
|
34
34
|
*/
|
|
35
35
|
async function addEscalationChain(sourceRole, targetRole) {
|
|
36
36
|
const pool = (0, db_1.getPool)();
|
|
37
|
-
|
|
38
|
-
await pool.query(sql_1.
|
|
39
|
-
await pool.query(sql_1.INSERT_ESCALATION_CHAIN, [sourceRole, targetRole]);
|
|
37
|
+
// One atomic statement: ensure both role FK targets + insert the chain link.
|
|
38
|
+
await pool.query(sql_1.ADD_ESCALATION_CHAIN, [sourceRole, targetRole]);
|
|
40
39
|
}
|
|
41
40
|
/**
|
|
42
41
|
* Remove a single escalation chain entry.
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export declare const GET_ESCALATION_TARGETS = "\n SELECT target_role\n FROM lt_config_role_escalations\n WHERE source_role = $1\n ORDER BY target_role";
|
|
2
2
|
export declare const GET_ALL_ESCALATION_CHAINS = "\n SELECT source_role, target_role\n FROM lt_config_role_escalations\n ORDER BY source_role, target_role";
|
|
3
3
|
export declare const INSERT_ESCALATION_CHAIN = "\n INSERT INTO lt_config_role_escalations (source_role, target_role)\n VALUES ($1, $2)\n ON CONFLICT DO NOTHING";
|
|
4
|
+
/**
|
|
5
|
+
* Add an escalation chain in ONE atomic statement: ensure both role FK targets
|
|
6
|
+
* exist, then insert the source→target link. Postgres checks the chain's FK to
|
|
7
|
+
* lt_roles at statement end (after the sibling ensure CTEs run), so brand-new
|
|
8
|
+
* roles are valid targets. Replaces the former ensure+ensure+insert three-call
|
|
9
|
+
* saga, which had no transaction guarding the writes together.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ADD_ESCALATION_CHAIN = "\n WITH ensured_source AS (\n INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING\n ), ensured_target AS (\n INSERT INTO lt_roles (role) VALUES ($2) ON CONFLICT DO NOTHING\n )\n INSERT INTO lt_config_role_escalations (source_role, target_role)\n VALUES ($1, $2)\n ON CONFLICT DO NOTHING";
|
|
4
12
|
export declare const DELETE_ESCALATION_CHAIN = "\n DELETE FROM lt_config_role_escalations\n WHERE source_role = $1 AND target_role = $2";
|
|
5
13
|
export declare const DELETE_ESCALATION_CHAINS_BY_SOURCE = "\n DELETE FROM lt_config_role_escalations\n WHERE source_role = $1";
|
|
6
14
|
export declare const CHECK_ESCALATION_CHAIN_EXISTS = "\n SELECT 1\n FROM lt_config_role_escalations\n WHERE source_role = $1 AND target_role = $2";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ─── Escalation chain queries ───────────────────────────────────────────────
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.COUNT_ACTIVE_ESCALATION_REFS = exports.COUNT_WORKFLOW_REFS = exports.COUNT_CHAIN_REFS = exports.COUNT_USER_ROLE_REFS = exports.LIST_ROLES_WITH_DETAILS = exports.DELETE_ROLE = exports.LIST_ROLES = exports.ENSURE_ROLE_EXISTS = exports.CHECK_ESCALATION_CHAIN_EXISTS = exports.DELETE_ESCALATION_CHAINS_BY_SOURCE = exports.DELETE_ESCALATION_CHAIN = exports.INSERT_ESCALATION_CHAIN = exports.GET_ALL_ESCALATION_CHAINS = exports.GET_ESCALATION_TARGETS = void 0;
|
|
4
|
+
exports.COUNT_ACTIVE_ESCALATION_REFS = exports.COUNT_WORKFLOW_REFS = exports.COUNT_CHAIN_REFS = exports.COUNT_USER_ROLE_REFS = exports.LIST_ROLES_WITH_DETAILS = exports.DELETE_ROLE = exports.LIST_ROLES = exports.ENSURE_ROLE_EXISTS = exports.CHECK_ESCALATION_CHAIN_EXISTS = exports.DELETE_ESCALATION_CHAINS_BY_SOURCE = exports.DELETE_ESCALATION_CHAIN = exports.ADD_ESCALATION_CHAIN = exports.INSERT_ESCALATION_CHAIN = exports.GET_ALL_ESCALATION_CHAINS = exports.GET_ESCALATION_TARGETS = void 0;
|
|
5
5
|
exports.GET_ESCALATION_TARGETS = `
|
|
6
6
|
SELECT target_role
|
|
7
7
|
FROM lt_config_role_escalations
|
|
@@ -15,6 +15,22 @@ exports.INSERT_ESCALATION_CHAIN = `
|
|
|
15
15
|
INSERT INTO lt_config_role_escalations (source_role, target_role)
|
|
16
16
|
VALUES ($1, $2)
|
|
17
17
|
ON CONFLICT DO NOTHING`;
|
|
18
|
+
/**
|
|
19
|
+
* Add an escalation chain in ONE atomic statement: ensure both role FK targets
|
|
20
|
+
* exist, then insert the source→target link. Postgres checks the chain's FK to
|
|
21
|
+
* lt_roles at statement end (after the sibling ensure CTEs run), so brand-new
|
|
22
|
+
* roles are valid targets. Replaces the former ensure+ensure+insert three-call
|
|
23
|
+
* saga, which had no transaction guarding the writes together.
|
|
24
|
+
*/
|
|
25
|
+
exports.ADD_ESCALATION_CHAIN = `
|
|
26
|
+
WITH ensured_source AS (
|
|
27
|
+
INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING
|
|
28
|
+
), ensured_target AS (
|
|
29
|
+
INSERT INTO lt_roles (role) VALUES ($2) ON CONFLICT DO NOTHING
|
|
30
|
+
)
|
|
31
|
+
INSERT INTO lt_config_role_escalations (source_role, target_role)
|
|
32
|
+
VALUES ($1, $2)
|
|
33
|
+
ON CONFLICT DO NOTHING`;
|
|
18
34
|
exports.DELETE_ESCALATION_CHAIN = `
|
|
19
35
|
DELETE FROM lt_config_role_escalations
|
|
20
36
|
WHERE source_role = $1 AND target_role = $2`;
|
|
@@ -31,17 +31,25 @@ async function createTask(input) {
|
|
|
31
31
|
input.executing_as || null,
|
|
32
32
|
input.status || null,
|
|
33
33
|
]);
|
|
34
|
-
|
|
35
|
-
(0
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
// ON CONFLICT (workflow_id) makes this idempotent: a retried proxyActivity or
|
|
35
|
+
// re-driven saga re-targets the same row. `_inserted` (xmax = 0) is true only
|
|
36
|
+
// when this call actually inserted, so the created event fires exactly once.
|
|
37
|
+
const row = rows[0];
|
|
38
|
+
const inserted = row._inserted === true;
|
|
39
|
+
delete row._inserted;
|
|
40
|
+
const task = row;
|
|
41
|
+
if (inserted) {
|
|
42
|
+
(0, publish_1.publishTaskEvent)({
|
|
43
|
+
type: 'task.created',
|
|
44
|
+
source: 'service',
|
|
45
|
+
workflowId: task.workflow_id || '',
|
|
46
|
+
workflowName: task.workflow_type || '',
|
|
47
|
+
taskQueue: task.task_queue || '',
|
|
48
|
+
taskId: task.id,
|
|
49
|
+
originId: task.origin_id || undefined,
|
|
50
|
+
status: task.status || 'pending',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
45
53
|
return task;
|
|
46
54
|
}
|
|
47
55
|
async function updateTask(id, input) {
|
|
@@ -70,7 +78,7 @@ async function updateTask(id, input) {
|
|
|
70
78
|
values.push(JSON.stringify(input.milestones));
|
|
71
79
|
}
|
|
72
80
|
values.push(id);
|
|
73
|
-
const { rows } = await pool.query(
|
|
81
|
+
const { rows } = await pool.query((0, sql_1.updateTaskById)(sets.join(', '), idx), values);
|
|
74
82
|
const task = rows[0];
|
|
75
83
|
// Publish status-driven events from the single write path
|
|
76
84
|
if (input.status && task) {
|
|
@@ -148,8 +156,8 @@ async function listTasks(filters) {
|
|
|
148
156
|
const limit = filters.limit || 50;
|
|
149
157
|
const offset = filters.offset || 0;
|
|
150
158
|
const [countResult, dataResult] = await Promise.all([
|
|
151
|
-
pool.query(
|
|
152
|
-
pool.query(
|
|
159
|
+
pool.query((0, sql_1.listTasksCount)(where), values),
|
|
160
|
+
pool.query((0, sql_1.listTasksData)(where, idx++, idx++), [...values, limit, offset]),
|
|
153
161
|
]);
|
|
154
162
|
return {
|
|
155
163
|
tasks: dataResult.rows,
|
|
@@ -44,27 +44,9 @@ async function listProcesses(filters) {
|
|
|
44
44
|
having = `HAVING COUNT(*) > COUNT(*) FILTER (WHERE status = 'completed')
|
|
45
45
|
AND COUNT(*) FILTER (WHERE status = 'needs_intervention') = 0`;
|
|
46
46
|
}
|
|
47
|
-
// Count
|
|
48
|
-
const countSql = having
|
|
49
|
-
|
|
50
|
-
SELECT origin_id FROM lt_tasks ${where}
|
|
51
|
-
GROUP BY origin_id ${having}
|
|
52
|
-
) sub`
|
|
53
|
-
: `SELECT COUNT(DISTINCT origin_id) FROM lt_tasks ${where}`;
|
|
54
|
-
const dataSql = `SELECT
|
|
55
|
-
origin_id,
|
|
56
|
-
COUNT(*)::int AS task_count,
|
|
57
|
-
COUNT(*) FILTER (WHERE status = 'completed')::int AS completed,
|
|
58
|
-
COUNT(*) FILTER (WHERE status = 'needs_intervention')::int AS escalated,
|
|
59
|
-
array_agg(DISTINCT workflow_type) AS workflow_types,
|
|
60
|
-
MIN(created_at) AS started_at,
|
|
61
|
-
MAX(COALESCE(completed_at, created_at)) AS last_activity
|
|
62
|
-
FROM lt_tasks
|
|
63
|
-
${where}
|
|
64
|
-
GROUP BY origin_id
|
|
65
|
-
${having}
|
|
66
|
-
ORDER BY MAX(created_at) DESC
|
|
67
|
-
LIMIT $${idx++} OFFSET $${idx++}`;
|
|
47
|
+
// Count wraps the grouped set in a subquery when a status HAVING filter is present.
|
|
48
|
+
const countSql = (0, sql_1.listProcessesCount)(where, having);
|
|
49
|
+
const dataSql = (0, sql_1.listProcessesData)(where, having, idx++, idx++);
|
|
68
50
|
const dataParams = [...filterParams, limit, offset];
|
|
69
51
|
const [countResult, dataResult] = await Promise.all([
|
|
70
52
|
pool.query(countSql, filterParams),
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
export declare const CREATE_TASK = "\n INSERT INTO lt_tasks\n (workflow_id, workflow_type, lt_type, task_queue, signal_id,\n parent_workflow_id, origin_id, parent_id, envelope, metadata, priority,\n trace_id, span_id, initiated_by, principal_type, executing_as, status)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, COALESCE($17, 'pending'))\n RETURNING
|
|
1
|
+
export declare const CREATE_TASK = "\n INSERT INTO lt_tasks\n (workflow_id, workflow_type, lt_type, task_queue, signal_id,\n parent_workflow_id, origin_id, parent_id, envelope, metadata, priority,\n trace_id, span_id, initiated_by, principal_type, executing_as, status)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, COALESCE($17, 'pending'))\n ON CONFLICT (workflow_id) DO UPDATE SET workflow_id = lt_tasks.workflow_id\n RETURNING *, (xmax = 0) AS _inserted";
|
|
2
2
|
export declare const APPEND_MILESTONES = "\n UPDATE lt_tasks\n SET milestones = milestones || $1::jsonb\n WHERE id = $2\n RETURNING *";
|
|
3
3
|
export declare const GET_TASK = "SELECT * FROM lt_tasks WHERE id = $1";
|
|
4
4
|
export declare const GET_TASK_BY_SIGNAL_ID = "SELECT * FROM lt_tasks WHERE signal_id = $1";
|
|
5
5
|
export declare const GET_TASK_BY_WORKFLOW_ID = "SELECT * FROM lt_tasks WHERE workflow_id = $1 ORDER BY created_at DESC LIMIT 1";
|
|
6
|
+
/**
|
|
7
|
+
* Build the partial-update statement for updateTask. The caller assembles the
|
|
8
|
+
* `SET` clause from whichever fields are present (each as a `$n` placeholder) and
|
|
9
|
+
* passes the placeholder index of the trailing `id` bind. One guarded UPDATE by
|
|
10
|
+
* primary key — no read-then-write.
|
|
11
|
+
*/
|
|
12
|
+
export declare function updateTaskById(setClause: string, idIdx: number): string;
|
|
13
|
+
/** Count query for listTasks. `where` is the caller-built `WHERE …` clause (or ''). */
|
|
14
|
+
export declare function listTasksCount(where: string): string;
|
|
15
|
+
/** Page query for listTasks. limit/offset placeholder indices follow the filter binds. */
|
|
16
|
+
export declare function listTasksData(where: string, limitIdx: number, offsetIdx: number): string;
|
|
6
17
|
export declare const RESOLVE_TASK_BY_WORKFLOW_ID = "SELECT workflow_type, task_queue FROM lt_tasks WHERE workflow_id = $1 ORDER BY created_at DESC LIMIT 1";
|
|
7
18
|
export declare const RESOLVE_CONFIG_TASK_QUEUE = "SELECT task_queue FROM lt_config_workflows WHERE workflow_type = $1";
|
|
8
19
|
/**
|
|
@@ -12,6 +23,18 @@ export declare const RESOLVE_CONFIG_TASK_QUEUE = "SELECT task_queue FROM lt_conf
|
|
|
12
23
|
*/
|
|
13
24
|
export declare const RESOLVE_JOB_ENTITY: (appId: string) => string;
|
|
14
25
|
export declare const GET_PROCESS_TASKS = "SELECT * FROM lt_tasks WHERE origin_id = $1 ORDER BY created_at ASC";
|
|
26
|
+
/**
|
|
27
|
+
* Count query for listProcesses. `where` restricts which origin_ids are in scope;
|
|
28
|
+
* `having` (empty string when no status filter) aggregates per-origin counts, so the
|
|
29
|
+
* count must wrap the grouped set in a subquery. Both fragments carry only `$n` binds.
|
|
30
|
+
*/
|
|
31
|
+
export declare function listProcessesCount(where: string, having: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Page query for listProcesses — one row per origin_id with rolled-up task counts,
|
|
34
|
+
* workflow types, and activity window. limit/offset placeholder indices follow the
|
|
35
|
+
* filter binds.
|
|
36
|
+
*/
|
|
37
|
+
export declare function listProcessesData(where: string, having: string, limitIdx: number, offsetIdx: number): string;
|
|
15
38
|
/**
|
|
16
39
|
* Build the process-stats totals CTE query for a given interval.
|
|
17
40
|
* The interval value MUST come from the VALID_PERIODS whitelist.
|
|
@@ -4,16 +4,29 @@
|
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GET_PROCESS_TASKS = exports.RESOLVE_JOB_ENTITY = exports.RESOLVE_CONFIG_TASK_QUEUE = exports.RESOLVE_TASK_BY_WORKFLOW_ID = exports.GET_TASK_BY_WORKFLOW_ID = exports.GET_TASK_BY_SIGNAL_ID = exports.GET_TASK = exports.APPEND_MILESTONES = exports.CREATE_TASK = void 0;
|
|
7
|
+
exports.updateTaskById = updateTaskById;
|
|
8
|
+
exports.listTasksCount = listTasksCount;
|
|
9
|
+
exports.listTasksData = listTasksData;
|
|
10
|
+
exports.listProcessesCount = listProcessesCount;
|
|
11
|
+
exports.listProcessesData = listProcessesData;
|
|
7
12
|
exports.processStatsTotals = processStatsTotals;
|
|
8
13
|
exports.processStatsByType = processStatsByType;
|
|
9
14
|
// ---- crud.ts queries ------------------------------------------------------
|
|
15
|
+
// A task is 1:1 with its workflow_id. ON CONFLICT (workflow_id) makes createTask
|
|
16
|
+
// idempotent under proxyActivity retry (maximumAttempts: 3) and re-driven sagas:
|
|
17
|
+
// a retry re-targets the SAME row instead of inserting a duplicate. The no-op
|
|
18
|
+
// DO UPDATE is required so RETURNING yields the existing row on conflict; the
|
|
19
|
+
// `(xmax = 0)` flag tells the caller whether this call actually inserted (so the
|
|
20
|
+
// `task.created` event fires exactly once). Requires the unique index from
|
|
21
|
+
// migration 012.
|
|
10
22
|
exports.CREATE_TASK = `
|
|
11
23
|
INSERT INTO lt_tasks
|
|
12
24
|
(workflow_id, workflow_type, lt_type, task_queue, signal_id,
|
|
13
25
|
parent_workflow_id, origin_id, parent_id, envelope, metadata, priority,
|
|
14
26
|
trace_id, span_id, initiated_by, principal_type, executing_as, status)
|
|
15
27
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, COALESCE($17, 'pending'))
|
|
16
|
-
|
|
28
|
+
ON CONFLICT (workflow_id) DO UPDATE SET workflow_id = lt_tasks.workflow_id
|
|
29
|
+
RETURNING *, (xmax = 0) AS _inserted`;
|
|
17
30
|
exports.APPEND_MILESTONES = `
|
|
18
31
|
UPDATE lt_tasks
|
|
19
32
|
SET milestones = milestones || $1::jsonb
|
|
@@ -22,6 +35,23 @@ exports.APPEND_MILESTONES = `
|
|
|
22
35
|
exports.GET_TASK = `SELECT * FROM lt_tasks WHERE id = $1`;
|
|
23
36
|
exports.GET_TASK_BY_SIGNAL_ID = `SELECT * FROM lt_tasks WHERE signal_id = $1`;
|
|
24
37
|
exports.GET_TASK_BY_WORKFLOW_ID = `SELECT * FROM lt_tasks WHERE workflow_id = $1 ORDER BY created_at DESC LIMIT 1`;
|
|
38
|
+
/**
|
|
39
|
+
* Build the partial-update statement for updateTask. The caller assembles the
|
|
40
|
+
* `SET` clause from whichever fields are present (each as a `$n` placeholder) and
|
|
41
|
+
* passes the placeholder index of the trailing `id` bind. One guarded UPDATE by
|
|
42
|
+
* primary key — no read-then-write.
|
|
43
|
+
*/
|
|
44
|
+
function updateTaskById(setClause, idIdx) {
|
|
45
|
+
return `UPDATE lt_tasks SET ${setClause} WHERE id = $${idIdx} RETURNING *`;
|
|
46
|
+
}
|
|
47
|
+
/** Count query for listTasks. `where` is the caller-built `WHERE …` clause (or ''). */
|
|
48
|
+
function listTasksCount(where) {
|
|
49
|
+
return `SELECT COUNT(*) FROM lt_tasks ${where}`;
|
|
50
|
+
}
|
|
51
|
+
/** Page query for listTasks. limit/offset placeholder indices follow the filter binds. */
|
|
52
|
+
function listTasksData(where, limitIdx, offsetIdx) {
|
|
53
|
+
return `SELECT * FROM lt_tasks ${where} ORDER BY created_at DESC LIMIT $${limitIdx} OFFSET $${offsetIdx}`;
|
|
54
|
+
}
|
|
25
55
|
// ---- resolve.ts queries ---------------------------------------------------
|
|
26
56
|
exports.RESOLVE_TASK_BY_WORKFLOW_ID = `SELECT workflow_type, task_queue FROM lt_tasks WHERE workflow_id = $1 ORDER BY created_at DESC LIMIT 1`;
|
|
27
57
|
exports.RESOLVE_CONFIG_TASK_QUEUE = `SELECT task_queue FROM lt_config_workflows WHERE workflow_type = $1`;
|
|
@@ -34,6 +64,40 @@ const RESOLVE_JOB_ENTITY = (appId) => `SELECT entity FROM "${appId}".jobs WHERE
|
|
|
34
64
|
exports.RESOLVE_JOB_ENTITY = RESOLVE_JOB_ENTITY;
|
|
35
65
|
// ---- process.ts queries ---------------------------------------------------
|
|
36
66
|
exports.GET_PROCESS_TASKS = `SELECT * FROM lt_tasks WHERE origin_id = $1 ORDER BY created_at ASC`;
|
|
67
|
+
/**
|
|
68
|
+
* Count query for listProcesses. `where` restricts which origin_ids are in scope;
|
|
69
|
+
* `having` (empty string when no status filter) aggregates per-origin counts, so the
|
|
70
|
+
* count must wrap the grouped set in a subquery. Both fragments carry only `$n` binds.
|
|
71
|
+
*/
|
|
72
|
+
function listProcessesCount(where, having) {
|
|
73
|
+
return having
|
|
74
|
+
? `SELECT COUNT(*) FROM (
|
|
75
|
+
SELECT origin_id FROM lt_tasks ${where}
|
|
76
|
+
GROUP BY origin_id ${having}
|
|
77
|
+
) sub`
|
|
78
|
+
: `SELECT COUNT(DISTINCT origin_id) FROM lt_tasks ${where}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Page query for listProcesses — one row per origin_id with rolled-up task counts,
|
|
82
|
+
* workflow types, and activity window. limit/offset placeholder indices follow the
|
|
83
|
+
* filter binds.
|
|
84
|
+
*/
|
|
85
|
+
function listProcessesData(where, having, limitIdx, offsetIdx) {
|
|
86
|
+
return `SELECT
|
|
87
|
+
origin_id,
|
|
88
|
+
COUNT(*)::int AS task_count,
|
|
89
|
+
COUNT(*) FILTER (WHERE status = 'completed')::int AS completed,
|
|
90
|
+
COUNT(*) FILTER (WHERE status = 'needs_intervention')::int AS escalated,
|
|
91
|
+
array_agg(DISTINCT workflow_type) AS workflow_types,
|
|
92
|
+
MIN(created_at) AS started_at,
|
|
93
|
+
MAX(COALESCE(completed_at, created_at)) AS last_activity
|
|
94
|
+
FROM lt_tasks
|
|
95
|
+
${where}
|
|
96
|
+
GROUP BY origin_id
|
|
97
|
+
${having}
|
|
98
|
+
ORDER BY MAX(created_at) DESC
|
|
99
|
+
LIMIT $${limitIdx} OFFSET $${offsetIdx}`;
|
|
100
|
+
}
|
|
37
101
|
/**
|
|
38
102
|
* Build the process-stats totals CTE query for a given interval.
|
|
39
103
|
* The interval value MUST come from the VALID_PERIODS whitelist.
|
|
@@ -14,6 +14,7 @@ exports.listUsers = listUsers;
|
|
|
14
14
|
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
15
15
|
const db_1 = require("../../lib/db");
|
|
16
16
|
const sql_1 = require("./sql");
|
|
17
|
+
const scope_1 = require("./scope");
|
|
17
18
|
// ─── Private helpers (exported for internal use by auth.ts) ──────────────────
|
|
18
19
|
async function attachRoles(user) {
|
|
19
20
|
const pool = (0, db_1.getPool)();
|
|
@@ -29,7 +30,13 @@ async function attachRolesToMany(users) {
|
|
|
29
30
|
const roleMap = new Map();
|
|
30
31
|
for (const row of rows) {
|
|
31
32
|
const list = roleMap.get(row.user_id) || [];
|
|
32
|
-
list.push({
|
|
33
|
+
list.push({
|
|
34
|
+
role: row.role,
|
|
35
|
+
type: row.type,
|
|
36
|
+
read_scope: row.read_scope,
|
|
37
|
+
write_scope: row.write_scope,
|
|
38
|
+
created_at: row.created_at,
|
|
39
|
+
});
|
|
33
40
|
roleMap.set(row.user_id, list);
|
|
34
41
|
}
|
|
35
42
|
return users.map((u) => ({ ...u, roles: roleMap.get(u.id) || [] }));
|
|
@@ -40,7 +47,16 @@ async function createUser(input) {
|
|
|
40
47
|
const passwordHash = input.password
|
|
41
48
|
? await bcryptjs_1.default.hash(input.password, 10)
|
|
42
49
|
: null;
|
|
43
|
-
|
|
50
|
+
// One atomic statement creates the user, ensures the role FK targets, and links
|
|
51
|
+
// the assignments — no partial-roles window, no per-role N+1. Roles + their
|
|
52
|
+
// effective scope travel as parallel text[] arrays. admin/superadmin normalize
|
|
53
|
+
// to ('all','all'); members store the requested scope.
|
|
54
|
+
const roleNames = (input.roles || []).map((r) => r.role);
|
|
55
|
+
const roleTypes = (input.roles || []).map((r) => r.type);
|
|
56
|
+
const effScopes = (input.roles || []).map((r) => (0, scope_1.effectiveScope)(r.type, r.read_scope ?? scope_1.DEFAULT_READ_SCOPE, r.write_scope ?? scope_1.DEFAULT_WRITE_SCOPE));
|
|
57
|
+
const roleReadScopes = effScopes.map((e) => e.read);
|
|
58
|
+
const roleWriteScopes = effScopes.map((e) => e.write);
|
|
59
|
+
const { rows } = await pool.query(sql_1.CREATE_USER_WITH_ROLES, [
|
|
44
60
|
input.external_id,
|
|
45
61
|
input.email || null,
|
|
46
62
|
input.display_name || null,
|
|
@@ -49,16 +65,12 @@ async function createUser(input) {
|
|
|
49
65
|
passwordHash,
|
|
50
66
|
input.oauth_provider || null,
|
|
51
67
|
input.oauth_provider_id || null,
|
|
68
|
+
roleNames,
|
|
69
|
+
roleTypes,
|
|
70
|
+
roleReadScopes,
|
|
71
|
+
roleWriteScopes,
|
|
52
72
|
]);
|
|
53
|
-
|
|
54
|
-
if (input.roles && input.roles.length > 0) {
|
|
55
|
-
for (const r of input.roles) {
|
|
56
|
-
// Ensure the role exists in lt_roles (FK constraint)
|
|
57
|
-
await pool.query(sql_1.ENSURE_ROLE_EXISTS, [r.role]);
|
|
58
|
-
await pool.query(sql_1.INSERT_USER_ROLE_IGNORE, [user.id, r.role, r.type]);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return attachRoles(user);
|
|
73
|
+
return attachRoles(rows[0]);
|
|
62
74
|
}
|
|
63
75
|
async function getUser(id) {
|
|
64
76
|
const pool = (0, db_1.getPool)();
|
|
@@ -140,6 +152,12 @@ async function listUsers(filters) {
|
|
|
140
152
|
conditions.push(`u.status = $${idx++}`);
|
|
141
153
|
values.push(filters.status);
|
|
142
154
|
}
|
|
155
|
+
if (filters.search) {
|
|
156
|
+
// Server-side free-text over the user's display fields — one term, one param.
|
|
157
|
+
const i = idx++;
|
|
158
|
+
conditions.push(`(u.display_name ILIKE '%' || $${i} || '%' OR u.email ILIKE '%' || $${i} || '%' OR u.external_id ILIKE '%' || $${i} || '%')`);
|
|
159
|
+
values.push(filters.search);
|
|
160
|
+
}
|
|
143
161
|
const join = needsJoin ? 'INNER JOIN lt_user_roles r ON r.user_id = u.id' : '';
|
|
144
162
|
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
145
163
|
const limit = filters.limit || 50;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { CreateUserInput, UpdateUserInput, VALID_ROLE_TYPES } from './types';
|
|
2
2
|
export { createUser, getUser, getUserByExternalId, getUserByEmail, updateUser, deleteUser, listUsers, } from './crud';
|
|
3
3
|
export { isValidRoleType, addUserRole, removeUserRole, getUserRoles, hasRole, hasRoleType, } from './roles';
|
|
4
|
-
export { isSuperAdmin, isGroupAdmin, canManageRole, hasGlobalEscalationAccess, hasRolesAsAdmin } from './rbac';
|
|
4
|
+
export { isSuperAdmin, isGroupAdmin, canManageRole, hasGlobalEscalationAccess, hasRolesAsAdmin, getRoleScope, getRoleWriteScope } from './rbac';
|
|
5
|
+
export { READ_SCOPES, WRITE_SCOPES, DEFAULT_READ_SCOPE, DEFAULT_WRITE_SCOPE, isValidReadScope, isValidWriteScope, isValidScopePair, effectiveScope, } from './scope';
|
|
5
6
|
export { verifyPassword } from './auth';
|
|
6
7
|
export { seedAdmin } from './seed-admin';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.seedAdmin = exports.verifyPassword = exports.hasRolesAsAdmin = exports.hasGlobalEscalationAccess = exports.canManageRole = exports.isGroupAdmin = exports.isSuperAdmin = exports.hasRoleType = exports.hasRole = exports.getUserRoles = exports.removeUserRole = exports.addUserRole = exports.isValidRoleType = exports.listUsers = exports.deleteUser = exports.updateUser = exports.getUserByEmail = exports.getUserByExternalId = exports.getUser = exports.createUser = exports.VALID_ROLE_TYPES = void 0;
|
|
3
|
+
exports.seedAdmin = exports.verifyPassword = exports.effectiveScope = exports.isValidScopePair = exports.isValidWriteScope = exports.isValidReadScope = exports.DEFAULT_WRITE_SCOPE = exports.DEFAULT_READ_SCOPE = exports.WRITE_SCOPES = exports.READ_SCOPES = exports.getRoleWriteScope = exports.getRoleScope = exports.hasRolesAsAdmin = exports.hasGlobalEscalationAccess = exports.canManageRole = exports.isGroupAdmin = exports.isSuperAdmin = exports.hasRoleType = exports.hasRole = exports.getUserRoles = exports.removeUserRole = exports.addUserRole = exports.isValidRoleType = exports.listUsers = exports.deleteUser = exports.updateUser = exports.getUserByEmail = exports.getUserByExternalId = exports.getUser = exports.createUser = exports.VALID_ROLE_TYPES = void 0;
|
|
4
4
|
var types_1 = require("./types");
|
|
5
5
|
Object.defineProperty(exports, "VALID_ROLE_TYPES", { enumerable: true, get: function () { return types_1.VALID_ROLE_TYPES; } });
|
|
6
6
|
var crud_1 = require("./crud");
|
|
@@ -24,6 +24,17 @@ Object.defineProperty(exports, "isGroupAdmin", { enumerable: true, get: function
|
|
|
24
24
|
Object.defineProperty(exports, "canManageRole", { enumerable: true, get: function () { return rbac_1.canManageRole; } });
|
|
25
25
|
Object.defineProperty(exports, "hasGlobalEscalationAccess", { enumerable: true, get: function () { return rbac_1.hasGlobalEscalationAccess; } });
|
|
26
26
|
Object.defineProperty(exports, "hasRolesAsAdmin", { enumerable: true, get: function () { return rbac_1.hasRolesAsAdmin; } });
|
|
27
|
+
Object.defineProperty(exports, "getRoleScope", { enumerable: true, get: function () { return rbac_1.getRoleScope; } });
|
|
28
|
+
Object.defineProperty(exports, "getRoleWriteScope", { enumerable: true, get: function () { return rbac_1.getRoleWriteScope; } });
|
|
29
|
+
var scope_1 = require("./scope");
|
|
30
|
+
Object.defineProperty(exports, "READ_SCOPES", { enumerable: true, get: function () { return scope_1.READ_SCOPES; } });
|
|
31
|
+
Object.defineProperty(exports, "WRITE_SCOPES", { enumerable: true, get: function () { return scope_1.WRITE_SCOPES; } });
|
|
32
|
+
Object.defineProperty(exports, "DEFAULT_READ_SCOPE", { enumerable: true, get: function () { return scope_1.DEFAULT_READ_SCOPE; } });
|
|
33
|
+
Object.defineProperty(exports, "DEFAULT_WRITE_SCOPE", { enumerable: true, get: function () { return scope_1.DEFAULT_WRITE_SCOPE; } });
|
|
34
|
+
Object.defineProperty(exports, "isValidReadScope", { enumerable: true, get: function () { return scope_1.isValidReadScope; } });
|
|
35
|
+
Object.defineProperty(exports, "isValidWriteScope", { enumerable: true, get: function () { return scope_1.isValidWriteScope; } });
|
|
36
|
+
Object.defineProperty(exports, "isValidScopePair", { enumerable: true, get: function () { return scope_1.isValidScopePair; } });
|
|
37
|
+
Object.defineProperty(exports, "effectiveScope", { enumerable: true, get: function () { return scope_1.effectiveScope; } });
|
|
27
38
|
var auth_1 = require("./auth");
|
|
28
39
|
Object.defineProperty(exports, "verifyPassword", { enumerable: true, get: function () { return auth_1.verifyPassword; } });
|
|
29
40
|
var seed_admin_1 = require("./seed-admin");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LTReadScope, LTWriteScope } from '../../types';
|
|
1
2
|
/**
|
|
2
3
|
* Is the user a superadmin? (global — can manage all users and roles)
|
|
3
4
|
*/
|
|
@@ -24,6 +25,23 @@ export declare function canManageRole(actorId: string, role: string): Promise<bo
|
|
|
24
25
|
* all roles, and can perform bulk actions.
|
|
25
26
|
*/
|
|
26
27
|
export declare function hasGlobalEscalationAccess(userId: string): Promise<boolean>;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the user's effective scope for a single role — the breadth at which
|
|
30
|
+
* they may SEARCH (read) and ACT (write). One indexed PK lookup on lt_user_roles.
|
|
31
|
+
* Returns `null` when the user is not a member of the role.
|
|
32
|
+
*
|
|
33
|
+
* The management tier (admin/superadmin) short-circuits both axes to `'all'` via
|
|
34
|
+
* effectiveScope. Callers that already hold global access should skip this.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getRoleScope(userId: string, role: string): Promise<{
|
|
37
|
+
read: LTReadScope;
|
|
38
|
+
write: LTWriteScope;
|
|
39
|
+
} | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Effective WRITE scope for a role: `'all'` (act on any item), `'self'` (only
|
|
42
|
+
* items assigned to the user), or `'none'` (read-only, or not a member).
|
|
43
|
+
*/
|
|
44
|
+
export declare function getRoleWriteScope(userId: string, role: string): Promise<LTWriteScope>;
|
|
27
45
|
/**
|
|
28
46
|
* Batch check: does the user have admin type for ALL specified roles?
|
|
29
47
|
* Single query — replaces the N+1 loop in checkBulkPermission.
|
|
@@ -4,9 +4,12 @@ exports.isSuperAdmin = isSuperAdmin;
|
|
|
4
4
|
exports.isGroupAdmin = isGroupAdmin;
|
|
5
5
|
exports.canManageRole = canManageRole;
|
|
6
6
|
exports.hasGlobalEscalationAccess = hasGlobalEscalationAccess;
|
|
7
|
+
exports.getRoleScope = getRoleScope;
|
|
8
|
+
exports.getRoleWriteScope = getRoleWriteScope;
|
|
7
9
|
exports.hasRolesAsAdmin = hasRolesAsAdmin;
|
|
8
10
|
const db_1 = require("../../lib/db");
|
|
9
11
|
const roles_1 = require("./roles");
|
|
12
|
+
const scope_1 = require("./scope");
|
|
10
13
|
const sql_1 = require("./sql");
|
|
11
14
|
// ─── RBAC helpers ─────────────────────────────────────────────────────────────
|
|
12
15
|
/**
|
|
@@ -48,6 +51,30 @@ async function hasGlobalEscalationAccess(userId) {
|
|
|
48
51
|
const roles = await (0, roles_1.getUserRoles)(userId);
|
|
49
52
|
return roles.some((r) => r.role === 'admin' && r.type === 'admin');
|
|
50
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the user's effective scope for a single role — the breadth at which
|
|
56
|
+
* they may SEARCH (read) and ACT (write). One indexed PK lookup on lt_user_roles.
|
|
57
|
+
* Returns `null` when the user is not a member of the role.
|
|
58
|
+
*
|
|
59
|
+
* The management tier (admin/superadmin) short-circuits both axes to `'all'` via
|
|
60
|
+
* effectiveScope. Callers that already hold global access should skip this.
|
|
61
|
+
*/
|
|
62
|
+
async function getRoleScope(userId, role) {
|
|
63
|
+
const pool = (0, db_1.getPool)();
|
|
64
|
+
const { rows } = await pool.query(sql_1.GET_ROLE_SCOPE, [userId, role]);
|
|
65
|
+
if (rows.length === 0)
|
|
66
|
+
return null;
|
|
67
|
+
const { type, read_scope, write_scope } = rows[0];
|
|
68
|
+
return (0, scope_1.effectiveScope)(type, read_scope, write_scope);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Effective WRITE scope for a role: `'all'` (act on any item), `'self'` (only
|
|
72
|
+
* items assigned to the user), or `'none'` (read-only, or not a member).
|
|
73
|
+
*/
|
|
74
|
+
async function getRoleWriteScope(userId, role) {
|
|
75
|
+
const scope = await getRoleScope(userId, role);
|
|
76
|
+
return scope ? scope.write : 'none';
|
|
77
|
+
}
|
|
51
78
|
/**
|
|
52
79
|
* Batch check: does the user have admin type for ALL specified roles?
|
|
53
80
|
* Single query — replaces the N+1 loop in checkBulkPermission.
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LTReadScope, LTRoleType, LTUserRole, LTWriteScope } from '../../types';
|
|
2
2
|
export declare function isValidRoleType(type: string): type is LTRoleType;
|
|
3
|
-
|
|
3
|
+
/** Optional work-surface scope for a membership. Defaults to ('all','all') = full worker. */
|
|
4
|
+
export interface RoleScopeInput {
|
|
5
|
+
read_scope?: LTReadScope;
|
|
6
|
+
write_scope?: LTWriteScope;
|
|
7
|
+
}
|
|
8
|
+
export declare function addUserRole(userId: string, role: string, type: LTRoleType, scope?: RoleScopeInput): Promise<LTUserRole>;
|
|
4
9
|
export declare function removeUserRole(userId: string, role: string): Promise<boolean>;
|
|
5
10
|
export declare function getUserRoles(userId: string): Promise<LTUserRole[]>;
|
|
6
11
|
export declare function hasRole(userId: string, role: string): Promise<boolean>;
|
|
@@ -8,17 +8,33 @@ exports.hasRole = hasRole;
|
|
|
8
8
|
exports.hasRoleType = hasRoleType;
|
|
9
9
|
const db_1 = require("../../lib/db");
|
|
10
10
|
const sql_1 = require("./sql");
|
|
11
|
+
const scope_1 = require("./scope");
|
|
11
12
|
const types_1 = require("./types");
|
|
12
13
|
// ─── Role management ──────────────────────────────────────────────────────────
|
|
13
14
|
function isValidRoleType(type) {
|
|
14
15
|
return types_1.VALID_ROLE_TYPES.includes(type);
|
|
15
16
|
}
|
|
16
|
-
async function addUserRole(userId, role, type) {
|
|
17
|
+
async function addUserRole(userId, role, type, scope) {
|
|
18
|
+
// admin/superadmin always store ('all','all'); members store the requested scope.
|
|
19
|
+
const eff = (0, scope_1.effectiveScope)(type, scope?.read_scope ?? scope_1.DEFAULT_READ_SCOPE, scope?.write_scope ?? scope_1.DEFAULT_WRITE_SCOPE);
|
|
17
20
|
const pool = (0, db_1.getPool)();
|
|
18
|
-
// Ensure the role
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
21
|
+
// Ensure-role (FK target) and the scoped user-role upsert commit together so a
|
|
22
|
+
// failure between them cannot leave the FK target without its assignment.
|
|
23
|
+
const client = await pool.connect();
|
|
24
|
+
try {
|
|
25
|
+
await client.query('BEGIN');
|
|
26
|
+
await client.query(sql_1.ENSURE_ROLE_EXISTS, [role]);
|
|
27
|
+
const { rows } = await client.query(sql_1.UPSERT_USER_ROLE, [userId, role, type, eff.read, eff.write]);
|
|
28
|
+
await client.query('COMMIT');
|
|
29
|
+
return rows[0];
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
await client.query('ROLLBACK');
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
client.release();
|
|
37
|
+
}
|
|
22
38
|
}
|
|
23
39
|
async function removeUserRole(userId, role) {
|
|
24
40
|
const pool = (0, db_1.getPool)();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LTReadScope, LTRoleType, LTWriteScope } from '../../types';
|
|
2
|
+
export declare const READ_SCOPES: LTReadScope[];
|
|
3
|
+
export declare const WRITE_SCOPES: LTWriteScope[];
|
|
4
|
+
/** Defaults preserve legacy `member` behavior (full role worker). */
|
|
5
|
+
export declare const DEFAULT_READ_SCOPE: LTReadScope;
|
|
6
|
+
export declare const DEFAULT_WRITE_SCOPE: LTWriteScope;
|
|
7
|
+
export declare function isValidReadScope(scope: string): scope is LTReadScope;
|
|
8
|
+
export declare function isValidWriteScope(scope: string): scope is LTWriteScope;
|
|
9
|
+
/**
|
|
10
|
+
* Is (read, write) a valid point in the lattice? Enforces write ⊆ read:
|
|
11
|
+
* write='all' requires read='all'. (write='self'/'none' allowed under read='self'.)
|
|
12
|
+
*/
|
|
13
|
+
export declare function isValidScopePair(read: LTReadScope, write: LTWriteScope): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the scope a membership actually grants. The management tier
|
|
16
|
+
* (admin/superadmin) short-circuits both axes to `all` — those users always
|
|
17
|
+
* search and act on the whole role queue regardless of stored scope columns.
|
|
18
|
+
*/
|
|
19
|
+
export declare function effectiveScope(type: LTRoleType, read: LTReadScope, write: LTWriteScope): {
|
|
20
|
+
read: LTReadScope;
|
|
21
|
+
write: LTWriteScope;
|
|
22
|
+
};
|