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,142 @@
1
+ """Google Maps service routes."""
2
+
3
+ from fastapi import APIRouter, Depends, HTTPException
4
+ from pydantic import BaseModel
5
+ from typing import Dict, Any, Optional
6
+ import httpx
7
+
8
+ from core.container import container
9
+ from services.maps import MapsService
10
+ from services.status_broadcaster import get_status_broadcaster
11
+ from core.logging import get_logger
12
+
13
+ logger = get_logger(__name__)
14
+ router = APIRouter(prefix="/python", tags=["maps"])
15
+
16
+
17
+ class GoogleMapsRequest(BaseModel):
18
+ node_id: str
19
+ node_type: str
20
+ parameters: Dict[str, Any]
21
+ api_key: Optional[str] = None
22
+
23
+
24
+ class ApiKeyValidationRequest(BaseModel):
25
+ api_key: str
26
+
27
+
28
+ @router.post("/maps/validate-key")
29
+ async def validate_google_maps_key(request: ApiKeyValidationRequest):
30
+ """Validate Google Maps API key and broadcast status via WebSocket."""
31
+ broadcaster = get_status_broadcaster()
32
+
33
+ try:
34
+ api_key = request.api_key.strip()
35
+ if not api_key:
36
+ await broadcaster.update_api_key_status(
37
+ provider="google_maps",
38
+ valid=False,
39
+ message="API key is required"
40
+ )
41
+ raise HTTPException(status_code=400, detail="API key is required")
42
+
43
+ # Test the API key with a simple geocoding request
44
+ async with httpx.AsyncClient() as client:
45
+ response = await client.get(
46
+ "https://maps.googleapis.com/maps/api/geocode/json",
47
+ params={
48
+ "address": "1600 Amphitheatre Parkway, Mountain View, CA",
49
+ "key": api_key
50
+ },
51
+ timeout=10.0
52
+ )
53
+
54
+ data = response.json()
55
+
56
+ if data.get("status") == "OK":
57
+ await broadcaster.update_api_key_status(
58
+ provider="google_maps",
59
+ valid=True,
60
+ message="API key validated successfully"
61
+ )
62
+ return {
63
+ "success": True,
64
+ "valid": True,
65
+ "message": "Google Maps API key is valid"
66
+ }
67
+ elif data.get("status") == "REQUEST_DENIED":
68
+ error_msg = data.get("error_message", "Invalid API key")
69
+ await broadcaster.update_api_key_status(
70
+ provider="google_maps",
71
+ valid=False,
72
+ message=error_msg
73
+ )
74
+ return {
75
+ "success": True,
76
+ "valid": False,
77
+ "message": error_msg
78
+ }
79
+ else:
80
+ # Other statuses like ZERO_RESULTS still mean the key works
81
+ await broadcaster.update_api_key_status(
82
+ provider="google_maps",
83
+ valid=True,
84
+ message="API key validated"
85
+ )
86
+ return {
87
+ "success": True,
88
+ "valid": True,
89
+ "message": f"API key is valid (status: {data.get('status')})"
90
+ }
91
+
92
+ except httpx.TimeoutException:
93
+ await broadcaster.update_api_key_status(
94
+ provider="google_maps",
95
+ valid=False,
96
+ message="Validation request timed out"
97
+ )
98
+ raise HTTPException(status_code=504, detail="Validation request timed out")
99
+ except httpx.RequestError as e:
100
+ await broadcaster.update_api_key_status(
101
+ provider="google_maps",
102
+ valid=False,
103
+ message=f"Network error: {str(e)}"
104
+ )
105
+ raise HTTPException(status_code=503, detail=f"Network error: {str(e)}")
106
+ except HTTPException:
107
+ raise
108
+ except Exception as e:
109
+ logger.error(f"API key validation error: {e}")
110
+ await broadcaster.update_api_key_status(
111
+ provider="google_maps",
112
+ valid=False,
113
+ message=f"Validation failed: {str(e)}"
114
+ )
115
+ raise HTTPException(status_code=500, detail=f"Validation failed: {str(e)}")
116
+
117
+
118
+ @router.post("/createmap/execute")
119
+ async def execute_createmap_node(
120
+ request: GoogleMapsRequest,
121
+ maps_service: MapsService = Depends(lambda: container.maps_service())
122
+ ):
123
+ """Execute Create Map node - Google Maps initialization."""
124
+ return await maps_service.create_map(request.node_id, request.parameters)
125
+
126
+
127
+ @router.post("/addlocations/execute")
128
+ async def execute_addlocations_node(
129
+ request: GoogleMapsRequest,
130
+ maps_service: MapsService = Depends(lambda: container.maps_service())
131
+ ):
132
+ """Execute Add Locations node - Google Maps Geocoding."""
133
+ return await maps_service.geocode_location(request.node_id, request.parameters)
134
+
135
+
136
+ @router.post("/shownearbyplaces/execute")
137
+ async def execute_shownearbyplaces_node(
138
+ request: GoogleMapsRequest,
139
+ maps_service: MapsService = Depends(lambda: container.maps_service())
140
+ ):
141
+ """Execute Show Nearby Places node - Google Places API."""
142
+ return await maps_service.find_nearby_places(request.node_id, request.parameters)
@@ -0,0 +1,289 @@
1
+ """Node.js API compatibility routes for seamless migration."""
2
+
3
+ from datetime import datetime
4
+ from uuid import uuid4
5
+ from fastapi import APIRouter, HTTPException, Depends
6
+ from pydantic import BaseModel
7
+ from typing import Dict, Any, List, Optional
8
+
9
+ from core.container import container
10
+ from core.database import Database
11
+ from services.workflow import WorkflowService
12
+ from core.logging import get_logger
13
+
14
+ logger = get_logger(__name__)
15
+ router = APIRouter(tags=["nodejs-compatibility"])
16
+
17
+
18
+ # Pydantic models matching Node.js API
19
+ class NodeExecuteRequest(BaseModel):
20
+ nodeId: str
21
+ nodeType: str
22
+ parameters: Optional[Dict[str, Any]] = {}
23
+ nodes: Optional[List[Dict[str, Any]]] = []
24
+ edges: Optional[List[Dict[str, Any]]] = []
25
+
26
+
27
+ class WorkflowSaveRequest(BaseModel):
28
+ name: str
29
+ data: Dict[str, Any]
30
+
31
+
32
+ # Health and status endpoints
33
+ @router.get("/")
34
+ async def root():
35
+ """Node.js compatible root endpoint."""
36
+ return {
37
+ "message": "React Flow Project API Server",
38
+ "status": "running",
39
+ "version": "2.0.0-python",
40
+ "endpoints": {
41
+ "health": "/api/health",
42
+ "workflows": "/api/workflows",
43
+ "nodes": "/api/nodes",
44
+ "execute": "/api/nodes/execute"
45
+ },
46
+ "services": {
47
+ "main": "http://localhost:3010",
48
+ "python": "http://localhost:3010"
49
+ },
50
+ "timestamp": datetime.now().isoformat()
51
+ }
52
+
53
+
54
+ @router.get("/api/health")
55
+ async def health_check():
56
+ """Node.js compatible health check."""
57
+ return {
58
+ "status": "OK",
59
+ "message": "React Flow Server is running",
60
+ "timestamp": datetime.now().isoformat()
61
+ }
62
+
63
+
64
+ # Workflow management endpoints
65
+ @router.get("/api/workflows")
66
+ async def get_workflows(database: Database = Depends(lambda: container.database())):
67
+ """Get all workflows (Node.js compatible)."""
68
+ try:
69
+ workflows = await database.get_all_workflows()
70
+ return {"workflows": workflows}
71
+ except Exception as e:
72
+ logger.error("Failed to get workflows", error=str(e))
73
+ return {"workflows": []}
74
+
75
+
76
+ @router.post("/api/workflows")
77
+ async def save_workflow(
78
+ request: WorkflowSaveRequest,
79
+ database: Database = Depends(lambda: container.database())
80
+ ):
81
+ """Save workflow (Node.js compatible)."""
82
+ try:
83
+ workflow_id = str(int(datetime.now().timestamp() * 1000)) # Node.js style ID
84
+ success = await database.save_workflow(
85
+ workflow_id=workflow_id,
86
+ name=request.name,
87
+ data=request.data
88
+ )
89
+
90
+ if success:
91
+ return {
92
+ "success": True,
93
+ "id": workflow_id,
94
+ "message": "Workflow saved successfully"
95
+ }
96
+ else:
97
+ raise HTTPException(status_code=500, detail="Failed to save workflow")
98
+
99
+ except Exception as e:
100
+ logger.error("Failed to save workflow", error=str(e))
101
+ raise HTTPException(status_code=500, detail=str(e))
102
+
103
+
104
+ @router.get("/api/workflows/{workflow_id}")
105
+ async def get_workflow(
106
+ workflow_id: str,
107
+ database: Database = Depends(lambda: container.database())
108
+ ):
109
+ """Get workflow by ID (Node.js compatible)."""
110
+ try:
111
+ workflow = await database.get_workflow(workflow_id)
112
+
113
+ if workflow:
114
+ return {
115
+ "id": workflow.id,
116
+ "name": workflow.name,
117
+ "data": workflow.data
118
+ }
119
+ else:
120
+ return {
121
+ "id": workflow_id,
122
+ "name": "Sample Workflow",
123
+ "data": {"nodes": [], "edges": []}
124
+ }
125
+
126
+ except Exception as e:
127
+ logger.error("Failed to get workflow", workflow_id=workflow_id, error=str(e))
128
+ return {
129
+ "id": workflow_id,
130
+ "name": "Sample Workflow",
131
+ "data": {"nodes": [], "edges": []}
132
+ }
133
+
134
+
135
+ # Main execution endpoint - Node.js compatible
136
+ @router.post("/api/nodes/execute")
137
+ async def execute_node(
138
+ request: NodeExecuteRequest,
139
+ workflow_service: WorkflowService = Depends(lambda: container.workflow_service())
140
+ ):
141
+ """Execute a single node (Node.js compatible)."""
142
+ try:
143
+ if not request.nodeId or not request.nodeType:
144
+ raise HTTPException(status_code=400, detail="nodeId and nodeType are required")
145
+
146
+ execution_id = str(uuid4())
147
+ logger.info("Executing node", node_id=request.nodeId, execution_id=execution_id)
148
+
149
+ # Execute the node using workflow service
150
+ result = await workflow_service.execute_node(
151
+ node_id=request.nodeId,
152
+ node_type=request.nodeType,
153
+ parameters=request.parameters or {},
154
+ nodes=request.nodes or [],
155
+ edges=request.edges or [],
156
+ session_id="default"
157
+ )
158
+
159
+ # Transform result to match Node.js format
160
+ if result.get("success"):
161
+ return {
162
+ "success": True,
163
+ "executionId": execution_id,
164
+ "nodeId": request.nodeId,
165
+ "nodeType": request.nodeType,
166
+ "result": result.get("result", {}),
167
+ "executionTime": result.get("execution_time", 0),
168
+ "timestamp": result.get("timestamp", datetime.now().isoformat())
169
+ }
170
+ else:
171
+ return {
172
+ "success": False,
173
+ "executionId": execution_id,
174
+ "nodeId": request.nodeId,
175
+ "nodeType": request.nodeType,
176
+ "error": result.get("error", "Unknown error"),
177
+ "executionTime": result.get("execution_time", 0),
178
+ "timestamp": result.get("timestamp", datetime.now().isoformat())
179
+ }
180
+
181
+ except Exception as e:
182
+ logger.error("Node execution error", node_id=request.nodeId, error=str(e))
183
+ raise HTTPException(status_code=500, detail=str(e))
184
+
185
+
186
+ # Execution status and output endpoints
187
+ @router.get("/api/executions/{execution_id}")
188
+ async def get_execution_status(execution_id: str):
189
+ """Get execution status (Node.js compatible)."""
190
+ # For simplicity, return success status
191
+ # In a full implementation, you'd track execution status
192
+ return {
193
+ "success": True,
194
+ "executionId": execution_id,
195
+ "status": "completed",
196
+ "timestamp": datetime.now().isoformat()
197
+ }
198
+
199
+
200
+ @router.get("/api/nodes/{node_id}/output")
201
+ async def get_node_output(
202
+ node_id: str,
203
+ workflow_service: WorkflowService = Depends(lambda: container.workflow_service())
204
+ ):
205
+ """Get node output data (Node.js compatible)."""
206
+ try:
207
+ result = await workflow_service.get_workflow_node_output(node_id)
208
+
209
+ if result.get("success"):
210
+ return {
211
+ "success": True,
212
+ "nodeId": node_id,
213
+ "output": result.get("data", {})
214
+ }
215
+ else:
216
+ return {
217
+ "success": False,
218
+ "nodeId": node_id,
219
+ "error": result.get("error", "Node output not found")
220
+ }
221
+
222
+ except Exception as e:
223
+ logger.error("Get node output error", node_id=node_id, error=str(e))
224
+ raise HTTPException(status_code=500, detail=str(e))
225
+
226
+
227
+ @router.post("/api/executions/clear")
228
+ async def clear_execution_cache():
229
+ """Clear execution cache (Node.js compatible)."""
230
+ try:
231
+ # Clear workflow service cache if implemented
232
+ return {
233
+ "success": True,
234
+ "message": "Execution cache cleared"
235
+ }
236
+ except Exception as e:
237
+ logger.error("Clear cache error", error=str(e))
238
+ raise HTTPException(status_code=500, detail=str(e))
239
+
240
+
241
+ # Legacy endpoint for backwards compatibility
242
+ @router.post("/api/execute/{node_id}")
243
+ async def legacy_execute_node(
244
+ node_id: str,
245
+ request: Dict[str, Any],
246
+ workflow_service: WorkflowService = Depends(lambda: container.workflow_service())
247
+ ):
248
+ """Legacy execution endpoint (Node.js compatible)."""
249
+ try:
250
+ node_type = request.get("nodeType")
251
+ if not node_type:
252
+ raise HTTPException(status_code=400, detail="nodeType is required")
253
+
254
+ execution_id = str(uuid4())
255
+
256
+ result = await workflow_service.execute_node(
257
+ node_id=node_id,
258
+ node_type=node_type,
259
+ parameters=request.get("parameters", {}),
260
+ nodes=request.get("nodes", []),
261
+ edges=request.get("edges", []),
262
+ session_id="default"
263
+ )
264
+
265
+ # Return in Node.js format
266
+ if result.get("success"):
267
+ return {
268
+ "success": True,
269
+ "executionId": execution_id,
270
+ "nodeId": node_id,
271
+ "nodeType": node_type,
272
+ "result": result.get("result", {}),
273
+ "executionTime": result.get("execution_time", 0),
274
+ "timestamp": result.get("timestamp", datetime.now().isoformat())
275
+ }
276
+ else:
277
+ return {
278
+ "success": False,
279
+ "executionId": execution_id,
280
+ "nodeId": node_id,
281
+ "nodeType": node_type,
282
+ "error": result.get("error", "Unknown error"),
283
+ "executionTime": result.get("execution_time", 0),
284
+ "timestamp": result.get("timestamp", datetime.now().isoformat())
285
+ }
286
+
287
+ except Exception as e:
288
+ logger.error("Legacy execution error", node_id=node_id, error=str(e))
289
+ raise HTTPException(status_code=500, detail=str(e))
@@ -0,0 +1,90 @@
1
+ """Dynamic webhook endpoint router for incoming HTTP requests.
2
+
3
+ Works like WhatsApp trigger - uses broadcaster.send_custom_event() to dispatch
4
+ to event_waiter which resolves waiting trigger nodes.
5
+ """
6
+ from fastapi import APIRouter, Request, HTTPException, Response
7
+ from fastapi.responses import JSONResponse
8
+ from typing import Dict, Any
9
+ import asyncio
10
+ import logging
11
+
12
+ from services.status_broadcaster import get_status_broadcaster
13
+
14
+ logger = logging.getLogger(__name__)
15
+ router = APIRouter(prefix="/webhook", tags=["webhook"])
16
+
17
+ # Pending responses: path -> asyncio.Future (for responseNode mode)
18
+ _pending_responses: Dict[str, asyncio.Future] = {}
19
+
20
+
21
+ def resolve_webhook_response(node_id: str, response_data: dict):
22
+ """Resolve a pending webhook response.
23
+
24
+ Called by webhookResponse node execution to send response back to caller.
25
+ Uses path from response_data to find the pending Future.
26
+ """
27
+ # Find pending response by path (stored when we started waiting)
28
+ for path, future in list(_pending_responses.items()):
29
+ if not future.done():
30
+ future.set_result(response_data)
31
+ logger.info(f"[Webhook] Response resolved for path: {path}")
32
+ return
33
+
34
+ logger.warning(f"[Webhook] No pending response found for node: {node_id}")
35
+
36
+
37
+ @router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
38
+ async def handle_webhook(path: str, request: Request):
39
+ """Handle incoming webhook requests.
40
+
41
+ Dispatches webhook_received event to trigger waiting webhookTrigger nodes.
42
+ Similar to how WhatsApp events dispatch to whatsappReceive nodes.
43
+ """
44
+ # Build webhook request data
45
+ body = await request.body()
46
+ json_body = None
47
+ content_type = request.headers.get("content-type", "")
48
+
49
+ if "application/json" in content_type and body:
50
+ try:
51
+ json_body = await request.json()
52
+ except:
53
+ pass
54
+
55
+ webhook_data = {
56
+ "method": request.method,
57
+ "path": path,
58
+ "headers": dict(request.headers),
59
+ "query": dict(request.query_params),
60
+ "body": body.decode('utf-8') if isinstance(body, bytes) else (body if body else ""),
61
+ "json": json_body
62
+ }
63
+
64
+ logger.info(f"[Webhook] Received: {request.method} /webhook/{path}")
65
+
66
+ # Dispatch event using broadcaster (same pattern as WhatsApp)
67
+ broadcaster = get_status_broadcaster()
68
+ await broadcaster.send_custom_event("webhook_received", webhook_data)
69
+
70
+ # For now, always return immediate response
71
+ # TODO: Support responseNode mode by storing Future and waiting
72
+ return JSONResponse(
73
+ content={
74
+ "status": "received",
75
+ "path": path,
76
+ "message": "Webhook received and dispatched to workflow"
77
+ },
78
+ status_code=200
79
+ )
80
+
81
+
82
+ @router.get("/")
83
+ async def list_info():
84
+ """Get webhook endpoint info."""
85
+ return {
86
+ "endpoint": "/webhook/{path}",
87
+ "description": "Send HTTP requests to trigger webhookTrigger nodes",
88
+ "usage": "Deploy a workflow with webhookTrigger node, then send requests to /webhook/{path}",
89
+ "example": "POST /webhook/my-webhook with JSON body"
90
+ }