@d34dman/flowdrop 0.0.36 → 0.0.38
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/dist/components/NodeSidebar.svelte +1 -0
- package/dist/components/form/FormCodeEditor.svelte +6 -1
- package/dist/components/interrupt/ChoicePrompt.svelte +389 -0
- package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +21 -0
- package/dist/components/interrupt/ConfirmationPrompt.svelte +280 -0
- package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +23 -0
- package/dist/components/interrupt/FormPrompt.svelte +223 -0
- package/dist/components/interrupt/FormPrompt.svelte.d.ts +21 -0
- package/dist/components/interrupt/InterruptBubble.svelte +621 -0
- package/dist/components/interrupt/InterruptBubble.svelte.d.ts +16 -0
- package/dist/components/interrupt/TextInputPrompt.svelte +333 -0
- package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +21 -0
- package/dist/components/interrupt/index.d.ts +13 -0
- package/dist/components/interrupt/index.js +15 -0
- package/dist/components/nodes/GatewayNode.svelte +21 -5
- package/dist/components/nodes/IdeaNode.svelte +48 -43
- package/dist/components/nodes/IdeaNode.svelte.d.ts +1 -1
- package/dist/components/nodes/SimpleNode.svelte +1 -3
- package/dist/components/nodes/TerminalNode.svelte +52 -11
- package/dist/components/nodes/ToolNode.svelte +22 -7
- package/dist/components/nodes/WorkflowNode.svelte +1 -3
- package/dist/components/playground/ChatPanel.svelte +131 -7
- package/dist/components/playground/ChatPanel.svelte.d.ts +2 -0
- package/dist/components/playground/MessageBubble.svelte +1 -3
- package/dist/components/playground/Playground.svelte +50 -5
- package/dist/components/playground/PlaygroundModal.svelte +8 -7
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -3
- package/dist/config/endpoints.d.ts +12 -0
- package/dist/config/endpoints.js +7 -0
- package/dist/playground/index.d.ts +5 -0
- package/dist/playground/index.js +21 -0
- package/dist/playground/mount.d.ts +3 -3
- package/dist/playground/mount.js +30 -22
- package/dist/services/interruptService.d.ts +133 -0
- package/dist/services/interruptService.js +279 -0
- package/dist/stores/interruptStore.d.ts +200 -0
- package/dist/stores/interruptStore.js +424 -0
- package/dist/stores/playgroundStore.d.ts +11 -1
- package/dist/stores/playgroundStore.js +34 -0
- package/dist/styles/base.css +89 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/interrupt.d.ts +303 -0
- package/dist/types/interrupt.js +125 -0
- package/dist/types/interruptState.d.ts +211 -0
- package/dist/types/interruptState.js +308 -0
- package/dist/utils/colors.js +1 -0
- package/dist/utils/connections.js +2 -2
- package/dist/utils/icons.js +1 -0
- package/package.json +1 -1
package/dist/playground/mount.js
CHANGED
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
* })(Drupal, window.FlowDrop);
|
|
44
44
|
* ```
|
|
45
45
|
*/
|
|
46
|
-
import { mount, unmount } from
|
|
47
|
-
import Playground from
|
|
48
|
-
import PlaygroundModal from
|
|
49
|
-
import { setEndpointConfig } from
|
|
50
|
-
import { playgroundService } from
|
|
51
|
-
import { currentSession, sessions, messages, playgroundActions } from
|
|
52
|
-
import { get } from
|
|
46
|
+
import { mount, unmount } from 'svelte';
|
|
47
|
+
import Playground from '../components/playground/Playground.svelte';
|
|
48
|
+
import PlaygroundModal from '../components/playground/PlaygroundModal.svelte';
|
|
49
|
+
import { setEndpointConfig } from '../services/api.js';
|
|
50
|
+
import { playgroundService } from '../services/playgroundService.js';
|
|
51
|
+
import { currentSession, sessions, messages, playgroundActions } from '../stores/playgroundStore.js';
|
|
52
|
+
import { get } from 'svelte/store';
|
|
53
53
|
/**
|
|
54
54
|
* Mount the Playground component in a container
|
|
55
55
|
*
|
|
@@ -80,23 +80,23 @@ import { get } from "svelte/store";
|
|
|
80
80
|
* ```
|
|
81
81
|
*/
|
|
82
82
|
export async function mountPlayground(container, options) {
|
|
83
|
-
const { workflowId, workflow, mode =
|
|
83
|
+
const { workflowId, workflow, mode = 'standalone', initialSessionId, endpointConfig, config = {}, height = '100%', width = '100%', onClose } = options;
|
|
84
84
|
// Validate required parameters
|
|
85
85
|
if (!workflowId) {
|
|
86
|
-
throw new Error(
|
|
86
|
+
throw new Error('workflowId is required for mountPlayground()');
|
|
87
87
|
}
|
|
88
88
|
if (!container) {
|
|
89
|
-
throw new Error(
|
|
89
|
+
throw new Error('container element is required for mountPlayground()');
|
|
90
90
|
}
|
|
91
91
|
// Validate onClose for modal mode
|
|
92
|
-
if (mode ===
|
|
93
|
-
throw new Error(
|
|
92
|
+
if (mode === 'modal' && !onClose) {
|
|
93
|
+
throw new Error('onClose callback is required for modal mode');
|
|
94
94
|
}
|
|
95
95
|
// Set endpoint configuration if provided
|
|
96
96
|
let finalEndpointConfig;
|
|
97
97
|
if (endpointConfig) {
|
|
98
98
|
// Merge with default configuration to ensure all required endpoints are present
|
|
99
|
-
const { defaultEndpointConfig } = await import(
|
|
99
|
+
const { defaultEndpointConfig } = await import('../config/endpoints.js');
|
|
100
100
|
finalEndpointConfig = {
|
|
101
101
|
...defaultEndpointConfig,
|
|
102
102
|
...endpointConfig,
|
|
@@ -111,7 +111,7 @@ export async function mountPlayground(container, options) {
|
|
|
111
111
|
// For modal mode, PlaygroundModal creates its own backdrop, so we mount directly to body
|
|
112
112
|
// For other modes, use the provided container
|
|
113
113
|
let targetContainer = container;
|
|
114
|
-
if (mode ===
|
|
114
|
+
if (mode === 'modal') {
|
|
115
115
|
// For modal mode, create a container in the body
|
|
116
116
|
// PlaygroundModal will handle the backdrop itself
|
|
117
117
|
targetContainer = document.body;
|
|
@@ -122,10 +122,12 @@ export async function mountPlayground(container, options) {
|
|
|
122
122
|
container.style.width = width;
|
|
123
123
|
}
|
|
124
124
|
// Mount the appropriate component
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
// Separate the mount calls to avoid TypeScript inference issues with different props types
|
|
126
|
+
let svelteApp;
|
|
127
|
+
if (mode === 'modal') {
|
|
128
|
+
svelteApp = mount(PlaygroundModal, {
|
|
129
|
+
target: targetContainer,
|
|
130
|
+
props: {
|
|
129
131
|
isOpen: true,
|
|
130
132
|
workflowId,
|
|
131
133
|
workflow,
|
|
@@ -138,7 +140,12 @@ export async function mountPlayground(container, options) {
|
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
|
-
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
svelteApp = mount(Playground, {
|
|
147
|
+
target: targetContainer,
|
|
148
|
+
props: {
|
|
142
149
|
workflowId,
|
|
143
150
|
workflow,
|
|
144
151
|
mode,
|
|
@@ -147,12 +154,13 @@ export async function mountPlayground(container, options) {
|
|
|
147
154
|
config,
|
|
148
155
|
onClose
|
|
149
156
|
}
|
|
150
|
-
|
|
157
|
+
});
|
|
158
|
+
}
|
|
151
159
|
// Store state for cleanup
|
|
152
160
|
const state = {
|
|
153
161
|
svelteApp,
|
|
154
162
|
container: targetContainer,
|
|
155
|
-
originalContainer: mode ===
|
|
163
|
+
originalContainer: mode === 'modal' ? container : undefined,
|
|
156
164
|
workflowId
|
|
157
165
|
};
|
|
158
166
|
// Create the mounted playground interface
|
|
@@ -203,7 +211,7 @@ export async function mountPlayground(container, options) {
|
|
|
203
211
|
* ```
|
|
204
212
|
*/
|
|
205
213
|
export function unmountPlayground(app) {
|
|
206
|
-
if (app && typeof app.destroy ===
|
|
214
|
+
if (app && typeof app.destroy === 'function') {
|
|
207
215
|
app.destroy();
|
|
208
216
|
}
|
|
209
217
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interrupt Service
|
|
3
|
+
*
|
|
4
|
+
* Handles API interactions for Human-in-the-Loop (HITL) interrupts,
|
|
5
|
+
* including fetching interrupt details, resolving interrupts with user
|
|
6
|
+
* responses, and optional dedicated polling for interrupt status.
|
|
7
|
+
*
|
|
8
|
+
* @module services/interruptService
|
|
9
|
+
*/
|
|
10
|
+
import type { Interrupt, InterruptPollingConfig } from '../types/interrupt.js';
|
|
11
|
+
/**
|
|
12
|
+
* Interrupt Service class
|
|
13
|
+
*
|
|
14
|
+
* Provides methods to interact with interrupt API endpoints including
|
|
15
|
+
* fetching, resolving, cancelling, and listing interrupts.
|
|
16
|
+
* Supports optional dedicated polling for interrupt status.
|
|
17
|
+
*/
|
|
18
|
+
export declare class InterruptService {
|
|
19
|
+
private static instance;
|
|
20
|
+
private pollingInterval;
|
|
21
|
+
private pollingSessionId;
|
|
22
|
+
private currentBackoff;
|
|
23
|
+
private pollingConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Private constructor for singleton pattern
|
|
26
|
+
*/
|
|
27
|
+
private constructor();
|
|
28
|
+
/**
|
|
29
|
+
* Get the singleton instance of InterruptService
|
|
30
|
+
*
|
|
31
|
+
* @returns The InterruptService singleton instance
|
|
32
|
+
*/
|
|
33
|
+
static getInstance(): InterruptService;
|
|
34
|
+
/**
|
|
35
|
+
* Configure interrupt polling settings
|
|
36
|
+
*
|
|
37
|
+
* @param config - Polling configuration options
|
|
38
|
+
*/
|
|
39
|
+
setPollingConfig(config: Partial<InterruptPollingConfig>): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get the current polling configuration
|
|
42
|
+
*
|
|
43
|
+
* @returns Current polling configuration
|
|
44
|
+
*/
|
|
45
|
+
getPollingConfig(): InterruptPollingConfig;
|
|
46
|
+
/**
|
|
47
|
+
* Check if interrupt endpoints are configured
|
|
48
|
+
*
|
|
49
|
+
* @returns True if interrupt endpoints are available
|
|
50
|
+
*/
|
|
51
|
+
isConfigured(): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Get the endpoint configuration
|
|
54
|
+
*
|
|
55
|
+
* @throws Error if endpoint configuration is not set
|
|
56
|
+
* @returns The endpoint configuration
|
|
57
|
+
*/
|
|
58
|
+
private getConfig;
|
|
59
|
+
/**
|
|
60
|
+
* Generic API request helper
|
|
61
|
+
*
|
|
62
|
+
* @param url - The URL to fetch
|
|
63
|
+
* @param options - Fetch options
|
|
64
|
+
* @returns The parsed JSON response
|
|
65
|
+
*/
|
|
66
|
+
private request;
|
|
67
|
+
/**
|
|
68
|
+
* Get interrupt details by ID
|
|
69
|
+
*
|
|
70
|
+
* @param interruptId - The interrupt UUID
|
|
71
|
+
* @returns The interrupt details
|
|
72
|
+
*/
|
|
73
|
+
getInterrupt(interruptId: string): Promise<Interrupt>;
|
|
74
|
+
/**
|
|
75
|
+
* Resolve an interrupt with user response
|
|
76
|
+
*
|
|
77
|
+
* @param interruptId - The interrupt UUID
|
|
78
|
+
* @param value - The user's response value
|
|
79
|
+
* @returns The updated interrupt
|
|
80
|
+
*/
|
|
81
|
+
resolveInterrupt(interruptId: string, value: unknown): Promise<Interrupt>;
|
|
82
|
+
/**
|
|
83
|
+
* Cancel a pending interrupt
|
|
84
|
+
*
|
|
85
|
+
* @param interruptId - The interrupt UUID
|
|
86
|
+
* @returns The updated interrupt
|
|
87
|
+
*/
|
|
88
|
+
cancelInterrupt(interruptId: string): Promise<Interrupt>;
|
|
89
|
+
/**
|
|
90
|
+
* List interrupts for a playground session
|
|
91
|
+
*
|
|
92
|
+
* @param sessionId - The session UUID
|
|
93
|
+
* @returns Array of interrupts for the session
|
|
94
|
+
*/
|
|
95
|
+
listSessionInterrupts(sessionId: string): Promise<Interrupt[]>;
|
|
96
|
+
/**
|
|
97
|
+
* List interrupts for a pipeline
|
|
98
|
+
*
|
|
99
|
+
* @param pipelineId - The pipeline UUID
|
|
100
|
+
* @returns Array of interrupts for the pipeline
|
|
101
|
+
*/
|
|
102
|
+
listPipelineInterrupts(pipelineId: string): Promise<Interrupt[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Start polling for interrupts in a session
|
|
105
|
+
*
|
|
106
|
+
* This is optional - interrupts are typically detected via message metadata.
|
|
107
|
+
* Use this for scenarios where dedicated polling is preferred.
|
|
108
|
+
*
|
|
109
|
+
* @param sessionId - The session UUID to poll
|
|
110
|
+
* @param callback - Callback function to handle new interrupts
|
|
111
|
+
*/
|
|
112
|
+
startPolling(sessionId: string, callback: (interrupts: Interrupt[]) => void): void;
|
|
113
|
+
/**
|
|
114
|
+
* Stop polling for interrupts
|
|
115
|
+
*/
|
|
116
|
+
stopPolling(): void;
|
|
117
|
+
/**
|
|
118
|
+
* Check if polling is active
|
|
119
|
+
*
|
|
120
|
+
* @returns True if polling is active
|
|
121
|
+
*/
|
|
122
|
+
isPolling(): boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Get the current polling session ID
|
|
125
|
+
*
|
|
126
|
+
* @returns The session ID being polled, or null
|
|
127
|
+
*/
|
|
128
|
+
getPollingSessionId(): string | null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Export singleton instance
|
|
132
|
+
*/
|
|
133
|
+
export declare const interruptService: InterruptService;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interrupt Service
|
|
3
|
+
*
|
|
4
|
+
* Handles API interactions for Human-in-the-Loop (HITL) interrupts,
|
|
5
|
+
* including fetching interrupt details, resolving interrupts with user
|
|
6
|
+
* responses, and optional dedicated polling for interrupt status.
|
|
7
|
+
*
|
|
8
|
+
* @module services/interruptService
|
|
9
|
+
*/
|
|
10
|
+
import { defaultInterruptPollingConfig } from '../types/interrupt.js';
|
|
11
|
+
import { buildEndpointUrl, getEndpointHeaders } from '../config/endpoints.js';
|
|
12
|
+
import { getEndpointConfig } from './api.js';
|
|
13
|
+
/**
|
|
14
|
+
* Interrupt Service class
|
|
15
|
+
*
|
|
16
|
+
* Provides methods to interact with interrupt API endpoints including
|
|
17
|
+
* fetching, resolving, cancelling, and listing interrupts.
|
|
18
|
+
* Supports optional dedicated polling for interrupt status.
|
|
19
|
+
*/
|
|
20
|
+
export class InterruptService {
|
|
21
|
+
static instance;
|
|
22
|
+
pollingInterval = null;
|
|
23
|
+
pollingSessionId = null;
|
|
24
|
+
currentBackoff;
|
|
25
|
+
pollingConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Private constructor for singleton pattern
|
|
28
|
+
*/
|
|
29
|
+
constructor() {
|
|
30
|
+
this.pollingConfig = { ...defaultInterruptPollingConfig };
|
|
31
|
+
this.currentBackoff = this.pollingConfig.interval ?? 2000;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the singleton instance of InterruptService
|
|
35
|
+
*
|
|
36
|
+
* @returns The InterruptService singleton instance
|
|
37
|
+
*/
|
|
38
|
+
static getInstance() {
|
|
39
|
+
if (!InterruptService.instance) {
|
|
40
|
+
InterruptService.instance = new InterruptService();
|
|
41
|
+
}
|
|
42
|
+
return InterruptService.instance;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Configure interrupt polling settings
|
|
46
|
+
*
|
|
47
|
+
* @param config - Polling configuration options
|
|
48
|
+
*/
|
|
49
|
+
setPollingConfig(config) {
|
|
50
|
+
this.pollingConfig = { ...this.pollingConfig, ...config };
|
|
51
|
+
this.currentBackoff = this.pollingConfig.interval ?? 2000;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the current polling configuration
|
|
55
|
+
*
|
|
56
|
+
* @returns Current polling configuration
|
|
57
|
+
*/
|
|
58
|
+
getPollingConfig() {
|
|
59
|
+
return { ...this.pollingConfig };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if interrupt endpoints are configured
|
|
63
|
+
*
|
|
64
|
+
* @returns True if interrupt endpoints are available
|
|
65
|
+
*/
|
|
66
|
+
isConfigured() {
|
|
67
|
+
const config = getEndpointConfig();
|
|
68
|
+
return Boolean(config?.endpoints?.interrupts);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the endpoint configuration
|
|
72
|
+
*
|
|
73
|
+
* @throws Error if endpoint configuration is not set
|
|
74
|
+
* @returns The endpoint configuration
|
|
75
|
+
*/
|
|
76
|
+
getConfig() {
|
|
77
|
+
const config = getEndpointConfig();
|
|
78
|
+
if (!config) {
|
|
79
|
+
throw new Error('Endpoint configuration not set. Call setEndpointConfig() first.');
|
|
80
|
+
}
|
|
81
|
+
if (!config.endpoints.interrupts) {
|
|
82
|
+
throw new Error('Interrupt endpoints not configured.');
|
|
83
|
+
}
|
|
84
|
+
return config;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Generic API request helper
|
|
88
|
+
*
|
|
89
|
+
* @param url - The URL to fetch
|
|
90
|
+
* @param options - Fetch options
|
|
91
|
+
* @returns The parsed JSON response
|
|
92
|
+
*/
|
|
93
|
+
async request(url, options = {}) {
|
|
94
|
+
const config = this.getConfig();
|
|
95
|
+
const headers = getEndpointHeaders(config, 'interrupts');
|
|
96
|
+
const response = await fetch(url, {
|
|
97
|
+
...options,
|
|
98
|
+
headers: {
|
|
99
|
+
...headers,
|
|
100
|
+
...options.headers
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const errorData = await response.json().catch(() => ({}));
|
|
105
|
+
const errorMessage = errorData.error ||
|
|
106
|
+
errorData.message ||
|
|
107
|
+
`HTTP ${response.status}: ${response.statusText}`;
|
|
108
|
+
throw new Error(errorMessage);
|
|
109
|
+
}
|
|
110
|
+
return response.json();
|
|
111
|
+
}
|
|
112
|
+
// =========================================================================
|
|
113
|
+
// Interrupt Operations
|
|
114
|
+
// =========================================================================
|
|
115
|
+
/**
|
|
116
|
+
* Get interrupt details by ID
|
|
117
|
+
*
|
|
118
|
+
* @param interruptId - The interrupt UUID
|
|
119
|
+
* @returns The interrupt details
|
|
120
|
+
*/
|
|
121
|
+
async getInterrupt(interruptId) {
|
|
122
|
+
const config = this.getConfig();
|
|
123
|
+
const url = buildEndpointUrl(config, config.endpoints.interrupts.get, {
|
|
124
|
+
interruptId
|
|
125
|
+
});
|
|
126
|
+
const response = await this.request(url);
|
|
127
|
+
if (!response.data) {
|
|
128
|
+
throw new Error('Interrupt not found');
|
|
129
|
+
}
|
|
130
|
+
return response.data;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resolve an interrupt with user response
|
|
134
|
+
*
|
|
135
|
+
* @param interruptId - The interrupt UUID
|
|
136
|
+
* @param value - The user's response value
|
|
137
|
+
* @returns The updated interrupt
|
|
138
|
+
*/
|
|
139
|
+
async resolveInterrupt(interruptId, value) {
|
|
140
|
+
const config = this.getConfig();
|
|
141
|
+
const url = buildEndpointUrl(config, config.endpoints.interrupts.resolve, {
|
|
142
|
+
interruptId
|
|
143
|
+
});
|
|
144
|
+
const resolution = { value };
|
|
145
|
+
const response = await this.request(url, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
body: JSON.stringify(resolution)
|
|
148
|
+
});
|
|
149
|
+
if (!response.data) {
|
|
150
|
+
throw new Error('Failed to resolve interrupt: No data returned');
|
|
151
|
+
}
|
|
152
|
+
return response.data;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Cancel a pending interrupt
|
|
156
|
+
*
|
|
157
|
+
* @param interruptId - The interrupt UUID
|
|
158
|
+
* @returns The updated interrupt
|
|
159
|
+
*/
|
|
160
|
+
async cancelInterrupt(interruptId) {
|
|
161
|
+
const config = this.getConfig();
|
|
162
|
+
const url = buildEndpointUrl(config, config.endpoints.interrupts.cancel, {
|
|
163
|
+
interruptId
|
|
164
|
+
});
|
|
165
|
+
const response = await this.request(url, {
|
|
166
|
+
method: 'POST'
|
|
167
|
+
});
|
|
168
|
+
if (!response.data) {
|
|
169
|
+
throw new Error('Failed to cancel interrupt: No data returned');
|
|
170
|
+
}
|
|
171
|
+
return response.data;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* List interrupts for a playground session
|
|
175
|
+
*
|
|
176
|
+
* @param sessionId - The session UUID
|
|
177
|
+
* @returns Array of interrupts for the session
|
|
178
|
+
*/
|
|
179
|
+
async listSessionInterrupts(sessionId) {
|
|
180
|
+
const config = this.getConfig();
|
|
181
|
+
const url = buildEndpointUrl(config, config.endpoints.interrupts.listBySession, {
|
|
182
|
+
sessionId
|
|
183
|
+
});
|
|
184
|
+
const response = await this.request(url);
|
|
185
|
+
return response.data ?? [];
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* List interrupts for a pipeline
|
|
189
|
+
*
|
|
190
|
+
* @param pipelineId - The pipeline UUID
|
|
191
|
+
* @returns Array of interrupts for the pipeline
|
|
192
|
+
*/
|
|
193
|
+
async listPipelineInterrupts(pipelineId) {
|
|
194
|
+
const config = this.getConfig();
|
|
195
|
+
const url = buildEndpointUrl(config, config.endpoints.interrupts.listByPipeline, {
|
|
196
|
+
pipelineId
|
|
197
|
+
});
|
|
198
|
+
const response = await this.request(url);
|
|
199
|
+
return response.data ?? [];
|
|
200
|
+
}
|
|
201
|
+
// =========================================================================
|
|
202
|
+
// Polling (Optional)
|
|
203
|
+
// =========================================================================
|
|
204
|
+
/**
|
|
205
|
+
* Start polling for interrupts in a session
|
|
206
|
+
*
|
|
207
|
+
* This is optional - interrupts are typically detected via message metadata.
|
|
208
|
+
* Use this for scenarios where dedicated polling is preferred.
|
|
209
|
+
*
|
|
210
|
+
* @param sessionId - The session UUID to poll
|
|
211
|
+
* @param callback - Callback function to handle new interrupts
|
|
212
|
+
*/
|
|
213
|
+
startPolling(sessionId, callback) {
|
|
214
|
+
if (!this.pollingConfig.enabled) {
|
|
215
|
+
console.warn('[InterruptService] Polling is disabled. Enable via setPollingConfig().');
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
// Stop any existing polling
|
|
219
|
+
this.stopPolling();
|
|
220
|
+
this.pollingSessionId = sessionId;
|
|
221
|
+
this.currentBackoff = this.pollingConfig.interval ?? 2000;
|
|
222
|
+
const poll = async () => {
|
|
223
|
+
if (this.pollingSessionId !== sessionId) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const interrupts = await this.listSessionInterrupts(sessionId);
|
|
228
|
+
const pendingInterrupts = interrupts.filter((i) => i.status === 'pending');
|
|
229
|
+
// Reset backoff on successful request
|
|
230
|
+
this.currentBackoff = this.pollingConfig.interval ?? 2000;
|
|
231
|
+
// Call the callback with pending interrupts
|
|
232
|
+
callback(pendingInterrupts);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
console.error('[InterruptService] Polling error:', error);
|
|
236
|
+
// Exponential backoff on error
|
|
237
|
+
const maxBackoff = this.pollingConfig.maxBackoff ?? 10000;
|
|
238
|
+
this.currentBackoff = Math.min(this.currentBackoff * 2, maxBackoff);
|
|
239
|
+
}
|
|
240
|
+
// Schedule next poll
|
|
241
|
+
if (this.pollingSessionId === sessionId) {
|
|
242
|
+
this.pollingInterval = setTimeout(poll, this.currentBackoff);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
// Start polling immediately
|
|
246
|
+
void poll();
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Stop polling for interrupts
|
|
250
|
+
*/
|
|
251
|
+
stopPolling() {
|
|
252
|
+
if (this.pollingInterval) {
|
|
253
|
+
clearTimeout(this.pollingInterval);
|
|
254
|
+
this.pollingInterval = null;
|
|
255
|
+
}
|
|
256
|
+
this.pollingSessionId = null;
|
|
257
|
+
this.currentBackoff = this.pollingConfig.interval ?? 2000;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Check if polling is active
|
|
261
|
+
*
|
|
262
|
+
* @returns True if polling is active
|
|
263
|
+
*/
|
|
264
|
+
isPolling() {
|
|
265
|
+
return this.pollingSessionId !== null;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Get the current polling session ID
|
|
269
|
+
*
|
|
270
|
+
* @returns The session ID being polled, or null
|
|
271
|
+
*/
|
|
272
|
+
getPollingSessionId() {
|
|
273
|
+
return this.pollingSessionId;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Export singleton instance
|
|
278
|
+
*/
|
|
279
|
+
export const interruptService = InterruptService.getInstance();
|