@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.
Files changed (49) hide show
  1. package/dist/components/NodeSidebar.svelte +1 -0
  2. package/dist/components/form/FormCodeEditor.svelte +6 -1
  3. package/dist/components/interrupt/ChoicePrompt.svelte +389 -0
  4. package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +21 -0
  5. package/dist/components/interrupt/ConfirmationPrompt.svelte +280 -0
  6. package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +23 -0
  7. package/dist/components/interrupt/FormPrompt.svelte +223 -0
  8. package/dist/components/interrupt/FormPrompt.svelte.d.ts +21 -0
  9. package/dist/components/interrupt/InterruptBubble.svelte +621 -0
  10. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +16 -0
  11. package/dist/components/interrupt/TextInputPrompt.svelte +333 -0
  12. package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +21 -0
  13. package/dist/components/interrupt/index.d.ts +13 -0
  14. package/dist/components/interrupt/index.js +15 -0
  15. package/dist/components/nodes/GatewayNode.svelte +21 -5
  16. package/dist/components/nodes/IdeaNode.svelte +48 -43
  17. package/dist/components/nodes/IdeaNode.svelte.d.ts +1 -1
  18. package/dist/components/nodes/SimpleNode.svelte +1 -3
  19. package/dist/components/nodes/TerminalNode.svelte +52 -11
  20. package/dist/components/nodes/ToolNode.svelte +22 -7
  21. package/dist/components/nodes/WorkflowNode.svelte +1 -3
  22. package/dist/components/playground/ChatPanel.svelte +131 -7
  23. package/dist/components/playground/ChatPanel.svelte.d.ts +2 -0
  24. package/dist/components/playground/MessageBubble.svelte +1 -3
  25. package/dist/components/playground/Playground.svelte +50 -5
  26. package/dist/components/playground/PlaygroundModal.svelte +8 -7
  27. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -3
  28. package/dist/config/endpoints.d.ts +12 -0
  29. package/dist/config/endpoints.js +7 -0
  30. package/dist/playground/index.d.ts +5 -0
  31. package/dist/playground/index.js +21 -0
  32. package/dist/playground/mount.d.ts +3 -3
  33. package/dist/playground/mount.js +30 -22
  34. package/dist/services/interruptService.d.ts +133 -0
  35. package/dist/services/interruptService.js +279 -0
  36. package/dist/stores/interruptStore.d.ts +200 -0
  37. package/dist/stores/interruptStore.js +424 -0
  38. package/dist/stores/playgroundStore.d.ts +11 -1
  39. package/dist/stores/playgroundStore.js +34 -0
  40. package/dist/styles/base.css +89 -0
  41. package/dist/types/index.d.ts +1 -1
  42. package/dist/types/interrupt.d.ts +303 -0
  43. package/dist/types/interrupt.js +125 -0
  44. package/dist/types/interruptState.d.ts +211 -0
  45. package/dist/types/interruptState.js +308 -0
  46. package/dist/utils/colors.js +1 -0
  47. package/dist/utils/connections.js +2 -2
  48. package/dist/utils/icons.js +1 -0
  49. package/package.json +1 -1
@@ -43,13 +43,13 @@
43
43
  * })(Drupal, window.FlowDrop);
44
44
  * ```
45
45
  */
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";
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 = "standalone", initialSessionId, endpointConfig, config = {}, height = "100%", width = "100%", onClose } = options;
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("workflowId is required for mountPlayground()");
86
+ throw new Error('workflowId is required for mountPlayground()');
87
87
  }
88
88
  if (!container) {
89
- throw new Error("container element is required for mountPlayground()");
89
+ throw new Error('container element is required for mountPlayground()');
90
90
  }
91
91
  // Validate onClose for modal mode
92
- if (mode === "modal" && !onClose) {
93
- throw new Error("onClose callback is required for modal mode");
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("../config/endpoints.js");
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 === "modal") {
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
- const svelteApp = mount(mode === "modal" ? PlaygroundModal : Playground, {
126
- target: targetContainer,
127
- props: mode === "modal"
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 === "modal" ? container : undefined,
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 === "function") {
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();