@hotmeshio/long-tail 0.10.13 → 0.11.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/README.md +2 -4
- package/build/api/escalations/analytics.d.ts +6 -0
- package/build/api/escalations/analytics.js +123 -0
- package/build/api/escalations/index.d.ts +1 -0
- package/build/api/escalations/index.js +4 -1
- package/build/api/escalations/metadata.js +1 -1
- package/build/api/escalations/resolve.js +4 -4
- package/build/api/roles.d.ts +4 -0
- package/build/api/roles.js +8 -0
- package/build/api/scan-codes/choice.d.ts +11 -0
- package/build/api/scan-codes/choice.js +136 -0
- package/build/api/scan-codes/context.d.ts +24 -0
- package/build/api/scan-codes/context.js +86 -0
- package/build/api/scan-codes/execute.d.ts +1 -0
- package/build/api/scan-codes/execute.js +49 -244
- package/build/api/scan-codes/identity.d.ts +18 -0
- package/build/api/scan-codes/identity.js +116 -0
- package/build/api/scan-codes/index.d.ts +2 -0
- package/build/api/scan-codes/index.js +6 -1
- package/build/api/scan-codes/locate.d.ts +14 -0
- package/build/api/scan-codes/locate.js +127 -0
- package/build/api/scan-codes/present.d.ts +9 -0
- package/build/api/scan-codes/present.js +59 -0
- package/build/api/scan-codes/verbs.d.ts +15 -0
- package/build/api/scan-codes/verbs.js +205 -0
- package/build/bin/ltc.js +33 -0
- package/build/lib/cli/commands/escalations.d.ts +31 -0
- package/build/lib/cli/commands/escalations.js +88 -0
- package/build/lib/db/schemas/028_role_entity_facet.sql +18 -0
- package/build/lib/db/schemas/029_scan_scheme_kind.sql +31 -0
- package/build/modules/config.d.ts +5 -0
- package/build/modules/config.js +12 -0
- package/build/routes/escalations/acting.d.ts +12 -0
- package/build/routes/escalations/acting.js +25 -0
- package/build/routes/escalations/analytics.d.ts +8 -0
- package/build/routes/escalations/analytics.js +63 -0
- package/build/routes/escalations/index.js +3 -0
- package/build/routes/escalations/resolve.js +8 -1
- package/build/routes/escalations/single.js +13 -4
- package/build/routes/roles.js +1 -1
- package/build/routes/scan-codes.js +28 -2
- package/build/scripts/ortho-populate.js +14 -0
- package/build/scripts/printer-cycle.d.ts +25 -0
- package/build/scripts/printer-cycle.js +169 -0
- package/build/scripts/twin-farm.js +19 -5
- package/build/sdk/index.d.ts +2 -0
- package/build/sdk/index.js +3 -0
- package/build/services/escalation/aggregate-sql.d.ts +36 -0
- package/build/services/escalation/aggregate-sql.js +253 -0
- package/build/services/escalation/aggregate-validate.d.ts +57 -0
- package/build/services/escalation/aggregate-validate.js +218 -0
- package/build/services/escalation/aggregates.d.ts +26 -0
- package/build/services/escalation/aggregates.js +204 -0
- package/build/services/escalation/client.js +57 -21
- package/build/services/escalation/facet-sql.js +17 -0
- package/build/services/escalation/index.d.ts +2 -0
- package/build/services/escalation/index.js +8 -1
- package/build/services/escalation/metrics-cache.d.ts +18 -0
- package/build/services/escalation/metrics-cache.js +47 -1
- package/build/services/escalation/queries.js +2 -0
- package/build/services/escalation/resolver-validation.d.ts +1 -1
- package/build/services/escalation/resolver-validation.js +12 -5
- package/build/services/escalation/submit-guard.d.ts +29 -0
- package/build/services/escalation/submit-guard.js +57 -0
- package/build/services/iam/acting-identity.d.ts +16 -0
- package/build/services/iam/acting-identity.js +60 -0
- package/build/services/role/index.js +2 -0
- package/build/services/role/sql.d.ts +10 -2
- package/build/services/role/sql.js +14 -1
- package/build/services/role/types.d.ts +16 -0
- package/build/services/scan-code/read.js +4 -0
- package/build/services/scan-code/sql.d.ts +4 -4
- package/build/services/scan-code/sql.js +14 -8
- package/build/services/scan-code/validate.d.ts +2 -0
- package/build/services/scan-code/validate.js +86 -0
- package/build/services/scan-code/write.d.ts +4 -0
- package/build/services/scan-code/write.js +16 -2
- package/build/services/user/crud.d.ts +6 -0
- package/build/services/user/crud.js +16 -0
- package/build/services/user/index.d.ts +1 -1
- package/build/services/user/index.js +2 -1
- package/build/services/user/sql.d.ts +8 -0
- package/build/services/user/sql.js +9 -1
- package/build/shared/form-validation/index.d.ts +4 -0
- package/build/shared/form-validation/index.js +4 -0
- package/build/shared/form-validation/x-lt-labels.d.ts +28 -0
- package/build/shared/form-validation/x-lt-labels.js +42 -0
- package/build/shared/form-validation/x-lt-query.d.ts +56 -0
- package/build/shared/form-validation/x-lt-query.js +49 -0
- package/build/shared/form-validation/x-lt-submit-guard.d.ts +46 -0
- package/build/shared/form-validation/x-lt-submit-guard.js +25 -0
- package/build/shared/form-validation/x-lt-submit-on-claim.d.ts +22 -0
- package/build/shared/form-validation/x-lt-submit-on-claim.js +28 -0
- package/build/system/mcp-servers/admin/escalations.js +34 -0
- package/build/system/mcp-servers/admin/scan-codes.js +17 -1
- package/build/system/mcp-servers/admin/schemas.d.ts +643 -25
- package/build/system/mcp-servers/admin/schemas.js +127 -19
- package/build/system/mcp-servers/admin/users.js +3 -1
- package/build/system/mcp-servers/human-queue-schemas.d.ts +2 -2
- package/build/system/seed/tool-manifests-admin.d.ts +2283 -0
- package/build/system/seed/tool-manifests-admin.js +8 -5
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/analytics.d.ts +154 -0
- package/build/types/analytics.js +14 -0
- package/build/types/facets.d.ts +13 -0
- package/build/types/index.d.ts +5 -2
- package/build/types/index.js +5 -1
- package/build/types/scan-choice.d.ts +49 -0
- package/build/types/scan-choice.js +9 -0
- package/build/types/scan-code.d.ts +72 -1
- package/build/types/scan-code.js +30 -1
- package/dashboard/dist/assets/{AdminDashboard-Bv3pHBzn.js → AdminDashboard-DJ3LJQ09.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-Bv3pHBzn.js.map → AdminDashboard-DJ3LJQ09.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-Dk2zlvDb.js → AgentConfigPage-BMMvaD0z.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-Dk2zlvDb.js.map → AgentConfigPage-BMMvaD0z.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-DUno6EGs.js → AgentDetailPage-C6wfsnRe.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-DUno6EGs.js.map → AgentDetailPage-C6wfsnRe.js.map} +1 -1
- package/dashboard/dist/assets/AgentsPage-Cwqka7nE.js +2 -0
- package/dashboard/dist/assets/{AgentsPage-CToVu1qj.js.map → AgentsPage-Cwqka7nE.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-CVlG_AVC.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-CVlG_AVC.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-DQOpVB4M.js → BotPicker-BBkJBXZB.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DQOpVB4M.js.map → BotPicker-BBkJBXZB.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-Dmf46PUJ.js → CapabilitiesPage-wxmE4917.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-Dmf46PUJ.js.map → CapabilitiesPage-wxmE4917.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-CDADefUY.js → CollapsibleSection-B3FsrHwH.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-CDADefUY.js.map → CollapsibleSection-B3FsrHwH.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-B3nCqJdC.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-B3nCqJdC.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-8LIEvomi.js → CredentialsPage-Bv875IHe.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-8LIEvomi.js.map → CredentialsPage-Bv875IHe.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-CK3WSsM5.js → CronLabel-CSzjWuwj.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-CK3WSsM5.js.map → CronLabel-CSzjWuwj.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-FPmlA6cE.js → CustomDurationPicker-CCpqf3e4.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-FPmlA6cE.js.map → CustomDurationPicker-CCpqf3e4.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-CL9gA7pu.js → DropZone-CJkk-U3o.js} +2 -2
- package/dashboard/dist/assets/{DropZone-CL9gA7pu.js.map → DropZone-CJkk-U3o.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-BZVkVxE0.js → ElapsedCell-WV6pUaKo.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-BZVkVxE0.js.map → ElapsedCell-WV6pUaKo.js.map} +1 -1
- package/dashboard/dist/assets/EntityTimelinePanel-DrL86Yrf.js +2 -0
- package/dashboard/dist/assets/EntityTimelinePanel-DrL86Yrf.js.map +1 -0
- package/dashboard/dist/assets/{EscalationListSchemaPage-kYLdUtZX.js → EscalationListSchemaPage-CTQCALw4.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-kYLdUtZX.js.map → EscalationListSchemaPage-CTQCALw4.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-kUmCDivo.js → EscalationSchemaPage-D_UgP0pv.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-kUmCDivo.js.map → EscalationSchemaPage-D_UgP0pv.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-D17auO-m.js → EscalationsOverview-DuIUyxr0.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-D17auO-m.js.map → EscalationsOverview-DuIUyxr0.js.map} +1 -1
- package/dashboard/dist/assets/EventTable-BV6unphX.js +2 -0
- package/dashboard/dist/assets/EventTable-BV6unphX.js.map +1 -0
- package/dashboard/dist/assets/FilterBar-DYFgzXNB.js +2 -0
- package/dashboard/dist/assets/FilterBar-DYFgzXNB.js.map +1 -0
- package/dashboard/dist/assets/{GraphInvokePage-D11uOnZi.js → GraphInvokePage-BGHVUipJ.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-D11uOnZi.js.map → GraphInvokePage-BGHVUipJ.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-B-S2cK3E.js +2 -0
- package/dashboard/dist/assets/{HomePage-Bcq10Bxx.js.map → HomePage-B-S2cK3E.js.map} +1 -1
- package/dashboard/dist/assets/ListToolbar-Bl7F3A28.js +2 -0
- package/dashboard/dist/assets/{ListToolbar-IPXsTb3Q.js.map → ListToolbar-Bl7F3A28.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-Dx2j0KkF.js → McpOverview-_T9IsCD_.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-Dx2j0KkF.js.map → McpOverview-_T9IsCD_.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-CzF_bna8.js → McpQueryDetailPage-DtsVqKPw.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-CzF_bna8.js.map → McpQueryDetailPage-DtsVqKPw.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryPage-DX5u-1bx.js +2 -0
- package/dashboard/dist/assets/{McpQueryPage-CNsuiw_q.js.map → McpQueryPage-DX5u-1bx.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-BhcqqtYS.js +2 -0
- package/dashboard/dist/assets/McpRunDetailPage-BhcqqtYS.js.map +1 -0
- package/dashboard/dist/assets/{McpRunsPage-CK96s3Nt.js → McpRunsPage-L-1y8wnX.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-CK96s3Nt.js.map → McpRunsPage-L-1y8wnX.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-B9Vc_Pz2.js → NamespacePill-in1SLd0W.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-B9Vc_Pz2.js.map → NamespacePill-in1SLd0W.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-i8KcDpKo.js +3 -0
- package/dashboard/dist/assets/OperationsPage-i8KcDpKo.js.map +1 -0
- package/dashboard/dist/assets/OperatorDashboard-CIbDzOlh.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-C-jE0IRA.js.map → OperatorDashboard-CIbDzOlh.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-Ce4GcIYo.js → PageHeader-BJgWMZtw.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-Ce4GcIYo.js.map → PageHeader-BJgWMZtw.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-Ck0KZvXD.js → PageHeaderWithStats-Ccio4vyG.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-Ck0KZvXD.js.map → PageHeaderWithStats-Ccio4vyG.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-CJ6jqo7h.js → PersonaDetailPage-BOaf0TW1.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-CJ6jqo7h.js.map → PersonaDetailPage-BOaf0TW1.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-D8hL3kL5.js → PersonasPage-DSdRb90f.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-D8hL3kL5.js.map → PersonasPage-DSdRb90f.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-Vnx8Z5_y.js → ProcessDetailPage-DN9NqfeP.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-Vnx8Z5_y.js.map → ProcessDetailPage-DN9NqfeP.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-DRYtvS3N.js → ProcessesListPage-CfQEWaD4.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-DRYtvS3N.js.map → ProcessesListPage-CfQEWaD4.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-BC5HL7ls.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-BC5HL7ls.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-CnuBPhPw.js → RolePill-BNAP-uK6.js} +2 -2
- package/dashboard/dist/assets/{RolePill-CnuBPhPw.js.map → RolePill-BNAP-uK6.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-DCnW0VmD.js +2 -0
- package/dashboard/dist/assets/RolesPage-DCnW0VmD.js.map +1 -0
- package/dashboard/dist/assets/RunAsSelector-twbuX7fB.js +2 -0
- package/dashboard/dist/assets/{RunAsSelector-CS3KQ-BF.js.map → RunAsSelector-twbuX7fB.js.map} +1 -1
- package/dashboard/dist/assets/ScanCodesPage-CPNg6K--.js +2 -0
- package/dashboard/dist/assets/ScanCodesPage-CPNg6K--.js.map +1 -0
- package/dashboard/dist/assets/ScanSchemeDetailPage-DyLmCamO.js +2 -0
- package/dashboard/dist/assets/ScanSchemeDetailPage-DyLmCamO.js.map +1 -0
- package/dashboard/dist/assets/ScanStationPage-DoZccU5B.js +2 -0
- package/dashboard/dist/assets/ScanStationPage-DoZccU5B.js.map +1 -0
- package/dashboard/dist/assets/{ScopeBadge-BUpYpS3x.js → ScopeBadge-BG7KkZ8Q.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-BUpYpS3x.js.map → ScopeBadge-BG7KkZ8Q.js.map} +1 -1
- package/dashboard/dist/assets/SegmentedTabs-DXNzY05q.js +2 -0
- package/dashboard/dist/assets/SegmentedTabs-DXNzY05q.js.map +1 -0
- package/dashboard/dist/assets/StickyPagination-DIeYMPPd.js +2 -0
- package/dashboard/dist/assets/StickyPagination-DIeYMPPd.js.map +1 -0
- package/dashboard/dist/assets/{StreamMessageDetail-DyJyZwBl.js → StreamMessageDetail-gU4CUeQI.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-DyJyZwBl.js.map → StreamMessageDetail-gU4CUeQI.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-BlzV74V5.js → SwimlaneTimeline-Br-nBA4l.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-BlzV74V5.js.map → SwimlaneTimeline-Br-nBA4l.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-BbZ25nNe.js → TagInput-CvM5n6ww.js} +2 -2
- package/dashboard/dist/assets/{TagInput-BbZ25nNe.js.map → TagInput-CvM5n6ww.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-B0p_Z4J_.js → TaskDetailPage-_9cr_R0u.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-B0p_Z4J_.js.map → TaskDetailPage-_9cr_R0u.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-D0QKBrN3.js → TaskQueuePill-D4JPHXOq.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-D0QKBrN3.js.map → TaskQueuePill-D4JPHXOq.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-CiGR2QiQ.js → TasksListPage-DVENC9Jp.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-CiGR2QiQ.js.map → TasksListPage-DVENC9Jp.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-D_DA9ucC.js → TimeAgo-C9yYnOui.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-D_DA9ucC.js.map → TimeAgo-C9yYnOui.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-C7rkC9nh.js → TimestampCell-DoB8cYgw.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-C7rkC9nh.js.map → TimestampCell-DoB8cYgw.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-8xY9rkoc.js → ToolPill-BUjbC-Tj.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-8xY9rkoc.js.map → ToolPill-BUjbC-Tj.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-E8efYGYs.js → ToolTestPanel-CBtsIP8K.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-E8efYGYs.js.map → ToolTestPanel-CBtsIP8K.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-B455XrRc.js → TopicDetailPage-mR4Jaz83.js} +4 -4
- package/dashboard/dist/assets/{TopicDetailPage-B455XrRc.js.map → TopicDetailPage-mR4Jaz83.js.map} +1 -1
- package/dashboard/dist/assets/TopicsPage-CP7HFxIC.js +2 -0
- package/dashboard/dist/assets/{TopicsPage-1VAc29Qw.js.map → TopicsPage-CP7HFxIC.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-DikzjQHJ.js → UserName-BnLF6yhz.js} +2 -2
- package/dashboard/dist/assets/{UserName-DikzjQHJ.js.map → UserName-BnLF6yhz.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-xL956RZi.js +2 -0
- package/dashboard/dist/assets/WorkflowExecutionPage-xL956RZi.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowPill-Da406nEj.js → WorkflowPill-tXU0-eXu.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-Da406nEj.js.map → WorkflowPill-tXU0-eXu.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowsDashboard-B-u7zFKM.js +2 -0
- package/dashboard/dist/assets/{WorkflowsDashboard-BMQd_0tW.js.map → WorkflowsDashboard-B-u7zFKM.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CdxjVifp.js → WorkflowsOverview-CqZVarK-.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CdxjVifp.js.map → WorkflowsOverview-CqZVarK-.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DNVffIXa.js → YamlWorkflowDetailPage-DiFLavW0.js} +10 -10
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DNVffIXa.js.map → YamlWorkflowDetailPage-DiFLavW0.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-BGP8wuos.js → YamlWorkflowsPage-BuQM6uZA.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-BGP8wuos.js.map → YamlWorkflowsPage-BuQM6uZA.js.map} +1 -1
- package/dashboard/dist/assets/{agents-BZJLIJ4h.js → agents-DU9GPovp.js} +2 -2
- package/dashboard/dist/assets/{agents-BZJLIJ4h.js.map → agents-DU9GPovp.js.map} +1 -1
- package/dashboard/dist/assets/{bots-ClLcDGJb.js → bots-Cy-Hmg7w.js} +2 -2
- package/dashboard/dist/assets/{bots-ClLcDGJb.js.map → bots-Cy-Hmg7w.js.map} +1 -1
- package/dashboard/dist/assets/capabilities-sSJJqrp-.js +2 -0
- package/dashboard/dist/assets/{capabilities-IaCcS7ge.js.map → capabilities-sSJJqrp-.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-Zi4m_MSx.js → controlplane-TVCjDHQ-.js} +2 -2
- package/dashboard/dist/assets/{controlplane-Zi4m_MSx.js.map → controlplane-TVCjDHQ-.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-BpkjKYGd.js +2 -0
- package/dashboard/dist/assets/escalation-columns-BpkjKYGd.js.map +1 -0
- package/dashboard/dist/assets/index-5TblaCXQ.css +1 -0
- package/dashboard/dist/assets/index-7BP6FaCZ.js +2 -0
- package/dashboard/dist/assets/{index-Dldi3Q3M.js.map → index-7BP6FaCZ.js.map} +1 -1
- package/dashboard/dist/assets/{index-BzkE48xJ.js → index-B7IaBPBg.js} +4 -4
- package/dashboard/dist/assets/{index-BzkE48xJ.js.map → index-B7IaBPBg.js.map} +1 -1
- package/dashboard/dist/assets/index-BE0phFw0.js +2 -0
- package/dashboard/dist/assets/{index-CLmZaNTK.js.map → index-BE0phFw0.js.map} +1 -1
- package/dashboard/dist/assets/{index-BqGnhkh0.js → index-BYNN9N1T.js} +2 -2
- package/dashboard/dist/assets/{index-BqGnhkh0.js.map → index-BYNN9N1T.js.map} +1 -1
- package/dashboard/dist/assets/{index-kiKZresb.js → index-CLPVqbVY.js} +2 -2
- package/dashboard/dist/assets/{index-kiKZresb.js.map → index-CLPVqbVY.js.map} +1 -1
- package/dashboard/dist/assets/{index-C3o4OisO.js → index-Cfv86_L9.js} +2 -2
- package/dashboard/dist/assets/{index-C3o4OisO.js.map → index-Cfv86_L9.js.map} +1 -1
- package/dashboard/dist/assets/{index-yGNt29V9.js → index-CtwbQ_-m.js} +2 -2
- package/dashboard/dist/assets/{index-yGNt29V9.js.map → index-CtwbQ_-m.js.map} +1 -1
- package/dashboard/dist/assets/index-DOai950R.js +3 -0
- package/dashboard/dist/assets/index-DOai950R.js.map +1 -0
- package/dashboard/dist/assets/index-DTtHNC-D.js +5 -0
- package/dashboard/dist/assets/{index-CvNBdkB1.js.map → index-DTtHNC-D.js.map} +1 -1
- package/dashboard/dist/assets/{index-CJu2PBNx.js → index-DsWnVyVN.js} +2 -2
- package/dashboard/dist/assets/{index-CJu2PBNx.js.map → index-DsWnVyVN.js.map} +1 -1
- package/dashboard/dist/assets/{index-D0zFReGL.js → index-Dy-MMYti.js} +2 -2
- package/dashboard/dist/assets/{index-D0zFReGL.js.map → index-Dy-MMYti.js.map} +1 -1
- package/dashboard/dist/assets/index-errgQT5x.js +65 -0
- package/dashboard/dist/assets/index-errgQT5x.js.map +1 -0
- package/dashboard/dist/assets/{index-CmnNqpWg.js → index-fgBDhnI6.js} +2 -2
- package/dashboard/dist/assets/{index-CmnNqpWg.js.map → index-fgBDhnI6.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-DWLxGTib.js → knowledge-arh4NfmL.js} +2 -2
- package/dashboard/dist/assets/{knowledge-DWLxGTib.js.map → knowledge-arh4NfmL.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-B6AARZ2I.js → mcp-QGf5jHCo.js} +2 -2
- package/dashboard/dist/assets/{mcp-B6AARZ2I.js.map → mcp-QGf5jHCo.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-DrkvBU3a.js → mcp-query-BEJN1nMZ.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-DrkvBU3a.js.map → mcp-query-BEJN1nMZ.js.map} +1 -1
- package/dashboard/dist/assets/{personas-Cmz2SAEU.js → personas-DYtw1COf.js} +2 -2
- package/dashboard/dist/assets/{personas-Cmz2SAEU.js.map → personas-DYtw1COf.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-CSgfVCMy.js → pipelines-Byi6PnbE.js} +2 -2
- package/dashboard/dist/assets/{pipelines-CSgfVCMy.js.map → pipelines-Byi6PnbE.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-UOtUkDk-.js → tasks-D0SQdENz.js} +2 -2
- package/dashboard/dist/assets/{tasks-UOtUkDk-.js.map → tasks-D0SQdENz.js.map} +1 -1
- package/dashboard/dist/assets/{topics-Df5hGh6o.js → topics-DgDv5ZXP.js} +2 -2
- package/dashboard/dist/assets/{topics-Df5hGh6o.js.map → topics-DgDv5ZXP.js.map} +1 -1
- package/dashboard/dist/assets/useEventHooks-DZaYzE7s.js +2 -0
- package/dashboard/dist/assets/{useEventHooks-PDbuSJ12.js.map → useEventHooks-DZaYzE7s.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-tZSOlge3.js → useNamespace-C1OqiNXN.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-tZSOlge3.js.map → useNamespace-C1OqiNXN.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-CINLzE32.js → useYamlActivityEvents-CkmXDm-Z.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-CINLzE32.js.map → useYamlActivityEvents-CkmXDm-Z.js.map} +1 -1
- package/dashboard/dist/assets/{users-G8qwyZKS.js → users-D4DCFPH1.js} +2 -2
- package/dashboard/dist/assets/{users-G8qwyZKS.js.map → users-D4DCFPH1.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-BpZiWH2f.js → vendor-icons-BxuyFxH2.js} +156 -136
- package/dashboard/dist/assets/vendor-icons-BxuyFxH2.js.map +1 -0
- package/dashboard/dist/assets/{workflows-DbPqi1TW.js → workflows-qyc0ku_5.js} +2 -2
- package/dashboard/dist/assets/{workflows-DbPqi1TW.js.map → workflows-qyc0ku_5.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DozD8hpR.js → yaml-workflows-DWFww9Xn.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DozD8hpR.js.map → yaml-workflows-DWFww9Xn.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +133 -0
- package/docs/api/http/roles.md +6 -0
- package/docs/api/mcp/admin.md +53 -0
- package/docs/api/sdk/escalations.md +58 -0
- package/docs/cli.md +11 -0
- package/docs/dashboard.md +22 -20
- package/docs/data.md +12 -4
- package/docs/design-principles.md +9 -7
- package/docs/escalation-analytics.md +251 -0
- package/docs/hitl/roles.md +1 -1
- package/docs/hitl/x-lt-embed.md +8 -4
- package/docs/hitl/x-lt-footer.md +67 -0
- package/docs/hitl/x-lt-list-schema.md +10 -3
- package/docs/hitl-guide.md +11 -2
- package/docs/iam.md +12 -0
- package/docs/operations.md +17 -0
- package/docs/scan-codes.md +112 -0
- package/docs/sdk.md +1 -1
- package/package.json +2 -1
- package/dashboard/dist/assets/AgentsPage-CToVu1qj.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DldboL4W.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DldboL4W.js.map +0 -1
- package/dashboard/dist/assets/CountdownTimer-DPjIkerB.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-DPjIkerB.js.map +0 -1
- package/dashboard/dist/assets/EventTable-CwPWl163.js +0 -2
- package/dashboard/dist/assets/EventTable-CwPWl163.js.map +0 -1
- package/dashboard/dist/assets/FilterBar-D7GghUsU.js +0 -2
- package/dashboard/dist/assets/FilterBar-D7GghUsU.js.map +0 -1
- package/dashboard/dist/assets/HomePage-Bcq10Bxx.js +0 -2
- package/dashboard/dist/assets/ListToolbar-IPXsTb3Q.js +0 -2
- package/dashboard/dist/assets/McpQueryPage-CNsuiw_q.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-BJguEMS0.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-BJguEMS0.js.map +0 -1
- package/dashboard/dist/assets/OperationsPage-DvWrLuH6.js +0 -2
- package/dashboard/dist/assets/OperationsPage-DvWrLuH6.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-C-jE0IRA.js +0 -2
- package/dashboard/dist/assets/RoleDetailPage-BKkUvTO-.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-BKkUvTO-.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-BHzWK9n6.js +0 -2
- package/dashboard/dist/assets/RolesPage-BHzWK9n6.js.map +0 -1
- package/dashboard/dist/assets/RunAsSelector-CS3KQ-BF.js +0 -2
- package/dashboard/dist/assets/ScanCodesPage-BW4P4zWf.js +0 -2
- package/dashboard/dist/assets/ScanCodesPage-BW4P4zWf.js.map +0 -1
- package/dashboard/dist/assets/ScanSchemeDetailPage-BLykA4j-.js +0 -2
- package/dashboard/dist/assets/ScanSchemeDetailPage-BLykA4j-.js.map +0 -1
- package/dashboard/dist/assets/StickyPagination-hMrnLXde.js +0 -2
- package/dashboard/dist/assets/StickyPagination-hMrnLXde.js.map +0 -1
- package/dashboard/dist/assets/TopicsPage-1VAc29Qw.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-C15GSAoe.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-C15GSAoe.js.map +0 -1
- package/dashboard/dist/assets/WorkflowsDashboard-BMQd_0tW.js +0 -2
- package/dashboard/dist/assets/capabilities-IaCcS7ge.js +0 -2
- package/dashboard/dist/assets/escalation-columns-BY2mg9DY.js +0 -2
- package/dashboard/dist/assets/escalation-columns-BY2mg9DY.js.map +0 -1
- package/dashboard/dist/assets/index-CLmZaNTK.js +0 -2
- package/dashboard/dist/assets/index-CfAi1OsI.js +0 -65
- package/dashboard/dist/assets/index-CfAi1OsI.js.map +0 -1
- package/dashboard/dist/assets/index-CvNBdkB1.js +0 -5
- package/dashboard/dist/assets/index-Dldi3Q3M.js +0 -2
- package/dashboard/dist/assets/index-mKcQGi2B.js +0 -3
- package/dashboard/dist/assets/index-mKcQGi2B.js.map +0 -1
- package/dashboard/dist/assets/index-zQiUPfxA.css +0 -1
- package/dashboard/dist/assets/useEventHooks-PDbuSJ12.js +0 -2
- package/dashboard/dist/assets/vendor-icons-BpZiWH2f.js.map +0 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.presentStep = presentStep;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const identity_1 = require("./identity");
|
|
6
|
+
const locate_1 = require("./locate");
|
|
7
|
+
const verbs_1 = require("./verbs");
|
|
8
|
+
// ── The info-choice outcome — locate → present reality + labeled choices ───
|
|
9
|
+
//
|
|
10
|
+
// Some objects carry one code for their whole life: the next action cannot be
|
|
11
|
+
// inferred from state alone, and the system should not guess. A PRESENT step
|
|
12
|
+
// locates the row, states its reality, and returns the step's configured,
|
|
13
|
+
// labeled choice set — the same escalation primitives, presented instead of
|
|
14
|
+
// auto-executed. The human's tap (or a second scan matching a choice's code)
|
|
15
|
+
// is the disambiguator; execution goes back through /execute-choice, which
|
|
16
|
+
// re-validates everything.
|
|
17
|
+
/**
|
|
18
|
+
* Locate the target and present the step's choices. Choices whose identity
|
|
19
|
+
* requirement is unsatisfied come back withheld — the screen renders the
|
|
20
|
+
* rule's "scan your badge" affordance beside them instead of a dead button.
|
|
21
|
+
*/
|
|
22
|
+
async function presentStep(step, ctx) {
|
|
23
|
+
const result = await (0, locate_1.locateForStep)(step, ctx, 2);
|
|
24
|
+
if (!result || result.escalations.length === 0)
|
|
25
|
+
return null;
|
|
26
|
+
const escalation = result.escalations[0];
|
|
27
|
+
// One satisfaction test covers the step's scope; per-choice requirements
|
|
28
|
+
// reuse it because a choice executes against the same step query.
|
|
29
|
+
const satisfied = await (0, identity_1.actingIdentitySatisfied)(step, ctx);
|
|
30
|
+
// One scan, one action: a single confirm-less choice executes directly when
|
|
31
|
+
// configured — presenting a one-button screen would be a pointless tap. An
|
|
32
|
+
// unsatisfied identity requirement still presents (the client's badge
|
|
33
|
+
// stop-over), never a silent or misattributed execution.
|
|
34
|
+
const only = step.choices?.length === 1 ? step.choices[0] : undefined;
|
|
35
|
+
const autoSelect = step.autoSelectSingle === true && only !== undefined && !only.confirm;
|
|
36
|
+
if (autoSelect && (!only.requireActingIdentity || satisfied)) {
|
|
37
|
+
return (0, verbs_1.dispatchChoiceVerb)({ query: step.query, verb: only.verb, params: only.params }, ctx, escalation);
|
|
38
|
+
}
|
|
39
|
+
const choices = (step.choices ?? []).map((choice, index) => ({
|
|
40
|
+
index,
|
|
41
|
+
label: choice.label,
|
|
42
|
+
verb: choice.verb,
|
|
43
|
+
...(choice.confirm ? { confirm: choice.confirm } : {}),
|
|
44
|
+
...(choice.requireActingIdentity ? { requireActingIdentity: true } : {}),
|
|
45
|
+
...(choice.code ? { code: choice.code } : {}),
|
|
46
|
+
withheld: !!choice.requireActingIdentity && !satisfied,
|
|
47
|
+
}));
|
|
48
|
+
return {
|
|
49
|
+
status: 200,
|
|
50
|
+
data: {
|
|
51
|
+
outcome: types_1.SCAN_OUTCOMES.CHOICES,
|
|
52
|
+
verb: step.verb,
|
|
53
|
+
escalation,
|
|
54
|
+
choices,
|
|
55
|
+
notPrimed: ctx.rule.notPrimed,
|
|
56
|
+
...(autoSelect ? { autoSelect: true } : {}),
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ScanExecuteResponse, type ScanStep } from '../../types';
|
|
2
|
+
import type { LTApiResult } from '../../types/sdk';
|
|
3
|
+
import { type StepContext } from './context';
|
|
4
|
+
export declare function claimStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
|
|
5
|
+
export declare function resolveStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
|
|
6
|
+
export declare function escalateStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
|
|
7
|
+
export declare function releaseStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
|
|
8
|
+
/**
|
|
9
|
+
* Dispatch one CHOICE verb against an already-presented row. Unlike the step
|
|
10
|
+
* walk (where a miss falls through to the next step), the screen showed this
|
|
11
|
+
* exact row — so a miss answers with the truth: FORBIDDEN when the actor
|
|
12
|
+
* holds no write scope here, CONFLICT when a concurrent actor won the race.
|
|
13
|
+
*/
|
|
14
|
+
export declare function dispatchChoiceVerb(step: ScanStep, ctx: StepContext, row: Record<string, any>): Promise<LTApiResult<ScanExecuteResponse>>;
|
|
15
|
+
export declare function cancelStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.claimStep = claimStep;
|
|
37
|
+
exports.resolveStep = resolveStep;
|
|
38
|
+
exports.escalateStep = escalateStep;
|
|
39
|
+
exports.releaseStep = releaseStep;
|
|
40
|
+
exports.dispatchChoiceVerb = dispatchChoiceVerb;
|
|
41
|
+
exports.cancelStep = cancelStep;
|
|
42
|
+
const scanCodeService = __importStar(require("../../services/scan-code"));
|
|
43
|
+
const escalationService = __importStar(require("../../services/escalation"));
|
|
44
|
+
const metadata_1 = require("../escalations/metadata");
|
|
45
|
+
const create_1 = require("../escalations/create");
|
|
46
|
+
const claim_1 = require("../escalations/claim");
|
|
47
|
+
const helpers_1 = require("../escalations/helpers");
|
|
48
|
+
const types_1 = require("../../types");
|
|
49
|
+
const context_1 = require("./context");
|
|
50
|
+
// ── Mutating verbs — each a single atomic operation under the ACTOR's RBAC ──
|
|
51
|
+
// ctx.auth is the effective actor: the badged person when a grant rode the
|
|
52
|
+
// request, otherwise the authenticated principal. Attribution and write
|
|
53
|
+
// scoping both derive from it, live, inside the escalation APIs.
|
|
54
|
+
async function claimStep(step, ctx) {
|
|
55
|
+
const result = await (0, metadata_1.claimByMetadata)({
|
|
56
|
+
key: ctx.scheme.target_facet,
|
|
57
|
+
value: ctx.parsed.target,
|
|
58
|
+
durationMinutes: step.params?.durationMinutes,
|
|
59
|
+
metadata: { ...(0, context_1.interpolatedMetadata)(step, ctx), ...(0, context_1.provenance)(ctx) },
|
|
60
|
+
restrictRoles: step.query?.roles,
|
|
61
|
+
}, ctx.auth);
|
|
62
|
+
if (result.status === 404)
|
|
63
|
+
return null;
|
|
64
|
+
if (result.status === 403)
|
|
65
|
+
return (0, context_1.forbidden)(result.error);
|
|
66
|
+
if (result.status !== 200)
|
|
67
|
+
return result;
|
|
68
|
+
return (0, context_1.executed)(result.data.escalation, step);
|
|
69
|
+
}
|
|
70
|
+
async function resolveStep(step, ctx) {
|
|
71
|
+
const result = await (0, metadata_1.resolveByMetadata)({
|
|
72
|
+
key: ctx.scheme.target_facet,
|
|
73
|
+
value: ctx.parsed.target,
|
|
74
|
+
resolverPayload: scanCodeService.interpolateScanTemplate(step.params?.resolverPayload ?? {}, (0, context_1.templateContext)(ctx)),
|
|
75
|
+
metadata: { ...(0, context_1.interpolatedMetadata)(step, ctx), ...(0, context_1.provenance)(ctx) },
|
|
76
|
+
restrictRoles: step.query?.roles,
|
|
77
|
+
extraFacets: step.query?.facets,
|
|
78
|
+
}, ctx.auth);
|
|
79
|
+
if (result.status === 404)
|
|
80
|
+
return null;
|
|
81
|
+
if (result.status === 403)
|
|
82
|
+
return (0, context_1.forbidden)(result.error);
|
|
83
|
+
if (result.status === 409)
|
|
84
|
+
return (0, context_1.conflict)(result.error);
|
|
85
|
+
if (result.status !== 200)
|
|
86
|
+
return result;
|
|
87
|
+
const escalation = result.data.escalation
|
|
88
|
+
?? (result.data.escalationId ? { id: result.data.escalationId } : undefined);
|
|
89
|
+
return (0, context_1.executed)(escalation, step);
|
|
90
|
+
}
|
|
91
|
+
async function escalateStep(step, ctx) {
|
|
92
|
+
// Close the located escalation first when configured — the close doubles as
|
|
93
|
+
// the condition check AND the double-scan guard (second scan finds nothing
|
|
94
|
+
// to close and falls through).
|
|
95
|
+
if (step.params?.closeCurrent === 'resolve') {
|
|
96
|
+
const closed = await resolveStep({ ...step, params: { ...step.params, resolverPayload: step.params?.resolverPayload ?? {} } }, ctx);
|
|
97
|
+
if (closed === null)
|
|
98
|
+
return null;
|
|
99
|
+
if (closed.data?.outcome !== types_1.SCAN_OUTCOMES.EXECUTED)
|
|
100
|
+
return closed;
|
|
101
|
+
}
|
|
102
|
+
else if (step.params?.closeCurrent === 'cancel') {
|
|
103
|
+
const cancelled = await cancelStep(step, ctx);
|
|
104
|
+
if (cancelled === null)
|
|
105
|
+
return null;
|
|
106
|
+
if (cancelled.data?.outcome !== types_1.SCAN_OUTCOMES.EXECUTED)
|
|
107
|
+
return cancelled;
|
|
108
|
+
}
|
|
109
|
+
const created = await (0, create_1.createEscalation)({
|
|
110
|
+
type: step.params?.escalationType ?? 'scan',
|
|
111
|
+
role: step.params.targetRole,
|
|
112
|
+
description: step.params?.description ?? ctx.rule.name,
|
|
113
|
+
metadata: {
|
|
114
|
+
[ctx.scheme.target_facet]: ctx.parsed.target,
|
|
115
|
+
...(0, context_1.interpolatedMetadata)(step, ctx),
|
|
116
|
+
...(0, context_1.provenance)(ctx),
|
|
117
|
+
},
|
|
118
|
+
}, ctx.auth);
|
|
119
|
+
if (created.status === 403)
|
|
120
|
+
return (0, context_1.forbidden)(created.error);
|
|
121
|
+
if (created.status !== 201)
|
|
122
|
+
return created;
|
|
123
|
+
return (0, context_1.executed)(created.data, step);
|
|
124
|
+
}
|
|
125
|
+
async function releaseStep(step, ctx) {
|
|
126
|
+
// Locate the caller's own claim; release-by-id re-asserts the assignee
|
|
127
|
+
// inside the SDK, so a lost race resolves to 409, never a foreign release.
|
|
128
|
+
const scope = await (0, helpers_1.getEscalationReadScope)(ctx.auth.userId);
|
|
129
|
+
const readable = [...scope.allRoles, ...scope.selfRoles];
|
|
130
|
+
const roles = (0, metadata_1.restrictScopeRoles)(readable, scope.global, step.query?.roles);
|
|
131
|
+
if (roles !== null && roles.length === 0)
|
|
132
|
+
return null;
|
|
133
|
+
const { escalations } = await escalationService.listEscalations({
|
|
134
|
+
metadata: (0, context_1.targetFilter)(step, ctx),
|
|
135
|
+
status: 'pending',
|
|
136
|
+
assigned_to: ctx.auth.userId,
|
|
137
|
+
visibleRoles: roles ?? undefined,
|
|
138
|
+
limit: 1,
|
|
139
|
+
});
|
|
140
|
+
if (escalations.length === 0)
|
|
141
|
+
return null;
|
|
142
|
+
const result = await (0, claim_1.releaseEscalation)({ id: escalations[0].id }, ctx.auth);
|
|
143
|
+
if (result.status === 403)
|
|
144
|
+
return (0, context_1.forbidden)(result.error);
|
|
145
|
+
if (result.status === 409)
|
|
146
|
+
return (0, context_1.conflict)(result.error);
|
|
147
|
+
if (result.status !== 200)
|
|
148
|
+
return result;
|
|
149
|
+
return (0, context_1.executed)(result.data.escalation, step);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Dispatch one CHOICE verb against an already-presented row. Unlike the step
|
|
153
|
+
* walk (where a miss falls through to the next step), the screen showed this
|
|
154
|
+
* exact row — so a miss answers with the truth: FORBIDDEN when the actor
|
|
155
|
+
* holds no write scope here, CONFLICT when a concurrent actor won the race.
|
|
156
|
+
*/
|
|
157
|
+
async function dispatchChoiceVerb(step, ctx, row) {
|
|
158
|
+
switch (step.verb) {
|
|
159
|
+
case types_1.SCAN_VERBS.SHOW_DETAIL:
|
|
160
|
+
return (0, context_1.executed)(row, step); // already located under the actor's scope
|
|
161
|
+
case types_1.SCAN_VERBS.CLAIM:
|
|
162
|
+
case types_1.SCAN_VERBS.CLAIM_SHOW_DETAIL:
|
|
163
|
+
return (await claimStep(step, ctx)) ?? (await missReason(step, ctx, 'the item is no longer claimable'));
|
|
164
|
+
case types_1.SCAN_VERBS.RESOLVE:
|
|
165
|
+
return (await resolveStep(step, ctx)) ?? (await missReason(step, ctx, 'the item is no longer resolvable'));
|
|
166
|
+
case types_1.SCAN_VERBS.ESCALATE:
|
|
167
|
+
return (await escalateStep(step, ctx)) ?? (await missReason(step, ctx, 'the item already moved on'));
|
|
168
|
+
case types_1.SCAN_VERBS.RELEASE:
|
|
169
|
+
return (await releaseStep(step, ctx)) ?? (0, context_1.conflict)('no claim of yours to release');
|
|
170
|
+
case types_1.SCAN_VERBS.CANCEL:
|
|
171
|
+
return (await cancelStep(step, ctx)) ?? (await missReason(step, ctx, 'the item is no longer cancellable'));
|
|
172
|
+
default:
|
|
173
|
+
throw new Error(`unknown choice verb "${step.verb}"`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function missReason(step, ctx, raceMessage) {
|
|
177
|
+
const scope = await (0, helpers_1.getEscalationWriteScope)(ctx.auth.userId);
|
|
178
|
+
if (!scope.global) {
|
|
179
|
+
const roles = (0, metadata_1.restrictScopeRoles)(scope.allRoles, scope.global, step.query?.roles);
|
|
180
|
+
if (roles !== null && roles.length === 0) {
|
|
181
|
+
return (0, context_1.forbidden)('your roles do not permit acting on this queue');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return (0, context_1.conflict)(raceMessage);
|
|
185
|
+
}
|
|
186
|
+
async function cancelStep(step, ctx) {
|
|
187
|
+
// Claim-as-lock: the atomic claim pins the row to this caller (or extends
|
|
188
|
+
// their claim), serializing concurrent double-scans before the cancel.
|
|
189
|
+
const claimed = await (0, metadata_1.claimByMetadata)({
|
|
190
|
+
key: ctx.scheme.target_facet,
|
|
191
|
+
value: ctx.parsed.target,
|
|
192
|
+
metadata: (0, context_1.provenance)(ctx),
|
|
193
|
+
restrictRoles: step.query?.roles,
|
|
194
|
+
}, ctx.auth);
|
|
195
|
+
if (claimed.status === 404)
|
|
196
|
+
return null;
|
|
197
|
+
if (claimed.status === 403)
|
|
198
|
+
return (0, context_1.forbidden)(claimed.error);
|
|
199
|
+
if (claimed.status !== 200)
|
|
200
|
+
return claimed;
|
|
201
|
+
const cancelled = await escalationService.cancelEscalation(claimed.data.escalation.id);
|
|
202
|
+
if (!cancelled)
|
|
203
|
+
return (0, context_1.conflict)('Escalation is not cancellable (already terminal)');
|
|
204
|
+
return (0, context_1.executed)(cancelled, step);
|
|
205
|
+
}
|
package/build/bin/ltc.js
CHANGED
|
@@ -205,6 +205,39 @@ escCmd.command('claim-by-facets')
|
|
|
205
205
|
.option('--all-or-none', 'Claim all matched rows or none')
|
|
206
206
|
.option('--json', 'JSON output')
|
|
207
207
|
.action(wrap(esc.claimByFacets));
|
|
208
|
+
escCmd.command('aggregate-facets')
|
|
209
|
+
.description('Grouped analytics over escalation intervals: membership at an instant, or dwell over a window')
|
|
210
|
+
.option('--role <role>', 'Single pond role')
|
|
211
|
+
.option('--roles <json>', 'Multiple roles, JSON array (no role at all = every pond; global principals only)')
|
|
212
|
+
.option('--entity <key>', "Entity facet key (e.g. serialNumber) — scopes to every role declaring it (the entity's system)")
|
|
213
|
+
.option('--group-state', "Group by the derived STATE label (each role's subtypes or itself) — 'how do the entities spend their time'")
|
|
214
|
+
.option('--facets <json>', 'Required facets (JSON object, metadata @>)')
|
|
215
|
+
.option('--exists <json>', 'Metadata keys that must be present (JSON array)')
|
|
216
|
+
.option('--prefix <json>', 'Case-insensitive prefix match on facet values, e.g. \'{"serialNumber":"PRN"}\'')
|
|
217
|
+
.option('--any-of <json>', 'Rows carrying ANY of these facet sets (JSON array, max 200) — target an explicit entity set')
|
|
218
|
+
.option('--group-columns <list>', 'Comma-separated group columns: role,subtype,status')
|
|
219
|
+
.option('--group-facets <list>', 'Comma-separated metadata keys to group by')
|
|
220
|
+
.option('--as-of <iso>', 'Membership at this ISO instant (default: now; past reconstructs the live set then)')
|
|
221
|
+
.option('--window <json>', 'Dwell window {"from":"...","to":"..."} — switches the measure to dwell')
|
|
222
|
+
.option('--distinct-by <key>', 'Count DISTINCT of this facet (entities, not rows; membership only)')
|
|
223
|
+
.option('--live-statuses <list>', 'Comma-separated statuses considered live (default: pending)')
|
|
224
|
+
.option('--order-by <json>', 'Result-group order, JSON array e.g. \'[{"field":"count","direction":"desc"}]\'')
|
|
225
|
+
.option('--limit <n>', 'Max result groups')
|
|
226
|
+
.option('--offset <n>', 'Result-group offset')
|
|
227
|
+
.option('--json', 'JSON output')
|
|
228
|
+
.action(wrap(esc.aggregateByFacets));
|
|
229
|
+
escCmd.command('timeline <key> <value>')
|
|
230
|
+
.description("One entity's ordered escalation-interval timeline (gaps = untracked time)")
|
|
231
|
+
.option('--roles <json>', 'Restrict to these roles, JSON array')
|
|
232
|
+
.option('--entity <key>', "Scope to the entity's system (every role declaring this entity facet); omitted with no --roles, the query spans every queue (global principals only)")
|
|
233
|
+
.option('--from <iso>', 'Window start (with --to; only overlapping intervals)')
|
|
234
|
+
.option('--to <iso>', 'Window end')
|
|
235
|
+
.option('--select-facets <list>', 'Comma-separated metadata keys to surface per interval')
|
|
236
|
+
.option('--order <dir>', 'asc (default) or desc — desc + --before pages a long history recent-first')
|
|
237
|
+
.option('--before <iso>', 'Strict upper bound on startedAt — the "load earlier" cursor')
|
|
238
|
+
.option('--limit <n>', 'Max intervals')
|
|
239
|
+
.option('--json', 'JSON output')
|
|
240
|
+
.action(wrap(esc.timelineByFacet));
|
|
208
241
|
// ── Workflows ────────────────────────────────────────────────────────────
|
|
209
242
|
const wfCmd = commander_1.program.command('workflows').alias('wf').description('Manage durable workflows');
|
|
210
243
|
wfCmd.command('list')
|
|
@@ -64,4 +64,35 @@ export declare function claimByFacets(opts: {
|
|
|
64
64
|
allOrNone?: boolean;
|
|
65
65
|
json?: boolean;
|
|
66
66
|
}): Promise<void>;
|
|
67
|
+
export declare function aggregateByFacets(opts: {
|
|
68
|
+
role?: string;
|
|
69
|
+
roles?: string;
|
|
70
|
+
entity?: string;
|
|
71
|
+
facets?: string;
|
|
72
|
+
exists?: string;
|
|
73
|
+
prefix?: string;
|
|
74
|
+
anyOf?: string;
|
|
75
|
+
groupColumns?: string;
|
|
76
|
+
groupFacets?: string;
|
|
77
|
+
groupState?: boolean;
|
|
78
|
+
asOf?: string;
|
|
79
|
+
window?: string;
|
|
80
|
+
distinctBy?: string;
|
|
81
|
+
liveStatuses?: string;
|
|
82
|
+
orderBy?: string;
|
|
83
|
+
limit?: string;
|
|
84
|
+
offset?: string;
|
|
85
|
+
json?: boolean;
|
|
86
|
+
}): Promise<void>;
|
|
87
|
+
export declare function timelineByFacet(key: string, value: string, opts: {
|
|
88
|
+
roles?: string;
|
|
89
|
+
entity?: string;
|
|
90
|
+
from?: string;
|
|
91
|
+
to?: string;
|
|
92
|
+
selectFacets?: string;
|
|
93
|
+
order?: string;
|
|
94
|
+
before?: string;
|
|
95
|
+
limit?: string;
|
|
96
|
+
json?: boolean;
|
|
97
|
+
}): Promise<void>;
|
|
67
98
|
export {};
|
|
@@ -15,6 +15,8 @@ exports.resolveByIds = resolveByIds;
|
|
|
15
15
|
exports.searchByFacets = searchByFacets;
|
|
16
16
|
exports.claimGroups = claimGroups;
|
|
17
17
|
exports.claimByFacets = claimByFacets;
|
|
18
|
+
exports.aggregateByFacets = aggregateByFacets;
|
|
19
|
+
exports.timelineByFacet = timelineByFacet;
|
|
18
20
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
19
21
|
const client_1 = require("../client");
|
|
20
22
|
const format_1 = require("../format");
|
|
@@ -207,3 +209,89 @@ async function claimByFacets(opts) {
|
|
|
207
209
|
}
|
|
208
210
|
console.log(`\n ${picocolors_1.default.green('✓')} Claimed ${data.claimed?.length || 0} escalation(s)\n`);
|
|
209
211
|
}
|
|
212
|
+
// --- Analytics commands -------------------------------------------------------
|
|
213
|
+
const AGG_COLUMNS = [
|
|
214
|
+
{ key: 'state', label: 'State', width: 16 },
|
|
215
|
+
{ key: 'role', label: 'Role', width: 16 },
|
|
216
|
+
{ key: 'subtype', label: 'Subtype', width: 16 },
|
|
217
|
+
{ key: 'facets', label: 'Facets', width: 28, format: (v) => (v && Object.keys(v).length ? JSON.stringify(v) : '') },
|
|
218
|
+
{ key: 'count', label: 'Count', width: 7, align: 'right' },
|
|
219
|
+
{ key: 'dwellSeconds', label: 'Dwell(s)', width: 10, align: 'right', format: (v) => (v == null ? '' : String(Math.round(v))) },
|
|
220
|
+
{ key: 'sampleCount', label: 'Rows', width: 6, align: 'right' },
|
|
221
|
+
];
|
|
222
|
+
const TIMELINE_COLUMNS = [
|
|
223
|
+
{ key: 'role', label: 'Role', width: 16 },
|
|
224
|
+
{ key: 'subtype', label: 'Subtype', width: 16 },
|
|
225
|
+
{ key: 'status', label: 'Status', width: 10, format: format_1.formatStatus },
|
|
226
|
+
{ key: 'startedAt', label: 'Started', width: 12, format: format_1.formatTime },
|
|
227
|
+
{ key: 'endedAt', label: 'Ended', width: 12, format: (v) => (v ? (0, format_1.formatTime)(v) : 'open') },
|
|
228
|
+
{ key: 'durationSeconds', label: 'Secs', width: 8, align: 'right', format: (v) => String(Math.round(v)) },
|
|
229
|
+
];
|
|
230
|
+
async function aggregateByFacets(opts) {
|
|
231
|
+
const query = {};
|
|
232
|
+
if (opts.role)
|
|
233
|
+
query.role = opts.role;
|
|
234
|
+
if (opts.roles)
|
|
235
|
+
query.roles = parseJsonOption('--roles', opts.roles);
|
|
236
|
+
if (opts.entity)
|
|
237
|
+
query.entity = opts.entity;
|
|
238
|
+
if (opts.facets)
|
|
239
|
+
query.facets = parseJsonOption('--facets', opts.facets);
|
|
240
|
+
if (opts.exists)
|
|
241
|
+
query.exists = parseJsonOption('--exists', opts.exists);
|
|
242
|
+
if (opts.prefix)
|
|
243
|
+
query.prefix = parseJsonOption('--prefix', opts.prefix);
|
|
244
|
+
if (opts.anyOf)
|
|
245
|
+
query.anyOf = parseJsonOption('--any-of', opts.anyOf);
|
|
246
|
+
const groupBy = {};
|
|
247
|
+
if (opts.groupColumns)
|
|
248
|
+
groupBy.columns = opts.groupColumns.split(',').map((s) => s.trim());
|
|
249
|
+
if (opts.groupFacets)
|
|
250
|
+
groupBy.facets = opts.groupFacets.split(',').map((s) => s.trim());
|
|
251
|
+
if (opts.groupState)
|
|
252
|
+
groupBy.state = true;
|
|
253
|
+
// --window switches the measure to dwell; otherwise membership (at --as-of, default now).
|
|
254
|
+
const measure = opts.window
|
|
255
|
+
? { kind: 'dwell', window: parseJsonOption('--window', opts.window) }
|
|
256
|
+
: { kind: 'membership', ...(opts.asOf ? { asOf: opts.asOf } : {}) };
|
|
257
|
+
const body = { query, groupBy, measure };
|
|
258
|
+
if (opts.distinctBy)
|
|
259
|
+
body.distinctBy = opts.distinctBy;
|
|
260
|
+
if (opts.liveStatuses)
|
|
261
|
+
body.liveStatuses = opts.liveStatuses.split(',').map((s) => s.trim());
|
|
262
|
+
if (opts.orderBy)
|
|
263
|
+
body.orderBy = parseJsonOption('--order-by', opts.orderBy);
|
|
264
|
+
if (opts.limit)
|
|
265
|
+
body.limit = parseInt(opts.limit, 10);
|
|
266
|
+
if (opts.offset)
|
|
267
|
+
body.offset = parseInt(opts.offset, 10);
|
|
268
|
+
const data = await (0, client_1.apiFetch)('/escalations/aggregate-by-facets', {
|
|
269
|
+
method: 'POST',
|
|
270
|
+
body: JSON.stringify(body),
|
|
271
|
+
});
|
|
272
|
+
(0, format_1.output)(data, data.groups || [], AGG_COLUMNS, opts);
|
|
273
|
+
if (data.overflow && !opts.json)
|
|
274
|
+
console.log(` ${picocolors_1.default.yellow('!')} more groups exist — raise --limit or --offset\n`);
|
|
275
|
+
}
|
|
276
|
+
async function timelineByFacet(key, value, opts) {
|
|
277
|
+
const body = { facet: { key, value } };
|
|
278
|
+
if (opts.entity)
|
|
279
|
+
body.query = { entity: opts.entity };
|
|
280
|
+
else if (opts.roles)
|
|
281
|
+
body.query = { roles: parseJsonOption('--roles', opts.roles) };
|
|
282
|
+
if (opts.from && opts.to)
|
|
283
|
+
body.window = { from: opts.from, to: opts.to };
|
|
284
|
+
if (opts.selectFacets)
|
|
285
|
+
body.select = { facets: opts.selectFacets.split(',').map((s) => s.trim()) };
|
|
286
|
+
if (opts.order)
|
|
287
|
+
body.order = opts.order;
|
|
288
|
+
if (opts.before)
|
|
289
|
+
body.before = opts.before;
|
|
290
|
+
if (opts.limit)
|
|
291
|
+
body.limit = parseInt(opts.limit, 10);
|
|
292
|
+
const data = await (0, client_1.apiFetch)('/escalations/timeline-by-facet', {
|
|
293
|
+
method: 'POST',
|
|
294
|
+
body: JSON.stringify(body),
|
|
295
|
+
});
|
|
296
|
+
(0, format_1.output)(data, data.intervals || [], TIMELINE_COLUMNS, opts);
|
|
297
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- entity_facet — the lt_escalations.metadata key identifying the ENTITY that
|
|
2
|
+
-- moves through this role (e.g. serialNumber, orderId). A role's escalations
|
|
3
|
+
-- are the intervals that entity spends in the role's state, so declaring the
|
|
4
|
+
-- key powers the derived surfaces: distinct-entity counts on the Pace Board,
|
|
5
|
+
-- per-entity dwell in the station panel, entity timelines, and the entity
|
|
6
|
+
-- lens. Roles sharing an entity_facet form that entity's SYSTEM. FACET_KEY-
|
|
7
|
+
-- validated at the API (letters, digits, underscore). NULL = the role has no
|
|
8
|
+
-- entity notion; the surfaces simply don't render.
|
|
9
|
+
--
|
|
10
|
+
-- entity_state_source — how this role names its contribution to the entity's
|
|
11
|
+
-- state space: 'role' (the station itself is the state — one state per role,
|
|
12
|
+
-- e.g. a harvesting or servicing queue) or 'subtype' (the role's subtypes are
|
|
13
|
+
-- its states — one role holding several, e.g. a fleet role whose escalations
|
|
14
|
+
-- park as ready/printing). Only meaningful while entity_facet is set.
|
|
15
|
+
ALTER TABLE lt_roles
|
|
16
|
+
ADD COLUMN IF NOT EXISTS entity_facet TEXT;
|
|
17
|
+
ALTER TABLE lt_roles
|
|
18
|
+
ADD COLUMN IF NOT EXISTS entity_state_source TEXT NOT NULL DEFAULT 'role';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
-- Scheme kind — what a code under this scheme IS:
|
|
2
|
+
-- 'action' the shipped ECA model: the target resolves against escalations
|
|
3
|
+
-- (scheme.target_facet names the escalation metadata key) and the
|
|
4
|
+
-- rule's steps run.
|
|
5
|
+
-- 'identity' a badge: the target resolves against USERS (scheme.target_facet
|
|
6
|
+
-- names the lt_users.metadata key it matches, e.g. badge_id) and a
|
|
7
|
+
-- match mints a short-lived acting-identity grant through the
|
|
8
|
+
-- ephemeral keystore. Identity schemes never walk steps; the
|
|
9
|
+
-- rule's fallback is the unknown-badge screen.
|
|
10
|
+
--
|
|
11
|
+
-- Grant policy (identity kind only): grant_ttl_seconds bounds the elevation
|
|
12
|
+
-- window; grant_max_uses = 0 means TTL-bound (unlimited exchanges within the
|
|
13
|
+
-- window), 1 expresses a strict one-scan-one-grant policy (each scan request
|
|
14
|
+
-- consumes the grant), n > 1 an n-request budget.
|
|
15
|
+
ALTER TABLE lt_config_scan_schemes
|
|
16
|
+
ADD COLUMN IF NOT EXISTS kind TEXT NOT NULL DEFAULT 'action'
|
|
17
|
+
CHECK (kind IN ('action', 'identity')),
|
|
18
|
+
ADD COLUMN IF NOT EXISTS grant_ttl_seconds INT,
|
|
19
|
+
ADD COLUMN IF NOT EXISTS grant_max_uses INT NOT NULL DEFAULT 0;
|
|
20
|
+
|
|
21
|
+
ALTER TABLE lt_config_scan_schemes
|
|
22
|
+
ADD CONSTRAINT chk_scan_scheme_identity_grant
|
|
23
|
+
CHECK (kind <> 'identity'
|
|
24
|
+
OR (grant_ttl_seconds BETWEEN 1 AND 86400 AND grant_max_uses >= 0));
|
|
25
|
+
|
|
26
|
+
-- The rule-configured "scan your badge" screen: rendered when a step (or a
|
|
27
|
+
-- presented choice) requires an acting identity and the request carries none
|
|
28
|
+
-- the effective actor can satisfy. Mirrors `fallback` exactly — same shape
|
|
29
|
+
-- ({ markdown, route }), different moment.
|
|
30
|
+
ALTER TABLE lt_config_scan_actions
|
|
31
|
+
ADD COLUMN IF NOT EXISTS not_primed JSONB NOT NULL DEFAULT '{}';
|
|
@@ -13,5 +13,10 @@ export declare const config: {
|
|
|
13
13
|
JWT_SECRET: string;
|
|
14
14
|
HONEYCOMB_API_KEY: string;
|
|
15
15
|
ROLE_ENFORCEMENT_CACHE_TTL_MS: number;
|
|
16
|
+
LT_ANALYTICS_MAX_GROUPS: number;
|
|
17
|
+
LT_ANALYTICS_MAX_WINDOW_DAYS: number;
|
|
18
|
+
LT_ANALYTICS_CACHE_TTL_MS: number;
|
|
19
|
+
LT_ANALYTICS_PAST_CACHE_TTL_MS: number;
|
|
20
|
+
LT_ANALYTICS_CACHE_MAX_ENTRIES: number;
|
|
16
21
|
};
|
|
17
22
|
export declare const postgres_options: Record<string, unknown>;
|
package/build/modules/config.js
CHANGED
|
@@ -24,6 +24,18 @@ exports.config = {
|
|
|
24
24
|
// admin flips enforce_schema or edits a schema; pinned snapshots are
|
|
25
25
|
// immutable and cache indefinitely.
|
|
26
26
|
ROLE_ENFORCEMENT_CACHE_TTL_MS: parseInt(process.env.ROLE_ENFORCEMENT_CACHE_TTL_MS || '30000', 10),
|
|
27
|
+
// Escalation analytics (aggregateByFacets / timelineByFacet) bounds.
|
|
28
|
+
// MAX_GROUPS caps the result-group page (the API signals `overflow` instead
|
|
29
|
+
// of silently truncating); MAX_WINDOW_DAYS caps a dwell/timeline window so
|
|
30
|
+
// one query can never sweep unbounded history. The two cache TTLs split by
|
|
31
|
+
// anchor: now-anchored results tolerate pace-board-grade staleness, while a
|
|
32
|
+
// fully-past window is immutable under every long-tail write path and may
|
|
33
|
+
// live longer; MAX_ENTRIES LRU-bounds the past cache.
|
|
34
|
+
LT_ANALYTICS_MAX_GROUPS: parseInt(process.env.LT_ANALYTICS_MAX_GROUPS || '5000', 10),
|
|
35
|
+
LT_ANALYTICS_MAX_WINDOW_DAYS: parseInt(process.env.LT_ANALYTICS_MAX_WINDOW_DAYS || '35', 10),
|
|
36
|
+
LT_ANALYTICS_CACHE_TTL_MS: parseInt(process.env.LT_ANALYTICS_CACHE_TTL_MS || '30000', 10),
|
|
37
|
+
LT_ANALYTICS_PAST_CACHE_TTL_MS: parseInt(process.env.LT_ANALYTICS_PAST_CACHE_TTL_MS || '600000', 10),
|
|
38
|
+
LT_ANALYTICS_CACHE_MAX_ENTRIES: parseInt(process.env.LT_ANALYTICS_CACHE_MAX_ENTRIES || '512', 10),
|
|
27
39
|
};
|
|
28
40
|
exports.postgres_options = {
|
|
29
41
|
host: exports.config.POSTGRES_HOST,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Request, Response } from 'express';
|
|
2
|
+
import type { LTApiAuth } from '../../types/sdk';
|
|
3
|
+
/** The header a shared station device rides its badge grant on. */
|
|
4
|
+
export declare const ACTING_TOKEN_HEADER = "x-lt-acting-token";
|
|
5
|
+
/**
|
|
6
|
+
* The effective actor for an escalation WORK verb (claim, release, resolve):
|
|
7
|
+
* the badged person when an acting grant rides the request, otherwise the
|
|
8
|
+
* authenticated principal. A supplied-but-dead grant answers 401 and returns
|
|
9
|
+
* null — never a silent fall-back to the session identity, which would
|
|
10
|
+
* misattribute the mutation. Read paths never consult this header.
|
|
11
|
+
*/
|
|
12
|
+
export declare function effectiveWorkAuth(req: Request, res: Response): Promise<LTApiAuth | null>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACTING_TOKEN_HEADER = void 0;
|
|
4
|
+
exports.effectiveWorkAuth = effectiveWorkAuth;
|
|
5
|
+
const acting_identity_1 = require("../../services/iam/acting-identity");
|
|
6
|
+
/** The header a shared station device rides its badge grant on. */
|
|
7
|
+
exports.ACTING_TOKEN_HEADER = 'x-lt-acting-token';
|
|
8
|
+
/**
|
|
9
|
+
* The effective actor for an escalation WORK verb (claim, release, resolve):
|
|
10
|
+
* the badged person when an acting grant rides the request, otherwise the
|
|
11
|
+
* authenticated principal. A supplied-but-dead grant answers 401 and returns
|
|
12
|
+
* null — never a silent fall-back to the session identity, which would
|
|
13
|
+
* misattribute the mutation. Read paths never consult this header.
|
|
14
|
+
*/
|
|
15
|
+
async function effectiveWorkAuth(req, res) {
|
|
16
|
+
const token = req.headers[exports.ACTING_TOKEN_HEADER];
|
|
17
|
+
if (!token || typeof token !== 'string')
|
|
18
|
+
return req.auth;
|
|
19
|
+
const resolved = await (0, acting_identity_1.resolveActingAuth)(token);
|
|
20
|
+
if (!resolved.ok) {
|
|
21
|
+
res.status(401).json({ error: resolved.error });
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return resolved.auth;
|
|
25
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Router } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Escalation analytics HTTP surface — grouped membership/dwell aggregates and
|
|
4
|
+
* per-entity timelines over the interval time-series. Read-only; the api layer
|
|
5
|
+
* enforces the read_all-per-role / global-for-cross-role gate. These mirror
|
|
6
|
+
* the SDK `escalations.aggregateByFacets / timelineByFacet`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerAnalyticsRoutes(router: Router): void;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.registerAnalyticsRoutes = registerAnalyticsRoutes;
|
|
37
|
+
const api = __importStar(require("../../api/escalations"));
|
|
38
|
+
/**
|
|
39
|
+
* Escalation analytics HTTP surface — grouped membership/dwell aggregates and
|
|
40
|
+
* per-entity timelines over the interval time-series. Read-only; the api layer
|
|
41
|
+
* enforces the read_all-per-role / global-for-cross-role gate. These mirror
|
|
42
|
+
* the SDK `escalations.aggregateByFacets / timelineByFacet`.
|
|
43
|
+
*/
|
|
44
|
+
function registerAnalyticsRoutes(router) {
|
|
45
|
+
/**
|
|
46
|
+
* POST /api/escalations/aggregate-by-facets
|
|
47
|
+
* Body: AggregateByFacetsInput — { query, groupBy, measure, distinctBy?,
|
|
48
|
+
* states?, liveStatuses?, orderBy?, limit?, offset? }
|
|
49
|
+
*/
|
|
50
|
+
router.post('/aggregate-by-facets', async (req, res) => {
|
|
51
|
+
const result = await api.aggregateByFacets(req.body ?? {}, req.auth);
|
|
52
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* POST /api/escalations/timeline-by-facet
|
|
56
|
+
* Body: TimelineByFacetInput — { facet, query?, window?, select?,
|
|
57
|
+
* liveStatuses?, limit? }
|
|
58
|
+
*/
|
|
59
|
+
router.post('/timeline-by-facet', async (req, res) => {
|
|
60
|
+
const result = await api.timelineByFacet(req.body ?? {}, req.auth);
|
|
61
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
62
|
+
});
|
|
63
|
+
}
|