@marktoflow/gui 2.0.0-alpha.5 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -180
- package/dist/client/assets/index-DQeR1ew6.css +1 -0
- package/dist/client/assets/index-LbIVPHbD.js +833 -0
- package/dist/client/assets/index-LbIVPHbD.js.map +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/marktoflow-logo.png +0 -0
- package/dist/server/index.js +31 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/admin.js +95 -0
- package/dist/server/routes/admin.js.map +1 -0
- package/dist/server/routes/ai.js +2 -2
- package/dist/server/routes/ai.js.map +1 -1
- package/dist/server/routes/collaboration.js +104 -0
- package/dist/server/routes/collaboration.js.map +1 -0
- package/dist/server/routes/execute.js +181 -14
- package/dist/server/routes/execute.js.map +1 -1
- package/dist/server/routes/form.js +160 -0
- package/dist/server/routes/form.js.map +1 -0
- package/dist/server/routes/settings.js +90 -0
- package/dist/server/routes/settings.js.map +1 -0
- package/dist/server/routes/templates.js +106 -0
- package/dist/server/routes/templates.js.map +1 -0
- package/dist/server/routes/versions.js +101 -0
- package/dist/server/routes/versions.js.map +1 -0
- package/dist/server/services/AIService.js +85 -2
- package/dist/server/services/AIService.js.map +1 -1
- package/dist/server/services/ExecutionManager.js +571 -0
- package/dist/server/services/ExecutionManager.js.map +1 -0
- package/dist/server/services/VersionService.js +65 -0
- package/dist/server/services/VersionService.js.map +1 -0
- package/dist/server/services/WorkflowService.js +8 -2
- package/dist/server/services/WorkflowService.js.map +1 -1
- package/dist/server/services/agents/copilot-provider.js +32 -0
- package/dist/server/services/agents/copilot-provider.js.map +1 -1
- package/dist/server/websocket/index.js +42 -0
- package/dist/server/websocket/index.js.map +1 -1
- package/dist/shared/constants.js +9 -0
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/settings.js +51 -0
- package/dist/shared/settings.js.map +1 -0
- package/package.json +14 -10
- package/public/marktoflow-logo.png +0 -0
- package/tests/integration/fixtures/test-workflow.md +6 -0
- package/.turbo/turbo-build.log +0 -42
- package/dist/client/assets/index-CM44OayM.js +0 -704
- package/dist/client/assets/index-CM44OayM.js.map +0 -1
- package/dist/client/assets/index-Dru63gi6.css +0 -1
- package/marktoflow-gui-2.0.0-alpha.5.tgz +0 -0
- package/playwright.config.ts +0 -27
- package/postcss.config.js +0 -6
- package/src/client/App.tsx +0 -520
- package/src/client/components/Canvas/Canvas.tsx +0 -425
- package/src/client/components/Canvas/ExecutionOverlay.tsx +0 -935
- package/src/client/components/Canvas/ForEachNode.tsx +0 -152
- package/src/client/components/Canvas/IfElseNode.tsx +0 -141
- package/src/client/components/Canvas/NodeContextMenu.tsx +0 -192
- package/src/client/components/Canvas/OutputNode.tsx +0 -111
- package/src/client/components/Canvas/ParallelNode.tsx +0 -157
- package/src/client/components/Canvas/StepNode.tsx +0 -106
- package/src/client/components/Canvas/SubWorkflowNode.tsx +0 -141
- package/src/client/components/Canvas/SwitchNode.tsx +0 -185
- package/src/client/components/Canvas/Toolbar.tsx +0 -227
- package/src/client/components/Canvas/TransformNode.tsx +0 -194
- package/src/client/components/Canvas/TriggerNode.tsx +0 -128
- package/src/client/components/Canvas/TryCatchNode.tsx +0 -164
- package/src/client/components/Canvas/WhileNode.tsx +0 -161
- package/src/client/components/Canvas/index.ts +0 -24
- package/src/client/components/Debug/VariableInspector.tsx +0 -148
- package/src/client/components/Editor/InputsEditor.tsx +0 -458
- package/src/client/components/Editor/NewStepWizard.tsx +0 -344
- package/src/client/components/Editor/StepEditor.tsx +0 -532
- package/src/client/components/Editor/YamlEditor.tsx +0 -160
- package/src/client/components/Panels/PropertiesPanel.tsx +0 -589
- package/src/client/components/Prompt/ChangePreview.tsx +0 -281
- package/src/client/components/Prompt/PromptHistoryPanel.tsx +0 -209
- package/src/client/components/Prompt/PromptInput.tsx +0 -110
- package/src/client/components/Settings/ProviderSwitcher.tsx +0 -228
- package/src/client/components/Sidebar/ImportDialog.tsx +0 -257
- package/src/client/components/Sidebar/Sidebar.tsx +0 -362
- package/src/client/components/common/Breadcrumb.tsx +0 -40
- package/src/client/components/common/Button.tsx +0 -68
- package/src/client/components/common/ContextMenu.tsx +0 -202
- package/src/client/components/common/KeyboardShortcuts.tsx +0 -149
- package/src/client/components/common/Modal.tsx +0 -93
- package/src/client/components/common/Tabs.tsx +0 -57
- package/src/client/components/common/ThemeToggle.tsx +0 -63
- package/src/client/components/index.ts +0 -32
- package/src/client/hooks/index.ts +0 -4
- package/src/client/hooks/useAIPrompt.ts +0 -108
- package/src/client/hooks/useCanvas.ts +0 -247
- package/src/client/hooks/useWebSocket.ts +0 -164
- package/src/client/hooks/useWorkflow.ts +0 -138
- package/src/client/main.tsx +0 -10
- package/src/client/stores/agentStore.ts +0 -109
- package/src/client/stores/canvasStore.ts +0 -348
- package/src/client/stores/editorStore.ts +0 -133
- package/src/client/stores/executionStore.ts +0 -502
- package/src/client/stores/index.ts +0 -4
- package/src/client/stores/layoutStore.ts +0 -103
- package/src/client/stores/navigationStore.ts +0 -49
- package/src/client/stores/promptStore.ts +0 -113
- package/src/client/stores/themeStore.ts +0 -75
- package/src/client/stores/workflowStore.ts +0 -185
- package/src/client/styles/globals.css +0 -452
- package/src/client/utils/cn.ts +0 -9
- package/src/client/utils/index.ts +0 -4
- package/src/client/utils/platform.ts +0 -46
- package/src/client/utils/serviceIcons.tsx +0 -97
- package/src/client/utils/stepValidation.ts +0 -155
- package/src/client/utils/workflowToGraph.ts +0 -523
- package/src/server/index.ts +0 -137
- package/src/server/routes/ai.ts +0 -91
- package/src/server/routes/execute.ts +0 -71
- package/src/server/routes/executions.ts +0 -136
- package/src/server/routes/tools.ts +0 -970
- package/src/server/routes/workflows.ts +0 -147
- package/src/server/services/AIService.ts +0 -105
- package/src/server/services/FileWatcher.ts +0 -69
- package/src/server/services/WorkflowService.ts +0 -601
- package/src/server/services/agents/claude-code-provider.ts +0 -320
- package/src/server/services/agents/claude-provider.ts +0 -248
- package/src/server/services/agents/codex-provider.ts +0 -398
- package/src/server/services/agents/copilot-provider.ts +0 -311
- package/src/server/services/agents/demo-provider.ts +0 -184
- package/src/server/services/agents/index.ts +0 -31
- package/src/server/services/agents/ollama-provider.ts +0 -267
- package/src/server/services/agents/prompts.ts +0 -509
- package/src/server/services/agents/registry.ts +0 -310
- package/src/server/services/agents/types.ts +0 -146
- package/src/server/websocket/index.ts +0 -117
- package/src/shared/constants.ts +0 -180
- package/src/shared/types.ts +0 -179
- package/tailwind.config.ts +0 -73
- package/tests/e2e/app.spec.ts +0 -90
- package/tests/e2e/canvas.spec.ts +0 -128
- package/tests/e2e/workflow.spec.ts +0 -185
- package/tests/integration/api.test.ts +0 -452
- package/tests/integration/testApp.ts +0 -31
- package/tests/setup.ts +0 -72
- package/tests/unit/ForEachNode.test.tsx +0 -308
- package/tests/unit/IfElseNode.test.tsx +0 -235
- package/tests/unit/ParallelNode.test.tsx +0 -344
- package/tests/unit/SwitchNode.test.tsx +0 -327
- package/tests/unit/TransformNode.test.tsx +0 -386
- package/tests/unit/TryCatchNode.test.tsx +0 -243
- package/tests/unit/WhileNode.test.tsx +0 -230
- package/tests/unit/agentStore.test.ts +0 -218
- package/tests/unit/canvasStore.test.ts +0 -502
- package/tests/unit/codexProvider.test.ts +0 -399
- package/tests/unit/components.test.tsx +0 -151
- package/tests/unit/executionStore.test.ts +0 -567
- package/tests/unit/layoutStore.test.ts +0 -194
- package/tests/unit/navigationStore.test.ts +0 -152
- package/tests/unit/platform.test.ts +0 -118
- package/tests/unit/serviceIcons.test.ts +0 -197
- package/tests/unit/stepValidation.test.ts +0 -226
- package/tests/unit/themeStore.test.ts +0 -141
- package/tests/unit/workflowToGraph.test.ts +0 -311
- package/tsconfig.json +0 -29
- package/tsconfig.server.json +0 -28
- package/vite.config.ts +0 -31
- package/vitest.config.ts +0 -26
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import { useCallback, useMemo } from 'react';
|
|
2
|
-
import { useReactFlow, type Node, type Edge } from '@xyflow/react';
|
|
3
|
-
import { useCanvasStore } from '../stores/canvasStore';
|
|
4
|
-
import dagre from 'dagre';
|
|
5
|
-
|
|
6
|
-
export function useCanvas() {
|
|
7
|
-
const {
|
|
8
|
-
nodes,
|
|
9
|
-
edges,
|
|
10
|
-
setNodes,
|
|
11
|
-
setEdges,
|
|
12
|
-
updateNodeData,
|
|
13
|
-
clearCanvas,
|
|
14
|
-
} = useCanvasStore();
|
|
15
|
-
|
|
16
|
-
const reactFlowInstance = useReactFlow();
|
|
17
|
-
|
|
18
|
-
// Get selected nodes
|
|
19
|
-
const selectedNodes = useMemo(
|
|
20
|
-
() => nodes.filter((node) => node.selected),
|
|
21
|
-
[nodes]
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
// Get selected edges
|
|
25
|
-
const selectedEdges = useMemo(
|
|
26
|
-
() => edges.filter((edge) => edge.selected),
|
|
27
|
-
[edges]
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
// Get node by ID
|
|
31
|
-
const getNode = useCallback(
|
|
32
|
-
(id: string): Node | undefined => {
|
|
33
|
-
return nodes.find((node) => node.id === id);
|
|
34
|
-
},
|
|
35
|
-
[nodes]
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// Get edge by ID
|
|
39
|
-
const getEdge = useCallback(
|
|
40
|
-
(id: string): Edge | undefined => {
|
|
41
|
-
return edges.find((edge) => edge.id === id);
|
|
42
|
-
},
|
|
43
|
-
[edges]
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
// Add a new node
|
|
47
|
-
const addNode = useCallback(
|
|
48
|
-
(node: Node) => {
|
|
49
|
-
setNodes([...nodes, node]);
|
|
50
|
-
},
|
|
51
|
-
[nodes, setNodes]
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
// Remove a node
|
|
55
|
-
const removeNode = useCallback(
|
|
56
|
-
(id: string) => {
|
|
57
|
-
setNodes(nodes.filter((node) => node.id !== id));
|
|
58
|
-
// Also remove connected edges
|
|
59
|
-
setEdges(edges.filter((edge) => edge.source !== id && edge.target !== id));
|
|
60
|
-
},
|
|
61
|
-
[nodes, edges, setNodes, setEdges]
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
// Duplicate selected nodes
|
|
65
|
-
const duplicateSelected = useCallback(() => {
|
|
66
|
-
const newNodes: Node[] = [];
|
|
67
|
-
const idMap = new Map<string, string>();
|
|
68
|
-
|
|
69
|
-
for (const node of selectedNodes) {
|
|
70
|
-
const newId = `${node.id}-copy-${Date.now()}`;
|
|
71
|
-
idMap.set(node.id, newId);
|
|
72
|
-
|
|
73
|
-
newNodes.push({
|
|
74
|
-
...node,
|
|
75
|
-
id: newId,
|
|
76
|
-
position: {
|
|
77
|
-
x: node.position.x + 50,
|
|
78
|
-
y: node.position.y + 50,
|
|
79
|
-
},
|
|
80
|
-
selected: false,
|
|
81
|
-
data: {
|
|
82
|
-
...node.data,
|
|
83
|
-
id: newId,
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
setNodes([...nodes, ...newNodes]);
|
|
89
|
-
|
|
90
|
-
// Duplicate edges between selected nodes
|
|
91
|
-
const newEdges: Edge[] = [];
|
|
92
|
-
for (const edge of edges) {
|
|
93
|
-
if (idMap.has(edge.source) && idMap.has(edge.target)) {
|
|
94
|
-
newEdges.push({
|
|
95
|
-
...edge,
|
|
96
|
-
id: `${edge.id}-copy-${Date.now()}`,
|
|
97
|
-
source: idMap.get(edge.source)!,
|
|
98
|
-
target: idMap.get(edge.target)!,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (newEdges.length > 0) {
|
|
104
|
-
setEdges([...edges, ...newEdges]);
|
|
105
|
-
}
|
|
106
|
-
}, [selectedNodes, nodes, edges, setNodes, setEdges]);
|
|
107
|
-
|
|
108
|
-
// Delete selected nodes and edges
|
|
109
|
-
const deleteSelected = useCallback(() => {
|
|
110
|
-
const selectedNodeIds = new Set(selectedNodes.map((n) => n.id));
|
|
111
|
-
const selectedEdgeIds = new Set(selectedEdges.map((e) => e.id));
|
|
112
|
-
|
|
113
|
-
setNodes(nodes.filter((node) => !selectedNodeIds.has(node.id)));
|
|
114
|
-
setEdges(
|
|
115
|
-
edges.filter(
|
|
116
|
-
(edge) =>
|
|
117
|
-
!selectedEdgeIds.has(edge.id) &&
|
|
118
|
-
!selectedNodeIds.has(edge.source) &&
|
|
119
|
-
!selectedNodeIds.has(edge.target)
|
|
120
|
-
)
|
|
121
|
-
);
|
|
122
|
-
}, [selectedNodes, selectedEdges, nodes, edges, setNodes, setEdges]);
|
|
123
|
-
|
|
124
|
-
// Auto-layout using dagre
|
|
125
|
-
const autoLayout = useCallback(() => {
|
|
126
|
-
const dagreGraph = new dagre.graphlib.Graph();
|
|
127
|
-
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
128
|
-
dagreGraph.setGraph({ rankdir: 'TB', nodesep: 50, ranksep: 80 });
|
|
129
|
-
|
|
130
|
-
// Add nodes
|
|
131
|
-
for (const node of nodes) {
|
|
132
|
-
dagreGraph.setNode(node.id, { width: 200, height: 100 });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Add edges
|
|
136
|
-
for (const edge of edges) {
|
|
137
|
-
dagreGraph.setEdge(edge.source, edge.target);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Run layout
|
|
141
|
-
dagre.layout(dagreGraph);
|
|
142
|
-
|
|
143
|
-
// Update node positions
|
|
144
|
-
const layoutedNodes = nodes.map((node) => {
|
|
145
|
-
const nodeWithPosition = dagreGraph.node(node.id);
|
|
146
|
-
return {
|
|
147
|
-
...node,
|
|
148
|
-
position: {
|
|
149
|
-
x: nodeWithPosition.x - 100,
|
|
150
|
-
y: nodeWithPosition.y - 50,
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
setNodes(layoutedNodes);
|
|
156
|
-
|
|
157
|
-
// Fit view after layout
|
|
158
|
-
setTimeout(() => {
|
|
159
|
-
reactFlowInstance.fitView({ padding: 0.2 });
|
|
160
|
-
}, 50);
|
|
161
|
-
}, [nodes, edges, setNodes, reactFlowInstance]);
|
|
162
|
-
|
|
163
|
-
// Fit view
|
|
164
|
-
const fitView = useCallback(() => {
|
|
165
|
-
reactFlowInstance.fitView({ padding: 0.2 });
|
|
166
|
-
}, [reactFlowInstance]);
|
|
167
|
-
|
|
168
|
-
// Zoom to node
|
|
169
|
-
const zoomToNode = useCallback(
|
|
170
|
-
(nodeId: string) => {
|
|
171
|
-
const node = getNode(nodeId);
|
|
172
|
-
if (node) {
|
|
173
|
-
reactFlowInstance.setCenter(node.position.x + 100, node.position.y + 50, {
|
|
174
|
-
zoom: 1.5,
|
|
175
|
-
duration: 300,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
[getNode, reactFlowInstance]
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
// Select node
|
|
183
|
-
const selectNode = useCallback(
|
|
184
|
-
(nodeId: string, addToSelection = false) => {
|
|
185
|
-
setNodes(
|
|
186
|
-
nodes.map((node) => ({
|
|
187
|
-
...node,
|
|
188
|
-
selected: addToSelection
|
|
189
|
-
? node.selected || node.id === nodeId
|
|
190
|
-
: node.id === nodeId,
|
|
191
|
-
}))
|
|
192
|
-
);
|
|
193
|
-
},
|
|
194
|
-
[nodes, setNodes]
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
// Clear selection
|
|
198
|
-
const clearSelection = useCallback(() => {
|
|
199
|
-
setNodes(nodes.map((node) => ({ ...node, selected: false })));
|
|
200
|
-
setEdges(edges.map((edge) => ({ ...edge, selected: false })));
|
|
201
|
-
}, [nodes, edges, setNodes, setEdges]);
|
|
202
|
-
|
|
203
|
-
// Undo/redo and copy/paste
|
|
204
|
-
const { undo, redo, canUndo, canRedo, copySelected, paste, canPaste } = useCanvasStore();
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
// State
|
|
208
|
-
nodes,
|
|
209
|
-
edges,
|
|
210
|
-
selectedNodes,
|
|
211
|
-
selectedEdges,
|
|
212
|
-
|
|
213
|
-
// Queries
|
|
214
|
-
getNode,
|
|
215
|
-
getEdge,
|
|
216
|
-
|
|
217
|
-
// Mutations
|
|
218
|
-
addNode,
|
|
219
|
-
removeNode,
|
|
220
|
-
updateNodeData,
|
|
221
|
-
setNodes,
|
|
222
|
-
setEdges,
|
|
223
|
-
clearCanvas,
|
|
224
|
-
|
|
225
|
-
// Selection
|
|
226
|
-
selectNode,
|
|
227
|
-
clearSelection,
|
|
228
|
-
duplicateSelected,
|
|
229
|
-
deleteSelected,
|
|
230
|
-
|
|
231
|
-
// Layout
|
|
232
|
-
autoLayout,
|
|
233
|
-
fitView,
|
|
234
|
-
zoomToNode,
|
|
235
|
-
|
|
236
|
-
// History
|
|
237
|
-
undo,
|
|
238
|
-
redo,
|
|
239
|
-
canUndo,
|
|
240
|
-
canRedo,
|
|
241
|
-
|
|
242
|
-
// Clipboard
|
|
243
|
-
copySelected,
|
|
244
|
-
paste,
|
|
245
|
-
canPaste,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useCallback, useState } from 'react';
|
|
2
|
-
import { io, Socket } from 'socket.io-client';
|
|
3
|
-
import { WS_EVENTS } from '@shared/constants';
|
|
4
|
-
import type {
|
|
5
|
-
WorkflowUpdatedEvent,
|
|
6
|
-
ExecutionStepEvent,
|
|
7
|
-
ExecutionCompletedEvent,
|
|
8
|
-
} from '@shared/types';
|
|
9
|
-
|
|
10
|
-
interface UseWebSocketOptions {
|
|
11
|
-
autoConnect?: boolean;
|
|
12
|
-
onWorkflowUpdated?: (event: WorkflowUpdatedEvent) => void;
|
|
13
|
-
onExecutionStep?: (event: ExecutionStepEvent) => void;
|
|
14
|
-
onExecutionCompleted?: (event: ExecutionCompletedEvent) => void;
|
|
15
|
-
onAIProcessing?: (processing: boolean) => void;
|
|
16
|
-
onAIResponse?: (response: any) => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface UseWebSocketReturn {
|
|
20
|
-
connected: boolean;
|
|
21
|
-
socket: Socket | null;
|
|
22
|
-
subscribeToWorkflow: (path: string) => void;
|
|
23
|
-
unsubscribeFromWorkflow: (path: string) => void;
|
|
24
|
-
subscribeToExecution: (runId: string) => void;
|
|
25
|
-
unsubscribeFromExecution: (runId: string) => void;
|
|
26
|
-
connect: () => void;
|
|
27
|
-
disconnect: () => void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function useWebSocket(
|
|
31
|
-
options: UseWebSocketOptions = {}
|
|
32
|
-
): UseWebSocketReturn {
|
|
33
|
-
const {
|
|
34
|
-
autoConnect = true,
|
|
35
|
-
onWorkflowUpdated,
|
|
36
|
-
onExecutionStep,
|
|
37
|
-
onExecutionCompleted,
|
|
38
|
-
onAIProcessing,
|
|
39
|
-
onAIResponse,
|
|
40
|
-
} = options;
|
|
41
|
-
|
|
42
|
-
const socketRef = useRef<Socket | null>(null);
|
|
43
|
-
const [connected, setConnected] = useState(false);
|
|
44
|
-
|
|
45
|
-
// Store callbacks in refs to avoid reconnection on callback changes
|
|
46
|
-
const callbacksRef = useRef({
|
|
47
|
-
onWorkflowUpdated,
|
|
48
|
-
onExecutionStep,
|
|
49
|
-
onExecutionCompleted,
|
|
50
|
-
onAIProcessing,
|
|
51
|
-
onAIResponse,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
callbacksRef.current = {
|
|
56
|
-
onWorkflowUpdated,
|
|
57
|
-
onExecutionStep,
|
|
58
|
-
onExecutionCompleted,
|
|
59
|
-
onAIProcessing,
|
|
60
|
-
onAIResponse,
|
|
61
|
-
};
|
|
62
|
-
}, [
|
|
63
|
-
onWorkflowUpdated,
|
|
64
|
-
onExecutionStep,
|
|
65
|
-
onExecutionCompleted,
|
|
66
|
-
onAIProcessing,
|
|
67
|
-
onAIResponse,
|
|
68
|
-
]);
|
|
69
|
-
|
|
70
|
-
const connect = useCallback(() => {
|
|
71
|
-
if (socketRef.current?.connected) return;
|
|
72
|
-
|
|
73
|
-
const socket = io({
|
|
74
|
-
transports: ['websocket'],
|
|
75
|
-
reconnection: true,
|
|
76
|
-
reconnectionAttempts: 5,
|
|
77
|
-
reconnectionDelay: 1000,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
socket.on('connect', () => {
|
|
81
|
-
console.log('WebSocket connected');
|
|
82
|
-
setConnected(true);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
socket.on('disconnect', () => {
|
|
86
|
-
console.log('WebSocket disconnected');
|
|
87
|
-
setConnected(false);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
socket.on('connect_error', (error) => {
|
|
91
|
-
console.error('WebSocket connection error:', error);
|
|
92
|
-
setConnected(false);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Event handlers
|
|
96
|
-
socket.on(WS_EVENTS.WORKFLOW_UPDATED, (event: WorkflowUpdatedEvent) => {
|
|
97
|
-
callbacksRef.current.onWorkflowUpdated?.(event);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
socket.on(WS_EVENTS.EXECUTION_STEP, (event: ExecutionStepEvent) => {
|
|
101
|
-
callbacksRef.current.onExecutionStep?.(event);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
socket.on(WS_EVENTS.EXECUTION_COMPLETED, (event: ExecutionCompletedEvent) => {
|
|
105
|
-
callbacksRef.current.onExecutionCompleted?.(event);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
socket.on(WS_EVENTS.AI_PROCESSING, ({ processing }: { processing: boolean }) => {
|
|
109
|
-
callbacksRef.current.onAIProcessing?.(processing);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
socket.on(WS_EVENTS.AI_RESPONSE, (response: any) => {
|
|
113
|
-
callbacksRef.current.onAIResponse?.(response);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
socketRef.current = socket;
|
|
117
|
-
}, []);
|
|
118
|
-
|
|
119
|
-
const disconnect = useCallback(() => {
|
|
120
|
-
if (socketRef.current) {
|
|
121
|
-
socketRef.current.disconnect();
|
|
122
|
-
socketRef.current = null;
|
|
123
|
-
setConnected(false);
|
|
124
|
-
}
|
|
125
|
-
}, []);
|
|
126
|
-
|
|
127
|
-
const subscribeToWorkflow = useCallback((path: string) => {
|
|
128
|
-
socketRef.current?.emit(WS_EVENTS.WORKFLOW_SUBSCRIBE, path);
|
|
129
|
-
}, []);
|
|
130
|
-
|
|
131
|
-
const unsubscribeFromWorkflow = useCallback((path: string) => {
|
|
132
|
-
socketRef.current?.emit(WS_EVENTS.WORKFLOW_UNSUBSCRIBE, path);
|
|
133
|
-
}, []);
|
|
134
|
-
|
|
135
|
-
const subscribeToExecution = useCallback((runId: string) => {
|
|
136
|
-
socketRef.current?.emit(WS_EVENTS.EXECUTION_SUBSCRIBE, runId);
|
|
137
|
-
}, []);
|
|
138
|
-
|
|
139
|
-
const unsubscribeFromExecution = useCallback((runId: string) => {
|
|
140
|
-
socketRef.current?.emit(WS_EVENTS.EXECUTION_UNSUBSCRIBE, runId);
|
|
141
|
-
}, []);
|
|
142
|
-
|
|
143
|
-
// Auto-connect on mount
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
if (autoConnect) {
|
|
146
|
-
connect();
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return () => {
|
|
150
|
-
disconnect();
|
|
151
|
-
};
|
|
152
|
-
}, [autoConnect, connect, disconnect]);
|
|
153
|
-
|
|
154
|
-
return {
|
|
155
|
-
connected,
|
|
156
|
-
socket: socketRef.current,
|
|
157
|
-
subscribeToWorkflow,
|
|
158
|
-
unsubscribeFromWorkflow,
|
|
159
|
-
subscribeToExecution,
|
|
160
|
-
unsubscribeFromExecution,
|
|
161
|
-
connect,
|
|
162
|
-
disconnect,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect } from 'react';
|
|
2
|
-
import { useWorkflowStore } from '../stores/workflowStore';
|
|
3
|
-
import { useCanvasStore } from '../stores/canvasStore';
|
|
4
|
-
import { useWebSocket } from './useWebSocket';
|
|
5
|
-
import { workflowToGraph } from '../utils/workflowToGraph';
|
|
6
|
-
|
|
7
|
-
export function useWorkflow() {
|
|
8
|
-
const {
|
|
9
|
-
workflows,
|
|
10
|
-
selectedWorkflow,
|
|
11
|
-
currentWorkflow,
|
|
12
|
-
isLoading,
|
|
13
|
-
error,
|
|
14
|
-
loadWorkflows,
|
|
15
|
-
selectWorkflow,
|
|
16
|
-
loadWorkflow,
|
|
17
|
-
saveWorkflow,
|
|
18
|
-
createWorkflow,
|
|
19
|
-
deleteWorkflow,
|
|
20
|
-
} = useWorkflowStore();
|
|
21
|
-
|
|
22
|
-
const { setNodes, setEdges } = useCanvasStore();
|
|
23
|
-
|
|
24
|
-
// Subscribe to workflow updates via WebSocket
|
|
25
|
-
const { subscribeToWorkflow, unsubscribeFromWorkflow, connected } = useWebSocket({
|
|
26
|
-
onWorkflowUpdated: (event) => {
|
|
27
|
-
if (event.path === selectedWorkflow) {
|
|
28
|
-
// Reload workflow when it changes
|
|
29
|
-
loadWorkflow(event.path);
|
|
30
|
-
}
|
|
31
|
-
// Refresh workflow list for add/remove events
|
|
32
|
-
if (event.event === 'add' || event.event === 'remove') {
|
|
33
|
-
loadWorkflows();
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Load workflows on mount
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
loadWorkflows();
|
|
41
|
-
}, [loadWorkflows]);
|
|
42
|
-
|
|
43
|
-
// Subscribe to selected workflow
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
if (connected && selectedWorkflow) {
|
|
46
|
-
subscribeToWorkflow(selectedWorkflow);
|
|
47
|
-
return () => {
|
|
48
|
-
unsubscribeFromWorkflow(selectedWorkflow);
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
}, [connected, selectedWorkflow, subscribeToWorkflow, unsubscribeFromWorkflow]);
|
|
52
|
-
|
|
53
|
-
// Update canvas when workflow changes
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (currentWorkflow) {
|
|
56
|
-
const { nodes, edges } = workflowToGraph(currentWorkflow as any);
|
|
57
|
-
setNodes(nodes);
|
|
58
|
-
setEdges(edges);
|
|
59
|
-
}
|
|
60
|
-
}, [currentWorkflow, setNodes, setEdges]);
|
|
61
|
-
|
|
62
|
-
// Select workflow and update canvas
|
|
63
|
-
const handleSelectWorkflow = useCallback(
|
|
64
|
-
(path: string) => {
|
|
65
|
-
selectWorkflow(path);
|
|
66
|
-
},
|
|
67
|
-
[selectWorkflow]
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
// Save workflow and refresh canvas
|
|
71
|
-
const handleSaveWorkflow = useCallback(
|
|
72
|
-
async (workflow: typeof currentWorkflow) => {
|
|
73
|
-
if (!workflow) return;
|
|
74
|
-
await saveWorkflow(workflow);
|
|
75
|
-
},
|
|
76
|
-
[saveWorkflow]
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
// Create new workflow
|
|
80
|
-
const handleCreateWorkflow = useCallback(
|
|
81
|
-
async (name: string) => {
|
|
82
|
-
await createWorkflow(name);
|
|
83
|
-
},
|
|
84
|
-
[createWorkflow]
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
// Delete workflow
|
|
88
|
-
const handleDeleteWorkflow = useCallback(
|
|
89
|
-
async (path: string) => {
|
|
90
|
-
await deleteWorkflow(path);
|
|
91
|
-
},
|
|
92
|
-
[deleteWorkflow]
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
// Get available variables at a given step index
|
|
96
|
-
const getAvailableVariables = useCallback(
|
|
97
|
-
(stepIndex: number): string[] => {
|
|
98
|
-
if (!currentWorkflow) return [];
|
|
99
|
-
|
|
100
|
-
const variables: string[] = [];
|
|
101
|
-
|
|
102
|
-
// Add input variables
|
|
103
|
-
if (currentWorkflow.inputs) {
|
|
104
|
-
for (const key of Object.keys(currentWorkflow.inputs)) {
|
|
105
|
-
variables.push(`inputs.${key}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Add output variables from previous steps
|
|
110
|
-
for (let i = 0; i < stepIndex && i < currentWorkflow.steps.length; i++) {
|
|
111
|
-
const step = currentWorkflow.steps[i];
|
|
112
|
-
if (step.outputVariable) {
|
|
113
|
-
variables.push(step.outputVariable);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return variables;
|
|
118
|
-
},
|
|
119
|
-
[currentWorkflow]
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
// State
|
|
124
|
-
workflows,
|
|
125
|
-
selectedWorkflow,
|
|
126
|
-
currentWorkflow,
|
|
127
|
-
isLoading,
|
|
128
|
-
error,
|
|
129
|
-
|
|
130
|
-
// Actions
|
|
131
|
-
selectWorkflow: handleSelectWorkflow,
|
|
132
|
-
saveWorkflow: handleSaveWorkflow,
|
|
133
|
-
createWorkflow: handleCreateWorkflow,
|
|
134
|
-
deleteWorkflow: handleDeleteWorkflow,
|
|
135
|
-
refreshWorkflows: loadWorkflows,
|
|
136
|
-
getAvailableVariables,
|
|
137
|
-
};
|
|
138
|
-
}
|
package/src/client/main.tsx
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent/AI Provider Store
|
|
3
|
-
* Manages available AI providers and active provider selection
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { create } from 'zustand';
|
|
7
|
-
|
|
8
|
-
export interface Provider {
|
|
9
|
-
id: string;
|
|
10
|
-
name: string;
|
|
11
|
-
status: 'ready' | 'needs_config' | 'unavailable';
|
|
12
|
-
isActive: boolean;
|
|
13
|
-
description?: string;
|
|
14
|
-
configOptions?: {
|
|
15
|
-
apiKey?: boolean;
|
|
16
|
-
baseUrl?: boolean;
|
|
17
|
-
model?: boolean;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface AgentStatus {
|
|
22
|
-
activeProvider: string | null;
|
|
23
|
-
providers: Provider[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface AgentState {
|
|
27
|
-
providers: Provider[];
|
|
28
|
-
activeProviderId: string | null;
|
|
29
|
-
isLoading: boolean;
|
|
30
|
-
error: string | null;
|
|
31
|
-
|
|
32
|
-
// Actions
|
|
33
|
-
loadProviders: () => Promise<void>;
|
|
34
|
-
setProvider: (id: string, config?: { apiKey?: string; baseUrl?: string; model?: string }) => Promise<boolean>;
|
|
35
|
-
refreshStatus: () => Promise<void>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const useAgentStore = create<AgentState>((set, get) => ({
|
|
39
|
-
providers: [],
|
|
40
|
-
activeProviderId: null,
|
|
41
|
-
isLoading: false,
|
|
42
|
-
error: null,
|
|
43
|
-
|
|
44
|
-
loadProviders: async () => {
|
|
45
|
-
set({ isLoading: true, error: null });
|
|
46
|
-
try {
|
|
47
|
-
const response = await fetch('/api/ai/providers');
|
|
48
|
-
if (!response.ok) {
|
|
49
|
-
throw new Error('Failed to load providers');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const status: AgentStatus = await response.json();
|
|
53
|
-
|
|
54
|
-
set({
|
|
55
|
-
providers: status.providers,
|
|
56
|
-
activeProviderId: status.activeProvider,
|
|
57
|
-
isLoading: false,
|
|
58
|
-
});
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error('Error loading providers:', error);
|
|
61
|
-
set({
|
|
62
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
63
|
-
isLoading: false,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
setProvider: async (id, config) => {
|
|
69
|
-
set({ isLoading: true, error: null });
|
|
70
|
-
try {
|
|
71
|
-
const response = await fetch(`/api/ai/providers/${id}`, {
|
|
72
|
-
method: 'POST',
|
|
73
|
-
headers: {
|
|
74
|
-
'Content-Type': 'application/json',
|
|
75
|
-
},
|
|
76
|
-
body: JSON.stringify(config || {}),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
if (!response.ok) {
|
|
80
|
-
const errorData = await response.json();
|
|
81
|
-
throw new Error(errorData.message || 'Failed to set provider');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const result = await response.json();
|
|
85
|
-
|
|
86
|
-
if (result.success && result.status) {
|
|
87
|
-
set({
|
|
88
|
-
providers: result.status.providers,
|
|
89
|
-
activeProviderId: result.status.activeProvider,
|
|
90
|
-
isLoading: false,
|
|
91
|
-
});
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return false;
|
|
96
|
-
} catch (error) {
|
|
97
|
-
console.error('Error setting provider:', error);
|
|
98
|
-
set({
|
|
99
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
100
|
-
isLoading: false,
|
|
101
|
-
});
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
refreshStatus: async () => {
|
|
107
|
-
await get().loadProviders();
|
|
108
|
-
},
|
|
109
|
-
}));
|