@hotmeshio/long-tail 0.5.11 β 0.6.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 +1 -0
- package/build/api/diagnostics.d.ts +5 -0
- package/build/api/diagnostics.js +5 -1
- 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 +23 -2
- package/build/api/escalations/list.js +111 -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/settings.d.ts +1 -1
- package/build/api/settings.js +3 -1
- package/build/api/users.d.ts +6 -0
- package/build/api/users.js +29 -1
- package/build/api/workflows/invocation.d.ts +6 -0
- package/build/api/workflows/invocation.js +6 -2
- 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/features.d.ts +19 -0
- package/build/modules/features.js +16 -0
- package/build/modules/version.d.ts +10 -5
- package/build/modules/version.js +27 -11
- package/build/routes/diagnostics.js +15 -2
- 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 +48 -0
- package/build/routes/escalations/resolve.js +22 -4
- package/build/routes/users.js +9 -5
- package/build/routes/workflows/invocation.js +12 -4
- package/build/sdk/index.d.ts +35 -2
- package/build/sdk/index.js +7 -0
- package/build/services/diagnostics/index.d.ts +40 -6
- package/build/services/diagnostics/index.js +54 -5
- 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 +47 -0
- package/build/services/escalation/queries.js +185 -2
- package/build/services/escalation/sql.d.ts +15 -2
- package/build/services/escalation/sql.js +80 -3
- 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/resolve.d.ts +5 -3
- package/build/services/task/resolve.js +9 -6
- package/build/services/task/sql.d.ts +30 -2
- package/build/services/task/sql.js +72 -2
- 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/start/adapters.js +3 -0
- package/build/system/mcp-servers/admin/diagnostics.d.ts +5 -4
- package/build/system/mcp-servers/admin/diagnostics.js +12 -10
- package/build/system/mcp-servers/admin/escalations.d.ts +6 -0
- package/build/system/mcp-servers/admin/escalations.js +247 -54
- package/build/system/mcp-servers/admin/index.js +3 -3
- package/build/system/mcp-servers/admin/schemas.d.ts +417 -3
- package/build/system/mcp-servers/admin/schemas.js +101 -5
- package/build/system/mcp-servers/admin/users.js +28 -2
- package/build/system/mcp-servers/admin/workflows.js +23 -12
- package/build/system/seed/tool-manifests-admin.d.ts +1324 -562
- package/build/system/seed/tool-manifests-admin.js +10 -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/startup.d.ts +9 -0
- package/build/types/user.d.ts +11 -0
- package/dashboard/dist/assets/AdminDashboard-2CZ8py9X.js +2 -0
- package/dashboard/dist/assets/{AdminDashboard-DZpduYdW.js.map β AdminDashboard-2CZ8py9X.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-SFnHMIP6.js β AgentConfigPage-D68sodgo.js} +7 -7
- package/dashboard/dist/assets/{AgentConfigPage-SFnHMIP6.js.map β AgentConfigPage-D68sodgo.js.map} +1 -1
- package/dashboard/dist/assets/AgentDetailPage-BmhuWbKi.js +4 -0
- package/dashboard/dist/assets/AgentDetailPage-BmhuWbKi.js.map +1 -0
- package/dashboard/dist/assets/AgentsPage-C4SKQVeU.js +2 -0
- package/dashboard/dist/assets/AgentsPage-C4SKQVeU.js.map +1 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-9RBEYwkt.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-9RBEYwkt.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-Dse2jW4b.js β BotPicker-D-FvXJOM.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-Dse2jW4b.js.map β BotPicker-D-FvXJOM.js.map} +1 -1
- package/dashboard/dist/assets/CapabilitiesPage-ByDf6WJr.js +2 -0
- package/dashboard/dist/assets/{CapabilitiesPage-DTfKCzBQ.js.map β CapabilitiesPage-ByDf6WJr.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-8vf38J6K.js β CollapsibleSection-BjGNYEvh.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-8vf38J6K.js.map β CollapsibleSection-BjGNYEvh.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-7C4M-hrH.js β CredentialsPage-DvCA-wQN.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-7C4M-hrH.js.map β CredentialsPage-DvCA-wQN.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-DrGWtaSY.js β CronLabel-DBMurd_c.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-DrGWtaSY.js.map β CronLabel-DBMurd_c.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-Vcmr4LiR.js β CustomDurationPicker-DHflFoEa.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-Vcmr4LiR.js.map β CustomDurationPicker-DHflFoEa.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-Dwpgbueb.js β DropZone-CUtdp99Z.js} +2 -2
- package/dashboard/dist/assets/{DropZone-Dwpgbueb.js.map β DropZone-CUtdp99Z.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-BNexhR5d.js β ElapsedCell-CSjcVOpo.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-BNexhR5d.js.map β ElapsedCell-CSjcVOpo.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BI3DEwYP.js β EscalationsOverview-BUqRNJbK.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BI3DEwYP.js.map β EscalationsOverview-BUqRNJbK.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-CwvX_L9t.js β EventTable-Chz-6XPE.js} +2 -2
- package/dashboard/dist/assets/{EventTable-CwvX_L9t.js.map β EventTable-Chz-6XPE.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-C-3b8WK0.js β GraphInvokePage-BXdgciWg.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-C-3b8WK0.js.map β GraphInvokePage-BXdgciWg.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-BS_-jcHr.js β HomePage-CEMxnk0r.js} +2 -2
- package/dashboard/dist/assets/{HomePage-BS_-jcHr.js.map β HomePage-CEMxnk0r.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-BH9rwQVE.js β ListToolbar-_73JCdbB.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-BH9rwQVE.js.map β ListToolbar-_73JCdbB.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-C9iOdE7E.js β McpOverview-DfdqxhSz.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-C9iOdE7E.js.map β McpOverview-DfdqxhSz.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-DMN-yqvJ.js β McpQueryDetailPage-DPAljieY.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-DMN-yqvJ.js.map β McpQueryDetailPage-DPAljieY.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryPage-D5cSziYS.js +2 -0
- package/dashboard/dist/assets/McpQueryPage-D5cSziYS.js.map +1 -0
- package/dashboard/dist/assets/{McpRunDetailPage-Wb47sRvo.js β McpRunDetailPage-DwNb9wZg.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-Wb47sRvo.js.map β McpRunDetailPage-DwNb9wZg.js.map} +1 -1
- package/dashboard/dist/assets/McpRunsPage-O5H6yStl.js +2 -0
- package/dashboard/dist/assets/McpRunsPage-O5H6yStl.js.map +1 -0
- package/dashboard/dist/assets/{NamespacePill-DnJl4Lre.js β NamespacePill-uW-xOedj.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-DnJl4Lre.js.map β NamespacePill-uW-xOedj.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-DYw9Ekv5.js +2 -0
- package/dashboard/dist/assets/OperatorDashboard-DYw9Ekv5.js.map +1 -0
- package/dashboard/dist/assets/PageHeader-DVOZjBfL.js +2 -0
- package/dashboard/dist/assets/PageHeader-DVOZjBfL.js.map +1 -0
- package/dashboard/dist/assets/{PageHeaderWithStats-BuKc9MKt.js β PageHeaderWithStats-DtTpDHOd.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-BuKc9MKt.js.map β PageHeaderWithStats-DtTpDHOd.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-CvPx-h8y.js β ProcessDetailPage-B0Dokcx9.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-CvPx-h8y.js.map β ProcessDetailPage-B0Dokcx9.js.map} +1 -1
- package/dashboard/dist/assets/ProcessesListPage-Cc87WIjL.js +2 -0
- package/dashboard/dist/assets/ProcessesListPage-Cc87WIjL.js.map +1 -0
- package/dashboard/dist/assets/RolePill-BaW-30TC.js +2 -0
- package/dashboard/dist/assets/RolePill-BaW-30TC.js.map +1 -0
- package/dashboard/dist/assets/{RolesPage-W5ZjOHpg.js β RolesPage-cp6PiF08.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-W5ZjOHpg.js.map β RolesPage-cp6PiF08.js.map} +1 -1
- package/dashboard/dist/assets/RowActions-BA9L9djf.js +2 -0
- package/dashboard/dist/assets/RowActions-BA9L9djf.js.map +1 -0
- package/dashboard/dist/assets/{RunAsSelector-CvxGTNGT.js β RunAsSelector-2LWkjSnO.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-CvxGTNGT.js.map β RunAsSelector-2LWkjSnO.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-BWhFSr2d.js β StickyPagination-BQEm--kI.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-BWhFSr2d.js.map β StickyPagination-BQEm--kI.js.map} +1 -1
- package/dashboard/dist/assets/StreamMessageDetail-BTtw3GjX.js +2 -0
- package/dashboard/dist/assets/{StreamMessageDetail-BF0tM0cd.js.map β StreamMessageDetail-BTtw3GjX.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-Bv-KHY9s.js β SwimlaneTimeline-DdXxP4fO.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-Bv-KHY9s.js.map β SwimlaneTimeline-DdXxP4fO.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-B00uqhjX.js β TagInput-x9XTuqMb.js} +2 -2
- package/dashboard/dist/assets/{TagInput-B00uqhjX.js.map β TagInput-x9XTuqMb.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-Bzb_fuLy.js β TaskDetailPage-B8veYG4X.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-Bzb_fuLy.js.map β TaskDetailPage-B8veYG4X.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-CUsofm0X.js β TaskQueuePill-DSEQZ-HT.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-CUsofm0X.js.map β TaskQueuePill-DSEQZ-HT.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-CBezWaZL.js β TasksListPage-CsN1gZj5.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-CBezWaZL.js.map β TasksListPage-CsN1gZj5.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-CKHfTzat.js β TimeAgo-n182EcLx.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-CKHfTzat.js.map β TimeAgo-n182EcLx.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-Dsje8GH-.js β TimestampCell-2dlcQAfX.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-Dsje8GH-.js.map β TimestampCell-2dlcQAfX.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-DjvedZSn.js β ToolPill-B_e4eT77.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-DjvedZSn.js.map β ToolPill-B_e4eT77.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-3OcG18e5.js β ToolTestPanel-Bhnncc6M.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-3OcG18e5.js.map β ToolTestPanel-Bhnncc6M.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-DCtv91hH.js β TopicDetailPage-29lsNsqI.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-DCtv91hH.js.map β TopicDetailPage-29lsNsqI.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-DVGSNc_E.js β TopicsPage-KJ7_LAvo.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-DVGSNc_E.js.map β TopicsPage-KJ7_LAvo.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-xHAgkk8M.js β UserName-CMoFi9zb.js} +2 -2
- package/dashboard/dist/assets/{UserName-xHAgkk8M.js.map β UserName-CMoFi9zb.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-CWNxsS8a.js β WorkflowExecutionPage-Cymfx-Bp.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-CWNxsS8a.js.map β WorkflowExecutionPage-Cymfx-Bp.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-F1oKUzmc.js β WorkflowPill-B66_AnaY.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-F1oKUzmc.js.map β WorkflowPill-B66_AnaY.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowsDashboard-CqLN8spd.js +2 -0
- package/dashboard/dist/assets/WorkflowsDashboard-CqLN8spd.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowsOverview-e7jWSj_n.js β WorkflowsOverview-0Rc2WT6q.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-e7jWSj_n.js.map β WorkflowsOverview-0Rc2WT6q.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CdAeam76.js β YamlWorkflowDetailPage-CARQ7_LN.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CdAeam76.js.map β YamlWorkflowDetailPage-CARQ7_LN.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-S_gKs2Di.js β YamlWorkflowsPage-B-7sz1nf.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-S_gKs2Di.js.map β YamlWorkflowsPage-B-7sz1nf.js.map} +1 -1
- package/dashboard/dist/assets/{agents-JE4aL07D.js β agents-zM43d5Yf.js} +2 -2
- package/dashboard/dist/assets/{agents-JE4aL07D.js.map β agents-zM43d5Yf.js.map} +1 -1
- package/dashboard/dist/assets/api-path-u2Jf9QXS.js +2 -0
- package/dashboard/dist/assets/api-path-u2Jf9QXS.js.map +1 -0
- package/dashboard/dist/assets/{bots-cAzKkDtQ.js β bots-D5BasOGz.js} +2 -2
- package/dashboard/dist/assets/{bots-cAzKkDtQ.js.map β bots-D5BasOGz.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-RBFXLAXF.js β capabilities-BT3PW9CZ.js} +2 -2
- package/dashboard/dist/assets/{capabilities-RBFXLAXF.js.map β capabilities-BT3PW9CZ.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-DGhv3rwI.js β controlplane-NpJr49rN.js} +2 -2
- package/dashboard/dist/assets/{controlplane-DGhv3rwI.js.map β controlplane-NpJr49rN.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-ne7dIaPa.js β escalation-DrzQolPG.js} +2 -2
- package/dashboard/dist/assets/{escalation-ne7dIaPa.js.map β escalation-DrzQolPG.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-QzwmtD60.js +2 -0
- package/dashboard/dist/assets/escalation-columns-QzwmtD60.js.map +1 -0
- package/dashboard/dist/assets/index-B5w4Gjyk.js +6 -0
- package/dashboard/dist/assets/index-B5w4Gjyk.js.map +1 -0
- package/dashboard/dist/assets/{index-CByjpYlU.js β index-BCT4kqGR.js} +2 -2
- package/dashboard/dist/assets/{index-CByjpYlU.js.map β index-BCT4kqGR.js.map} +1 -1
- package/dashboard/dist/assets/{index-CgyucK0e.js β index-C69XvBfg.js} +2 -2
- package/dashboard/dist/assets/{index-CgyucK0e.js.map β index-C69XvBfg.js.map} +1 -1
- package/dashboard/dist/assets/index-CHw4Fl0t.css +1 -0
- package/dashboard/dist/assets/index-C_NwHTMh.js +2 -0
- package/dashboard/dist/assets/index-C_NwHTMh.js.map +1 -0
- package/dashboard/dist/assets/{index-DgzgRTNg.js β index-Cq0trGqf.js} +2 -2
- package/dashboard/dist/assets/{index-DgzgRTNg.js.map β index-Cq0trGqf.js.map} +1 -1
- package/dashboard/dist/assets/{index-DPJW96d2.js β index-DE0r9SEX.js} +26 -26
- package/dashboard/dist/assets/index-DE0r9SEX.js.map +1 -0
- package/dashboard/dist/assets/{index-BTqhAT6p.js β index-DH_CiD0W.js} +2 -2
- package/dashboard/dist/assets/{index-BTqhAT6p.js.map β index-DH_CiD0W.js.map} +1 -1
- package/dashboard/dist/assets/{index-C2l4GcZ7.js β index-K23FdS-T.js} +3 -3
- package/dashboard/dist/assets/{index-C2l4GcZ7.js.map β index-K23FdS-T.js.map} +1 -1
- package/dashboard/dist/assets/index-f6K1UNrn.js +2 -0
- package/dashboard/dist/assets/index-f6K1UNrn.js.map +1 -0
- package/dashboard/dist/assets/{index-BbJuC9kg.js β index-qr1fndqM.js} +2 -2
- package/dashboard/dist/assets/{index-BbJuC9kg.js.map β index-qr1fndqM.js.map} +1 -1
- package/dashboard/dist/assets/index-vKTvJ_li.js +2 -0
- package/dashboard/dist/assets/index-vKTvJ_li.js.map +1 -0
- package/dashboard/dist/assets/{index-CQDn-bLr.js β index-y-3ODtEE.js} +2 -2
- package/dashboard/dist/assets/{index-CQDn-bLr.js.map β index-y-3ODtEE.js.map} +1 -1
- package/dashboard/dist/assets/{index-QxAYCJ1S.js β index-y56Ds-LB.js} +2 -2
- package/dashboard/dist/assets/{index-QxAYCJ1S.js.map β index-y56Ds-LB.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-DzRpsm3D.js β knowledge-DSSATOTX.js} +2 -2
- package/dashboard/dist/assets/{knowledge-DzRpsm3D.js.map β knowledge-DSSATOTX.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-B1lc6nL4.js β mcp-CfJFVo5k.js} +2 -2
- package/dashboard/dist/assets/{mcp-B1lc6nL4.js.map β mcp-CfJFVo5k.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-CXuweaN3.js β mcp-query-DKVx2Q-X.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-CXuweaN3.js.map β mcp-query-DKVx2Q-X.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-D4VLWDvu.js β pipelines-BSWzE-6M.js} +2 -2
- package/dashboard/dist/assets/{pipelines-D4VLWDvu.js.map β pipelines-BSWzE-6M.js.map} +1 -1
- package/dashboard/dist/assets/{roles-CSpHRRu8.js β roles-t9HzhI5N.js} +2 -2
- package/dashboard/dist/assets/{roles-CSpHRRu8.js.map β roles-t9HzhI5N.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-CWUsKhM4.js β tasks-B7rUihoH.js} +2 -2
- package/dashboard/dist/assets/{tasks-CWUsKhM4.js.map β tasks-B7rUihoH.js.map} +1 -1
- package/dashboard/dist/assets/{topics-BXwstrzB.js β topics-PC2fEiyD.js} +2 -2
- package/dashboard/dist/assets/{topics-BXwstrzB.js.map β topics-PC2fEiyD.js.map} +1 -1
- package/dashboard/dist/assets/useDebouncedValue-BA5S7V54.js +2 -0
- package/dashboard/dist/assets/useDebouncedValue-BA5S7V54.js.map +1 -0
- package/dashboard/dist/assets/{useEventHooks-CjygrbYy.js β useEventHooks-DK9mw1cI.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-CjygrbYy.js.map β useEventHooks-DK9mw1cI.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-CsepDyR5.js β useNamespace-3IjtUtAw.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-CsepDyR5.js.map β useNamespace-3IjtUtAw.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-gWIG6akB.js β useYamlActivityEvents-uxukp3gO.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-gWIG6akB.js.map β useYamlActivityEvents-uxukp3gO.js.map} +1 -1
- package/dashboard/dist/assets/users-CC4B5Fm5.js +2 -0
- package/dashboard/dist/assets/users-CC4B5Fm5.js.map +1 -0
- package/dashboard/dist/assets/{vendor-icons-Dj2F0Jrb.js β vendor-icons-Dy5Jpknf.js} +114 -109
- package/dashboard/dist/assets/vendor-icons-Dy5Jpknf.js.map +1 -0
- package/dashboard/dist/assets/{workflows-C7Cam2Ok.js β workflows-S7MF1nNk.js} +2 -2
- package/dashboard/dist/assets/{workflows-C7Cam2Ok.js.map β workflows-S7MF1nNk.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-ChUOE22W.js β yaml-workflows-WfdaMHNn.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-ChUOE22W.js.map β yaml-workflows-WfdaMHNn.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 +164 -14
- 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-DZpduYdW.js +0 -2
- package/dashboard/dist/assets/AgentDetailPage-C-2iSaDs.js +0 -4
- package/dashboard/dist/assets/AgentDetailPage-C-2iSaDs.js.map +0 -1
- package/dashboard/dist/assets/AgentsPage-C3FztFd_.js +0 -2
- package/dashboard/dist/assets/AgentsPage-C3FztFd_.js.map +0 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-DIqdBCwe.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DIqdBCwe.js.map +0 -1
- package/dashboard/dist/assets/CapabilitiesPage-DTfKCzBQ.js +0 -2
- package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js +0 -2
- package/dashboard/dist/assets/CopyableId-DaT0ZRHg.js.map +0 -1
- package/dashboard/dist/assets/McpQueryPage-Db1LUKVs.js +0 -2
- package/dashboard/dist/assets/McpQueryPage-Db1LUKVs.js.map +0 -1
- package/dashboard/dist/assets/McpRunsPage--tgT5uo7.js +0 -2
- package/dashboard/dist/assets/McpRunsPage--tgT5uo7.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-BWVUbJkZ.js +0 -2
- package/dashboard/dist/assets/OperatorDashboard-BWVUbJkZ.js.map +0 -1
- package/dashboard/dist/assets/PageHeader-Bnt2iQQa.js +0 -2
- package/dashboard/dist/assets/PageHeader-Bnt2iQQa.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/ProcessesListPage-CWiRM_h3.js +0 -2
- package/dashboard/dist/assets/ProcessesListPage-CWiRM_h3.js.map +0 -1
- package/dashboard/dist/assets/RolePill-DQTT1s9v.js +0 -2
- package/dashboard/dist/assets/RolePill-DQTT1s9v.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/StreamMessageDetail-BF0tM0cd.js +0 -2
- package/dashboard/dist/assets/WorkflowsDashboard-M13lIaw7.js +0 -2
- package/dashboard/dist/assets/WorkflowsDashboard-M13lIaw7.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-DqTkKbCv.js +0 -2
- package/dashboard/dist/assets/escalation-columns-DqTkKbCv.js.map +0 -1
- package/dashboard/dist/assets/index-CEpw_Fh8.js +0 -2
- package/dashboard/dist/assets/index-CEpw_Fh8.js.map +0 -1
- package/dashboard/dist/assets/index-ClvYSNZd.css +0 -1
- package/dashboard/dist/assets/index-CucXk58o.js +0 -6
- package/dashboard/dist/assets/index-CucXk58o.js.map +0 -1
- package/dashboard/dist/assets/index-DPJW96d2.js.map +0 -1
- package/dashboard/dist/assets/index-DeZhNCf5.js +0 -2
- package/dashboard/dist/assets/index-DeZhNCf5.js.map +0 -1
- package/dashboard/dist/assets/index-Do8Xp3pa.js +0 -2
- package/dashboard/dist/assets/index-Do8Xp3pa.js.map +0 -1
- package/dashboard/dist/assets/users-DvhHbR0R.js +0 -2
- package/dashboard/dist/assets/users-DvhHbR0R.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-Dj2F0Jrb.js.map +0 -1
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_WRITE_SCOPE = exports.DEFAULT_READ_SCOPE = exports.WRITE_SCOPES = exports.READ_SCOPES = void 0;
|
|
4
|
+
exports.isValidReadScope = isValidReadScope;
|
|
5
|
+
exports.isValidWriteScope = isValidWriteScope;
|
|
6
|
+
exports.isValidScopePair = isValidScopePair;
|
|
7
|
+
exports.effectiveScope = effectiveScope;
|
|
8
|
+
// βββ Role scope: the work-surface axes for an open-role membership βββββββββββ
|
|
9
|
+
//
|
|
10
|
+
// A membership's `type` (member/admin/superadmin) is the management/global tier.
|
|
11
|
+
// Orthogonal to it, a member also carries two scope axes that gate the four task
|
|
12
|
+
// queue verbs:
|
|
13
|
+
//
|
|
14
|
+
// read_scope (self | all) β governs SEARCH (which items appear)
|
|
15
|
+
// write_scope (none | self | all) β governs CLAIM / ACK / DELETE (which items
|
|
16
|
+
// the user may act on)
|
|
17
|
+
//
|
|
18
|
+
// `self` = items assigned to the user (`assigned_to = userId`). `all` = the whole
|
|
19
|
+
// role queue. The only constraint is write β read: you cannot act on what you
|
|
20
|
+
// cannot see (enforced in the DB by chk_lt_user_roles_scope). admin/superadmin
|
|
21
|
+
// ignore scope and always operate on all.
|
|
22
|
+
exports.READ_SCOPES = ['self', 'all'];
|
|
23
|
+
exports.WRITE_SCOPES = ['none', 'self', 'all'];
|
|
24
|
+
/** Defaults preserve legacy `member` behavior (full role worker). */
|
|
25
|
+
exports.DEFAULT_READ_SCOPE = 'all';
|
|
26
|
+
exports.DEFAULT_WRITE_SCOPE = 'all';
|
|
27
|
+
function isValidReadScope(scope) {
|
|
28
|
+
return exports.READ_SCOPES.includes(scope);
|
|
29
|
+
}
|
|
30
|
+
function isValidWriteScope(scope) {
|
|
31
|
+
return exports.WRITE_SCOPES.includes(scope);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Is (read, write) a valid point in the lattice? Enforces write β read:
|
|
35
|
+
* write='all' requires read='all'. (write='self'/'none' allowed under read='self'.)
|
|
36
|
+
*/
|
|
37
|
+
function isValidScopePair(read, write) {
|
|
38
|
+
return !(write === 'all' && read === 'self');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the scope a membership actually grants. The management tier
|
|
42
|
+
* (admin/superadmin) short-circuits both axes to `all` β those users always
|
|
43
|
+
* search and act on the whole role queue regardless of stored scope columns.
|
|
44
|
+
*/
|
|
45
|
+
function effectiveScope(type, read, write) {
|
|
46
|
+
if (type === 'admin' || type === 'superadmin') {
|
|
47
|
+
return { read: 'all', write: 'all' };
|
|
48
|
+
}
|
|
49
|
+
return { read, write };
|
|
50
|
+
}
|
|
@@ -20,23 +20,40 @@ async function seedAdmin(input) {
|
|
|
20
20
|
catch { /* ON CONFLICT DO NOTHING */ }
|
|
21
21
|
const existing = await (0, crud_1.getUserByExternalId)(externalId);
|
|
22
22
|
if (existing) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
return ensureSuperadmin(existing.id, externalId);
|
|
24
|
+
}
|
|
25
|
+
// Create. Concurrent container startups can both reach here; external_id UNIQUE
|
|
26
|
+
// is the arbiter β on the loser's 23505, adopt the winner's row instead of
|
|
27
|
+
// crashing the boot sequence.
|
|
28
|
+
try {
|
|
29
|
+
const user = await (0, crud_1.createUser)({
|
|
30
|
+
external_id: externalId,
|
|
31
|
+
email: email || undefined,
|
|
32
|
+
display_name: displayName || externalId,
|
|
33
|
+
password: password || undefined,
|
|
34
|
+
roles: [{ role: 'superadmin', type: 'superadmin' }],
|
|
35
|
+
});
|
|
36
|
+
logger_1.loggerRegistry.info(`[seed-admin] created superadmin user: ${externalId}`);
|
|
37
|
+
return user.id;
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (err?.code === '23505') {
|
|
41
|
+
const winner = await (0, crud_1.getUserByExternalId)(externalId);
|
|
42
|
+
if (winner) {
|
|
43
|
+
return ensureSuperadmin(winner.id, externalId);
|
|
44
|
+
}
|
|
29
45
|
}
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Ensure an existing user carries the superadmin role; returns its id. */
|
|
50
|
+
async function ensureSuperadmin(userId, externalId) {
|
|
51
|
+
const roles = await (0, roles_1.getUserRoles)(userId);
|
|
52
|
+
const hasSuperadmin = roles.some((r) => r.role === 'superadmin' && r.type === 'superadmin');
|
|
53
|
+
if (!hasSuperadmin) {
|
|
54
|
+
await (0, roles_1.addUserRole)(userId, 'superadmin', 'superadmin');
|
|
55
|
+
logger_1.loggerRegistry.info(`[seed-admin] granted superadmin role to ${externalId}`);
|
|
32
56
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
email: email || undefined,
|
|
36
|
-
display_name: displayName || externalId,
|
|
37
|
-
password: password || undefined,
|
|
38
|
-
roles: [{ role: 'superadmin', type: 'superadmin' }],
|
|
39
|
-
});
|
|
40
|
-
logger_1.loggerRegistry.info(`[seed-admin] created superadmin user: ${externalId}`);
|
|
41
|
-
return user.id;
|
|
57
|
+
logger_1.loggerRegistry.info(`[seed-admin] ${externalId} already exists, skipping creation`);
|
|
58
|
+
return userId;
|
|
42
59
|
}
|
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
/** Ensure a role name exists in the lt_roles lookup table (FK target). */
|
|
2
2
|
export declare const ENSURE_ROLE_EXISTS = "INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING";
|
|
3
3
|
/** Fetch roles for a single user, ordered by creation time. */
|
|
4
|
-
export declare const GET_ROLES_BY_USER_ID = "SELECT role, type, created_at FROM lt_user_roles WHERE user_id = $1 ORDER BY created_at";
|
|
4
|
+
export declare const GET_ROLES_BY_USER_ID = "SELECT role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = $1 ORDER BY created_at";
|
|
5
5
|
/** Fetch a user row by external_id. */
|
|
6
6
|
export declare const GET_USER_BY_EXTERNAL_ID = "SELECT * FROM lt_users WHERE external_id = $1";
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Create a user and its role assignments in ONE atomic statement. The user row,
|
|
9
|
+
* the lt_roles FK targets ($9 = role names), and the lt_user_roles links
|
|
10
|
+
* ($9 names Γ $10 types Γ $11 read_scope Γ $12 write_scope) all commit together β
|
|
11
|
+
* a failure can never leave a half-provisioned user with a partial role set, and
|
|
12
|
+
* there is no per-role N+1. All four are passed as parallel text[] arrays (empty
|
|
13
|
+
* arrays β no roles). Postgres checks the lt_user_rolesβlt_roles FK at statement
|
|
14
|
+
* end, after the sibling role-ensure CTE has run, so brand-new roles are valid
|
|
15
|
+
* FK targets.
|
|
16
|
+
*/
|
|
17
|
+
export declare const CREATE_USER_WITH_ROLES = "WITH new_user AS (\n INSERT INTO lt_users (external_id, email, display_name, status, metadata, password_hash, oauth_provider, oauth_provider_id)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n RETURNING *\n ), ensured_roles AS (\n INSERT INTO lt_roles (role)\n SELECT DISTINCT unnest($9::text[])\n ON CONFLICT DO NOTHING\n ), assigned_roles AS (\n INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope)\n SELECT (SELECT id FROM new_user), x.role, x.type, x.read_scope, x.write_scope\n FROM unnest($9::text[], $10::text[], $11::text[], $12::text[]) AS x(role, type, read_scope, write_scope)\n ON CONFLICT DO NOTHING\n )\n SELECT * FROM new_user";
|
|
8
18
|
export declare const GET_USER_BY_EMAIL = "SELECT * FROM lt_users WHERE email = $1 LIMIT 1";
|
|
9
|
-
export declare const INSERT_USER_ROLE_IGNORE = "INSERT INTO lt_user_roles (user_id, role, type) VALUES ($1, $2, $3)\n ON CONFLICT DO NOTHING";
|
|
10
19
|
export declare const GET_USER_BY_ID = "SELECT * FROM lt_users WHERE id = $1";
|
|
11
20
|
/** Check if a user exists by id. Lightweight β returns only the id column. */
|
|
12
21
|
export declare const VERIFY_USER_BY_ID = "SELECT id FROM lt_users WHERE id = $1 LIMIT 1";
|
|
13
22
|
/** Fetch user + roles in a single query. Returns one row per role (or one row with nulls if no roles). */
|
|
14
|
-
export declare const GET_USER_WITH_ROLES = "SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,\n r.role, r.type AS role_type\n FROM lt_users u\n LEFT JOIN lt_user_roles r ON r.user_id = u.id\n WHERE u.external_id = $1\n ORDER BY r.created_at";
|
|
23
|
+
export declare const GET_USER_WITH_ROLES = "SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,\n r.role, r.type AS role_type, r.read_scope, r.write_scope\n FROM lt_users u\n LEFT JOIN lt_user_roles r ON r.user_id = u.id\n WHERE u.external_id = $1\n ORDER BY r.created_at";
|
|
15
24
|
export declare const DELETE_USER_BY_ID = "DELETE FROM lt_users WHERE id = $1";
|
|
16
25
|
/** Batch-load roles for many users (avoids N+1). */
|
|
17
|
-
export declare const GET_ROLES_BY_USER_IDS = "SELECT user_id, role, type, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at";
|
|
18
|
-
/** Upsert a userβrole assignment, promoting the type if the row exists. */
|
|
19
|
-
export declare const UPSERT_USER_ROLE = "INSERT INTO lt_user_roles (user_id, role, type) VALUES ($1, $2, $3)\n ON CONFLICT (user_id, role) DO UPDATE SET type = EXCLUDED.
|
|
26
|
+
export declare const GET_ROLES_BY_USER_IDS = "SELECT user_id, role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at";
|
|
27
|
+
/** Upsert a userβrole assignment, promoting the type/scope if the row exists. */
|
|
28
|
+
export declare const UPSERT_USER_ROLE = "INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (user_id, role) DO UPDATE SET\n type = EXCLUDED.type,\n read_scope = EXCLUDED.read_scope,\n write_scope = EXCLUDED.write_scope\n RETURNING role, type, read_scope, write_scope, created_at";
|
|
20
29
|
export declare const DELETE_USER_ROLE = "DELETE FROM lt_user_roles WHERE user_id = $1 AND role = $2";
|
|
21
30
|
export declare const HAS_ROLE = "SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1";
|
|
22
31
|
export declare const HAS_ROLE_TYPE = "SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND type = $2 LIMIT 1";
|
|
32
|
+
/** Fetch the management tier + scope for one (user, role) β used for write enforcement. */
|
|
33
|
+
export declare const GET_ROLE_SCOPE = "SELECT type, read_scope, write_scope FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1";
|
|
23
34
|
/** Check if a user is an admin (or superadmin) of a specific role group. */
|
|
24
35
|
export declare const IS_GROUP_ADMIN = "SELECT 1 FROM lt_user_roles\n WHERE user_id = $1\n AND (\n (role = $2 AND type IN ('admin', 'superadmin'))\n OR type = 'superadmin'\n )\n LIMIT 1";
|
|
@@ -1,26 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// βββ Shared queries (used by multiple modules) ββββββββββββββββββββββββββββββ
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.IS_GROUP_ADMIN = exports.HAS_ROLE_TYPE = exports.HAS_ROLE = exports.DELETE_USER_ROLE = exports.UPSERT_USER_ROLE = exports.GET_ROLES_BY_USER_IDS = exports.DELETE_USER_BY_ID = exports.GET_USER_WITH_ROLES = exports.VERIFY_USER_BY_ID = exports.GET_USER_BY_ID = exports.
|
|
4
|
+
exports.IS_GROUP_ADMIN = exports.GET_ROLE_SCOPE = exports.HAS_ROLE_TYPE = exports.HAS_ROLE = exports.DELETE_USER_ROLE = exports.UPSERT_USER_ROLE = exports.GET_ROLES_BY_USER_IDS = exports.DELETE_USER_BY_ID = exports.GET_USER_WITH_ROLES = exports.VERIFY_USER_BY_ID = exports.GET_USER_BY_ID = exports.GET_USER_BY_EMAIL = exports.CREATE_USER_WITH_ROLES = exports.GET_USER_BY_EXTERNAL_ID = exports.GET_ROLES_BY_USER_ID = exports.ENSURE_ROLE_EXISTS = void 0;
|
|
5
5
|
/** Ensure a role name exists in the lt_roles lookup table (FK target). */
|
|
6
6
|
exports.ENSURE_ROLE_EXISTS = 'INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING';
|
|
7
7
|
/** Fetch roles for a single user, ordered by creation time. */
|
|
8
|
-
exports.GET_ROLES_BY_USER_ID = 'SELECT role, type, created_at FROM lt_user_roles WHERE user_id = $1 ORDER BY created_at';
|
|
8
|
+
exports.GET_ROLES_BY_USER_ID = 'SELECT role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = $1 ORDER BY created_at';
|
|
9
9
|
/** Fetch a user row by external_id. */
|
|
10
10
|
exports.GET_USER_BY_EXTERNAL_ID = 'SELECT * FROM lt_users WHERE external_id = $1';
|
|
11
11
|
// βββ User CRUD βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Create a user and its role assignments in ONE atomic statement. The user row,
|
|
14
|
+
* the lt_roles FK targets ($9 = role names), and the lt_user_roles links
|
|
15
|
+
* ($9 names Γ $10 types Γ $11 read_scope Γ $12 write_scope) all commit together β
|
|
16
|
+
* a failure can never leave a half-provisioned user with a partial role set, and
|
|
17
|
+
* there is no per-role N+1. All four are passed as parallel text[] arrays (empty
|
|
18
|
+
* arrays β no roles). Postgres checks the lt_user_rolesβlt_roles FK at statement
|
|
19
|
+
* end, after the sibling role-ensure CTE has run, so brand-new roles are valid
|
|
20
|
+
* FK targets.
|
|
21
|
+
*/
|
|
22
|
+
exports.CREATE_USER_WITH_ROLES = `WITH new_user AS (
|
|
23
|
+
INSERT INTO lt_users (external_id, email, display_name, status, metadata, password_hash, oauth_provider, oauth_provider_id)
|
|
24
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
25
|
+
RETURNING *
|
|
26
|
+
), ensured_roles AS (
|
|
27
|
+
INSERT INTO lt_roles (role)
|
|
28
|
+
SELECT DISTINCT unnest($9::text[])
|
|
29
|
+
ON CONFLICT DO NOTHING
|
|
30
|
+
), assigned_roles AS (
|
|
31
|
+
INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope)
|
|
32
|
+
SELECT (SELECT id FROM new_user), x.role, x.type, x.read_scope, x.write_scope
|
|
33
|
+
FROM unnest($9::text[], $10::text[], $11::text[], $12::text[]) AS x(role, type, read_scope, write_scope)
|
|
34
|
+
ON CONFLICT DO NOTHING
|
|
35
|
+
)
|
|
36
|
+
SELECT * FROM new_user`;
|
|
15
37
|
exports.GET_USER_BY_EMAIL = `SELECT * FROM lt_users WHERE email = $1 LIMIT 1`;
|
|
16
|
-
exports.INSERT_USER_ROLE_IGNORE = `INSERT INTO lt_user_roles (user_id, role, type) VALUES ($1, $2, $3)
|
|
17
|
-
ON CONFLICT DO NOTHING`;
|
|
18
38
|
exports.GET_USER_BY_ID = 'SELECT * FROM lt_users WHERE id = $1';
|
|
19
39
|
/** Check if a user exists by id. Lightweight β returns only the id column. */
|
|
20
40
|
exports.VERIFY_USER_BY_ID = 'SELECT id FROM lt_users WHERE id = $1 LIMIT 1';
|
|
21
41
|
/** Fetch user + roles in a single query. Returns one row per role (or one row with nulls if no roles). */
|
|
22
42
|
exports.GET_USER_WITH_ROLES = `SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,
|
|
23
|
-
r.role, r.type AS role_type
|
|
43
|
+
r.role, r.type AS role_type, r.read_scope, r.write_scope
|
|
24
44
|
FROM lt_users u
|
|
25
45
|
LEFT JOIN lt_user_roles r ON r.user_id = u.id
|
|
26
46
|
WHERE u.external_id = $1
|
|
@@ -28,14 +48,19 @@ exports.GET_USER_WITH_ROLES = `SELECT u.id, u.external_id, u.display_name, u.sta
|
|
|
28
48
|
exports.DELETE_USER_BY_ID = 'DELETE FROM lt_users WHERE id = $1';
|
|
29
49
|
// βββ Role management βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
30
50
|
/** Batch-load roles for many users (avoids N+1). */
|
|
31
|
-
exports.GET_ROLES_BY_USER_IDS = 'SELECT user_id, role, type, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at';
|
|
32
|
-
/** Upsert a userβrole assignment, promoting the type if the row exists. */
|
|
33
|
-
exports.UPSERT_USER_ROLE = `INSERT INTO lt_user_roles (user_id, role, type) VALUES ($1, $2, $3)
|
|
34
|
-
ON CONFLICT (user_id, role) DO UPDATE SET
|
|
35
|
-
|
|
51
|
+
exports.GET_ROLES_BY_USER_IDS = 'SELECT user_id, role, type, read_scope, write_scope, created_at FROM lt_user_roles WHERE user_id = ANY($1) ORDER BY created_at';
|
|
52
|
+
/** Upsert a userβrole assignment, promoting the type/scope if the row exists. */
|
|
53
|
+
exports.UPSERT_USER_ROLE = `INSERT INTO lt_user_roles (user_id, role, type, read_scope, write_scope) VALUES ($1, $2, $3, $4, $5)
|
|
54
|
+
ON CONFLICT (user_id, role) DO UPDATE SET
|
|
55
|
+
type = EXCLUDED.type,
|
|
56
|
+
read_scope = EXCLUDED.read_scope,
|
|
57
|
+
write_scope = EXCLUDED.write_scope
|
|
58
|
+
RETURNING role, type, read_scope, write_scope, created_at`;
|
|
36
59
|
exports.DELETE_USER_ROLE = 'DELETE FROM lt_user_roles WHERE user_id = $1 AND role = $2';
|
|
37
60
|
exports.HAS_ROLE = 'SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1';
|
|
38
61
|
exports.HAS_ROLE_TYPE = 'SELECT 1 FROM lt_user_roles WHERE user_id = $1 AND type = $2 LIMIT 1';
|
|
62
|
+
/** Fetch the management tier + scope for one (user, role) β used for write enforcement. */
|
|
63
|
+
exports.GET_ROLE_SCOPE = 'SELECT type, read_scope, write_scope FROM lt_user_roles WHERE user_id = $1 AND role = $2 LIMIT 1';
|
|
39
64
|
// βββ RBAC ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
40
65
|
/** Check if a user is an admin (or superadmin) of a specific role group. */
|
|
41
66
|
exports.IS_GROUP_ADMIN = `SELECT 1 FROM lt_user_roles
|
|
@@ -18,34 +18,51 @@ async function ssoProvision(identity, ssoConfig) {
|
|
|
18
18
|
const ltRoles = resolveRoles(identity.roles || [], defaultRoleType, ssoConfig.roleMap);
|
|
19
19
|
const existing = await (0, crud_1.getUserByExternalId)(identity.externalId);
|
|
20
20
|
if (existing) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
return syncExistingUser(existing.id, ltRoles);
|
|
22
|
+
}
|
|
23
|
+
// Create new user. Two concurrent first-logins for the same identity can both
|
|
24
|
+
// pass the existence check above; external_id UNIQUE is the arbiter. The loser's
|
|
25
|
+
// insert raises 23505 β adopt the winner's committed row and sync roles rather
|
|
26
|
+
// than failing the login.
|
|
27
|
+
try {
|
|
28
|
+
const user = await (0, crud_1.createUser)({
|
|
29
|
+
external_id: identity.externalId,
|
|
30
|
+
email: identity.email || undefined,
|
|
31
|
+
display_name: identity.displayName || identity.externalId,
|
|
32
|
+
roles: ltRoles.map((r) => ({ role: r.role, type: r.type })),
|
|
33
|
+
metadata: identity.metadata,
|
|
34
|
+
});
|
|
35
|
+
logger_1.loggerRegistry.info(`[lt-sso] provisioned user: ${identity.externalId} β ${user.id}`);
|
|
30
36
|
return {
|
|
31
|
-
userId:
|
|
32
|
-
roles:
|
|
33
|
-
created:
|
|
37
|
+
userId: user.id,
|
|
38
|
+
roles: (user.roles || []).map((r) => ({ role: r.role, type: r.type })),
|
|
39
|
+
created: true,
|
|
34
40
|
};
|
|
35
41
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
catch (err) {
|
|
43
|
+
if (err?.code === '23505') {
|
|
44
|
+
const winner = await (0, crud_1.getUserByExternalId)(identity.externalId);
|
|
45
|
+
if (winner) {
|
|
46
|
+
return syncExistingUser(winner.id, ltRoles);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Ensure all resolved roles exist on an already-provisioned user. */
|
|
53
|
+
async function syncExistingUser(userId, ltRoles) {
|
|
54
|
+
const currentRoles = await (0, roles_1.getUserRoles)(userId);
|
|
55
|
+
for (const lr of ltRoles) {
|
|
56
|
+
const has = currentRoles.some((r) => r.role === lr.role);
|
|
57
|
+
if (!has) {
|
|
58
|
+
await (0, roles_1.addUserRole)(userId, lr.role, lr.type);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const updatedRoles = await (0, roles_1.getUserRoles)(userId);
|
|
45
62
|
return {
|
|
46
|
-
userId
|
|
47
|
-
roles:
|
|
48
|
-
created:
|
|
63
|
+
userId,
|
|
64
|
+
roles: updatedRoles.map((r) => ({ role: r.role, type: r.type })),
|
|
65
|
+
created: false,
|
|
49
66
|
};
|
|
50
67
|
}
|
|
51
68
|
function resolveRoles(hostRoles, defaultRoleType, roleMap) {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import type { LTRoleType, LTUserStatus } from '../../types';
|
|
1
|
+
import type { LTReadScope, LTRoleType, LTUserStatus, LTWriteScope } from '../../types';
|
|
2
2
|
export declare const VALID_ROLE_TYPES: LTRoleType[];
|
|
3
|
+
/** A role grant on user create: management tier + optional work-surface scope. */
|
|
4
|
+
export interface RoleGrantInput {
|
|
5
|
+
role: string;
|
|
6
|
+
type: LTRoleType;
|
|
7
|
+
read_scope?: LTReadScope;
|
|
8
|
+
write_scope?: LTWriteScope;
|
|
9
|
+
}
|
|
3
10
|
export interface CreateUserInput {
|
|
4
11
|
external_id: string;
|
|
5
12
|
email?: string;
|
|
@@ -7,10 +14,7 @@ export interface CreateUserInput {
|
|
|
7
14
|
password?: string;
|
|
8
15
|
status?: LTUserStatus;
|
|
9
16
|
metadata?: Record<string, any>;
|
|
10
|
-
roles?:
|
|
11
|
-
role: string;
|
|
12
|
-
type: LTRoleType;
|
|
13
|
-
}[];
|
|
17
|
+
roles?: RoleGrantInput[];
|
|
14
18
|
/** OAuth identity link (set during OAuth auto-provisioning). */
|
|
15
19
|
oauth_provider?: string;
|
|
16
20
|
oauth_provider_id?: string;
|
package/build/start/adapters.js
CHANGED
|
@@ -15,12 +15,15 @@ const adapter_1 = require("../services/mcp/adapter");
|
|
|
15
15
|
const escalation_strategy_1 = require("../services/escalation-strategy");
|
|
16
16
|
const default_1 = require("../services/escalation-strategy/default");
|
|
17
17
|
const mcp_2 = require("../services/escalation-strategy/mcp");
|
|
18
|
+
const features_1 = require("../modules/features");
|
|
18
19
|
const socket_auth_1 = require("./socket-auth");
|
|
19
20
|
/**
|
|
20
21
|
* Register all adapters (logging, telemetry, events, maintenance,
|
|
21
22
|
* escalation strategy, MCP) based on the startup config.
|
|
22
23
|
*/
|
|
23
24
|
function registerAdapters(startConfig) {
|
|
25
|
+
// Dashboard feature flags (default permissive; deployment opts out).
|
|
26
|
+
(0, features_1.configureFeatureFlags)(startConfig.features);
|
|
24
27
|
// Logging (register first so subsequent log calls use it)
|
|
25
28
|
if (startConfig.logging?.adapter) {
|
|
26
29
|
logger_1.loggerRegistry.register(startConfig.logging.adapter);
|