machinaos 0.0.1

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 (288) hide show
  1. package/.env.template +71 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/bin/cli.js +159 -0
  5. package/client/.dockerignore +45 -0
  6. package/client/Dockerfile +68 -0
  7. package/client/eslint.config.js +29 -0
  8. package/client/index.html +13 -0
  9. package/client/nginx.conf +66 -0
  10. package/client/package.json +48 -0
  11. package/client/src/App.tsx +27 -0
  12. package/client/src/Dashboard.tsx +1173 -0
  13. package/client/src/ParameterPanel.tsx +301 -0
  14. package/client/src/components/AIAgentNode.tsx +321 -0
  15. package/client/src/components/APIKeyValidator.tsx +118 -0
  16. package/client/src/components/ClaudeChatModelNode.tsx +18 -0
  17. package/client/src/components/ConditionalEdge.tsx +189 -0
  18. package/client/src/components/CredentialsModal.tsx +306 -0
  19. package/client/src/components/EdgeConditionEditor.tsx +443 -0
  20. package/client/src/components/GeminiChatModelNode.tsx +18 -0
  21. package/client/src/components/GenericNode.tsx +357 -0
  22. package/client/src/components/LocationParameterPanel.tsx +154 -0
  23. package/client/src/components/ModelNode.tsx +286 -0
  24. package/client/src/components/OpenAIChatModelNode.tsx +18 -0
  25. package/client/src/components/OutputPanel.tsx +471 -0
  26. package/client/src/components/ParameterRenderer.tsx +1874 -0
  27. package/client/src/components/SkillEditorModal.tsx +417 -0
  28. package/client/src/components/SquareNode.tsx +797 -0
  29. package/client/src/components/StartNode.tsx +250 -0
  30. package/client/src/components/ToolkitNode.tsx +365 -0
  31. package/client/src/components/TriggerNode.tsx +463 -0
  32. package/client/src/components/auth/LoginPage.tsx +247 -0
  33. package/client/src/components/auth/ProtectedRoute.tsx +59 -0
  34. package/client/src/components/base/BaseChatModelNode.tsx +271 -0
  35. package/client/src/components/icons/AIProviderIcons.tsx +50 -0
  36. package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
  37. package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
  38. package/client/src/components/maps/index.ts +26 -0
  39. package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
  40. package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
  41. package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
  42. package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
  43. package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
  44. package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
  45. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
  46. package/client/src/components/parameterPanel/index.ts +42 -0
  47. package/client/src/components/shared/DataPanel.tsx +142 -0
  48. package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
  49. package/client/src/components/ui/AIResultModal.tsx +204 -0
  50. package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
  51. package/client/src/components/ui/CodeEditor.tsx +81 -0
  52. package/client/src/components/ui/CollapsibleSection.tsx +88 -0
  53. package/client/src/components/ui/ComponentItem.tsx +154 -0
  54. package/client/src/components/ui/ComponentPalette.tsx +321 -0
  55. package/client/src/components/ui/ConsolePanel.tsx +1074 -0
  56. package/client/src/components/ui/ErrorBoundary.tsx +196 -0
  57. package/client/src/components/ui/InputNodesPanel.tsx +204 -0
  58. package/client/src/components/ui/MapSelector.tsx +314 -0
  59. package/client/src/components/ui/Modal.tsx +149 -0
  60. package/client/src/components/ui/NodeContextMenu.tsx +192 -0
  61. package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
  62. package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
  63. package/client/src/components/ui/SettingsPanel.tsx +243 -0
  64. package/client/src/components/ui/TopToolbar.tsx +736 -0
  65. package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
  66. package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
  67. package/client/src/config/antdTheme.ts +186 -0
  68. package/client/src/config/api.ts +54 -0
  69. package/client/src/contexts/AuthContext.tsx +221 -0
  70. package/client/src/contexts/ThemeContext.tsx +42 -0
  71. package/client/src/contexts/WebSocketContext.tsx +1971 -0
  72. package/client/src/factories/baseChatModelFactory.ts +256 -0
  73. package/client/src/hooks/useAndroidOperations.ts +164 -0
  74. package/client/src/hooks/useApiKeyValidation.ts +107 -0
  75. package/client/src/hooks/useApiKeys.ts +238 -0
  76. package/client/src/hooks/useAppTheme.ts +17 -0
  77. package/client/src/hooks/useComponentPalette.ts +51 -0
  78. package/client/src/hooks/useCopyPaste.ts +155 -0
  79. package/client/src/hooks/useDragAndDrop.ts +124 -0
  80. package/client/src/hooks/useDragVariable.ts +88 -0
  81. package/client/src/hooks/useExecution.ts +313 -0
  82. package/client/src/hooks/useParameterPanel.ts +176 -0
  83. package/client/src/hooks/useReactFlowNodes.ts +189 -0
  84. package/client/src/hooks/useToolSchema.ts +209 -0
  85. package/client/src/hooks/useWhatsApp.ts +196 -0
  86. package/client/src/hooks/useWorkflowManagement.ts +46 -0
  87. package/client/src/index.css +315 -0
  88. package/client/src/main.tsx +19 -0
  89. package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
  90. package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
  91. package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
  92. package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
  93. package/client/src/nodeDefinitions/chatNodes.ts +135 -0
  94. package/client/src/nodeDefinitions/codeNodes.ts +54 -0
  95. package/client/src/nodeDefinitions/documentNodes.ts +379 -0
  96. package/client/src/nodeDefinitions/index.ts +15 -0
  97. package/client/src/nodeDefinitions/locationNodes.ts +463 -0
  98. package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
  99. package/client/src/nodeDefinitions/skillNodes.ts +211 -0
  100. package/client/src/nodeDefinitions/toolNodes.ts +198 -0
  101. package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
  102. package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
  103. package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
  104. package/client/src/nodeDefinitions.ts +104 -0
  105. package/client/src/schemas/workflowSchema.ts +264 -0
  106. package/client/src/services/dynamicParameterService.ts +96 -0
  107. package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
  108. package/client/src/services/executionService.ts +232 -0
  109. package/client/src/services/workflowApi.ts +91 -0
  110. package/client/src/store/useAppStore.ts +582 -0
  111. package/client/src/styles/theme.ts +508 -0
  112. package/client/src/styles/zIndex.ts +17 -0
  113. package/client/src/types/ComponentTypes.ts +39 -0
  114. package/client/src/types/EdgeCondition.ts +231 -0
  115. package/client/src/types/INodeProperties.ts +288 -0
  116. package/client/src/types/NodeTypes.ts +28 -0
  117. package/client/src/utils/formatters.ts +33 -0
  118. package/client/src/utils/googleMapsLoader.ts +140 -0
  119. package/client/src/utils/locationUtils.ts +85 -0
  120. package/client/src/utils/nodeUtils.ts +31 -0
  121. package/client/src/utils/workflow.ts +30 -0
  122. package/client/src/utils/workflowExport.ts +120 -0
  123. package/client/src/vite-env.d.ts +12 -0
  124. package/client/tailwind.config.js +60 -0
  125. package/client/tsconfig.json +25 -0
  126. package/client/tsconfig.node.json +11 -0
  127. package/client/vite.config.js +35 -0
  128. package/docker-compose.prod.yml +107 -0
  129. package/docker-compose.yml +104 -0
  130. package/docs-MachinaOs/README.md +85 -0
  131. package/docs-MachinaOs/deployment/docker.mdx +228 -0
  132. package/docs-MachinaOs/deployment/production.mdx +345 -0
  133. package/docs-MachinaOs/docs.json +75 -0
  134. package/docs-MachinaOs/faq.mdx +309 -0
  135. package/docs-MachinaOs/favicon.svg +5 -0
  136. package/docs-MachinaOs/installation.mdx +160 -0
  137. package/docs-MachinaOs/introduction.mdx +114 -0
  138. package/docs-MachinaOs/logo/dark.svg +6 -0
  139. package/docs-MachinaOs/logo/light.svg +6 -0
  140. package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
  141. package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
  142. package/docs-MachinaOs/nodes/android.mdx +411 -0
  143. package/docs-MachinaOs/nodes/overview.mdx +181 -0
  144. package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
  145. package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
  146. package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
  147. package/docs-MachinaOs/quickstart.mdx +119 -0
  148. package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
  149. package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
  150. package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
  151. package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
  152. package/nul +0 -0
  153. package/package.json +70 -0
  154. package/scripts/build.js +158 -0
  155. package/scripts/check-ports.ps1 +33 -0
  156. package/scripts/clean.js +40 -0
  157. package/scripts/docker.js +93 -0
  158. package/scripts/kill-port.ps1 +154 -0
  159. package/scripts/start.js +210 -0
  160. package/scripts/stop.js +325 -0
  161. package/server/.dockerignore +44 -0
  162. package/server/Dockerfile +45 -0
  163. package/server/constants.py +249 -0
  164. package/server/core/__init__.py +1 -0
  165. package/server/core/cache.py +461 -0
  166. package/server/core/config.py +128 -0
  167. package/server/core/container.py +99 -0
  168. package/server/core/database.py +1211 -0
  169. package/server/core/logging.py +314 -0
  170. package/server/main.py +289 -0
  171. package/server/middleware/__init__.py +5 -0
  172. package/server/middleware/auth.py +89 -0
  173. package/server/models/__init__.py +1 -0
  174. package/server/models/auth.py +52 -0
  175. package/server/models/cache.py +24 -0
  176. package/server/models/database.py +211 -0
  177. package/server/models/nodes.py +455 -0
  178. package/server/package.json +9 -0
  179. package/server/pyproject.toml +72 -0
  180. package/server/requirements.txt +83 -0
  181. package/server/routers/__init__.py +1 -0
  182. package/server/routers/android.py +294 -0
  183. package/server/routers/auth.py +203 -0
  184. package/server/routers/database.py +151 -0
  185. package/server/routers/maps.py +142 -0
  186. package/server/routers/nodejs_compat.py +289 -0
  187. package/server/routers/webhook.py +90 -0
  188. package/server/routers/websocket.py +2127 -0
  189. package/server/routers/whatsapp.py +761 -0
  190. package/server/routers/workflow.py +200 -0
  191. package/server/services/__init__.py +1 -0
  192. package/server/services/ai.py +2415 -0
  193. package/server/services/android/__init__.py +27 -0
  194. package/server/services/android/broadcaster.py +114 -0
  195. package/server/services/android/client.py +608 -0
  196. package/server/services/android/manager.py +78 -0
  197. package/server/services/android/protocol.py +165 -0
  198. package/server/services/android_service.py +588 -0
  199. package/server/services/auth.py +131 -0
  200. package/server/services/chat_client.py +160 -0
  201. package/server/services/deployment/__init__.py +12 -0
  202. package/server/services/deployment/manager.py +706 -0
  203. package/server/services/deployment/state.py +47 -0
  204. package/server/services/deployment/triggers.py +275 -0
  205. package/server/services/event_waiter.py +785 -0
  206. package/server/services/execution/__init__.py +77 -0
  207. package/server/services/execution/cache.py +769 -0
  208. package/server/services/execution/conditions.py +373 -0
  209. package/server/services/execution/dlq.py +132 -0
  210. package/server/services/execution/executor.py +1351 -0
  211. package/server/services/execution/models.py +531 -0
  212. package/server/services/execution/recovery.py +235 -0
  213. package/server/services/handlers/__init__.py +126 -0
  214. package/server/services/handlers/ai.py +355 -0
  215. package/server/services/handlers/android.py +260 -0
  216. package/server/services/handlers/code.py +278 -0
  217. package/server/services/handlers/document.py +598 -0
  218. package/server/services/handlers/http.py +193 -0
  219. package/server/services/handlers/polyglot.py +105 -0
  220. package/server/services/handlers/tools.py +845 -0
  221. package/server/services/handlers/triggers.py +107 -0
  222. package/server/services/handlers/utility.py +822 -0
  223. package/server/services/handlers/whatsapp.py +476 -0
  224. package/server/services/maps.py +289 -0
  225. package/server/services/memory_store.py +103 -0
  226. package/server/services/node_executor.py +375 -0
  227. package/server/services/parameter_resolver.py +218 -0
  228. package/server/services/polyglot_client.py +169 -0
  229. package/server/services/scheduler.py +155 -0
  230. package/server/services/skill_loader.py +417 -0
  231. package/server/services/status_broadcaster.py +826 -0
  232. package/server/services/temporal/__init__.py +23 -0
  233. package/server/services/temporal/activities.py +344 -0
  234. package/server/services/temporal/client.py +76 -0
  235. package/server/services/temporal/executor.py +147 -0
  236. package/server/services/temporal/worker.py +251 -0
  237. package/server/services/temporal/workflow.py +355 -0
  238. package/server/services/temporal/ws_client.py +236 -0
  239. package/server/services/text.py +111 -0
  240. package/server/services/user_auth.py +172 -0
  241. package/server/services/websocket_client.py +29 -0
  242. package/server/services/workflow.py +597 -0
  243. package/server/skills/android-skill/SKILL.md +82 -0
  244. package/server/skills/assistant-personality/SKILL.md +45 -0
  245. package/server/skills/code-skill/SKILL.md +140 -0
  246. package/server/skills/http-skill/SKILL.md +161 -0
  247. package/server/skills/maps-skill/SKILL.md +170 -0
  248. package/server/skills/memory-skill/SKILL.md +154 -0
  249. package/server/skills/scheduler-skill/SKILL.md +84 -0
  250. package/server/skills/whatsapp-skill/SKILL.md +283 -0
  251. package/server/uv.lock +2916 -0
  252. package/server/whatsapp-rpc/.dockerignore +30 -0
  253. package/server/whatsapp-rpc/Dockerfile +44 -0
  254. package/server/whatsapp-rpc/Dockerfile.web +17 -0
  255. package/server/whatsapp-rpc/README.md +139 -0
  256. package/server/whatsapp-rpc/cli.js +95 -0
  257. package/server/whatsapp-rpc/configs/config.yaml +7 -0
  258. package/server/whatsapp-rpc/docker-compose.yml +35 -0
  259. package/server/whatsapp-rpc/docs/API.md +410 -0
  260. package/server/whatsapp-rpc/go.mod +67 -0
  261. package/server/whatsapp-rpc/go.sum +203 -0
  262. package/server/whatsapp-rpc/package.json +30 -0
  263. package/server/whatsapp-rpc/schema.json +1294 -0
  264. package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
  265. package/server/whatsapp-rpc/scripts/cli.js +162 -0
  266. package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
  267. package/server/whatsapp-rpc/src/go/config/config.go +49 -0
  268. package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
  269. package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
  270. package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
  271. package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
  272. package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
  273. package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
  274. package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
  275. package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
  276. package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
  277. package/server/whatsapp-rpc/web/app.py +609 -0
  278. package/server/whatsapp-rpc/web/requirements.txt +6 -0
  279. package/server/whatsapp-rpc/web/rpc_client.py +427 -0
  280. package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
  281. package/server/whatsapp-rpc/web/templates/base.html +150 -0
  282. package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
  283. package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
  284. package/server/whatsapp-rpc/web/templates/groups.html +328 -0
  285. package/server/whatsapp-rpc/web/templates/messages.html +465 -0
  286. package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
  287. package/server/whatsapp-rpc/web/templates/send.html +259 -0
  288. package/server/whatsapp-rpc/web/templates/settings.html +459 -0
@@ -0,0 +1,313 @@
1
+ /**
2
+ * useExecution Hook - WebSocket-based node execution
3
+ *
4
+ * Provides node execution functionality via WebSocket instead of REST API.
5
+ * This replaces the REST-based ExecutionService for real-time execution.
6
+ */
7
+
8
+ import { useCallback, useState } from 'react';
9
+ import { Node, Edge } from 'reactflow';
10
+ import { useWebSocket } from '../contexts/WebSocketContext';
11
+ import { nodeDefinitions } from '../nodeDefinitions';
12
+ import { INodeExecutionData } from '../types/INodeProperties';
13
+
14
+ // Execution result interface (compatible with ExecutionService)
15
+ export interface ExecutionResult {
16
+ success: boolean;
17
+ nodeId: string;
18
+ nodeType: string;
19
+ nodeName: string;
20
+ timestamp: string;
21
+ executionTime: number;
22
+ outputs?: Record<string, any>;
23
+ error?: string;
24
+ data?: any;
25
+ nodeData: INodeExecutionData[][];
26
+ }
27
+
28
+ export interface UseExecutionResult {
29
+ // Execute a node via WebSocket
30
+ executeNode: (
31
+ nodeId: string,
32
+ nodeType: string,
33
+ nodes?: Node[],
34
+ edges?: Edge[]
35
+ ) => Promise<ExecutionResult>;
36
+
37
+ // Execute AI node via WebSocket
38
+ executeAiNode: (
39
+ nodeId: string,
40
+ nodeType: string,
41
+ parameters: Record<string, any>,
42
+ model: string
43
+ ) => Promise<ExecutionResult>;
44
+
45
+ // Execution state
46
+ isExecuting: boolean;
47
+ executingNodeId: string | null;
48
+ lastError: string | null;
49
+
50
+ // Connection status
51
+ isConnected: boolean;
52
+ }
53
+
54
+ export const useExecution = (): UseExecutionResult => {
55
+ const {
56
+ executeNode: wsExecuteNode,
57
+ executeAiNode: wsExecuteAiNode,
58
+ getNodeParameters,
59
+ isConnected
60
+ } = useWebSocket();
61
+
62
+ const [isExecuting, setIsExecuting] = useState(false);
63
+ const [executingNodeId, setExecutingNodeId] = useState<string | null>(null);
64
+ const [lastError, setLastError] = useState<string | null>(null);
65
+
66
+ /**
67
+ * Execute a workflow node via WebSocket
68
+ */
69
+ const executeNode = useCallback(async (
70
+ nodeId: string,
71
+ nodeType: string,
72
+ nodes?: Node[],
73
+ edges?: Edge[]
74
+ ): Promise<ExecutionResult> => {
75
+ const startTime = Date.now();
76
+
77
+ try {
78
+ console.log(`[WebSocket Execution] Starting: ${nodeId} (type: ${nodeType})`);
79
+
80
+ setIsExecuting(true);
81
+ setExecutingNodeId(nodeId);
82
+ setLastError(null);
83
+
84
+ // Load parameters from WebSocket/backend
85
+ const paramsResult = await getNodeParameters(nodeId);
86
+ const parameters = paramsResult?.parameters || {};
87
+
88
+ console.log(`[WebSocket Execution] Parameters for ${nodeId}:`, parameters);
89
+
90
+ // Prepare nodes and edges for execution
91
+ const nodeData = nodes?.map(node => ({
92
+ id: node.id,
93
+ type: node.type || '',
94
+ data: node.data || {}
95
+ })) || [];
96
+
97
+ const edgeData = edges?.map(edge => ({
98
+ id: edge.id,
99
+ source: edge.source,
100
+ target: edge.target,
101
+ sourceHandle: edge.sourceHandle || undefined,
102
+ targetHandle: edge.targetHandle || undefined
103
+ })) || [];
104
+
105
+ // Execute via WebSocket
106
+ const result = await wsExecuteNode(nodeId, nodeType, parameters, nodeData, edgeData);
107
+
108
+ console.log(`[WebSocket Execution] Result:`, result);
109
+
110
+ const executionTime = Date.now() - startTime;
111
+
112
+ if (result.success) {
113
+ // Extract output data
114
+ const outputData = result.result || {
115
+ nodeId,
116
+ success: true,
117
+ message: 'Execution completed successfully',
118
+ timestamp: result.timestamp || new Date().toISOString()
119
+ };
120
+
121
+ const nodeExecutionData: INodeExecutionData[][] = [[{
122
+ json: outputData
123
+ }]];
124
+
125
+ return {
126
+ success: true,
127
+ nodeId,
128
+ nodeType,
129
+ nodeName: nodeDefinitions[nodeType]?.displayName || nodeType,
130
+ timestamp: result.timestamp || new Date().toISOString(),
131
+ executionTime: result.execution_time || executionTime,
132
+ outputs: outputData,
133
+ data: outputData,
134
+ nodeData: nodeExecutionData
135
+ };
136
+ } else {
137
+ // Handle error
138
+ const errorOutputData = result.result || {
139
+ error: result.error,
140
+ nodeId,
141
+ success: false,
142
+ timestamp: result.timestamp || new Date().toISOString()
143
+ };
144
+
145
+ const errorData: INodeExecutionData[][] = [[{
146
+ json: errorOutputData
147
+ }]];
148
+
149
+ setLastError(result.error || 'Execution failed');
150
+
151
+ return {
152
+ success: false,
153
+ nodeId,
154
+ nodeType,
155
+ nodeName: nodeDefinitions[nodeType]?.displayName || nodeType,
156
+ timestamp: result.timestamp || new Date().toISOString(),
157
+ executionTime: result.execution_time || executionTime,
158
+ error: result.error,
159
+ outputs: errorOutputData,
160
+ data: errorOutputData,
161
+ nodeData: errorData
162
+ };
163
+ }
164
+
165
+ } catch (error: any) {
166
+ console.error(`[WebSocket Execution] Failed for node ${nodeId}:`, error);
167
+
168
+ const errorMessage = error.message || 'WebSocket execution failed';
169
+ setLastError(errorMessage);
170
+
171
+ const catchErrorData = {
172
+ error: errorMessage,
173
+ nodeId,
174
+ success: false,
175
+ timestamp: new Date().toISOString()
176
+ };
177
+
178
+ return {
179
+ success: false,
180
+ nodeId,
181
+ nodeType,
182
+ nodeName: nodeDefinitions[nodeType]?.displayName || nodeType,
183
+ timestamp: new Date().toISOString(),
184
+ executionTime: Date.now() - startTime,
185
+ error: errorMessage,
186
+ outputs: catchErrorData,
187
+ data: catchErrorData,
188
+ nodeData: [[{ json: catchErrorData }]]
189
+ };
190
+
191
+ } finally {
192
+ setIsExecuting(false);
193
+ setExecutingNodeId(null);
194
+ }
195
+ }, [wsExecuteNode, getNodeParameters]);
196
+
197
+ /**
198
+ * Execute an AI node via WebSocket
199
+ */
200
+ const executeAiNode = useCallback(async (
201
+ nodeId: string,
202
+ nodeType: string,
203
+ parameters: Record<string, any>,
204
+ model: string
205
+ ): Promise<ExecutionResult> => {
206
+ const startTime = Date.now();
207
+
208
+ try {
209
+ console.log(`[WebSocket AI Execution] Starting: ${nodeId} (type: ${nodeType})`);
210
+
211
+ setIsExecuting(true);
212
+ setExecutingNodeId(nodeId);
213
+ setLastError(null);
214
+
215
+ // Execute AI node via WebSocket
216
+ const result = await wsExecuteAiNode(nodeId, nodeType, parameters, model);
217
+
218
+ console.log(`[WebSocket AI Execution] Result:`, result);
219
+
220
+ const executionTime = Date.now() - startTime;
221
+
222
+ if (result.success) {
223
+ const outputData = result.result || {
224
+ nodeId,
225
+ success: true,
226
+ message: 'AI execution completed successfully',
227
+ timestamp: result.timestamp || new Date().toISOString()
228
+ };
229
+
230
+ const nodeExecutionData: INodeExecutionData[][] = [[{
231
+ json: outputData
232
+ }]];
233
+
234
+ return {
235
+ success: true,
236
+ nodeId,
237
+ nodeType,
238
+ nodeName: nodeDefinitions[nodeType]?.displayName || nodeType,
239
+ timestamp: result.timestamp || new Date().toISOString(),
240
+ executionTime: result.execution_time || executionTime,
241
+ outputs: outputData,
242
+ data: outputData,
243
+ nodeData: nodeExecutionData
244
+ };
245
+ } else {
246
+ const errorOutputData = result.result || {
247
+ error: result.error,
248
+ nodeId,
249
+ success: false,
250
+ timestamp: result.timestamp || new Date().toISOString()
251
+ };
252
+
253
+ const errorData: INodeExecutionData[][] = [[{
254
+ json: errorOutputData
255
+ }]];
256
+
257
+ setLastError(result.error || 'AI execution failed');
258
+
259
+ return {
260
+ success: false,
261
+ nodeId,
262
+ nodeType,
263
+ nodeName: nodeDefinitions[nodeType]?.displayName || nodeType,
264
+ timestamp: result.timestamp || new Date().toISOString(),
265
+ executionTime: result.execution_time || executionTime,
266
+ error: result.error,
267
+ outputs: errorOutputData,
268
+ data: errorOutputData,
269
+ nodeData: errorData
270
+ };
271
+ }
272
+
273
+ } catch (error: any) {
274
+ console.error(`[WebSocket AI Execution] Failed for node ${nodeId}:`, error);
275
+
276
+ const errorMessage = error.message || 'WebSocket AI execution failed';
277
+ setLastError(errorMessage);
278
+
279
+ const catchErrorData = {
280
+ error: errorMessage,
281
+ nodeId,
282
+ success: false,
283
+ timestamp: new Date().toISOString()
284
+ };
285
+
286
+ return {
287
+ success: false,
288
+ nodeId,
289
+ nodeType,
290
+ nodeName: nodeDefinitions[nodeType]?.displayName || nodeType,
291
+ timestamp: new Date().toISOString(),
292
+ executionTime: Date.now() - startTime,
293
+ error: errorMessage,
294
+ outputs: catchErrorData,
295
+ data: catchErrorData,
296
+ nodeData: [[{ json: catchErrorData }]]
297
+ };
298
+
299
+ } finally {
300
+ setIsExecuting(false);
301
+ setExecutingNodeId(null);
302
+ }
303
+ }, [wsExecuteAiNode]);
304
+
305
+ return {
306
+ executeNode,
307
+ executeAiNode,
308
+ isExecuting,
309
+ executingNodeId,
310
+ lastError,
311
+ isConnected
312
+ };
313
+ };
@@ -0,0 +1,176 @@
1
+ import { useState, useEffect, useMemo, useCallback } from 'react';
2
+ import { useAppStore } from '../store/useAppStore';
3
+ import { nodeDefinitions } from '../nodeDefinitions';
4
+ import { useWebSocket } from '../contexts/WebSocketContext';
5
+ import { SKILL_NODE_TYPES } from '../nodeDefinitions/skillNodes';
6
+
7
+ export const useParameterPanel = () => {
8
+ const { selectedNode, setSelectedNode, updateNodeData } = useAppStore();
9
+ const [parameters, setParameters] = useState<any>({});
10
+ const [originalParameters, setOriginalParameters] = useState<any>({});
11
+ const [isLoading, setIsLoading] = useState(false);
12
+ const [isSaving, setIsSaving] = useState(false);
13
+ const [error, setError] = useState<string | null>(null);
14
+
15
+ // Use WebSocket for parameter operations
16
+ const { getNodeParameters, saveNodeParameters, sendRequest, isConnected } = useWebSocket();
17
+
18
+ // Use stable references to prevent multiple effect runs
19
+ const nodeId = selectedNode?.id;
20
+ const nodeType = selectedNode?.type;
21
+
22
+ useEffect(() => {
23
+ if (!nodeType || !nodeId) {
24
+ setParameters({});
25
+ setOriginalParameters({});
26
+ setError(null);
27
+ return;
28
+ }
29
+
30
+ const loadParameters = async () => {
31
+ const nodeDefinition = nodeDefinitions[nodeType];
32
+ if (!nodeDefinition) return;
33
+
34
+ const defaults: any = {};
35
+
36
+ // Set parameter defaults
37
+ if (nodeDefinition.properties) {
38
+ nodeDefinition.properties.forEach((param: any) => {
39
+ defaults[param.name] = param.default !== undefined ? param.default : null;
40
+ });
41
+ }
42
+
43
+ setIsLoading(true);
44
+ setError(null);
45
+
46
+ try {
47
+ // Load saved parameters via WebSocket
48
+ const result = await getNodeParameters(nodeId);
49
+ // Extract parameters from NodeParameters response
50
+ const savedParams = result?.parameters || {};
51
+ let initialParams = { ...defaults, ...savedParams };
52
+
53
+ console.log('[useParameterPanel] Loading params for node:', nodeId, nodeType);
54
+ console.log('[useParameterPanel] Defaults:', defaults);
55
+ console.log('[useParameterPanel] Saved from backend:', savedParams);
56
+
57
+ // For skill nodes, load skill content (instructions) from SKILL.md files
58
+ if (SKILL_NODE_TYPES.includes(nodeType) && nodeType !== 'customSkill') {
59
+ const skillName = initialParams.skillName;
60
+ if (skillName) {
61
+ try {
62
+ console.log('[useParameterPanel] Loading skill content for:', skillName);
63
+ const skillResult = await sendRequest('get_skill_content', { skill_name: skillName });
64
+ if (skillResult?.success && skillResult?.instructions) {
65
+ initialParams = {
66
+ ...initialParams,
67
+ instructions: skillResult.instructions
68
+ };
69
+ console.log('[useParameterPanel] Loaded skill instructions:', skillResult.instructions.substring(0, 100) + '...');
70
+ }
71
+ } catch (skillErr) {
72
+ console.error('[useParameterPanel] Failed to load skill content:', skillErr);
73
+ }
74
+ }
75
+ }
76
+
77
+ console.log('[useParameterPanel] Merged initial:', initialParams);
78
+
79
+ setParameters(initialParams);
80
+ setOriginalParameters(initialParams);
81
+ } catch (err) {
82
+ console.error('Failed to load parameters via WebSocket:', err);
83
+ // Use defaults if WebSocket fails
84
+ setParameters(defaults);
85
+ setOriginalParameters(defaults);
86
+ setError('Failed to load saved parameters');
87
+ } finally {
88
+ setIsLoading(false);
89
+ }
90
+ };
91
+
92
+ loadParameters();
93
+ }, [nodeId, nodeType, getNodeParameters, sendRequest]);
94
+
95
+ const hasUnsavedChanges = useMemo(() => {
96
+ return JSON.stringify(parameters) !== JSON.stringify(originalParameters);
97
+ }, [parameters, originalParameters]);
98
+
99
+ const handleParameterChange = useCallback((paramName: string, value: any) => {
100
+ setParameters((prevParams: any) => ({
101
+ ...prevParams,
102
+ [paramName]: value
103
+ }));
104
+ }, []);
105
+
106
+ const handleSave = useCallback(async () => {
107
+ if (selectedNode) {
108
+ setIsSaving(true);
109
+ setError(null);
110
+
111
+ try {
112
+ // Save parameters to database via WebSocket
113
+ const success = await saveNodeParameters(selectedNode.id, parameters);
114
+
115
+ if (success) {
116
+ // For skill nodes, also save skill content (instructions) to SKILL.md files
117
+ if (selectedNode.type && SKILL_NODE_TYPES.includes(selectedNode.type) && selectedNode.type !== 'customSkill') {
118
+ const skillName = parameters.skillName;
119
+ const instructions = parameters.instructions;
120
+ if (skillName && instructions !== undefined) {
121
+ try {
122
+ console.log('[useParameterPanel] Saving skill content for:', skillName);
123
+ const skillResult = await sendRequest('save_skill_content', {
124
+ skill_name: skillName,
125
+ instructions: instructions
126
+ });
127
+ if (!skillResult?.success) {
128
+ console.error('[useParameterPanel] Failed to save skill content:', skillResult?.error);
129
+ setError(skillResult?.error || 'Failed to save skill content');
130
+ } else {
131
+ console.log('[useParameterPanel] Skill content saved successfully');
132
+ }
133
+ } catch (skillErr) {
134
+ console.error('[useParameterPanel] Failed to save skill content:', skillErr);
135
+ setError('Failed to save skill content');
136
+ }
137
+ }
138
+ }
139
+
140
+ // Also update the node data in store for faster loading
141
+ updateNodeData(selectedNode.id, parameters);
142
+ setOriginalParameters({ ...parameters });
143
+ } else {
144
+ setError('Failed to save parameters');
145
+ }
146
+ } catch (err) {
147
+ console.error('Failed to save parameters via WebSocket:', err);
148
+ setError('Failed to save parameters');
149
+ } finally {
150
+ setIsSaving(false);
151
+ }
152
+ }
153
+ }, [selectedNode, parameters, updateNodeData, saveNodeParameters, sendRequest]);
154
+
155
+ const handleCancel = useCallback(() => {
156
+ setParameters({ ...originalParameters });
157
+ setSelectedNode(null);
158
+ }, [originalParameters, setSelectedNode]);
159
+
160
+ const nodeDefinition = selectedNode?.type ? nodeDefinitions[selectedNode.type] : null;
161
+
162
+ return {
163
+ selectedNode,
164
+ nodeDefinition,
165
+ parameters,
166
+ hasUnsavedChanges,
167
+ handleParameterChange,
168
+ handleSave,
169
+ handleCancel,
170
+ // New state for loading/saving status
171
+ isLoading,
172
+ isSaving,
173
+ error,
174
+ isConnected,
175
+ };
176
+ };
@@ -0,0 +1,189 @@
1
+ import { useCallback } from 'react';
2
+ import { Node, Edge, addEdge, Connection } from 'reactflow';
3
+ import { useAppStore } from '../store/useAppStore';
4
+ import { nodeDefinitions } from '../nodeDefinitions';
5
+ import { INodeInputDefinition, INodeOutputDefinition, NodeConnectionType } from '../types/INodeProperties';
6
+
7
+ interface UseReactFlowNodesProps {
8
+ setNodes: (nodes: Node[] | ((nodes: Node[]) => Node[])) => void;
9
+ setEdges: (edges: Edge[] | ((edges: Edge[]) => Edge[])) => void;
10
+ }
11
+
12
+ export const useReactFlowNodes = ({ setNodes, setEdges }: UseReactFlowNodesProps) => {
13
+ const { selectedNode, setSelectedNode } = useAppStore();
14
+
15
+ // Helper function to get node inputs/outputs for both enhanced and legacy nodes
16
+ const getNodeInputs = (nodeType: string): INodeInputDefinition[] => {
17
+ const definition = nodeDefinitions[nodeType];
18
+ if (!definition?.inputs) return [];
19
+
20
+ // Enhanced nodes: array of input objects
21
+ if (definition.inputs.length > 0 && typeof definition.inputs[0] === 'object') {
22
+ return definition.inputs as INodeInputDefinition[];
23
+ }
24
+
25
+ // Legacy nodes: array of strings - convert to input objects
26
+ return (definition.inputs as string[]).map((input, index) => ({
27
+ name: `input_${index}`,
28
+ displayName: 'Input',
29
+ type: (input as NodeConnectionType) || 'main',
30
+ description: 'Node input connection'
31
+ }));
32
+ };
33
+
34
+ const getNodeOutputs = (nodeType: string): INodeOutputDefinition[] => {
35
+ const definition = nodeDefinitions[nodeType];
36
+ if (!definition?.outputs) return [];
37
+
38
+ // Enhanced nodes: array of output objects
39
+ if (definition.outputs.length > 0 && typeof definition.outputs[0] === 'object') {
40
+ return definition.outputs as INodeOutputDefinition[];
41
+ }
42
+
43
+ // Legacy nodes: array of strings - convert to output objects
44
+ return (definition.outputs as string[]).map((output, index) => ({
45
+ name: `output_${index}`,
46
+ displayName: 'Output',
47
+ type: (output as NodeConnectionType) || 'main',
48
+ description: 'Node output connection'
49
+ }));
50
+ };
51
+
52
+ // Validate connection compatibility
53
+ const isValidConnection = (connection: Connection, nodes: Node[]): boolean => {
54
+ const sourceNode = nodes.find(n => n.id === connection.source);
55
+ const targetNode = nodes.find(n => n.id === connection.target);
56
+
57
+ if (!sourceNode || !targetNode) {
58
+ console.warn('Connection validation: Source or target node not found');
59
+ return false;
60
+ }
61
+
62
+ const sourceOutputs = getNodeOutputs(sourceNode.type || '');
63
+ const targetInputs = getNodeInputs(targetNode.type || '');
64
+
65
+ // Find the specific output and input being connected
66
+ const sourceHandle = connection.sourceHandle || 'output_0';
67
+ const targetHandle = connection.targetHandle || 'input_0';
68
+
69
+ const sourceOutput = sourceOutputs.find(output =>
70
+ `output-${output.name}` === sourceHandle || output.name === sourceHandle
71
+ );
72
+ const targetInput = targetInputs.find(input =>
73
+ `input-${input.name}` === targetHandle || input.name === targetHandle
74
+ );
75
+
76
+ if (!sourceOutput || !targetInput) {
77
+ console.log('[Connection Debug]', {
78
+ sourceType: sourceNode.type,
79
+ targetType: targetNode.type,
80
+ sourceHandle,
81
+ targetHandle,
82
+ sourceOutputs: sourceOutputs.map(o => ({ name: o.name, computed: `output-${o.name}` })),
83
+ targetInputs: targetInputs.map(i => ({ name: i.name, computed: `input-${i.name}` })),
84
+ sourceOutput: sourceOutput ? 'found' : 'NOT FOUND',
85
+ targetInput: targetInput ? 'found' : 'NOT FOUND'
86
+ });
87
+ console.warn('Connection validation: Handle not found', { sourceHandle, targetHandle });
88
+ // Allow connection if we can't find handle definitions (fallback for legacy nodes)
89
+ return true;
90
+ }
91
+
92
+ // Check type compatibility
93
+ const isCompatible = areTypesCompatible(sourceOutput.type, targetInput.type);
94
+
95
+ if (!isCompatible) {
96
+ console.warn(`Connection rejected: Incompatible types`, {
97
+ source: `${sourceNode.type}.${sourceOutput.name} (${sourceOutput.type})`,
98
+ target: `${targetNode.type}.${targetInput.name} (${targetInput.type})`
99
+ });
100
+ }
101
+
102
+ return isCompatible;
103
+ };
104
+
105
+ // Check if two connection types are compatible
106
+ const areTypesCompatible = (outputType: NodeConnectionType, inputType: NodeConnectionType): boolean => {
107
+ // Same types are always compatible
108
+ if (outputType === inputType) return true;
109
+
110
+ // 'main' is compatible with most types (universal data format)
111
+ if (outputType === 'main' || inputType === 'main') return true;
112
+
113
+ // Specific compatibility rules
114
+ const compatibilityMatrix: Record<NodeConnectionType, NodeConnectionType[]> = {
115
+ 'main': ['main', 'trigger', 'ai', 'file', 'binary', 'webhook'], // Main accepts everything
116
+ 'trigger': ['main', 'trigger'], // Triggers only connect to main or other triggers
117
+ 'ai': ['main', 'ai'], // AI outputs connect to main or other AI inputs
118
+ 'file': ['main', 'file', 'binary'], // Files can connect to binary
119
+ 'binary': ['main', 'file', 'binary'], // Binary connects to file/binary
120
+ 'webhook': ['main', 'webhook'] // Webhooks connect to main or other webhooks
121
+ };
122
+
123
+ return compatibilityMatrix[outputType]?.includes(inputType) ?? false;
124
+ };
125
+
126
+ const onConnect = useCallback(
127
+ (params: Edge | Connection) => {
128
+ const { currentWorkflow } = useAppStore.getState();
129
+ const nodes = currentWorkflow?.nodes || [];
130
+
131
+ // Convert Edge to Connection format for validation
132
+ const connection: Connection = {
133
+ source: params.source,
134
+ target: params.target,
135
+ sourceHandle: params.sourceHandle ?? null,
136
+ targetHandle: params.targetHandle ?? null
137
+ };
138
+
139
+ // Debug: Log all connection attempts
140
+ console.log('[onConnect] Connection attempt:', {
141
+ source: params.source,
142
+ target: params.target,
143
+ sourceHandle: params.sourceHandle,
144
+ targetHandle: params.targetHandle
145
+ });
146
+
147
+ // Validate connection before adding
148
+ if (!isValidConnection(connection, nodes)) {
149
+ // Show user feedback for rejected connection (non-blocking)
150
+ console.warn('Connection rejected: Incompatible connection types', connection);
151
+ // Could implement a toast notification system here instead of alert
152
+ setTimeout(() => {
153
+ alert('Connection not allowed: Incompatible connection types.\n\nTip: Connect outputs to compatible inputs (e.g., AI → Main, File → Binary).');
154
+ }, 0);
155
+ return;
156
+ }
157
+
158
+ console.log('[onConnect] Connection accepted, adding edge');
159
+ // Add the connection
160
+ setEdges((eds) => addEdge(params, eds));
161
+ },
162
+ [setEdges]
163
+ );
164
+
165
+ const onNodesDelete = useCallback(
166
+ (deleted: Node[]) => {
167
+ setNodes((nds) => nds.filter((node) => !deleted.find((d) => d.id === node.id)));
168
+
169
+ // Clear selected node if it was deleted
170
+ if (selectedNode && deleted.find((d) => d.id === selectedNode.id)) {
171
+ setSelectedNode(null);
172
+ }
173
+ },
174
+ [setNodes, selectedNode, setSelectedNode]
175
+ );
176
+
177
+ const onEdgesDelete = useCallback(
178
+ (deleted: Edge[]) => {
179
+ setEdges((eds) => eds.filter((edge) => !deleted.find((d) => d.id === edge.id)));
180
+ },
181
+ [setEdges]
182
+ );
183
+
184
+ return {
185
+ onConnect,
186
+ onNodesDelete,
187
+ onEdgesDelete,
188
+ };
189
+ };