@hotmeshio/long-tail 0.5.6 → 0.5.9
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/diagnostics.d.ts +15 -0
- package/build/api/diagnostics.js +33 -0
- package/build/api/escalations/cancel.d.ts +20 -0
- package/build/api/escalations/cancel.js +93 -0
- package/build/api/escalations/index.d.ts +1 -0
- package/build/api/escalations/index.js +4 -1
- package/build/api/escalations/resolve.js +2 -0
- package/build/api/workflows/invocation.js +6 -0
- package/build/lib/events/publish.d.ts +1 -1
- package/build/routes/diagnostics.d.ts +2 -0
- package/build/routes/diagnostics.js +88 -0
- package/build/routes/escalations/bulk.js +4 -0
- package/build/routes/escalations/single.js +9 -0
- package/build/routes/index.js +2 -0
- package/build/services/diagnostics/index.d.ts +52 -0
- package/build/services/diagnostics/index.js +169 -0
- package/build/services/diagnostics/patterns.d.ts +19 -0
- package/build/services/diagnostics/patterns.js +147 -0
- package/build/services/escalation/bulk.d.ts +9 -0
- package/build/services/escalation/bulk.js +21 -0
- package/build/services/escalation/crud.d.ts +17 -0
- package/build/services/escalation/crud.js +57 -0
- package/build/services/orchestrator/condition.d.ts +1 -1
- package/build/services/orchestrator/condition.js +3 -0
- package/build/services/yaml-workflow/deployer.js +5 -0
- package/build/services/yaml-workflow/pipe-validator.d.ts +44 -0
- package/build/services/yaml-workflow/pipe-validator.js +215 -0
- package/build/system/mcp-servers/admin/diagnostics.d.ts +10 -0
- package/build/system/mcp-servers/admin/diagnostics.js +94 -0
- package/build/system/mcp-servers/admin/index.js +4 -2
- package/build/system/mcp-servers/admin/schemas.d.ts +36 -0
- package/build/system/mcp-servers/admin/schemas.js +16 -1
- package/build/system/workflows/mcp-workflow-builder/index.js +15 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/dashboard/dist/assets/AdminDashboard-0LSslIQ7.js +2 -0
- package/dashboard/dist/assets/{AdminDashboard-DIQEViVY.js.map → AdminDashboard-0LSslIQ7.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-Caf5-th-.js → AgentConfigPage-hqBr1540.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-Caf5-th-.js.map → AgentConfigPage-hqBr1540.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-_a4tKaM3.js → AgentDetailPage-bnXR1ezD.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-_a4tKaM3.js.map → AgentDetailPage-bnXR1ezD.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-BNlAkEIK.js → AgentsPage-tJIlrnfM.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-BNlAkEIK.js.map → AgentsPage-tJIlrnfM.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-B7gEfcw3.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-B7gEfcw3.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-DBvd1yFL.js → BotPicker-fFE4GAaO.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DBvd1yFL.js.map → BotPicker-fFE4GAaO.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-BIdG9RTG.js → CapabilitiesPage-DotZ7iQc.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-BIdG9RTG.js.map → CapabilitiesPage-DotZ7iQc.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-CggyKhyZ.js → CollapsibleSection-D97SYypF.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-CggyKhyZ.js.map → CollapsibleSection-D97SYypF.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-B4sXGLRY.js → CredentialsPage-C08U6dlv.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-B4sXGLRY.js.map → CredentialsPage-C08U6dlv.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-GcS5qzZr.js → CronLabel-C2TuCl4h.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-GcS5qzZr.js.map → CronLabel-C2TuCl4h.js.map} +1 -1
- package/dashboard/dist/assets/CustomDurationPicker-C3zGsESx.js +2 -0
- package/dashboard/dist/assets/CustomDurationPicker-C3zGsESx.js.map +1 -0
- package/dashboard/dist/assets/{ElapsedCell-BFOMsgwF.js → ElapsedCell-DF4m0cv_.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-BFOMsgwF.js.map → ElapsedCell-DF4m0cv_.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BfjW6haL.js → EscalationsOverview-blGOYKtJ.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BfjW6haL.js.map → EscalationsOverview-blGOYKtJ.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-N0XmLk3C.js → EventTable-DzrMH-ux.js} +2 -2
- package/dashboard/dist/assets/{EventTable-N0XmLk3C.js.map → EventTable-DzrMH-ux.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-DHiGHiCk.js → GraphInvokePage-QVAUHmjI.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-DHiGHiCk.js.map → GraphInvokePage-QVAUHmjI.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-jckQolbx.js → HomePage-DvYgvCcO.js} +2 -2
- package/dashboard/dist/assets/{HomePage-jckQolbx.js.map → HomePage-DvYgvCcO.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-B71DqaXr.js → ListToolbar-D6tQ3ZG2.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-B71DqaXr.js.map → ListToolbar-D6tQ3ZG2.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-CPD6NRGt.js → McpOverview-BClbGYb8.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-CPD6NRGt.js.map → McpOverview-BClbGYb8.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-YyqXw6Dt.js → McpQueryDetailPage-DHjfDRdm.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-YyqXw6Dt.js.map → McpQueryDetailPage-DHjfDRdm.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-BWf45EYD.js → McpQueryPage-qtEuMDrd.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-BWf45EYD.js.map → McpQueryPage-qtEuMDrd.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-Cv43tkjz.js → McpRunDetailPage-DRbIZMU2.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-Cv43tkjz.js.map → McpRunDetailPage-DRbIZMU2.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-BFards4K.js → McpRunsPage-CMfwXQ4R.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-BFards4K.js.map → McpRunsPage-CMfwXQ4R.js.map} +1 -1
- package/dashboard/dist/assets/{OperatorDashboard-BJZxuOKI.js → OperatorDashboard-Dq2pfV68.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-BJZxuOKI.js.map → OperatorDashboard-Dq2pfV68.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-BjkLfyrm.js → ProcessDetailPage-DjQuNERz.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-BjkLfyrm.js.map → ProcessDetailPage-DjQuNERz.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-DOzeduP3.js → ProcessesListPage-vJimii8-.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-DOzeduP3.js.map → ProcessesListPage-vJimii8-.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-DSSQrGTh.js → RolesPage-CFCN_mRF.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-DSSQrGTh.js.map → RolesPage-CFCN_mRF.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-CM7lk1YB.js → RunAsSelector-sBxAvlRf.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-CM7lk1YB.js.map → RunAsSelector-sBxAvlRf.js.map} +1 -1
- package/dashboard/dist/assets/{StatusBadge-XQlNFwmH.js → StatusBadge-Bl3FXVmF.js} +2 -2
- package/dashboard/dist/assets/{StatusBadge-XQlNFwmH.js.map → StatusBadge-Bl3FXVmF.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-DtCgJNpV.js → SwimlaneTimeline-2Eet4OOL.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-DtCgJNpV.js.map → SwimlaneTimeline-2Eet4OOL.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-BC9xnyLT.js → TaskDetailPage-CgFN5uWn.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-BC9xnyLT.js.map → TaskDetailPage-CgFN5uWn.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DtbLfUS-.js → TasksListPage-qj25rRZL.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DtbLfUS-.js.map → TasksListPage-qj25rRZL.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-xrDlFnPy.js → TimeAgo-C7LylVjP.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-xrDlFnPy.js.map → TimeAgo-C7LylVjP.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-BneATvjR.js → TimestampCell-CrCtoBlo.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-BneATvjR.js.map → TimestampCell-CrCtoBlo.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-1lQ_GQod.js → ToolTestPanel-DYGlB-QW.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-1lQ_GQod.js.map → ToolTestPanel-DYGlB-QW.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-CDtlXyut.js → TopicDetailPage-DLY6X_R6.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-CDtlXyut.js.map → TopicDetailPage-DLY6X_R6.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-COX2kDKK.js → TopicsPage-C5nOLsLx.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-COX2kDKK.js.map → TopicsPage-C5nOLsLx.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-Cb2WvhCN.js → UserName-CgUZyiwm.js} +2 -2
- package/dashboard/dist/assets/{UserName-Cb2WvhCN.js.map → UserName-CgUZyiwm.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-BWCTOTXF.js → WorkflowExecutionPage-CDr5CTM2.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-BWCTOTXF.js.map → WorkflowExecutionPage-CDr5CTM2.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-DOaDSC98.js → WorkflowsDashboard-tCBP0i0Y.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-DOaDSC98.js.map → WorkflowsDashboard-tCBP0i0Y.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CeE7a51J.js → WorkflowsOverview-CaMeZwde.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CeE7a51J.js.map → WorkflowsOverview-CaMeZwde.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-cNpR0PHg.js → YamlWorkflowDetailPage-C3uyCiIX.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-cNpR0PHg.js.map → YamlWorkflowDetailPage-C3uyCiIX.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-DWvNfzji.js → YamlWorkflowsPage-DRnYgsvA.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-DWvNfzji.js.map → YamlWorkflowsPage-DRnYgsvA.js.map} +1 -1
- package/dashboard/dist/assets/{agents-BvTiBKBQ.js → agents-HMkTc425.js} +2 -2
- package/dashboard/dist/assets/{agents-BvTiBKBQ.js.map → agents-HMkTc425.js.map} +1 -1
- package/dashboard/dist/assets/{bots-CeMR1Lnp.js → bots-CDe2jXNx.js} +2 -2
- package/dashboard/dist/assets/{bots-CeMR1Lnp.js.map → bots-CDe2jXNx.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-D7uJlnxj.js → capabilities-DhnM7Ztb.js} +2 -2
- package/dashboard/dist/assets/{capabilities-D7uJlnxj.js.map → capabilities-DhnM7Ztb.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-Cf6lZsSF.js → controlplane-BwIJ9ZC_.js} +2 -2
- package/dashboard/dist/assets/{controlplane-Cf6lZsSF.js.map → controlplane-BwIJ9ZC_.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-BCFBWGP-.js → escalation-Djk5XKmX.js} +2 -2
- package/dashboard/dist/assets/{escalation-BCFBWGP-.js.map → escalation-Djk5XKmX.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-Do7k4fte.js → escalation-columns-BKqSywVA.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-Do7k4fte.js.map → escalation-columns-BKqSywVA.js.map} +1 -1
- package/dashboard/dist/assets/{index-eWNx4UQ8.js → index-BFQ2t-p1.js} +22 -22
- package/dashboard/dist/assets/index-BFQ2t-p1.js.map +1 -0
- package/dashboard/dist/assets/{index-D3SjB3UM.js → index-BNkpns_E.js} +2 -2
- package/dashboard/dist/assets/{index-D3SjB3UM.js.map → index-BNkpns_E.js.map} +1 -1
- package/dashboard/dist/assets/{index-BS5pcmBR.js → index-BW-fIquS.js} +2 -2
- package/dashboard/dist/assets/{index-BS5pcmBR.js.map → index-BW-fIquS.js.map} +1 -1
- package/dashboard/dist/assets/{index-C51Jzvoz.js → index-BYpWOUEe.js} +2 -2
- package/dashboard/dist/assets/{index-C51Jzvoz.js.map → index-BYpWOUEe.js.map} +1 -1
- package/dashboard/dist/assets/{index-BDhEpETz.js → index-CPIYW81T.js} +2 -2
- package/dashboard/dist/assets/{index-BDhEpETz.js.map → index-CPIYW81T.js.map} +1 -1
- package/dashboard/dist/assets/{index-B216fPN_.js → index-Cydgj_Co.js} +2 -2
- package/dashboard/dist/assets/{index-B216fPN_.js.map → index-Cydgj_Co.js.map} +1 -1
- package/dashboard/dist/assets/{index-C1p_rvkY.js → index-DBi9Q9Hw.js} +2 -2
- package/dashboard/dist/assets/{index-C1p_rvkY.js.map → index-DBi9Q9Hw.js.map} +1 -1
- package/dashboard/dist/assets/{index-8C9YLM_u.js → index-DRIJM1L-.js} +2 -2
- package/dashboard/dist/assets/{index-8C9YLM_u.js.map → index-DRIJM1L-.js.map} +1 -1
- package/dashboard/dist/assets/index-DSki_Ub6.css +1 -0
- package/dashboard/dist/assets/{index-gKm3HAgO.js → index-Dhy2PVAw.js} +2 -2
- package/dashboard/dist/assets/{index-gKm3HAgO.js.map → index-Dhy2PVAw.js.map} +1 -1
- package/dashboard/dist/assets/index-E2hQ8DIi.js +6 -0
- package/dashboard/dist/assets/index-E2hQ8DIi.js.map +1 -0
- package/dashboard/dist/assets/{index-D0fjUKvx.js → index-mEQ6kCnt.js} +2 -2
- package/dashboard/dist/assets/{index-D0fjUKvx.js.map → index-mEQ6kCnt.js.map} +1 -1
- package/dashboard/dist/assets/{index-oulO44Iy.js → index-rUST2YsO.js} +2 -2
- package/dashboard/dist/assets/{index-oulO44Iy.js.map → index-rUST2YsO.js.map} +1 -1
- package/dashboard/dist/assets/{index-DSjbScPX.js → index-u08vQUaw.js} +2 -2
- package/dashboard/dist/assets/{index-DSjbScPX.js.map → index-u08vQUaw.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-Bje8_ndK.js → knowledge-jQE3dBVi.js} +2 -2
- package/dashboard/dist/assets/{knowledge-Bje8_ndK.js.map → knowledge-jQE3dBVi.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-Bpl6p11J.js → mcp-DtEGfZiz.js} +2 -2
- package/dashboard/dist/assets/{mcp-Bpl6p11J.js.map → mcp-DtEGfZiz.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-DMXj-t5f.js → mcp-query-CiTLVuDC.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-DMXj-t5f.js.map → mcp-query-CiTLVuDC.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-CFh9nBxn.js → pipelines-ClNdzuUK.js} +2 -2
- package/dashboard/dist/assets/{pipelines-CFh9nBxn.js.map → pipelines-ClNdzuUK.js.map} +1 -1
- package/dashboard/dist/assets/{roles-CtjQQWSQ.js → roles-BmHbRaPT.js} +2 -2
- package/dashboard/dist/assets/{roles-CtjQQWSQ.js.map → roles-BmHbRaPT.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-Nz9lVCT1.js → tasks-BPONxLcq.js} +2 -2
- package/dashboard/dist/assets/{tasks-Nz9lVCT1.js.map → tasks-BPONxLcq.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CiG2N8Z_.js → topics-XREPRxwo.js} +2 -2
- package/dashboard/dist/assets/{topics-CiG2N8Z_.js.map → topics-XREPRxwo.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-Bimqt7hU.js → useEventHooks-M7SaEfel.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-Bimqt7hU.js.map → useEventHooks-M7SaEfel.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-FrG0Monz.js → useNamespace-BQHGkdBv.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-FrG0Monz.js.map → useNamespace-BQHGkdBv.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-JmD4dH5n.js → useYamlActivityEvents-B7JtUGSE.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-JmD4dH5n.js.map → useYamlActivityEvents-B7JtUGSE.js.map} +1 -1
- package/dashboard/dist/assets/{users-DgyonYq6.js → users-BpjSNhQe.js} +2 -2
- package/dashboard/dist/assets/{users-DgyonYq6.js.map → users-BpjSNhQe.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-BOwGINtF.js → workflows-k4jE0ECC.js} +2 -2
- package/dashboard/dist/assets/{workflows-BOwGINtF.js.map → workflows-k4jE0ECC.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-I1tcjCFK.js → yaml-workflows-DpviSLEL.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-I1tcjCFK.js.map → yaml-workflows-DpviSLEL.js.map} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/docs/api/http/escalations.md +89 -5
- package/docs/api/sdk/escalations.md +60 -1
- package/docs/hitl-guide.md +65 -1
- package/package.json +3 -2
- package/dashboard/dist/assets/AdminDashboard-DIQEViVY.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-Cqy5ECm4.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-Cqy5ECm4.js.map +0 -1
- package/dashboard/dist/assets/CustomDurationPicker-CUYE36-K.js +0 -2
- package/dashboard/dist/assets/CustomDurationPicker-CUYE36-K.js.map +0 -1
- package/dashboard/dist/assets/index-BCfzac3h.css +0 -1
- package/dashboard/dist/assets/index-BUu3WKvk.js +0 -6
- package/dashboard/dist/assets/index-BUu3WKvk.js.map +0 -1
- package/dashboard/dist/assets/index-eWNx4UQ8.js.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LTApiResult } from '../types/sdk';
|
|
2
|
+
export declare function diagnose(input: {
|
|
3
|
+
workflowId: string;
|
|
4
|
+
appId?: string;
|
|
5
|
+
}): Promise<LTApiResult>;
|
|
6
|
+
export declare function stalledJobs(input: {
|
|
7
|
+
appId?: string;
|
|
8
|
+
stalledMinutes?: number;
|
|
9
|
+
workflowType?: string;
|
|
10
|
+
limit?: number;
|
|
11
|
+
}): Promise<LTApiResult>;
|
|
12
|
+
export declare function orphanedSignals(input: {
|
|
13
|
+
appId?: string;
|
|
14
|
+
limit?: number;
|
|
15
|
+
}): Promise<LTApiResult>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.diagnose = diagnose;
|
|
4
|
+
exports.stalledJobs = stalledJobs;
|
|
5
|
+
exports.orphanedSignals = orphanedSignals;
|
|
6
|
+
const diagnostics_1 = require("../services/diagnostics");
|
|
7
|
+
async function diagnose(input) {
|
|
8
|
+
try {
|
|
9
|
+
const diagnosis = await (0, diagnostics_1.diagnoseJob)(input.workflowId, input.appId);
|
|
10
|
+
return { status: 200, data: diagnosis };
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
return { status: 500, error: err.message };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function stalledJobs(input) {
|
|
17
|
+
try {
|
|
18
|
+
const result = await (0, diagnostics_1.findStalledJobs)(input);
|
|
19
|
+
return { status: 200, data: result };
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return { status: 500, error: err.message };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function orphanedSignals(input) {
|
|
26
|
+
try {
|
|
27
|
+
const result = await (0, diagnostics_1.findOrphanedSignals)(input);
|
|
28
|
+
return { status: 200, data: result };
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return { status: 500, error: err.message };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LTApiResult, LTApiAuth } from '../../types/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Cancel a single escalation. The caller must hold global escalation access
|
|
4
|
+
* or the escalation's role.
|
|
5
|
+
*
|
|
6
|
+
* @returns `{ status: 200, data: { cancelled: true } }` or 403/404/409/500
|
|
7
|
+
*/
|
|
8
|
+
export declare function cancelSingleEscalation(input: {
|
|
9
|
+
id: string;
|
|
10
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
11
|
+
/**
|
|
12
|
+
* Cancel multiple escalations at once.
|
|
13
|
+
*
|
|
14
|
+
* @param input.ids — array of escalation UUIDs
|
|
15
|
+
* @param auth — authenticated user context (admin required)
|
|
16
|
+
* @returns `{ status: 200, data: { cancelled, skipped } }`
|
|
17
|
+
*/
|
|
18
|
+
export declare function bulkCancel(input: {
|
|
19
|
+
ids: string[];
|
|
20
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.cancelSingleEscalation = cancelSingleEscalation;
|
|
37
|
+
exports.bulkCancel = bulkCancel;
|
|
38
|
+
const escalationService = __importStar(require("../../services/escalation"));
|
|
39
|
+
const helpers_1 = require("./helpers");
|
|
40
|
+
const userService = __importStar(require("../../services/user"));
|
|
41
|
+
/**
|
|
42
|
+
* Cancel a single escalation. The caller must hold global escalation access
|
|
43
|
+
* or the escalation's role.
|
|
44
|
+
*
|
|
45
|
+
* @returns `{ status: 200, data: { cancelled: true } }` or 403/404/409/500
|
|
46
|
+
*/
|
|
47
|
+
async function cancelSingleEscalation(input, auth) {
|
|
48
|
+
try {
|
|
49
|
+
const { id } = input;
|
|
50
|
+
if (!id)
|
|
51
|
+
return { status: 400, error: 'id is required' };
|
|
52
|
+
const escalation = await escalationService.getEscalation(id);
|
|
53
|
+
if (!escalation)
|
|
54
|
+
return { status: 404, error: 'Escalation not found' };
|
|
55
|
+
const hasGlobal = await (0, helpers_1.hasGlobalEscalationAccess)(auth.userId);
|
|
56
|
+
if (!hasGlobal) {
|
|
57
|
+
const hasRole = await userService.hasRole(auth.userId, escalation.role);
|
|
58
|
+
if (!hasRole)
|
|
59
|
+
return { status: 403, error: 'Forbidden' };
|
|
60
|
+
}
|
|
61
|
+
const cancelled = await escalationService.cancelEscalation(id);
|
|
62
|
+
if (!cancelled) {
|
|
63
|
+
return { status: 409, error: 'Escalation is not cancellable (already terminal)' };
|
|
64
|
+
}
|
|
65
|
+
return { status: 200, data: { cancelled: true, escalationId: id } };
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
return { status: 500, error: err.message };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Cancel multiple escalations at once.
|
|
73
|
+
*
|
|
74
|
+
* @param input.ids — array of escalation UUIDs
|
|
75
|
+
* @param auth — authenticated user context (admin required)
|
|
76
|
+
* @returns `{ status: 200, data: { cancelled, skipped } }`
|
|
77
|
+
*/
|
|
78
|
+
async function bulkCancel(input, auth) {
|
|
79
|
+
try {
|
|
80
|
+
const { ids } = input;
|
|
81
|
+
if (!(0, helpers_1.validateIds)(ids)) {
|
|
82
|
+
return { status: 400, error: 'ids must be a non-empty array' };
|
|
83
|
+
}
|
|
84
|
+
const perm = await (0, helpers_1.checkBulkPermission)(auth.userId, ids);
|
|
85
|
+
if (!perm.allowed)
|
|
86
|
+
return perm;
|
|
87
|
+
const result = await escalationService.bulkCancelEscalations(ids);
|
|
88
|
+
return { status: 200, data: result };
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return { status: 500, error: err.message };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -5,3 +5,4 @@ export { claimEscalation, releaseEscalation } from './claim';
|
|
|
5
5
|
export { releaseExpiredClaims, updatePriority, bulkClaim, bulkAssign, bulkEscalate, bulkTriage } from './bulk';
|
|
6
6
|
export { resolveEscalation, resolveBySignalKey } from './resolve';
|
|
7
7
|
export { findByMetadata, claimByMetadata, resolveByMetadata } from './metadata';
|
|
8
|
+
export { cancelSingleEscalation, bulkCancel } from './cancel';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getEscalationStats = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
|
|
3
|
+
exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getEscalationStats = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
|
|
4
4
|
var create_1 = require("./create");
|
|
5
5
|
Object.defineProperty(exports, "createEscalation", { enumerable: true, get: function () { return create_1.createEscalation; } });
|
|
6
6
|
var list_1 = require("./list");
|
|
@@ -29,3 +29,6 @@ var metadata_1 = require("./metadata");
|
|
|
29
29
|
Object.defineProperty(exports, "findByMetadata", { enumerable: true, get: function () { return metadata_1.findByMetadata; } });
|
|
30
30
|
Object.defineProperty(exports, "claimByMetadata", { enumerable: true, get: function () { return metadata_1.claimByMetadata; } });
|
|
31
31
|
Object.defineProperty(exports, "resolveByMetadata", { enumerable: true, get: function () { return metadata_1.resolveByMetadata; } });
|
|
32
|
+
var cancel_1 = require("./cancel");
|
|
33
|
+
Object.defineProperty(exports, "cancelSingleEscalation", { enumerable: true, get: function () { return cancel_1.cancelSingleEscalation; } });
|
|
34
|
+
Object.defineProperty(exports, "bulkCancel", { enumerable: true, get: function () { return cancel_1.bulkCancel; } });
|
|
@@ -66,6 +66,8 @@ async function resolveEscalation(input, _auth) {
|
|
|
66
66
|
const escalation = await escalationService.getEscalation(id);
|
|
67
67
|
if (!escalation)
|
|
68
68
|
return { status: 404, error: 'Escalation not found' };
|
|
69
|
+
if (escalation.status === 'cancelled')
|
|
70
|
+
return { status: 409, error: 'Escalation is cancelled' };
|
|
69
71
|
if (escalation.status !== 'pending')
|
|
70
72
|
return { status: 409, error: 'Escalation not available for resolution' };
|
|
71
73
|
// Path A: conditionLT signal
|
|
@@ -42,6 +42,7 @@ const workers_1 = require("../../workers");
|
|
|
42
42
|
const exportService = __importStar(require("../../services/export"));
|
|
43
43
|
const task_1 = require("../../services/task");
|
|
44
44
|
const workflow_invocation_1 = require("../../services/workflow-invocation");
|
|
45
|
+
const crud_1 = require("../../services/escalation/crud");
|
|
45
46
|
function isResolveError(err) {
|
|
46
47
|
return err?.message?.includes('Cannot resolve workflow');
|
|
47
48
|
}
|
|
@@ -154,6 +155,11 @@ async function terminateWorkflow(input) {
|
|
|
154
155
|
const client = (0, workers_1.createClient)();
|
|
155
156
|
const handle = await client.workflow.getHandle(resolved.taskQueue, resolved.workflowName, input.workflowId);
|
|
156
157
|
await handle.terminate();
|
|
158
|
+
// HotMesh's interrupt() cancels escalations in the same Postgres transaction,
|
|
159
|
+
// but only when the engine app_id matches the escalation row's app_id. The
|
|
160
|
+
// Durable client uses app_id='durable' while escalations are stored with
|
|
161
|
+
// app_id='hmsh', so the automatic cancel is a no-op. Cancel explicitly here.
|
|
162
|
+
await (0, crud_1.cancelEscalationsByWorkflowId)(input.workflowId);
|
|
157
163
|
return {
|
|
158
164
|
status: 200,
|
|
159
165
|
data: { terminated: true, workflowId: input.workflowId },
|
|
@@ -35,7 +35,7 @@ export declare function publishTaskEvent(params: {
|
|
|
35
35
|
* Subject: system.escalation.{escalationId}.{action}
|
|
36
36
|
*/
|
|
37
37
|
export declare function publishEscalationEvent(params: {
|
|
38
|
-
type: 'escalation.created' | 'escalation.resolved' | 'escalation.claimed' | 'escalation.released';
|
|
38
|
+
type: 'escalation.created' | 'escalation.resolved' | 'escalation.claimed' | 'escalation.released' | 'escalation.cancelled';
|
|
39
39
|
source: string;
|
|
40
40
|
workflowId: string;
|
|
41
41
|
workflowName: string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const express_1 = require("express");
|
|
37
|
+
const api = __importStar(require("../api/diagnostics"));
|
|
38
|
+
const auth_1 = require("../modules/auth");
|
|
39
|
+
const router = (0, express_1.Router)();
|
|
40
|
+
/**
|
|
41
|
+
* GET /api/diagnostics/jobs/:workflowId
|
|
42
|
+
* Full diagnosis for a single workflow job.
|
|
43
|
+
*
|
|
44
|
+
* Query params:
|
|
45
|
+
* app_id — HotMesh namespace (default: durable)
|
|
46
|
+
*/
|
|
47
|
+
router.get('/jobs/:workflowId', auth_1.requireAdmin, async (req, res) => {
|
|
48
|
+
const result = await api.diagnose({
|
|
49
|
+
workflowId: req.params.workflowId,
|
|
50
|
+
appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
|
|
51
|
+
});
|
|
52
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* GET /api/diagnostics/stalled
|
|
56
|
+
* Running jobs with no recent progress.
|
|
57
|
+
*
|
|
58
|
+
* Query params:
|
|
59
|
+
* app_id — HotMesh namespace (default: durable)
|
|
60
|
+
* stalled_minutes — minimum minutes since last event (default: 5)
|
|
61
|
+
* workflow_type — filter by workflow function name
|
|
62
|
+
* limit — max results (default: 50, max: 200)
|
|
63
|
+
*/
|
|
64
|
+
router.get('/stalled', auth_1.requireAdmin, async (req, res) => {
|
|
65
|
+
const result = await api.stalledJobs({
|
|
66
|
+
appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
|
|
67
|
+
stalledMinutes: req.query.stalled_minutes ? Number(req.query.stalled_minutes) : undefined,
|
|
68
|
+
workflowType: typeof req.query.workflow_type === 'string' ? req.query.workflow_type : undefined,
|
|
69
|
+
limit: req.query.limit ? Number(req.query.limit) : undefined,
|
|
70
|
+
});
|
|
71
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* GET /api/diagnostics/orphaned-signals
|
|
75
|
+
* Suspended jobs with no escalation row.
|
|
76
|
+
*
|
|
77
|
+
* Query params:
|
|
78
|
+
* app_id — HotMesh namespace (default: durable)
|
|
79
|
+
* limit — max results (default: 100, max: 500)
|
|
80
|
+
*/
|
|
81
|
+
router.get('/orphaned-signals', auth_1.requireAdmin, async (req, res) => {
|
|
82
|
+
const result = await api.orphanedSignals({
|
|
83
|
+
appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
|
|
84
|
+
limit: req.query.limit ? Number(req.query.limit) : undefined,
|
|
85
|
+
});
|
|
86
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
87
|
+
});
|
|
88
|
+
exports.default = router;
|
|
@@ -64,4 +64,8 @@ function registerBulkRoutes(router) {
|
|
|
64
64
|
const result = await api.bulkTriage({ ids: req.body?.ids, hint: req.body?.hint }, req.auth);
|
|
65
65
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
66
66
|
});
|
|
67
|
+
router.post('/bulk-cancel', async (req, res) => {
|
|
68
|
+
const result = await api.bulkCancel({ ids: req.body?.ids }, req.auth);
|
|
69
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
70
|
+
});
|
|
67
71
|
}
|
|
@@ -83,4 +83,13 @@ function registerSingleRoutes(router) {
|
|
|
83
83
|
const result = await api.releaseEscalation({ id: req.params.id }, req.auth);
|
|
84
84
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
85
85
|
});
|
|
86
|
+
/**
|
|
87
|
+
* POST /api/escalations/:id/cancel
|
|
88
|
+
* Permanently cancel an escalation. Caller must hold the escalation's role
|
|
89
|
+
* or have global escalation access. Terminal escalations return 409.
|
|
90
|
+
*/
|
|
91
|
+
router.post('/:id/cancel', async (req, res) => {
|
|
92
|
+
const result = await api.cancelSingleEscalation({ id: req.params.id }, req.auth);
|
|
93
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
94
|
+
});
|
|
86
95
|
}
|
package/build/routes/index.js
CHANGED
|
@@ -70,4 +70,6 @@ router.use('/capabilities', capabilities_1.default);
|
|
|
70
70
|
router.use('/topics', topics_1.default);
|
|
71
71
|
const overview_1 = __importDefault(require("./overview"));
|
|
72
72
|
router.use('/overview', overview_1.default);
|
|
73
|
+
const diagnostics_1 = __importDefault(require("./diagnostics"));
|
|
74
|
+
router.use('/diagnostics', diagnostics_1.default);
|
|
73
75
|
exports.default = router;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Finding } from './patterns';
|
|
2
|
+
import type { StreamMessage } from '../controlplane/types';
|
|
3
|
+
export type { Finding };
|
|
4
|
+
export interface EscalationSummary {
|
|
5
|
+
exists: boolean;
|
|
6
|
+
id: string | null;
|
|
7
|
+
status: string | null;
|
|
8
|
+
signal_key: string | null;
|
|
9
|
+
role: string | null;
|
|
10
|
+
type: string | null;
|
|
11
|
+
created_at: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface StreamSummary {
|
|
14
|
+
worker_total: number;
|
|
15
|
+
engine_total: number;
|
|
16
|
+
dead_lettered: number;
|
|
17
|
+
pending: number;
|
|
18
|
+
in_flight: number;
|
|
19
|
+
}
|
|
20
|
+
export interface JobDiagnosis {
|
|
21
|
+
workflow_id: string;
|
|
22
|
+
app_id: string;
|
|
23
|
+
status: 'running' | 'completed' | 'failed' | 'not_found';
|
|
24
|
+
stalled_for_ms: number | null;
|
|
25
|
+
workflow_type: string | null;
|
|
26
|
+
last_event_at: string | null;
|
|
27
|
+
stream_summary: StreamSummary;
|
|
28
|
+
escalation: EscalationSummary;
|
|
29
|
+
findings: Finding[];
|
|
30
|
+
execution_events: unknown[];
|
|
31
|
+
stream_messages: {
|
|
32
|
+
worker: StreamMessage[];
|
|
33
|
+
engine: StreamMessage[];
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export declare function diagnoseJob(workflowId: string, appId?: string): Promise<JobDiagnosis>;
|
|
37
|
+
export declare function findStalledJobs(params: {
|
|
38
|
+
appId?: string;
|
|
39
|
+
stalledMinutes?: number;
|
|
40
|
+
workflowType?: string | null;
|
|
41
|
+
limit?: number;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
jobs: unknown[];
|
|
44
|
+
total: number;
|
|
45
|
+
}>;
|
|
46
|
+
export declare function findOrphanedSignals(params: {
|
|
47
|
+
appId?: string;
|
|
48
|
+
limit?: number;
|
|
49
|
+
}): Promise<{
|
|
50
|
+
orphans: unknown[];
|
|
51
|
+
total: number;
|
|
52
|
+
}>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.diagnoseJob = diagnoseJob;
|
|
4
|
+
exports.findStalledJobs = findStalledJobs;
|
|
5
|
+
exports.findOrphanedSignals = findOrphanedSignals;
|
|
6
|
+
const db_1 = require("../../lib/db");
|
|
7
|
+
const export_1 = require("../export");
|
|
8
|
+
const controlplane_1 = require("../controlplane");
|
|
9
|
+
const resolve_1 = require("../task/resolve");
|
|
10
|
+
const patterns_1 = require("./patterns");
|
|
11
|
+
// ── SQL ──────────────────────────────────────────────────────────────────────
|
|
12
|
+
const ESCALATION_BY_JOB = `
|
|
13
|
+
SELECT id, status, signal_key, role, type, assigned_to, created_at
|
|
14
|
+
FROM public.hmsh_escalations
|
|
15
|
+
WHERE workflow_id = $1
|
|
16
|
+
ORDER BY created_at DESC LIMIT 5
|
|
17
|
+
`;
|
|
18
|
+
const STALLED_JOBS = (appId) => `
|
|
19
|
+
SELECT
|
|
20
|
+
REPLACE(j.key, $1, '') AS workflow_id,
|
|
21
|
+
j.entity AS workflow_type,
|
|
22
|
+
j.status,
|
|
23
|
+
j.created_at,
|
|
24
|
+
j.updated_at,
|
|
25
|
+
EXTRACT(EPOCH FROM (NOW() - j.updated_at)) * 1000 AS stalled_ms
|
|
26
|
+
FROM "${appId}".jobs j
|
|
27
|
+
WHERE j.status > 0
|
|
28
|
+
AND j.is_live = TRUE
|
|
29
|
+
AND j.updated_at < NOW() - ($2 || ' minutes')::INTERVAL
|
|
30
|
+
AND ($3::text IS NULL OR j.entity = $3)
|
|
31
|
+
ORDER BY j.updated_at ASC
|
|
32
|
+
LIMIT $4
|
|
33
|
+
`;
|
|
34
|
+
const ORPHANED_SIGNALS = (appId) => `
|
|
35
|
+
WITH committed_waiters AS (
|
|
36
|
+
SELECT
|
|
37
|
+
ws.jid AS job_id,
|
|
38
|
+
ws.message->'data'->>'signalId' AS signal_id,
|
|
39
|
+
ws.message->'data'->>'queueConfig' AS queue_config,
|
|
40
|
+
ws.workflow_name,
|
|
41
|
+
ws.created_at AS suspended_at
|
|
42
|
+
FROM "${appId}".worker_streams ws
|
|
43
|
+
WHERE ws.aid LIKE '%/waiter'
|
|
44
|
+
AND ws.expired_at IS NOT NULL
|
|
45
|
+
AND ws.dead_lettered_at IS NULL
|
|
46
|
+
),
|
|
47
|
+
live_jobs AS (
|
|
48
|
+
SELECT REPLACE(key, 'hmsh:${appId}:j:', '') AS wid
|
|
49
|
+
FROM "${appId}".jobs
|
|
50
|
+
WHERE status > 0 AND is_live = TRUE
|
|
51
|
+
)
|
|
52
|
+
SELECT
|
|
53
|
+
cw.job_id,
|
|
54
|
+
cw.signal_id,
|
|
55
|
+
cw.workflow_name,
|
|
56
|
+
cw.suspended_at,
|
|
57
|
+
EXTRACT(EPOCH FROM (NOW() - cw.suspended_at)) * 1000 AS stalled_ms,
|
|
58
|
+
cw.queue_config IS NULL AS missing_queue_config
|
|
59
|
+
FROM committed_waiters cw
|
|
60
|
+
JOIN live_jobs lj ON lj.wid = cw.job_id
|
|
61
|
+
WHERE NOT EXISTS (
|
|
62
|
+
SELECT 1 FROM public.hmsh_escalations e WHERE e.workflow_id = cw.job_id
|
|
63
|
+
)
|
|
64
|
+
ORDER BY cw.suspended_at ASC
|
|
65
|
+
LIMIT $1
|
|
66
|
+
`;
|
|
67
|
+
// ── diagnoseJob ──────────────────────────────────────────────────────────────
|
|
68
|
+
async function diagnoseJob(workflowId, appId = 'durable') {
|
|
69
|
+
const pool = (0, db_1.getPool)();
|
|
70
|
+
let resolved;
|
|
71
|
+
try {
|
|
72
|
+
resolved = await (0, resolve_1.resolveWorkflowHandle)(workflowId);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return notFound(workflowId, appId);
|
|
76
|
+
}
|
|
77
|
+
const [execution, workerResult, engineResult, escalationResult] = await Promise.all([
|
|
78
|
+
(0, export_1.exportWorkflowExecution)(workflowId, resolved.taskQueue, resolved.workflowName)
|
|
79
|
+
.catch(() => null),
|
|
80
|
+
(0, controlplane_1.getStreamMessages)(appId, { source: 'worker', jid: workflowId, limit: 100, order: 'asc' }),
|
|
81
|
+
(0, controlplane_1.getStreamMessages)(appId, { source: 'engine', jid: workflowId, limit: 100, order: 'asc' }),
|
|
82
|
+
pool.query(ESCALATION_BY_JOB, [workflowId]),
|
|
83
|
+
]);
|
|
84
|
+
const escalationRow = escalationResult.rows[0] ?? null;
|
|
85
|
+
const workerMessages = workerResult.messages;
|
|
86
|
+
const engineMessages = engineResult.messages;
|
|
87
|
+
const events = execution?.events ?? [];
|
|
88
|
+
const lastEvent = events.at(-1);
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
const isCompleted = events.some(e => e.event_type === 'workflow_execution_completed');
|
|
91
|
+
const isFailed = events.some(e => e.event_type === 'workflow_execution_failed');
|
|
92
|
+
const status = isCompleted ? 'completed' : isFailed ? 'failed' : 'running';
|
|
93
|
+
const lastEventAt = lastEvent?.event_time ?? null;
|
|
94
|
+
const stalledMs = status === 'running' && lastEventAt
|
|
95
|
+
? now - new Date(lastEventAt).getTime()
|
|
96
|
+
: null;
|
|
97
|
+
const allMessages = [...workerMessages, ...engineMessages];
|
|
98
|
+
const streamSummary = {
|
|
99
|
+
worker_total: workerMessages.length,
|
|
100
|
+
engine_total: engineMessages.length,
|
|
101
|
+
dead_lettered: allMessages.filter(m => m.dead_lettered_at).length,
|
|
102
|
+
pending: allMessages.filter(m => !m.reserved_at && !m.expired_at && !m.dead_lettered_at).length,
|
|
103
|
+
in_flight: allMessages.filter(m => m.reserved_at && !m.expired_at && !m.dead_lettered_at).length,
|
|
104
|
+
};
|
|
105
|
+
const findings = execution
|
|
106
|
+
? (0, patterns_1.matchPatterns)(execution, workerMessages, engineMessages, escalationRow)
|
|
107
|
+
: [{ condition: 'export_unavailable', confidence: 0.9, severity: 'warning', evidence: ['Execution export unavailable — job may have expired or HotMesh engine is not running'], treatment: [] }];
|
|
108
|
+
return {
|
|
109
|
+
workflow_id: workflowId,
|
|
110
|
+
app_id: appId,
|
|
111
|
+
status,
|
|
112
|
+
stalled_for_ms: stalledMs,
|
|
113
|
+
workflow_type: resolved.workflowName,
|
|
114
|
+
last_event_at: lastEventAt,
|
|
115
|
+
stream_summary: streamSummary,
|
|
116
|
+
escalation: {
|
|
117
|
+
exists: !!escalationRow,
|
|
118
|
+
id: escalationRow?.id ?? null,
|
|
119
|
+
status: escalationRow?.status ?? null,
|
|
120
|
+
signal_key: escalationRow?.signal_key ?? null,
|
|
121
|
+
role: escalationRow?.role ?? null,
|
|
122
|
+
type: escalationRow?.type ?? null,
|
|
123
|
+
created_at: escalationRow?.created_at ?? null,
|
|
124
|
+
},
|
|
125
|
+
findings,
|
|
126
|
+
execution_events: events,
|
|
127
|
+
stream_messages: { worker: workerMessages, engine: engineMessages },
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// ── findStalledJobs ──────────────────────────────────────────────────────────
|
|
131
|
+
async function findStalledJobs(params) {
|
|
132
|
+
const appId = params.appId ?? 'durable';
|
|
133
|
+
const stalledMinutes = params.stalledMinutes ?? 5;
|
|
134
|
+
const limit = Math.min(params.limit ?? 50, 200);
|
|
135
|
+
const keyPrefix = `hmsh:${appId}:j:`;
|
|
136
|
+
const pool = (0, db_1.getPool)();
|
|
137
|
+
const { rows } = await pool.query(STALLED_JOBS(appId), [
|
|
138
|
+
keyPrefix,
|
|
139
|
+
String(stalledMinutes),
|
|
140
|
+
params.workflowType ?? null,
|
|
141
|
+
limit,
|
|
142
|
+
]).catch(() => ({ rows: [] }));
|
|
143
|
+
return { jobs: rows, total: rows.length };
|
|
144
|
+
}
|
|
145
|
+
// ── findOrphanedSignals ──────────────────────────────────────────────────────
|
|
146
|
+
async function findOrphanedSignals(params) {
|
|
147
|
+
const appId = params.appId ?? 'durable';
|
|
148
|
+
const limit = Math.min(params.limit ?? 100, 500);
|
|
149
|
+
const pool = (0, db_1.getPool)();
|
|
150
|
+
const { rows } = await pool.query(ORPHANED_SIGNALS(appId), [limit])
|
|
151
|
+
.catch(() => ({ rows: [] }));
|
|
152
|
+
return { orphans: rows, total: rows.length };
|
|
153
|
+
}
|
|
154
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
155
|
+
function notFound(workflowId, appId) {
|
|
156
|
+
return {
|
|
157
|
+
workflow_id: workflowId,
|
|
158
|
+
app_id: appId,
|
|
159
|
+
status: 'not_found',
|
|
160
|
+
stalled_for_ms: null,
|
|
161
|
+
workflow_type: null,
|
|
162
|
+
last_event_at: null,
|
|
163
|
+
stream_summary: { worker_total: 0, engine_total: 0, dead_lettered: 0, pending: 0, in_flight: 0 },
|
|
164
|
+
escalation: { exists: false, id: null, status: null, signal_key: null, role: null, type: null, created_at: null },
|
|
165
|
+
findings: [{ condition: 'not_found', confidence: 1, severity: 'critical', evidence: ['Workflow ID not found — no task record or job entity'], treatment: [] }],
|
|
166
|
+
execution_events: [],
|
|
167
|
+
stream_messages: { worker: [], engine: [] },
|
|
168
|
+
};
|
|
169
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { WorkflowExecution } from '@hotmeshio/hotmesh/build/types/exporter';
|
|
2
|
+
import type { StreamMessage } from '../controlplane/types';
|
|
3
|
+
export interface Finding {
|
|
4
|
+
condition: string;
|
|
5
|
+
confidence: number;
|
|
6
|
+
severity: 'critical' | 'warning' | 'info';
|
|
7
|
+
evidence: string[];
|
|
8
|
+
treatment: RecoveryOption[];
|
|
9
|
+
}
|
|
10
|
+
export interface RecoveryOption {
|
|
11
|
+
action: string;
|
|
12
|
+
note: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Match event stream + stream messages against known failure patterns.
|
|
17
|
+
* Returns findings ordered by severity (critical first).
|
|
18
|
+
*/
|
|
19
|
+
export declare function matchPatterns(execution: WorkflowExecution, workerMessages: StreamMessage[], engineMessages: StreamMessage[], escalationRow: Record<string, unknown> | null): Finding[];
|