@hotmeshio/long-tail 0.4.21 → 0.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api/dba.d.ts +2 -1
- package/build/api/dba.js +3 -2
- package/build/api/escalations/claim.d.ts +2 -0
- package/build/api/escalations/claim.js +10 -6
- package/build/api/escalations/helpers.d.ts +24 -1
- package/build/api/escalations/helpers.js +48 -3
- package/build/api/escalations/metadata.d.ts +10 -2
- package/build/api/escalations/metadata.js +29 -6
- package/build/api/exports.d.ts +1 -0
- package/build/api/exports.js +1 -1
- package/build/api/maintenance.d.ts +1 -0
- package/build/api/maintenance.js +3 -3
- package/build/api/workflows/discovery.js +5 -2
- package/build/modules/auth.d.ts +1 -1
- package/build/modules/auth.js +3 -3
- package/build/modules/maintenance.js +1 -0
- package/build/routes/controlplane.js +40 -29
- package/build/routes/dba.js +13 -2
- package/build/routes/escalations/metadata.js +2 -1
- package/build/routes/escalations/single.js +1 -1
- package/build/routes/exports.js +6 -0
- package/build/routes/maintenance.js +1 -0
- package/build/sdk/index.d.ts +2 -0
- package/build/services/auth/bot-api-key.js +6 -24
- package/build/services/auth/service-token.js +6 -18
- package/build/services/auth/sql.d.ts +11 -0
- package/build/services/auth/sql.js +37 -0
- package/build/services/controlplane/index.js +7 -5
- package/build/services/dba.d.ts +3 -1
- package/build/services/dba.js +5 -4
- package/build/services/escalation/crud.d.ts +19 -3
- package/build/services/escalation/crud.js +32 -15
- package/build/services/escalation/sql.d.ts +9 -2
- package/build/services/escalation/sql.js +36 -14
- package/build/services/export/index.d.ts +2 -2
- package/build/services/export/index.js +16 -10
- package/build/services/maintenance/index.js +7 -4
- package/build/services/pipelines/queries.js +5 -2
- package/build/services/pipelines/sql.d.ts +0 -1
- package/build/services/pipelines/sql.js +1 -3
- package/build/system/mcp-servers/admin/controlplane.js +1 -1
- package/build/system/mcp-servers/admin/maintenance.js +1 -0
- package/build/system/mcp-servers/admin/pipelines.js +4 -4
- package/build/system/mcp-servers/admin/schemas.d.ts +31 -25
- package/build/system/mcp-servers/admin/schemas.js +9 -7
- package/build/system/seed/tool-manifests-admin.d.ts +59 -0
- package/build/system/seed/tool-manifests-admin.js +2 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/maintenance.d.ts +2 -0
- package/dashboard/dist/assets/{AdminDashboard-BwUGcCxQ.js → AdminDashboard-CgJC8ZZF.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-BwUGcCxQ.js.map → AdminDashboard-CgJC8ZZF.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-DgrYzLwq.js → AgentConfigPage-Bjl2Lsvo.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-DgrYzLwq.js.map → AgentConfigPage-Bjl2Lsvo.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-XJpl7wfJ.js → AgentDetailPage-D5dHrfaM.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-XJpl7wfJ.js.map → AgentDetailPage-D5dHrfaM.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-CGpVG6r8.js → AgentsPage-Mom3N1Av.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-CGpVG6r8.js.map → AgentsPage-Mom3N1Av.js.map} +1 -1
- package/dashboard/dist/assets/{AvailableEscalationsPage-DR1e0TQZ.js → AvailableEscalationsPage-B2ZAb41C.js} +2 -2
- package/dashboard/dist/assets/{AvailableEscalationsPage-DR1e0TQZ.js.map → AvailableEscalationsPage-B2ZAb41C.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-BKtjl6IL.js → BotPicker-dCvnjynP.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-BKtjl6IL.js.map → BotPicker-dCvnjynP.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-kCB8fyOj.js → CapabilitiesPage-CK2fJ9Sy.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-kCB8fyOj.js.map → CapabilitiesPage-CK2fJ9Sy.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-C3tU61hB.js → CollapsibleSection-bW0UZN9b.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-C3tU61hB.js.map → CollapsibleSection-bW0UZN9b.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-Dt4nJs_B.js → CredentialsPage-DVOK3aaR.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-Dt4nJs_B.js.map → CredentialsPage-DVOK3aaR.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-BdE6mHyA.js → CronLabel-Cv5em7OP.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-BdE6mHyA.js.map → CronLabel-Cv5em7OP.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-B_Yxfb-u.js → CustomDurationPicker-Dy4NBqhZ.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-B_Yxfb-u.js.map → CustomDurationPicker-Dy4NBqhZ.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-tcGx5PFI.js → ElapsedCell-TQqWaVRq.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-tcGx5PFI.js.map → ElapsedCell-TQqWaVRq.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-1KO5dXzk.js → EscalationsOverview-Cv5UvuHI.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-1KO5dXzk.js.map → EscalationsOverview-Cv5UvuHI.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-DnpsQ6Ew.js → EventTable-Doky6fCO.js} +2 -2
- package/dashboard/dist/assets/{EventTable-DnpsQ6Ew.js.map → EventTable-Doky6fCO.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-CzvVyTq4.js +2 -0
- package/dashboard/dist/assets/HomePage-CzvVyTq4.js.map +1 -0
- package/dashboard/dist/assets/{ListToolbar-jrVba7QN.js → ListToolbar-Cfec9gz_.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-jrVba7QN.js.map → ListToolbar-Cfec9gz_.js.map} +1 -1
- package/dashboard/dist/assets/McpOverview-BN4GsBGI.js +2 -0
- package/dashboard/dist/assets/McpOverview-BN4GsBGI.js.map +1 -0
- package/dashboard/dist/assets/McpQueryDetailPage-lCW668WQ.js +5 -0
- package/dashboard/dist/assets/McpQueryDetailPage-lCW668WQ.js.map +1 -0
- package/dashboard/dist/assets/{McpQueryPage-WZfTY43_.js → McpQueryPage-BK5L2PqJ.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-WZfTY43_.js.map → McpQueryPage-BK5L2PqJ.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-CQOeYqxa.js +2 -0
- package/dashboard/dist/assets/McpRunDetailPage-CQOeYqxa.js.map +1 -0
- package/dashboard/dist/assets/McpRunsPage-QsXid9Xe.js +2 -0
- package/dashboard/dist/assets/McpRunsPage-QsXid9Xe.js.map +1 -0
- package/dashboard/dist/assets/{OperatorDashboard-Cy7ySMXj.js → OperatorDashboard-CZQSINho.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-Cy7ySMXj.js.map → OperatorDashboard-CZQSINho.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-DYIfvWyc.js → ProcessDetailPage-DUCOOvOK.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-DYIfvWyc.js.map → ProcessDetailPage-DUCOOvOK.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-DR1RGaMl.js → ProcessesListPage-CXvSLTIM.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-DR1RGaMl.js.map → ProcessesListPage-CXvSLTIM.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-DlQR0Iz_.js +2 -0
- package/dashboard/dist/assets/RolesPage-DlQR0Iz_.js.map +1 -0
- package/dashboard/dist/assets/{RunAsSelector-B-ksMoEj.js → RunAsSelector-DP-jxsv6.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-B-ksMoEj.js.map → RunAsSelector-DP-jxsv6.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-Cr_K5qpu.js → SwimlaneTimeline-BmASA0nN.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-Cr_K5qpu.js.map → SwimlaneTimeline-BmASA0nN.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-BO5p7AEe.js → TaskDetailPage-CRowpkeZ.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-BO5p7AEe.js.map → TaskDetailPage-CRowpkeZ.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-BRg-uFtF.js → TasksListPage-uJ6z37J-.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-BRg-uFtF.js.map → TasksListPage-uJ6z37J-.js.map} +1 -1
- package/dashboard/dist/assets/TimeAgo-BxwngK1D.js +2 -0
- package/dashboard/dist/assets/{TimeAgo-BSzN6rAH.js.map → TimeAgo-BxwngK1D.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-DL6zMNEQ.js → TimestampCell-CDmichOM.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-DL6zMNEQ.js.map → TimestampCell-CDmichOM.js.map} +1 -1
- package/dashboard/dist/assets/ToolTestPanel-xjTn8sU8.js +2 -0
- package/dashboard/dist/assets/ToolTestPanel-xjTn8sU8.js.map +1 -0
- package/dashboard/dist/assets/{TopicDetailPage-D7gCsPKB.js → TopicDetailPage-Dm0hDlS8.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-D7gCsPKB.js.map → TopicDetailPage-Dm0hDlS8.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-B3Aa8Haz.js → TopicsPage-letISGGD.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-B3Aa8Haz.js.map → TopicsPage-letISGGD.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-BjHIJWgh.js → UserName-W6_Iz2Qb.js} +2 -2
- package/dashboard/dist/assets/{UserName-BjHIJWgh.js.map → UserName-W6_Iz2Qb.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-BQ7AYlQA.js → WorkflowExecutionPage-Cfx-xlRT.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-BQ7AYlQA.js.map → WorkflowExecutionPage-Cfx-xlRT.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowsDashboard-DC4XHMWt.js +2 -0
- package/dashboard/dist/assets/WorkflowsDashboard-DC4XHMWt.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowsOverview-B4DUcVxs.js → WorkflowsOverview-GefO_yn0.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-B4DUcVxs.js.map → WorkflowsOverview-GefO_yn0.js.map} +1 -1
- package/dashboard/dist/assets/YamlWorkflowsPage-CMsrFooO.js +2 -0
- package/dashboard/dist/assets/YamlWorkflowsPage-CMsrFooO.js.map +1 -0
- package/dashboard/dist/assets/{agents-CkvQDr9b.js → agents-BI5OeN84.js} +2 -2
- package/dashboard/dist/assets/{agents-CkvQDr9b.js.map → agents-BI5OeN84.js.map} +1 -1
- package/dashboard/dist/assets/{bots-CzuMCVgU.js → bots-1UzUCsrR.js} +2 -2
- package/dashboard/dist/assets/{bots-CzuMCVgU.js.map → bots-1UzUCsrR.js.map} +1 -1
- package/dashboard/dist/assets/capabilities-BUbl-ojp.js +2 -0
- package/dashboard/dist/assets/{capabilities-CbGmS0ty.js.map → capabilities-BUbl-ojp.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-DGvwkuYx.js → controlplane-DTFrH_vN.js} +2 -2
- package/dashboard/dist/assets/{controlplane-DGvwkuYx.js.map → controlplane-DTFrH_vN.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-B7ysVToF.js → escalation-BQhCt4W0.js} +2 -2
- package/dashboard/dist/assets/{escalation-B7ysVToF.js.map → escalation-BQhCt4W0.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-CHQEJU1j.js → escalation-columns-J20k5CcY.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-CHQEJU1j.js.map → escalation-columns-J20k5CcY.js.map} +1 -1
- package/dashboard/dist/assets/{helpers-BFOjXa4r.js → helpers-ge6Eu90Y.js} +2 -2
- package/dashboard/dist/assets/{helpers-BFOjXa4r.js.map → helpers-ge6Eu90Y.js.map} +1 -1
- package/dashboard/dist/assets/index-C-mbURj-.js +2 -0
- package/dashboard/dist/assets/index-C-mbURj-.js.map +1 -0
- package/dashboard/dist/assets/{index-BduDiGcw.js → index-C45DvtAZ.js} +2 -2
- package/dashboard/dist/assets/{index-BduDiGcw.js.map → index-C45DvtAZ.js.map} +1 -1
- package/dashboard/dist/assets/{index-B9_1AZaG.js → index-C9ClHiiW.js} +2 -2
- package/dashboard/dist/assets/{index-B9_1AZaG.js.map → index-C9ClHiiW.js.map} +1 -1
- package/dashboard/dist/assets/index-CLUYzdwz.js +2 -0
- package/dashboard/dist/assets/{index-DQHmfTPo.js.map → index-CLUYzdwz.js.map} +1 -1
- package/dashboard/dist/assets/{index-l_8R6U4r.js → index-CVGgSoda.js} +2 -2
- package/dashboard/dist/assets/{index-l_8R6U4r.js.map → index-CVGgSoda.js.map} +1 -1
- package/dashboard/dist/assets/{index-_BRA9uFL.js → index-CWEOhAiK.js} +3 -3
- package/dashboard/dist/assets/{index-_BRA9uFL.js.map → index-CWEOhAiK.js.map} +1 -1
- package/dashboard/dist/assets/{index-BFaDxPxA.js → index-CWlP6vHG.js} +2 -2
- package/dashboard/dist/assets/{index-BFaDxPxA.js.map → index-CWlP6vHG.js.map} +1 -1
- package/dashboard/dist/assets/index-DasoTRjT.js +2 -0
- package/dashboard/dist/assets/{index-BeLphL59.js.map → index-DasoTRjT.js.map} +1 -1
- package/dashboard/dist/assets/{index-CvOGgvzP.js → index-FhasoOjO.js} +2 -2
- package/dashboard/dist/assets/{index-CvOGgvzP.js.map → index-FhasoOjO.js.map} +1 -1
- package/dashboard/dist/assets/{index-a98qWLB-.js → index-WQQJ_cp7.js} +2 -2
- package/dashboard/dist/assets/{index-a98qWLB-.js.map → index-WQQJ_cp7.js.map} +1 -1
- package/dashboard/dist/assets/{index-CbrMW-gM.js → index-hAZiac0C.js} +2 -2
- package/dashboard/dist/assets/{index-CbrMW-gM.js.map → index-hAZiac0C.js.map} +1 -1
- package/dashboard/dist/assets/{index-v0OQpgXS.js → index-si70YcIP.js} +2 -2
- package/dashboard/dist/assets/{index-v0OQpgXS.js.map → index-si70YcIP.js.map} +1 -1
- package/dashboard/dist/assets/{index-CRiBkHPb.js → index-vgxjge70.js} +2 -2
- package/dashboard/dist/assets/{index-CRiBkHPb.js.map → index-vgxjge70.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-BlF8UMrk.js → knowledge-D9Tuh-o-.js} +2 -2
- package/dashboard/dist/assets/{knowledge-BlF8UMrk.js.map → knowledge-D9Tuh-o-.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-MtXuky8q.js → mcp-BO8QnWyk.js} +2 -2
- package/dashboard/dist/assets/{mcp-MtXuky8q.js.map → mcp-BO8QnWyk.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-DQ-J1Q0K.js → mcp-query-WLtQtr51.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-DQ-J1Q0K.js.map → mcp-query-WLtQtr51.js.map} +1 -1
- package/dashboard/dist/assets/pipelines-BAVf9xud.js +2 -0
- package/dashboard/dist/assets/pipelines-BAVf9xud.js.map +1 -0
- package/dashboard/dist/assets/{roles-D-LhJ82d.js → roles-mGO2-2hA.js} +2 -2
- package/dashboard/dist/assets/{roles-D-LhJ82d.js.map → roles-mGO2-2hA.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-BrP_8uEN.js → tasks-JVRVCx0f.js} +2 -2
- package/dashboard/dist/assets/{tasks-BrP_8uEN.js.map → tasks-JVRVCx0f.js.map} +1 -1
- package/dashboard/dist/assets/{topics-DUk-zX5D.js → topics-BLVnahd7.js} +2 -2
- package/dashboard/dist/assets/{topics-DUk-zX5D.js.map → topics-BLVnahd7.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-XNNzwADV.js → useEventHooks-BwjAi0Qq.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-XNNzwADV.js.map → useEventHooks-BwjAi0Qq.js.map} +1 -1
- package/dashboard/dist/assets/useNamespace-DkHmXddZ.js +2 -0
- package/dashboard/dist/assets/useNamespace-DkHmXddZ.js.map +1 -0
- package/dashboard/dist/assets/{useYamlActivityEvents-DANQ5jIY.js → useYamlActivityEvents-CsaR5dWj.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-DANQ5jIY.js.map → useYamlActivityEvents-CsaR5dWj.js.map} +1 -1
- package/dashboard/dist/assets/{users-vj0JgOkA.js → users-BvizpAkV.js} +2 -2
- package/dashboard/dist/assets/{users-vj0JgOkA.js.map → users-BvizpAkV.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-CmqgGPzI.js → workflows-CyEYa01a.js} +2 -2
- package/dashboard/dist/assets/{workflows-CmqgGPzI.js.map → workflows-CyEYa01a.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DNFyjBXH.js → yaml-workflows-i3GzrEme.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DNFyjBXH.js.map → yaml-workflows-i3GzrEme.js.map} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/docs/api/http/controlplane.md +6 -4
- package/docs/api/http/dba.md +1 -0
- package/docs/api/http/escalations.md +38 -2
- package/docs/api/http/exports.md +26 -0
- package/docs/api/http/maintenance.md +1 -0
- package/docs/api/mcp/admin.md +7 -7
- package/docs/api/sdk/controlplane.md +4 -4
- package/docs/api/sdk/escalations.md +25 -5
- package/package.json +1 -1
- package/dashboard/dist/assets/HomePage-B2Jgo1J1.js +0 -2
- package/dashboard/dist/assets/HomePage-B2Jgo1J1.js.map +0 -1
- package/dashboard/dist/assets/McpOverview-BzyxJyc9.js +0 -2
- package/dashboard/dist/assets/McpOverview-BzyxJyc9.js.map +0 -1
- package/dashboard/dist/assets/McpQueryDetailPage-DXNseeKl.js +0 -5
- package/dashboard/dist/assets/McpQueryDetailPage-DXNseeKl.js.map +0 -1
- package/dashboard/dist/assets/McpRunDetailPage-DKZp-p7S.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-DKZp-p7S.js.map +0 -1
- package/dashboard/dist/assets/McpRunsPage-SXyiwc0d.js +0 -2
- package/dashboard/dist/assets/McpRunsPage-SXyiwc0d.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-pMERxj15.js +0 -2
- package/dashboard/dist/assets/RolesPage-pMERxj15.js.map +0 -1
- package/dashboard/dist/assets/TimeAgo-BSzN6rAH.js +0 -2
- package/dashboard/dist/assets/ToolTestPanel-fLzNp79U.js +0 -2
- package/dashboard/dist/assets/ToolTestPanel-fLzNp79U.js.map +0 -1
- package/dashboard/dist/assets/WorkflowsDashboard-D-G8Xudz.js +0 -2
- package/dashboard/dist/assets/WorkflowsDashboard-D-G8Xudz.js.map +0 -1
- package/dashboard/dist/assets/YamlWorkflowsPage-CnTNOku0.js +0 -2
- package/dashboard/dist/assets/YamlWorkflowsPage-CnTNOku0.js.map +0 -1
- package/dashboard/dist/assets/capabilities-CbGmS0ty.js +0 -2
- package/dashboard/dist/assets/index-BeLphL59.js +0 -2
- package/dashboard/dist/assets/index-DDlrQeTj.js +0 -2
- package/dashboard/dist/assets/index-DDlrQeTj.js.map +0 -1
- package/dashboard/dist/assets/index-DQHmfTPo.js +0 -2
- package/dashboard/dist/assets/namespaces-DtsT_GoV.js +0 -2
- package/dashboard/dist/assets/namespaces-DtsT_GoV.js.map +0 -1
- package/dashboard/dist/assets/pipelines-BjlCm9VH.js +0 -2
- package/dashboard/dist/assets/pipelines-BjlCm9VH.js.map +0 -1
|
@@ -43,26 +43,8 @@ exports.listBotApiKeys = listBotApiKeys;
|
|
|
43
43
|
const crypto = __importStar(require("crypto"));
|
|
44
44
|
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
45
45
|
const db_1 = require("../../lib/db");
|
|
46
|
+
const sql_1 = require("./sql");
|
|
46
47
|
const TOKEN_PREFIX = 'lt_bot_';
|
|
47
|
-
const INSERT_KEY = `
|
|
48
|
-
INSERT INTO lt_bot_api_keys (name, user_id, key_hash, scopes, expires_at)
|
|
49
|
-
VALUES ($1, $2, $3, $4, $5) RETURNING id`;
|
|
50
|
-
const GET_KEYS_BY_USER = `
|
|
51
|
-
SELECT id, name, user_id, key_hash, scopes
|
|
52
|
-
FROM lt_bot_api_keys
|
|
53
|
-
WHERE user_id = $1
|
|
54
|
-
AND (expires_at IS NULL OR expires_at > NOW())`;
|
|
55
|
-
const GET_ALL_ACTIVE_KEYS = `
|
|
56
|
-
SELECT id, name, user_id, key_hash, scopes
|
|
57
|
-
FROM lt_bot_api_keys
|
|
58
|
-
WHERE (expires_at IS NULL OR expires_at > NOW())`;
|
|
59
|
-
const UPDATE_LAST_USED = `
|
|
60
|
-
UPDATE lt_bot_api_keys SET last_used_at = NOW() WHERE id = $1`;
|
|
61
|
-
const DELETE_KEY = `
|
|
62
|
-
DELETE FROM lt_bot_api_keys WHERE id = $1`;
|
|
63
|
-
const LIST_BY_USER = `
|
|
64
|
-
SELECT id, name, user_id, scopes, expires_at, last_used_at, created_at, updated_at
|
|
65
|
-
FROM lt_bot_api_keys WHERE user_id = $1 ORDER BY created_at`;
|
|
66
48
|
/**
|
|
67
49
|
* Generate a new API key for a bot account.
|
|
68
50
|
* Returns the raw key once — it is never stored in plaintext.
|
|
@@ -71,7 +53,7 @@ async function generateBotApiKey(name, userId, scopes = [], expiresAt) {
|
|
|
71
53
|
const rawKey = `${TOKEN_PREFIX}${crypto.randomBytes(32).toString('hex')}`;
|
|
72
54
|
const keyHash = await bcryptjs_1.default.hash(rawKey, 10);
|
|
73
55
|
const pool = await (0, db_1.getPool)();
|
|
74
|
-
const { rows } = await pool.query(
|
|
56
|
+
const { rows } = await pool.query(sql_1.INSERT_BOT_KEY, [
|
|
75
57
|
name, userId, keyHash, scopes, expiresAt || null,
|
|
76
58
|
]);
|
|
77
59
|
return { id: rows[0].id, rawKey };
|
|
@@ -84,10 +66,10 @@ async function validateBotApiKey(rawKey) {
|
|
|
84
66
|
if (!rawKey.startsWith(TOKEN_PREFIX))
|
|
85
67
|
return null;
|
|
86
68
|
const pool = await (0, db_1.getPool)();
|
|
87
|
-
const { rows } = await pool.query(
|
|
69
|
+
const { rows } = await pool.query(sql_1.GET_ALL_ACTIVE_BOT_KEYS);
|
|
88
70
|
for (const row of rows) {
|
|
89
71
|
if (await bcryptjs_1.default.compare(rawKey, row.key_hash)) {
|
|
90
|
-
await pool.query(
|
|
72
|
+
await pool.query(sql_1.UPDATE_BOT_KEY_LAST_USED, [row.id]);
|
|
91
73
|
const { key_hash, ...record } = row;
|
|
92
74
|
return record;
|
|
93
75
|
}
|
|
@@ -99,7 +81,7 @@ async function validateBotApiKey(rawKey) {
|
|
|
99
81
|
*/
|
|
100
82
|
async function revokeBotApiKey(id) {
|
|
101
83
|
const pool = await (0, db_1.getPool)();
|
|
102
|
-
const result = await pool.query(
|
|
84
|
+
const result = await pool.query(sql_1.DELETE_BOT_KEY, [id]);
|
|
103
85
|
return (result.rowCount ?? 0) > 0;
|
|
104
86
|
}
|
|
105
87
|
/**
|
|
@@ -107,6 +89,6 @@ async function revokeBotApiKey(id) {
|
|
|
107
89
|
*/
|
|
108
90
|
async function listBotApiKeys(userId) {
|
|
109
91
|
const pool = await (0, db_1.getPool)();
|
|
110
|
-
const { rows } = await pool.query(
|
|
92
|
+
const { rows } = await pool.query(sql_1.LIST_BOT_KEYS_BY_USER, [userId]);
|
|
111
93
|
return rows;
|
|
112
94
|
}
|
|
@@ -43,20 +43,8 @@ exports.listServiceTokens = listServiceTokens;
|
|
|
43
43
|
const crypto = __importStar(require("crypto"));
|
|
44
44
|
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
45
45
|
const db_1 = require("../../lib/db");
|
|
46
|
+
const sql_1 = require("./sql");
|
|
46
47
|
const TOKEN_PREFIX = 'lt_svc_';
|
|
47
|
-
const INSERT_TOKEN = `
|
|
48
|
-
INSERT INTO lt_service_tokens (name, token_hash, server_id, scopes, expires_at)
|
|
49
|
-
VALUES ($1, $2, $3, $4, $5) RETURNING id`;
|
|
50
|
-
const GET_ALL_TOKENS = `
|
|
51
|
-
SELECT id, name, token_hash FROM lt_service_tokens
|
|
52
|
-
WHERE (expires_at IS NULL OR expires_at > NOW())`;
|
|
53
|
-
const UPDATE_LAST_USED = `
|
|
54
|
-
UPDATE lt_service_tokens SET last_used_at = NOW() WHERE id = $1`;
|
|
55
|
-
const DELETE_TOKEN = `
|
|
56
|
-
DELETE FROM lt_service_tokens WHERE id = $1`;
|
|
57
|
-
const LIST_BY_SERVER = `
|
|
58
|
-
SELECT id, name, server_id, scopes, expires_at, last_used_at, created_at, updated_at
|
|
59
|
-
FROM lt_service_tokens WHERE server_id = $1 ORDER BY created_at`;
|
|
60
48
|
/**
|
|
61
49
|
* Generate a new service token for an external MCP server.
|
|
62
50
|
* Returns the raw token once — it is never stored in plaintext.
|
|
@@ -65,7 +53,7 @@ async function generateServiceToken(name, serverId, scopes, expiresAt) {
|
|
|
65
53
|
const rawToken = `${TOKEN_PREFIX}${crypto.randomBytes(32).toString('hex')}`;
|
|
66
54
|
const tokenHash = await bcryptjs_1.default.hash(rawToken, 10);
|
|
67
55
|
const pool = await (0, db_1.getPool)();
|
|
68
|
-
const { rows } = await pool.query(
|
|
56
|
+
const { rows } = await pool.query(sql_1.INSERT_SERVICE_TOKEN, [
|
|
69
57
|
name, tokenHash, serverId, scopes, expiresAt || null,
|
|
70
58
|
]);
|
|
71
59
|
return { id: rows[0].id, rawToken };
|
|
@@ -77,10 +65,10 @@ async function validateServiceToken(rawToken) {
|
|
|
77
65
|
if (!rawToken.startsWith(TOKEN_PREFIX))
|
|
78
66
|
return null;
|
|
79
67
|
const pool = await (0, db_1.getPool)();
|
|
80
|
-
const { rows } = await pool.query(
|
|
68
|
+
const { rows } = await pool.query(sql_1.GET_ALL_ACTIVE_SERVICE_TOKENS);
|
|
81
69
|
for (const row of rows) {
|
|
82
70
|
if (await bcryptjs_1.default.compare(rawToken, row.token_hash)) {
|
|
83
|
-
await pool.query(
|
|
71
|
+
await pool.query(sql_1.UPDATE_SERVICE_TOKEN_LAST_USED, [row.id]);
|
|
84
72
|
const { token_hash, ...record } = row;
|
|
85
73
|
return record;
|
|
86
74
|
}
|
|
@@ -92,7 +80,7 @@ async function validateServiceToken(rawToken) {
|
|
|
92
80
|
*/
|
|
93
81
|
async function revokeServiceToken(id) {
|
|
94
82
|
const pool = await (0, db_1.getPool)();
|
|
95
|
-
const result = await pool.query(
|
|
83
|
+
const result = await pool.query(sql_1.DELETE_SERVICE_TOKEN, [id]);
|
|
96
84
|
return (result.rowCount ?? 0) > 0;
|
|
97
85
|
}
|
|
98
86
|
/**
|
|
@@ -100,6 +88,6 @@ async function revokeServiceToken(id) {
|
|
|
100
88
|
*/
|
|
101
89
|
async function listServiceTokens(serverId) {
|
|
102
90
|
const pool = await (0, db_1.getPool)();
|
|
103
|
-
const { rows } = await pool.query(
|
|
91
|
+
const { rows } = await pool.query(sql_1.LIST_SERVICE_TOKENS_BY_SERVER, [serverId]);
|
|
104
92
|
return rows;
|
|
105
93
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const INSERT_BOT_KEY = "\n INSERT INTO lt_bot_api_keys (name, user_id, key_hash, scopes, expires_at)\n VALUES ($1, $2, $3, $4, $5) RETURNING id";
|
|
2
|
+
export declare const GET_BOT_KEYS_BY_USER = "\n SELECT id, name, user_id, key_hash, scopes\n FROM lt_bot_api_keys\n WHERE user_id = $1\n AND (expires_at IS NULL OR expires_at > NOW())";
|
|
3
|
+
export declare const GET_ALL_ACTIVE_BOT_KEYS = "\n SELECT id, name, user_id, key_hash, scopes\n FROM lt_bot_api_keys\n WHERE (expires_at IS NULL OR expires_at > NOW())";
|
|
4
|
+
export declare const UPDATE_BOT_KEY_LAST_USED = "\n UPDATE lt_bot_api_keys SET last_used_at = NOW() WHERE id = $1";
|
|
5
|
+
export declare const DELETE_BOT_KEY = "\n DELETE FROM lt_bot_api_keys WHERE id = $1";
|
|
6
|
+
export declare const LIST_BOT_KEYS_BY_USER = "\n SELECT id, name, user_id, scopes, expires_at, last_used_at, created_at, updated_at\n FROM lt_bot_api_keys WHERE user_id = $1 ORDER BY created_at";
|
|
7
|
+
export declare const INSERT_SERVICE_TOKEN = "\n INSERT INTO lt_service_tokens (name, token_hash, server_id, scopes, expires_at)\n VALUES ($1, $2, $3, $4, $5) RETURNING id";
|
|
8
|
+
export declare const GET_ALL_ACTIVE_SERVICE_TOKENS = "\n SELECT id, name, token_hash FROM lt_service_tokens\n WHERE (expires_at IS NULL OR expires_at > NOW())";
|
|
9
|
+
export declare const UPDATE_SERVICE_TOKEN_LAST_USED = "\n UPDATE lt_service_tokens SET last_used_at = NOW() WHERE id = $1";
|
|
10
|
+
export declare const DELETE_SERVICE_TOKEN = "\n DELETE FROM lt_service_tokens WHERE id = $1";
|
|
11
|
+
export declare const LIST_SERVICE_TOKENS_BY_SERVER = "\n SELECT id, name, server_id, scopes, expires_at, last_used_at, created_at, updated_at\n FROM lt_service_tokens WHERE server_id = $1 ORDER BY created_at";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ── Bot API key queries ─────────────────────────────────────────────────────
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.LIST_SERVICE_TOKENS_BY_SERVER = exports.DELETE_SERVICE_TOKEN = exports.UPDATE_SERVICE_TOKEN_LAST_USED = exports.GET_ALL_ACTIVE_SERVICE_TOKENS = exports.INSERT_SERVICE_TOKEN = exports.LIST_BOT_KEYS_BY_USER = exports.DELETE_BOT_KEY = exports.UPDATE_BOT_KEY_LAST_USED = exports.GET_ALL_ACTIVE_BOT_KEYS = exports.GET_BOT_KEYS_BY_USER = exports.INSERT_BOT_KEY = void 0;
|
|
5
|
+
exports.INSERT_BOT_KEY = `
|
|
6
|
+
INSERT INTO lt_bot_api_keys (name, user_id, key_hash, scopes, expires_at)
|
|
7
|
+
VALUES ($1, $2, $3, $4, $5) RETURNING id`;
|
|
8
|
+
exports.GET_BOT_KEYS_BY_USER = `
|
|
9
|
+
SELECT id, name, user_id, key_hash, scopes
|
|
10
|
+
FROM lt_bot_api_keys
|
|
11
|
+
WHERE user_id = $1
|
|
12
|
+
AND (expires_at IS NULL OR expires_at > NOW())`;
|
|
13
|
+
exports.GET_ALL_ACTIVE_BOT_KEYS = `
|
|
14
|
+
SELECT id, name, user_id, key_hash, scopes
|
|
15
|
+
FROM lt_bot_api_keys
|
|
16
|
+
WHERE (expires_at IS NULL OR expires_at > NOW())`;
|
|
17
|
+
exports.UPDATE_BOT_KEY_LAST_USED = `
|
|
18
|
+
UPDATE lt_bot_api_keys SET last_used_at = NOW() WHERE id = $1`;
|
|
19
|
+
exports.DELETE_BOT_KEY = `
|
|
20
|
+
DELETE FROM lt_bot_api_keys WHERE id = $1`;
|
|
21
|
+
exports.LIST_BOT_KEYS_BY_USER = `
|
|
22
|
+
SELECT id, name, user_id, scopes, expires_at, last_used_at, created_at, updated_at
|
|
23
|
+
FROM lt_bot_api_keys WHERE user_id = $1 ORDER BY created_at`;
|
|
24
|
+
// ── Service token queries ───────────────────────────────────────────────────
|
|
25
|
+
exports.INSERT_SERVICE_TOKEN = `
|
|
26
|
+
INSERT INTO lt_service_tokens (name, token_hash, server_id, scopes, expires_at)
|
|
27
|
+
VALUES ($1, $2, $3, $4, $5) RETURNING id`;
|
|
28
|
+
exports.GET_ALL_ACTIVE_SERVICE_TOKENS = `
|
|
29
|
+
SELECT id, name, token_hash FROM lt_service_tokens
|
|
30
|
+
WHERE (expires_at IS NULL OR expires_at > NOW())`;
|
|
31
|
+
exports.UPDATE_SERVICE_TOKEN_LAST_USED = `
|
|
32
|
+
UPDATE lt_service_tokens SET last_used_at = NOW() WHERE id = $1`;
|
|
33
|
+
exports.DELETE_SERVICE_TOKEN = `
|
|
34
|
+
DELETE FROM lt_service_tokens WHERE id = $1`;
|
|
35
|
+
exports.LIST_SERVICE_TOKENS_BY_SERVER = `
|
|
36
|
+
SELECT id, name, server_id, scopes, expires_at, last_used_at, created_at, updated_at
|
|
37
|
+
FROM lt_service_tokens WHERE server_id = $1 ORDER BY created_at`;
|
|
@@ -91,10 +91,12 @@ async function getStreamStats(schema, duration = '1h', streamName) {
|
|
|
91
91
|
throw new Error(`Invalid duration: ${duration}`);
|
|
92
92
|
const pool = (0, db_1.getPool)();
|
|
93
93
|
const stream = streamName || null;
|
|
94
|
+
// Graceful degradation: HotMesh schema may not exist if no engine has started.
|
|
95
|
+
const emptyCount = { rows: [{ count: 0 }] };
|
|
94
96
|
const [pendingRes, processedRes, byStreamRes] = await Promise.all([
|
|
95
|
-
pool.query((0, sql_1.COUNT_PENDING)(schema), [stream]),
|
|
96
|
-
pool.query((0, sql_1.COUNT_PROCESSED_SINCE)(schema), [interval, stream]),
|
|
97
|
-
pool.query((0, sql_1.VOLUME_BY_STREAM)(schema), [interval, stream]),
|
|
97
|
+
pool.query((0, sql_1.COUNT_PENDING)(schema), [stream]).catch(() => emptyCount),
|
|
98
|
+
pool.query((0, sql_1.COUNT_PROCESSED_SINCE)(schema), [interval, stream]).catch(() => emptyCount),
|
|
99
|
+
pool.query((0, sql_1.VOLUME_BY_STREAM)(schema), [interval, stream]).catch(() => ({ rows: [] })),
|
|
98
100
|
]);
|
|
99
101
|
return {
|
|
100
102
|
pending: pendingRes.rows[0]?.count ?? 0,
|
|
@@ -131,8 +133,8 @@ async function getStreamMessages(schema, params) {
|
|
|
131
133
|
const queryParams = [streamName, status, msgType, topic, workflowName, jid, aid, limit, offset];
|
|
132
134
|
const countParams = [streamName, status, msgType, topic, workflowName, jid, aid];
|
|
133
135
|
const [messagesRes, countRes] = await Promise.all([
|
|
134
|
-
pool.query((0, stream_messages_sql_1.LIST_STREAM_MESSAGES)(schema, sortColumn, sortOrder, source), queryParams),
|
|
135
|
-
pool.query((0, stream_messages_sql_1.COUNT_STREAM_MESSAGES)(schema, source), countParams),
|
|
136
|
+
pool.query((0, stream_messages_sql_1.LIST_STREAM_MESSAGES)(schema, sortColumn, sortOrder, source), queryParams).catch(() => ({ rows: [] })),
|
|
137
|
+
pool.query((0, stream_messages_sql_1.COUNT_STREAM_MESSAGES)(schema, source), countParams).catch(() => ({ rows: [{ count: 0 }] })),
|
|
136
138
|
]);
|
|
137
139
|
return {
|
|
138
140
|
messages: messagesRes.rows,
|
package/build/services/dba.d.ts
CHANGED
|
@@ -4,10 +4,11 @@ import type { PruneOptions, PruneResult } from '@hotmeshio/hotmesh/build/types/d
|
|
|
4
4
|
* any schema migrations (e.g. adding the `pruned_at` column).
|
|
5
5
|
* Idempotent — safe to call on every startup.
|
|
6
6
|
*/
|
|
7
|
-
export declare function deploy(): Promise<void>;
|
|
7
|
+
export declare function deploy(appId: string): Promise<void>;
|
|
8
8
|
/**
|
|
9
9
|
* Prune expired jobs, streams, and/or execution artifacts.
|
|
10
10
|
*
|
|
11
|
+
* - `appId` — HotMesh application namespace (required)
|
|
11
12
|
* - `jobs` — hard-delete expired job rows older than `expire`
|
|
12
13
|
* - `streams` — hard-delete expired stream messages (engine + worker) older than `expire`
|
|
13
14
|
* - `attributes` — strip execution artifacts from completed jobs
|
|
@@ -17,6 +18,7 @@ export declare function deploy(): Promise<void>;
|
|
|
17
18
|
* - `keepHmark` — preserve hmark rows during stripping
|
|
18
19
|
*/
|
|
19
20
|
export declare function prune(options: {
|
|
21
|
+
appId: string;
|
|
20
22
|
expire?: string;
|
|
21
23
|
jobs?: boolean;
|
|
22
24
|
streams?: boolean;
|
package/build/services/dba.js
CHANGED
|
@@ -4,18 +4,18 @@ exports.deploy = deploy;
|
|
|
4
4
|
exports.prune = prune;
|
|
5
5
|
const hotmesh_1 = require("@hotmeshio/hotmesh");
|
|
6
6
|
const db_1 = require("../lib/db");
|
|
7
|
-
const APP_ID = 'durable';
|
|
8
7
|
/**
|
|
9
8
|
* Deploy the server-side prune() Postgres function and run
|
|
10
9
|
* any schema migrations (e.g. adding the `pruned_at` column).
|
|
11
10
|
* Idempotent — safe to call on every startup.
|
|
12
11
|
*/
|
|
13
|
-
async function deploy() {
|
|
14
|
-
await hotmesh_1.DBA.deploy((0, db_1.getConnection)(),
|
|
12
|
+
async function deploy(appId) {
|
|
13
|
+
await hotmesh_1.DBA.deploy((0, db_1.getConnection)(), appId);
|
|
15
14
|
}
|
|
16
15
|
/**
|
|
17
16
|
* Prune expired jobs, streams, and/or execution artifacts.
|
|
18
17
|
*
|
|
18
|
+
* - `appId` — HotMesh application namespace (required)
|
|
19
19
|
* - `jobs` — hard-delete expired job rows older than `expire`
|
|
20
20
|
* - `streams` — hard-delete expired stream messages (engine + worker) older than `expire`
|
|
21
21
|
* - `attributes` — strip execution artifacts from completed jobs
|
|
@@ -25,5 +25,6 @@ async function deploy() {
|
|
|
25
25
|
* - `keepHmark` — preserve hmark rows during stripping
|
|
26
26
|
*/
|
|
27
27
|
async function prune(options) {
|
|
28
|
-
|
|
28
|
+
const { appId, ...rest } = options;
|
|
29
|
+
return hotmesh_1.DBA.prune({ appId, connection: (0, db_1.getConnection)(), ...rest });
|
|
29
30
|
}
|
|
@@ -61,8 +61,24 @@ export declare function findByMetadata(key: string, value: string, status?: stri
|
|
|
61
61
|
export declare function claimByMetadata(key: string, value: string, userId: string, durationMinutes?: number, metadata?: Record<string, any>, allowedRoles?: string[] | null): Promise<(ClaimResult & {
|
|
62
62
|
candidatesExist: number;
|
|
63
63
|
}) | null>;
|
|
64
|
+
export interface ResolveByMetadataResult {
|
|
65
|
+
/** 'resolved' = done atomically. 'signal_required' = signal_id present, caller must signal. */
|
|
66
|
+
outcome: 'resolved' | 'signal_required' | 'not_found';
|
|
67
|
+
/** The resolved escalation (when outcome = 'resolved') */
|
|
68
|
+
escalation?: LTEscalationRecord;
|
|
69
|
+
/** Signal info (when outcome = 'signal_required') */
|
|
70
|
+
signalId?: string;
|
|
71
|
+
escalationId?: string;
|
|
72
|
+
workflowId?: string;
|
|
73
|
+
workflowType?: string;
|
|
74
|
+
taskQueue?: string;
|
|
75
|
+
}
|
|
64
76
|
/**
|
|
65
|
-
* Atomic resolve by metadata
|
|
66
|
-
*
|
|
77
|
+
* Atomic resolve by metadata with signal guard.
|
|
78
|
+
*
|
|
79
|
+
* Single query, two outcomes:
|
|
80
|
+
* 1. No signal_id → claim + resolve atomically. Returns { outcome: 'resolved', escalation }.
|
|
81
|
+
* 2. signal_id present → resolve skipped. Returns { outcome: 'signal_required', signalId, escalationId, ... }
|
|
82
|
+
* so the caller can signal the workflow. conditionLT handles the rest.
|
|
67
83
|
*/
|
|
68
|
-
export declare function resolveByMetadataAtomic(key: string, value: string, userId: string, resolverPayload: Record<string, any>, metadata?: Record<string, any>, allowedRoles?: string[] | null): Promise<
|
|
84
|
+
export declare function resolveByMetadataAtomic(key: string, value: string, userId: string, resolverPayload: Record<string, any>, metadata?: Record<string, any>, allowedRoles?: string[] | null): Promise<ResolveByMetadataResult>;
|
|
@@ -249,8 +249,12 @@ async function claimByMetadata(key, value, userId, durationMinutes = 30, metadat
|
|
|
249
249
|
};
|
|
250
250
|
}
|
|
251
251
|
/**
|
|
252
|
-
* Atomic resolve by metadata
|
|
253
|
-
*
|
|
252
|
+
* Atomic resolve by metadata with signal guard.
|
|
253
|
+
*
|
|
254
|
+
* Single query, two outcomes:
|
|
255
|
+
* 1. No signal_id → claim + resolve atomically. Returns { outcome: 'resolved', escalation }.
|
|
256
|
+
* 2. signal_id present → resolve skipped. Returns { outcome: 'signal_required', signalId, escalationId, ... }
|
|
257
|
+
* so the caller can signal the workflow. conditionLT handles the rest.
|
|
254
258
|
*/
|
|
255
259
|
async function resolveByMetadataAtomic(key, value, userId, resolverPayload, metadata, allowedRoles) {
|
|
256
260
|
const pool = (0, db_1.getPool)();
|
|
@@ -260,17 +264,30 @@ async function resolveByMetadataAtomic(key, value, userId, resolverPayload, meta
|
|
|
260
264
|
const roles = allowedRoles ?? null;
|
|
261
265
|
const { rows } = await pool.query(sql_1.RESOLVE_BY_METADATA_ATOMIC, [filter, userId, payloadJson, metaPatch, roles]);
|
|
262
266
|
if (rows.length === 0)
|
|
263
|
-
return
|
|
264
|
-
const
|
|
265
|
-
(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
267
|
+
return { outcome: 'not_found' };
|
|
268
|
+
const row = rows[0];
|
|
269
|
+
if (row.outcome === 'resolved') {
|
|
270
|
+
const { target_id, signal_id, target_workflow_id, target_workflow_type, target_task_queue, outcome, ...rest } = row;
|
|
271
|
+
const escalation = rest;
|
|
272
|
+
(0, publish_1.publishEscalationEvent)({
|
|
273
|
+
type: 'escalation.resolved',
|
|
274
|
+
source: 'service',
|
|
275
|
+
workflowId: escalation.workflow_id || '',
|
|
276
|
+
workflowName: escalation.workflow_type || '',
|
|
277
|
+
taskQueue: escalation.task_queue || '',
|
|
278
|
+
escalationId: escalation.id,
|
|
279
|
+
status: 'resolved',
|
|
280
|
+
data: { resolved_by: userId },
|
|
281
|
+
});
|
|
282
|
+
return { outcome: 'resolved', escalation };
|
|
283
|
+
}
|
|
284
|
+
// Signal-backed escalation — return the signal info for the caller
|
|
285
|
+
return {
|
|
286
|
+
outcome: 'signal_required',
|
|
287
|
+
signalId: row.signal_id,
|
|
288
|
+
escalationId: row.target_id,
|
|
289
|
+
workflowId: row.target_workflow_id,
|
|
290
|
+
workflowType: row.target_workflow_type,
|
|
291
|
+
taskQueue: row.target_task_queue,
|
|
292
|
+
};
|
|
276
293
|
}
|
|
@@ -28,8 +28,15 @@ export declare const FIND_BY_METADATA = "SELECT *, COUNT(*) OVER() AS _total\nFR
|
|
|
28
28
|
*/
|
|
29
29
|
export declare const CLAIM_BY_METADATA_GUARDED = "WITH target AS (\n SELECT id, assigned_to\n FROM lt_escalations\n WHERE metadata @> $1::jsonb\n AND status = 'pending'\n AND (assigned_to IS NULL OR assigned_until <= NOW() OR assigned_to = $2)\n AND ($5::text[] IS NULL OR role = ANY($5))\n ORDER BY priority ASC, created_at ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED\n),\nupdated AS (\n UPDATE lt_escalations e\n SET assigned_to = $2,\n claimed_at = NOW(),\n assigned_until = NOW() + INTERVAL '1 minute' * $3,\n metadata = CASE WHEN $4::jsonb IS NOT NULL\n THEN COALESCE(e.metadata, '{}'::jsonb) || $4::jsonb\n ELSE e.metadata END,\n updated_at = NOW()\n FROM target t\n WHERE e.id = t.id\n RETURNING e.*, t.assigned_to AS prev_assigned_to\n)\nSELECT *,\n (SELECT COUNT(*) FROM lt_escalations WHERE metadata @> $1::jsonb AND status = 'pending') AS candidates_exist\nFROM updated";
|
|
30
30
|
/**
|
|
31
|
-
* Atomic resolve by metadata
|
|
31
|
+
* Atomic resolve by metadata with signal guard.
|
|
32
|
+
*
|
|
33
|
+
* Single query, two outcomes:
|
|
34
|
+
* 1. No signal_id → claim + resolve atomically. `resolved` is populated.
|
|
35
|
+
* 2. signal_id present → resolve CTE skips (guard in WHERE). `resolved` is null,
|
|
36
|
+
* but `target_id`, `signal_id`, `workflow_id`, `task_queue`, `workflow_type`
|
|
37
|
+
* are returned so the caller can signal the workflow directly.
|
|
38
|
+
*
|
|
32
39
|
* $1 = metadata filter (jsonb), $2 = userId, $3 = resolver_payload (jsonb),
|
|
33
40
|
* $4 = metadata patch (jsonb, nullable), $5 = allowed roles (text[], null = no filter)
|
|
34
41
|
*/
|
|
35
|
-
export declare const RESOLVE_BY_METADATA_ATOMIC = "WITH target AS (\n SELECT
|
|
42
|
+
export declare const RESOLVE_BY_METADATA_ATOMIC = "WITH target AS (\n SELECT *\n FROM lt_escalations\n WHERE metadata @> $1::jsonb\n AND status = 'pending'\n AND ($5::text[] IS NULL OR role = ANY($5))\n ORDER BY priority ASC, created_at ASC\n LIMIT 1\n FOR UPDATE\n),\nclaimed AS (\n UPDATE lt_escalations e\n SET assigned_to = COALESCE(e.assigned_to, $2),\n claimed_at = COALESCE(e.claimed_at, NOW()),\n assigned_until = CASE\n WHEN e.assigned_to IS NOT NULL AND e.assigned_until > NOW() THEN e.assigned_until\n ELSE NOW() + INTERVAL '5 minutes' END,\n metadata = CASE WHEN $4::jsonb IS NOT NULL\n THEN COALESCE(e.metadata, '{}'::jsonb) || $4::jsonb\n ELSE e.metadata END\n FROM target\n WHERE e.id = target.id\n AND (target.metadata->>'signal_id') IS NULL\n RETURNING e.*\n),\nresolved AS (\n UPDATE lt_escalations e\n SET status = 'resolved',\n resolved_at = NOW(),\n resolver_payload = $3,\n updated_at = NOW()\n FROM claimed\n WHERE e.id = claimed.id\n RETURNING e.*\n)\nSELECT\n resolved.*,\n target.id AS target_id,\n target.metadata->>'signal_id' AS signal_id,\n target.workflow_id AS target_workflow_id,\n target.workflow_type AS target_workflow_type,\n target.task_queue AS target_task_queue,\n CASE WHEN resolved.id IS NOT NULL THEN 'resolved' ELSE 'signal_required' END AS outcome\nFROM target\nLEFT JOIN resolved ON resolved.id = target.id";
|
|
@@ -176,38 +176,60 @@ SELECT *,
|
|
|
176
176
|
(SELECT COUNT(*) FROM lt_escalations WHERE metadata @> $1::jsonb AND status = 'pending') AS candidates_exist
|
|
177
177
|
FROM updated`;
|
|
178
178
|
/**
|
|
179
|
-
* Atomic resolve by metadata
|
|
179
|
+
* Atomic resolve by metadata with signal guard.
|
|
180
|
+
*
|
|
181
|
+
* Single query, two outcomes:
|
|
182
|
+
* 1. No signal_id → claim + resolve atomically. `resolved` is populated.
|
|
183
|
+
* 2. signal_id present → resolve CTE skips (guard in WHERE). `resolved` is null,
|
|
184
|
+
* but `target_id`, `signal_id`, `workflow_id`, `task_queue`, `workflow_type`
|
|
185
|
+
* are returned so the caller can signal the workflow directly.
|
|
186
|
+
*
|
|
180
187
|
* $1 = metadata filter (jsonb), $2 = userId, $3 = resolver_payload (jsonb),
|
|
181
188
|
* $4 = metadata patch (jsonb, nullable), $5 = allowed roles (text[], null = no filter)
|
|
182
189
|
*/
|
|
183
190
|
exports.RESOLVE_BY_METADATA_ATOMIC = `\
|
|
184
191
|
WITH target AS (
|
|
185
|
-
SELECT
|
|
192
|
+
SELECT *
|
|
186
193
|
FROM lt_escalations
|
|
187
194
|
WHERE metadata @> $1::jsonb
|
|
188
195
|
AND status = 'pending'
|
|
189
196
|
AND ($5::text[] IS NULL OR role = ANY($5))
|
|
190
197
|
ORDER BY priority ASC, created_at ASC
|
|
191
198
|
LIMIT 1
|
|
192
|
-
FOR UPDATE
|
|
199
|
+
FOR UPDATE
|
|
193
200
|
),
|
|
194
201
|
claimed AS (
|
|
195
202
|
UPDATE lt_escalations e
|
|
196
|
-
SET assigned_to = $2,
|
|
197
|
-
claimed_at = NOW(),
|
|
198
|
-
assigned_until =
|
|
203
|
+
SET assigned_to = COALESCE(e.assigned_to, $2),
|
|
204
|
+
claimed_at = COALESCE(e.claimed_at, NOW()),
|
|
205
|
+
assigned_until = CASE
|
|
206
|
+
WHEN e.assigned_to IS NOT NULL AND e.assigned_until > NOW() THEN e.assigned_until
|
|
207
|
+
ELSE NOW() + INTERVAL '5 minutes' END,
|
|
199
208
|
metadata = CASE WHEN $4::jsonb IS NOT NULL
|
|
200
209
|
THEN COALESCE(e.metadata, '{}'::jsonb) || $4::jsonb
|
|
201
210
|
ELSE e.metadata END
|
|
202
211
|
FROM target
|
|
203
212
|
WHERE e.id = target.id
|
|
213
|
+
AND (target.metadata->>'signal_id') IS NULL
|
|
214
|
+
RETURNING e.*
|
|
215
|
+
),
|
|
216
|
+
resolved AS (
|
|
217
|
+
UPDATE lt_escalations e
|
|
218
|
+
SET status = 'resolved',
|
|
219
|
+
resolved_at = NOW(),
|
|
220
|
+
resolver_payload = $3,
|
|
221
|
+
updated_at = NOW()
|
|
222
|
+
FROM claimed
|
|
223
|
+
WHERE e.id = claimed.id
|
|
204
224
|
RETURNING e.*
|
|
205
225
|
)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
226
|
+
SELECT
|
|
227
|
+
resolved.*,
|
|
228
|
+
target.id AS target_id,
|
|
229
|
+
target.metadata->>'signal_id' AS signal_id,
|
|
230
|
+
target.workflow_id AS target_workflow_id,
|
|
231
|
+
target.workflow_type AS target_workflow_type,
|
|
232
|
+
target.task_queue AS target_task_queue,
|
|
233
|
+
CASE WHEN resolved.id IS NOT NULL THEN 'resolved' ELSE 'signal_required' END AS outcome
|
|
234
|
+
FROM target
|
|
235
|
+
LEFT JOIN resolved ON resolved.id = target.id`;
|
|
@@ -33,7 +33,7 @@ export declare function getWorkflowState(workflowId: string, taskQueue: string,
|
|
|
33
33
|
state: Record<string, any>;
|
|
34
34
|
}>;
|
|
35
35
|
/**
|
|
36
|
-
* List workflow jobs from
|
|
36
|
+
* List workflow jobs from {schema}.jobs where entity IS NOT NULL.
|
|
37
37
|
* Returns paginated results sorted by active first, then created_at DESC.
|
|
38
38
|
*/
|
|
39
|
-
export declare function listJobs(params: JobListParams): Promise<JobListResult>;
|
|
39
|
+
export declare function listJobs(app_id: string, params: JobListParams): Promise<JobListResult>;
|
|
@@ -6,6 +6,7 @@ exports.exportWorkflowExecution = exportWorkflowExecution;
|
|
|
6
6
|
exports.getWorkflowState = getWorkflowState;
|
|
7
7
|
exports.listJobs = listJobs;
|
|
8
8
|
const db_1 = require("../../lib/db");
|
|
9
|
+
const hotmesh_utils_1 = require("../hotmesh-utils");
|
|
9
10
|
const client_1 = require("./client");
|
|
10
11
|
const post_process_1 = require("./post-process");
|
|
11
12
|
/** Error thrown when a workflow job is not found (expired or never existed). */
|
|
@@ -131,10 +132,13 @@ function buildJobOrderBy(sortBy, order) {
|
|
|
131
132
|
return `j.${sortBy} ${dir}`;
|
|
132
133
|
}
|
|
133
134
|
/**
|
|
134
|
-
* List workflow jobs from
|
|
135
|
+
* List workflow jobs from {schema}.jobs where entity IS NOT NULL.
|
|
135
136
|
* Returns paginated results sorted by active first, then created_at DESC.
|
|
136
137
|
*/
|
|
137
|
-
async function listJobs(params) {
|
|
138
|
+
async function listJobs(app_id, params) {
|
|
139
|
+
const appId = (0, hotmesh_utils_1.sanitizeAppId)(app_id);
|
|
140
|
+
const schema = (0, hotmesh_utils_1.quoteSchema)(appId);
|
|
141
|
+
const keyPrefix = `hmsh:${appId}:j:`;
|
|
138
142
|
const limit = Math.min(params.limit ?? 20, 100);
|
|
139
143
|
const offset = params.offset ?? 0;
|
|
140
144
|
const pool = (0, db_1.getPool)();
|
|
@@ -172,11 +176,13 @@ async function listJobs(params) {
|
|
|
172
176
|
conditions.push('j.status < 0');
|
|
173
177
|
}
|
|
174
178
|
const where = conditions.join(' AND ');
|
|
179
|
+
// Graceful degradation: schema may not exist if no engine has started.
|
|
180
|
+
const emptyCount = { rows: [{ count: '0' }] };
|
|
175
181
|
const [countResult, dataResult] = await Promise.all([
|
|
176
|
-
pool.query(`SELECT COUNT(*) FROM
|
|
182
|
+
pool.query(`SELECT COUNT(*) FROM ${schema}.jobs j WHERE ${where}`, values).catch(() => emptyCount),
|
|
177
183
|
pool.query(`WITH ju_symbols AS (
|
|
178
|
-
SELECT value FROM
|
|
179
|
-
WHERE key LIKE 'hmsh:
|
|
184
|
+
SELECT value FROM ${schema}.symbols
|
|
185
|
+
WHERE key LIKE 'hmsh:${appId}:sym:keys:%' AND field = 'metadata/ju'
|
|
180
186
|
)
|
|
181
187
|
SELECT j.key, j.entity, j.status, j.is_live, j.created_at,
|
|
182
188
|
CASE WHEN j.updated_at != j.created_at THEN j.updated_at
|
|
@@ -184,20 +190,20 @@ async function listJobs(params) {
|
|
|
184
190
|
ELSE j.updated_at
|
|
185
191
|
END as updated_at,
|
|
186
192
|
ws.id as set_id
|
|
187
|
-
FROM
|
|
188
|
-
LEFT JOIN
|
|
193
|
+
FROM ${schema}.jobs j
|
|
194
|
+
LEFT JOIN ${schema}.jobs_attributes ju
|
|
189
195
|
ON ju.job_id = j.id
|
|
190
196
|
AND ju.symbol IN (SELECT value FROM ju_symbols)
|
|
191
197
|
AND (ju.dimension IS NULL OR ju.dimension = '')
|
|
192
198
|
LEFT JOIN lt_workflow_sets ws
|
|
193
|
-
ON ws.source_workflow_id = REPLACE(j.key, '
|
|
199
|
+
ON ws.source_workflow_id = REPLACE(j.key, '${keyPrefix}', '')
|
|
194
200
|
AND j.entity = 'mcpWorkflowPlanner'
|
|
195
201
|
WHERE ${where}
|
|
196
202
|
ORDER BY ${buildJobOrderBy(params.sort_by, params.order)}
|
|
197
|
-
LIMIT $${idx++} OFFSET $${idx++}`, [...values, limit, offset]),
|
|
203
|
+
LIMIT $${idx++} OFFSET $${idx++}`, [...values, limit, offset]).catch(() => ({ rows: [] })),
|
|
198
204
|
]);
|
|
199
205
|
const jobs = dataResult.rows.map((row) => ({
|
|
200
|
-
workflow_id: row.key.replace(
|
|
206
|
+
workflow_id: row.key.replace(keyPrefix, ''),
|
|
201
207
|
entity: row.entity,
|
|
202
208
|
status: (row.status > 0 ? 'running' : row.status === 0 ? 'completed' : 'failed'),
|
|
203
209
|
is_live: row.is_live,
|
|
@@ -43,11 +43,12 @@ const CRON_ID = 'lt-maintenance-nightly';
|
|
|
43
43
|
/**
|
|
44
44
|
* Translate a single maintenance rule into the appropriate dbaService.prune() call.
|
|
45
45
|
*/
|
|
46
|
-
async function executeRule(rule) {
|
|
46
|
+
async function executeRule(appId, rule) {
|
|
47
47
|
switch (true) {
|
|
48
48
|
// Delete stream messages
|
|
49
49
|
case rule.target === 'streams' && rule.action === 'delete':
|
|
50
50
|
await dbaService.prune({
|
|
51
|
+
appId,
|
|
51
52
|
expire: rule.olderThan,
|
|
52
53
|
streams: true,
|
|
53
54
|
jobs: false,
|
|
@@ -56,6 +57,7 @@ async function executeRule(rule) {
|
|
|
56
57
|
// Delete transient jobs (entity IS NULL)
|
|
57
58
|
case rule.target === 'jobs' && rule.action === 'delete' && rule.hasEntity === false:
|
|
58
59
|
await dbaService.prune({
|
|
60
|
+
appId,
|
|
59
61
|
expire: rule.olderThan,
|
|
60
62
|
jobs: false,
|
|
61
63
|
streams: false,
|
|
@@ -65,6 +67,7 @@ async function executeRule(rule) {
|
|
|
65
67
|
// Strip execution artifacts from entity jobs (keep jdata/udata/jmark/hmark)
|
|
66
68
|
case rule.target === 'jobs' && rule.action === 'prune' && rule.hasEntity === true:
|
|
67
69
|
await dbaService.prune({
|
|
70
|
+
appId,
|
|
68
71
|
expire: rule.olderThan,
|
|
69
72
|
jobs: false,
|
|
70
73
|
streams: false,
|
|
@@ -75,6 +78,7 @@ async function executeRule(rule) {
|
|
|
75
78
|
// Hard-delete old pruned jobs
|
|
76
79
|
case rule.target === 'jobs' && rule.action === 'delete' && rule.pruned === true:
|
|
77
80
|
await dbaService.prune({
|
|
81
|
+
appId,
|
|
78
82
|
expire: rule.olderThan,
|
|
79
83
|
jobs: true,
|
|
80
84
|
streams: false,
|
|
@@ -112,8 +116,7 @@ class LTMaintenanceRegistry {
|
|
|
112
116
|
async connect() {
|
|
113
117
|
if (this.connected || !this._config)
|
|
114
118
|
return;
|
|
115
|
-
const rules = this._config
|
|
116
|
-
const schedule = this._config.schedule;
|
|
119
|
+
const { appId, rules, schedule } = this._config;
|
|
117
120
|
const connection = (0, db_1.getConnection)();
|
|
118
121
|
await hotmesh_1.Virtual.cron({
|
|
119
122
|
topic: CRON_TOPIC,
|
|
@@ -122,7 +125,7 @@ class LTMaintenanceRegistry {
|
|
|
122
125
|
logger_1.loggerRegistry.info('[lt-maintenance] starting maintenance cycle...');
|
|
123
126
|
for (const rule of rules) {
|
|
124
127
|
try {
|
|
125
|
-
await executeRule(rule);
|
|
128
|
+
await executeRule(appId, rule);
|
|
126
129
|
}
|
|
127
130
|
catch (err) {
|
|
128
131
|
logger_1.loggerRegistry.error(`[lt-maintenance] rule failed: ${JSON.stringify(rule)} ${err?.message}`);
|
|
@@ -65,9 +65,12 @@ async function listJobs(params) {
|
|
|
65
65
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
66
66
|
const keyPrefix = `hmsh:${appId}:j:`;
|
|
67
67
|
const orderBy = buildOrderBy(params.sort_by, params.order);
|
|
68
|
+
// Graceful degradation: HotMesh schema may not exist yet if no
|
|
69
|
+
// engine/worker has initialized. Return empty results, not 500.
|
|
70
|
+
const empty = { rows: [{ count: '0' }] };
|
|
68
71
|
const [countResult, dataResult] = await Promise.all([
|
|
69
|
-
pool.query((0, sql_1.COUNT_JOBS)(schema, where), values),
|
|
70
|
-
pool.query((0, sql_1.LIST_JOBS)(schema, appId, where, idx++, idx++, orderBy), [...values, limit, offset]),
|
|
72
|
+
pool.query((0, sql_1.COUNT_JOBS)(schema, where), values).catch(() => empty),
|
|
73
|
+
pool.query((0, sql_1.LIST_JOBS)(schema, appId, where, idx++, idx++, orderBy), [...values, limit, offset]).catch(() => ({ rows: [] })),
|
|
71
74
|
]);
|
|
72
75
|
const jobs = dataResult.rows.map((row) => ({
|
|
73
76
|
workflow_id: row.key.startsWith(keyPrefix) ? row.key.slice(keyPrefix.length) : row.key,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare const DISTINCT_ENTITIES_DURABLE = "\n SELECT DISTINCT entity FROM durable.jobs WHERE entity IS NOT NULL AND entity != '' ORDER BY entity";
|
|
2
1
|
export declare const DISTINCT_ENTITIES: (schema: string) => string;
|
|
3
2
|
export declare const ACTIVE_GRAPH_TOPICS = "SELECT DISTINCT graph_topic FROM lt_yaml_workflows WHERE app_id = $1 AND status IN ('active', 'deployed')";
|
|
4
3
|
export declare const COUNT_JOBS: (schema: string, where: string) => string;
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
// Schema placeholders (${schema}) are interpolated at call time
|
|
4
4
|
// because Postgres does not support parameterized schema names.
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.LIST_JOBS = exports.GET_JOB_ATTRIBUTES = exports.GET_JOB = exports.COUNT_JOBS = exports.ACTIVE_GRAPH_TOPICS = exports.DISTINCT_ENTITIES =
|
|
7
|
-
exports.DISTINCT_ENTITIES_DURABLE = `
|
|
8
|
-
SELECT DISTINCT entity FROM durable.jobs WHERE entity IS NOT NULL AND entity != '' ORDER BY entity`;
|
|
6
|
+
exports.LIST_JOBS = exports.GET_JOB_ATTRIBUTES = exports.GET_JOB = exports.COUNT_JOBS = exports.ACTIVE_GRAPH_TOPICS = exports.DISTINCT_ENTITIES = void 0;
|
|
9
7
|
const DISTINCT_ENTITIES = (schema) => `SELECT DISTINCT entity FROM ${schema}.jobs WHERE entity IS NOT NULL AND entity != '' ORDER BY entity`;
|
|
10
8
|
exports.DISTINCT_ENTITIES = DISTINCT_ENTITIES;
|
|
11
9
|
exports.ACTIVE_GRAPH_TOPICS = `SELECT DISTINCT graph_topic FROM lt_yaml_workflows WHERE app_id = $1 AND status IN ('active', 'deployed')`;
|
|
@@ -57,7 +57,7 @@ function registerControlPlaneTools(server) {
|
|
|
57
57
|
inputSchema: schemas_1.rollCallSchema,
|
|
58
58
|
}, async (args) => {
|
|
59
59
|
const result = await api.rollCall({
|
|
60
|
-
appId: args.app_id
|
|
60
|
+
appId: args.app_id,
|
|
61
61
|
delay: args.delay,
|
|
62
62
|
});
|
|
63
63
|
if (result.error) {
|