machinaos 0.0.1 → 0.0.6

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 (420) hide show
  1. package/.env.template +71 -71
  2. package/LICENSE +21 -21
  3. package/README.md +145 -87
  4. package/bin/cli.js +62 -106
  5. package/client/.dockerignore +45 -45
  6. package/client/Dockerfile +68 -68
  7. package/client/dist/assets/index-DFSC53FP.css +1 -0
  8. package/client/dist/assets/index-fJ-1gTf5.js +613 -0
  9. package/client/dist/index.html +14 -0
  10. package/client/eslint.config.js +34 -16
  11. package/client/nginx.conf +66 -66
  12. package/client/package.json +61 -48
  13. package/client/src/App.tsx +27 -27
  14. package/client/src/Dashboard.tsx +1200 -1172
  15. package/client/src/ParameterPanel.tsx +302 -300
  16. package/client/src/components/AIAgentNode.tsx +315 -321
  17. package/client/src/components/APIKeyValidator.tsx +117 -117
  18. package/client/src/components/ClaudeChatModelNode.tsx +17 -17
  19. package/client/src/components/CredentialsModal.tsx +1200 -306
  20. package/client/src/components/GeminiChatModelNode.tsx +17 -17
  21. package/client/src/components/GenericNode.tsx +356 -356
  22. package/client/src/components/LocationParameterPanel.tsx +153 -153
  23. package/client/src/components/ModelNode.tsx +285 -285
  24. package/client/src/components/OpenAIChatModelNode.tsx +17 -17
  25. package/client/src/components/OutputPanel.tsx +470 -470
  26. package/client/src/components/ParameterRenderer.tsx +1873 -1873
  27. package/client/src/components/SkillEditorModal.tsx +3 -3
  28. package/client/src/components/SquareNode.tsx +812 -796
  29. package/client/src/components/ToolkitNode.tsx +365 -365
  30. package/client/src/components/auth/LoginPage.tsx +247 -247
  31. package/client/src/components/auth/ProtectedRoute.tsx +59 -59
  32. package/client/src/components/base/BaseChatModelNode.tsx +270 -270
  33. package/client/src/components/icons/AIProviderIcons.tsx +50 -50
  34. package/client/src/components/maps/GoogleMapsPicker.tsx +136 -136
  35. package/client/src/components/maps/MapsPreviewPanel.tsx +109 -109
  36. package/client/src/components/maps/index.ts +25 -25
  37. package/client/src/components/parameterPanel/InputSection.tsx +1094 -1094
  38. package/client/src/components/parameterPanel/LocationPanelLayout.tsx +64 -64
  39. package/client/src/components/parameterPanel/MapsSection.tsx +91 -91
  40. package/client/src/components/parameterPanel/MiddleSection.tsx +867 -571
  41. package/client/src/components/parameterPanel/OutputSection.tsx +80 -80
  42. package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +81 -81
  43. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -436
  44. package/client/src/components/parameterPanel/index.ts +41 -41
  45. package/client/src/components/shared/DataPanel.tsx +142 -142
  46. package/client/src/components/shared/JSONTreeRenderer.tsx +105 -105
  47. package/client/src/components/ui/AIResultModal.tsx +203 -203
  48. package/client/src/components/ui/ApiKeyInput.tsx +93 -0
  49. package/client/src/components/ui/CodeEditor.tsx +81 -81
  50. package/client/src/components/ui/CollapsibleSection.tsx +87 -87
  51. package/client/src/components/ui/ComponentItem.tsx +153 -153
  52. package/client/src/components/ui/ComponentPalette.tsx +320 -320
  53. package/client/src/components/ui/ConsolePanel.tsx +151 -43
  54. package/client/src/components/ui/ErrorBoundary.tsx +195 -195
  55. package/client/src/components/ui/InputNodesPanel.tsx +203 -203
  56. package/client/src/components/ui/MapSelector.tsx +313 -313
  57. package/client/src/components/ui/Modal.tsx +151 -148
  58. package/client/src/components/ui/NodeOutputPanel.tsx +1150 -1150
  59. package/client/src/components/ui/OutputDisplayPanel.tsx +381 -381
  60. package/client/src/components/ui/QRCodeDisplay.tsx +182 -0
  61. package/client/src/components/ui/TopToolbar.tsx +736 -736
  62. package/client/src/components/ui/WorkflowSidebar.tsx +293 -293
  63. package/client/src/config/antdTheme.ts +186 -186
  64. package/client/src/contexts/AuthContext.tsx +221 -221
  65. package/client/src/contexts/ThemeContext.tsx +42 -42
  66. package/client/src/contexts/WebSocketContext.tsx +2144 -1971
  67. package/client/src/factories/baseChatModelFactory.ts +255 -255
  68. package/client/src/hooks/useAndroidOperations.ts +118 -164
  69. package/client/src/hooks/useApiKeyValidation.ts +106 -106
  70. package/client/src/hooks/useApiKeys.ts +238 -238
  71. package/client/src/hooks/useAppTheme.ts +17 -17
  72. package/client/src/hooks/useComponentPalette.ts +50 -50
  73. package/client/src/hooks/useDragAndDrop.ts +123 -123
  74. package/client/src/hooks/useDragVariable.ts +88 -88
  75. package/client/src/hooks/useExecution.ts +319 -313
  76. package/client/src/hooks/useParameterPanel.ts +176 -176
  77. package/client/src/hooks/useReactFlowNodes.ts +188 -188
  78. package/client/src/hooks/useToolSchema.ts +209 -209
  79. package/client/src/hooks/useWhatsApp.ts +196 -196
  80. package/client/src/hooks/useWorkflowManagement.ts +45 -45
  81. package/client/src/index.css +314 -314
  82. package/client/src/nodeDefinitions/aiAgentNodes.ts +335 -335
  83. package/client/src/nodeDefinitions/aiModelNodes.ts +340 -340
  84. package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -383
  85. package/client/src/nodeDefinitions/chatNodes.ts +135 -135
  86. package/client/src/nodeDefinitions/codeNodes.ts +54 -54
  87. package/client/src/nodeDefinitions/index.ts +14 -14
  88. package/client/src/nodeDefinitions/locationNodes.ts +462 -462
  89. package/client/src/nodeDefinitions/schedulerNodes.ts +220 -220
  90. package/client/src/nodeDefinitions/skillNodes.ts +17 -5
  91. package/client/src/nodeDefinitions/utilityNodes.ts +284 -284
  92. package/client/src/nodeDefinitions/whatsappNodes.ts +821 -865
  93. package/client/src/nodeDefinitions.ts +101 -103
  94. package/client/src/services/dynamicParameterService.ts +95 -95
  95. package/client/src/services/execution/aiAgentExecutionService.ts +34 -34
  96. package/client/src/services/executionService.ts +227 -231
  97. package/client/src/services/workflowApi.ts +91 -91
  98. package/client/src/store/useAppStore.ts +578 -581
  99. package/client/src/styles/theme.ts +513 -508
  100. package/client/src/styles/zIndex.ts +16 -16
  101. package/client/src/types/ComponentTypes.ts +38 -38
  102. package/client/src/types/INodeProperties.ts +287 -287
  103. package/client/src/types/NodeTypes.ts +27 -27
  104. package/client/src/utils/formatters.ts +32 -32
  105. package/client/src/utils/googleMapsLoader.ts +139 -139
  106. package/client/src/utils/locationUtils.ts +84 -84
  107. package/client/src/utils/nodeUtils.ts +30 -30
  108. package/client/src/utils/workflow.ts +29 -29
  109. package/client/src/vite-env.d.ts +12 -12
  110. package/client/tailwind.config.js +59 -59
  111. package/client/tsconfig.json +25 -25
  112. package/client/vite.config.js +35 -35
  113. package/package.json +78 -70
  114. package/scripts/build.js +153 -45
  115. package/scripts/clean.js +40 -40
  116. package/scripts/start.js +234 -210
  117. package/scripts/stop.js +301 -325
  118. package/server/.dockerignore +44 -44
  119. package/server/Dockerfile +45 -45
  120. package/server/constants.py +244 -249
  121. package/server/core/cache.py +460 -460
  122. package/server/core/config.py +127 -127
  123. package/server/core/container.py +98 -98
  124. package/server/core/database.py +1296 -1210
  125. package/server/core/logging.py +313 -313
  126. package/server/main.py +288 -288
  127. package/server/middleware/__init__.py +5 -5
  128. package/server/middleware/auth.py +89 -89
  129. package/server/models/auth.py +52 -52
  130. package/server/models/cache.py +24 -24
  131. package/server/models/database.py +235 -210
  132. package/server/models/nodes.py +435 -455
  133. package/server/pyproject.toml +75 -72
  134. package/server/requirements.txt +83 -83
  135. package/server/routers/android.py +294 -294
  136. package/server/routers/auth.py +203 -203
  137. package/server/routers/database.py +150 -150
  138. package/server/routers/maps.py +141 -141
  139. package/server/routers/nodejs_compat.py +288 -288
  140. package/server/routers/webhook.py +90 -90
  141. package/server/routers/websocket.py +2239 -2127
  142. package/server/routers/whatsapp.py +761 -761
  143. package/server/routers/workflow.py +199 -199
  144. package/server/services/ai.py +2444 -2414
  145. package/server/services/android_service.py +588 -588
  146. package/server/services/auth.py +130 -130
  147. package/server/services/chat_client.py +160 -160
  148. package/server/services/deployment/manager.py +706 -706
  149. package/server/services/event_waiter.py +675 -785
  150. package/server/services/execution/executor.py +1351 -1351
  151. package/server/services/execution/models.py +1 -1
  152. package/server/services/handlers/__init__.py +122 -126
  153. package/server/services/handlers/ai.py +390 -355
  154. package/server/services/handlers/android.py +69 -260
  155. package/server/services/handlers/code.py +278 -278
  156. package/server/services/handlers/http.py +193 -193
  157. package/server/services/handlers/tools.py +146 -32
  158. package/server/services/handlers/triggers.py +107 -107
  159. package/server/services/handlers/utility.py +822 -822
  160. package/server/services/handlers/whatsapp.py +423 -476
  161. package/server/services/maps.py +288 -288
  162. package/server/services/memory_store.py +103 -103
  163. package/server/services/node_executor.py +372 -375
  164. package/server/services/scheduler.py +155 -155
  165. package/server/services/skill_loader.py +1 -1
  166. package/server/services/status_broadcaster.py +834 -826
  167. package/server/services/temporal/__init__.py +23 -23
  168. package/server/services/temporal/activities.py +344 -344
  169. package/server/services/temporal/client.py +76 -76
  170. package/server/services/temporal/executor.py +147 -147
  171. package/server/services/temporal/worker.py +251 -251
  172. package/server/services/temporal/workflow.py +355 -355
  173. package/server/services/temporal/ws_client.py +236 -236
  174. package/server/services/text.py +110 -110
  175. package/server/services/user_auth.py +172 -172
  176. package/server/services/websocket_client.py +29 -29
  177. package/server/services/workflow.py +597 -597
  178. package/server/skills/android-skill/SKILL.md +4 -4
  179. package/server/skills/code-skill/SKILL.md +123 -89
  180. package/server/skills/maps-skill/SKILL.md +3 -3
  181. package/server/skills/memory-skill/SKILL.md +1 -1
  182. package/server/skills/web-search-skill/SKILL.md +154 -0
  183. package/server/skills/whatsapp-skill/SKILL.md +3 -3
  184. package/server/uv.lock +461 -100
  185. package/server/whatsapp-rpc/.dockerignore +30 -30
  186. package/server/whatsapp-rpc/Dockerfile +44 -44
  187. package/server/whatsapp-rpc/Dockerfile.web +17 -17
  188. package/server/whatsapp-rpc/README.md +139 -139
  189. package/server/whatsapp-rpc/bin/whatsapp-rpc-server +0 -0
  190. package/server/whatsapp-rpc/cli.js +95 -95
  191. package/server/whatsapp-rpc/configs/config.yaml +6 -6
  192. package/server/whatsapp-rpc/docker-compose.yml +35 -35
  193. package/server/whatsapp-rpc/docs/API.md +410 -410
  194. package/server/whatsapp-rpc/node_modules/.package-lock.json +259 -0
  195. package/server/whatsapp-rpc/node_modules/chalk/license +9 -0
  196. package/server/whatsapp-rpc/node_modules/chalk/package.json +83 -0
  197. package/server/whatsapp-rpc/node_modules/chalk/readme.md +297 -0
  198. package/server/whatsapp-rpc/node_modules/chalk/source/index.d.ts +325 -0
  199. package/server/whatsapp-rpc/node_modules/chalk/source/index.js +225 -0
  200. package/server/whatsapp-rpc/node_modules/chalk/source/utilities.js +33 -0
  201. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  202. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  203. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  204. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  205. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  206. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  207. package/server/whatsapp-rpc/node_modules/commander/LICENSE +22 -0
  208. package/server/whatsapp-rpc/node_modules/commander/Readme.md +1148 -0
  209. package/server/whatsapp-rpc/node_modules/commander/esm.mjs +16 -0
  210. package/server/whatsapp-rpc/node_modules/commander/index.js +26 -0
  211. package/server/whatsapp-rpc/node_modules/commander/lib/argument.js +145 -0
  212. package/server/whatsapp-rpc/node_modules/commander/lib/command.js +2179 -0
  213. package/server/whatsapp-rpc/node_modules/commander/lib/error.js +43 -0
  214. package/server/whatsapp-rpc/node_modules/commander/lib/help.js +462 -0
  215. package/server/whatsapp-rpc/node_modules/commander/lib/option.js +329 -0
  216. package/server/whatsapp-rpc/node_modules/commander/lib/suggestSimilar.js +100 -0
  217. package/server/whatsapp-rpc/node_modules/commander/package-support.json +16 -0
  218. package/server/whatsapp-rpc/node_modules/commander/package.json +80 -0
  219. package/server/whatsapp-rpc/node_modules/commander/typings/esm.d.mts +3 -0
  220. package/server/whatsapp-rpc/node_modules/commander/typings/index.d.ts +884 -0
  221. package/server/whatsapp-rpc/node_modules/cross-spawn/LICENSE +21 -0
  222. package/server/whatsapp-rpc/node_modules/cross-spawn/README.md +89 -0
  223. package/server/whatsapp-rpc/node_modules/cross-spawn/index.js +39 -0
  224. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/enoent.js +59 -0
  225. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/parse.js +91 -0
  226. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/escape.js +47 -0
  227. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/readShebang.js +23 -0
  228. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/resolveCommand.js +52 -0
  229. package/server/whatsapp-rpc/node_modules/cross-spawn/package.json +73 -0
  230. package/server/whatsapp-rpc/node_modules/execa/index.d.ts +955 -0
  231. package/server/whatsapp-rpc/node_modules/execa/index.js +309 -0
  232. package/server/whatsapp-rpc/node_modules/execa/lib/command.js +119 -0
  233. package/server/whatsapp-rpc/node_modules/execa/lib/error.js +87 -0
  234. package/server/whatsapp-rpc/node_modules/execa/lib/kill.js +102 -0
  235. package/server/whatsapp-rpc/node_modules/execa/lib/pipe.js +42 -0
  236. package/server/whatsapp-rpc/node_modules/execa/lib/promise.js +36 -0
  237. package/server/whatsapp-rpc/node_modules/execa/lib/stdio.js +49 -0
  238. package/server/whatsapp-rpc/node_modules/execa/lib/stream.js +133 -0
  239. package/server/whatsapp-rpc/node_modules/execa/lib/verbose.js +19 -0
  240. package/server/whatsapp-rpc/node_modules/execa/license +9 -0
  241. package/server/whatsapp-rpc/node_modules/execa/package.json +90 -0
  242. package/server/whatsapp-rpc/node_modules/execa/readme.md +822 -0
  243. package/server/whatsapp-rpc/node_modules/get-stream/license +9 -0
  244. package/server/whatsapp-rpc/node_modules/get-stream/package.json +53 -0
  245. package/server/whatsapp-rpc/node_modules/get-stream/readme.md +291 -0
  246. package/server/whatsapp-rpc/node_modules/get-stream/source/array-buffer.js +84 -0
  247. package/server/whatsapp-rpc/node_modules/get-stream/source/array.js +32 -0
  248. package/server/whatsapp-rpc/node_modules/get-stream/source/buffer.js +20 -0
  249. package/server/whatsapp-rpc/node_modules/get-stream/source/contents.js +101 -0
  250. package/server/whatsapp-rpc/node_modules/get-stream/source/index.d.ts +119 -0
  251. package/server/whatsapp-rpc/node_modules/get-stream/source/index.js +5 -0
  252. package/server/whatsapp-rpc/node_modules/get-stream/source/string.js +36 -0
  253. package/server/whatsapp-rpc/node_modules/get-stream/source/utils.js +11 -0
  254. package/server/whatsapp-rpc/node_modules/get-them-args/LICENSE +21 -0
  255. package/server/whatsapp-rpc/node_modules/get-them-args/README.md +95 -0
  256. package/server/whatsapp-rpc/node_modules/get-them-args/index.js +97 -0
  257. package/server/whatsapp-rpc/node_modules/get-them-args/package.json +36 -0
  258. package/server/whatsapp-rpc/node_modules/human-signals/LICENSE +201 -0
  259. package/server/whatsapp-rpc/node_modules/human-signals/README.md +168 -0
  260. package/server/whatsapp-rpc/node_modules/human-signals/build/src/core.js +273 -0
  261. package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.d.ts +73 -0
  262. package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.js +70 -0
  263. package/server/whatsapp-rpc/node_modules/human-signals/build/src/realtime.js +16 -0
  264. package/server/whatsapp-rpc/node_modules/human-signals/build/src/signals.js +34 -0
  265. package/server/whatsapp-rpc/node_modules/human-signals/package.json +61 -0
  266. package/server/whatsapp-rpc/node_modules/is-stream/index.d.ts +81 -0
  267. package/server/whatsapp-rpc/node_modules/is-stream/index.js +29 -0
  268. package/server/whatsapp-rpc/node_modules/is-stream/license +9 -0
  269. package/server/whatsapp-rpc/node_modules/is-stream/package.json +44 -0
  270. package/server/whatsapp-rpc/node_modules/is-stream/readme.md +60 -0
  271. package/server/whatsapp-rpc/node_modules/isexe/LICENSE +15 -0
  272. package/server/whatsapp-rpc/node_modules/isexe/README.md +51 -0
  273. package/server/whatsapp-rpc/node_modules/isexe/index.js +57 -0
  274. package/server/whatsapp-rpc/node_modules/isexe/mode.js +41 -0
  275. package/server/whatsapp-rpc/node_modules/isexe/package.json +31 -0
  276. package/server/whatsapp-rpc/node_modules/isexe/test/basic.js +221 -0
  277. package/server/whatsapp-rpc/node_modules/isexe/windows.js +42 -0
  278. package/server/whatsapp-rpc/node_modules/kill-port/.editorconfig +12 -0
  279. package/server/whatsapp-rpc/node_modules/kill-port/.gitattributes +1 -0
  280. package/server/whatsapp-rpc/node_modules/kill-port/LICENSE +21 -0
  281. package/server/whatsapp-rpc/node_modules/kill-port/README.md +140 -0
  282. package/server/whatsapp-rpc/node_modules/kill-port/cli.js +25 -0
  283. package/server/whatsapp-rpc/node_modules/kill-port/example.js +21 -0
  284. package/server/whatsapp-rpc/node_modules/kill-port/index.js +46 -0
  285. package/server/whatsapp-rpc/node_modules/kill-port/logo.png +0 -0
  286. package/server/whatsapp-rpc/node_modules/kill-port/package.json +41 -0
  287. package/server/whatsapp-rpc/node_modules/kill-port/pnpm-lock.yaml +4606 -0
  288. package/server/whatsapp-rpc/node_modules/kill-port/test.js +16 -0
  289. package/server/whatsapp-rpc/node_modules/merge-stream/LICENSE +21 -0
  290. package/server/whatsapp-rpc/node_modules/merge-stream/README.md +78 -0
  291. package/server/whatsapp-rpc/node_modules/merge-stream/index.js +41 -0
  292. package/server/whatsapp-rpc/node_modules/merge-stream/package.json +19 -0
  293. package/server/whatsapp-rpc/node_modules/mimic-fn/index.d.ts +52 -0
  294. package/server/whatsapp-rpc/node_modules/mimic-fn/index.js +71 -0
  295. package/server/whatsapp-rpc/node_modules/mimic-fn/license +9 -0
  296. package/server/whatsapp-rpc/node_modules/mimic-fn/package.json +45 -0
  297. package/server/whatsapp-rpc/node_modules/mimic-fn/readme.md +90 -0
  298. package/server/whatsapp-rpc/node_modules/npm-run-path/index.d.ts +90 -0
  299. package/server/whatsapp-rpc/node_modules/npm-run-path/index.js +52 -0
  300. package/server/whatsapp-rpc/node_modules/npm-run-path/license +9 -0
  301. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.d.ts +31 -0
  302. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.js +12 -0
  303. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/license +9 -0
  304. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/package.json +41 -0
  305. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/readme.md +57 -0
  306. package/server/whatsapp-rpc/node_modules/npm-run-path/package.json +49 -0
  307. package/server/whatsapp-rpc/node_modules/npm-run-path/readme.md +104 -0
  308. package/server/whatsapp-rpc/node_modules/onetime/index.d.ts +59 -0
  309. package/server/whatsapp-rpc/node_modules/onetime/index.js +41 -0
  310. package/server/whatsapp-rpc/node_modules/onetime/license +9 -0
  311. package/server/whatsapp-rpc/node_modules/onetime/package.json +45 -0
  312. package/server/whatsapp-rpc/node_modules/onetime/readme.md +94 -0
  313. package/server/whatsapp-rpc/node_modules/path-key/index.d.ts +40 -0
  314. package/server/whatsapp-rpc/node_modules/path-key/index.js +16 -0
  315. package/server/whatsapp-rpc/node_modules/path-key/license +9 -0
  316. package/server/whatsapp-rpc/node_modules/path-key/package.json +39 -0
  317. package/server/whatsapp-rpc/node_modules/path-key/readme.md +61 -0
  318. package/server/whatsapp-rpc/node_modules/shebang-command/index.js +19 -0
  319. package/server/whatsapp-rpc/node_modules/shebang-command/license +9 -0
  320. package/server/whatsapp-rpc/node_modules/shebang-command/package.json +34 -0
  321. package/server/whatsapp-rpc/node_modules/shebang-command/readme.md +34 -0
  322. package/server/whatsapp-rpc/node_modules/shebang-regex/index.d.ts +22 -0
  323. package/server/whatsapp-rpc/node_modules/shebang-regex/index.js +2 -0
  324. package/server/whatsapp-rpc/node_modules/shebang-regex/license +9 -0
  325. package/server/whatsapp-rpc/node_modules/shebang-regex/package.json +35 -0
  326. package/server/whatsapp-rpc/node_modules/shebang-regex/readme.md +33 -0
  327. package/server/whatsapp-rpc/node_modules/shell-exec/LICENSE +21 -0
  328. package/server/whatsapp-rpc/node_modules/shell-exec/README.md +60 -0
  329. package/server/whatsapp-rpc/node_modules/shell-exec/index.js +47 -0
  330. package/server/whatsapp-rpc/node_modules/shell-exec/package.json +29 -0
  331. package/server/whatsapp-rpc/node_modules/signal-exit/LICENSE.txt +16 -0
  332. package/server/whatsapp-rpc/node_modules/signal-exit/README.md +74 -0
  333. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts +12 -0
  334. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts.map +1 -0
  335. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js +10 -0
  336. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js.map +1 -0
  337. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts +48 -0
  338. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts.map +1 -0
  339. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js +279 -0
  340. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js.map +1 -0
  341. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/package.json +3 -0
  342. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts +29 -0
  343. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts.map +1 -0
  344. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js +42 -0
  345. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js.map +1 -0
  346. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts +12 -0
  347. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts.map +1 -0
  348. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js +4 -0
  349. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js.map +1 -0
  350. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts +48 -0
  351. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts.map +1 -0
  352. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js +275 -0
  353. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js.map +1 -0
  354. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/package.json +3 -0
  355. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts +29 -0
  356. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts.map +1 -0
  357. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js +39 -0
  358. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js.map +1 -0
  359. package/server/whatsapp-rpc/node_modules/signal-exit/package.json +106 -0
  360. package/server/whatsapp-rpc/node_modules/strip-final-newline/index.js +14 -0
  361. package/server/whatsapp-rpc/node_modules/strip-final-newline/license +9 -0
  362. package/server/whatsapp-rpc/node_modules/strip-final-newline/package.json +43 -0
  363. package/server/whatsapp-rpc/node_modules/strip-final-newline/readme.md +35 -0
  364. package/server/whatsapp-rpc/node_modules/which/CHANGELOG.md +166 -0
  365. package/server/whatsapp-rpc/node_modules/which/LICENSE +15 -0
  366. package/server/whatsapp-rpc/node_modules/which/README.md +54 -0
  367. package/server/whatsapp-rpc/node_modules/which/bin/node-which +52 -0
  368. package/server/whatsapp-rpc/node_modules/which/package.json +43 -0
  369. package/server/whatsapp-rpc/node_modules/which/which.js +125 -0
  370. package/server/whatsapp-rpc/package-lock.json +272 -0
  371. package/server/whatsapp-rpc/package.json +30 -30
  372. package/server/whatsapp-rpc/schema.json +1294 -1294
  373. package/server/whatsapp-rpc/scripts/clean.cjs +66 -66
  374. package/server/whatsapp-rpc/scripts/cli.js +162 -162
  375. package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -166
  376. package/server/whatsapp-rpc/src/python/pyproject.toml +15 -15
  377. package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -4
  378. package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -427
  379. package/server/whatsapp-rpc/web/app.py +609 -609
  380. package/server/whatsapp-rpc/web/requirements.txt +6 -6
  381. package/server/whatsapp-rpc/web/rpc_client.py +427 -427
  382. package/server/whatsapp-rpc/web/static/openapi.yaml +59 -59
  383. package/server/whatsapp-rpc/web/templates/base.html +149 -149
  384. package/server/whatsapp-rpc/web/templates/contacts.html +240 -240
  385. package/server/whatsapp-rpc/web/templates/dashboard.html +319 -319
  386. package/server/whatsapp-rpc/web/templates/groups.html +328 -328
  387. package/server/whatsapp-rpc/web/templates/messages.html +465 -465
  388. package/server/whatsapp-rpc/web/templates/messaging.html +680 -680
  389. package/server/whatsapp-rpc/web/templates/send.html +258 -258
  390. package/server/whatsapp-rpc/web/templates/settings.html +459 -459
  391. package/client/src/components/ui/AndroidSettingsPanel.tsx +0 -401
  392. package/client/src/components/ui/WhatsAppSettingsPanel.tsx +0 -345
  393. package/client/src/nodeDefinitions/androidDeviceNodes.ts +0 -140
  394. package/docker-compose.prod.yml +0 -107
  395. package/docker-compose.yml +0 -104
  396. package/docs-MachinaOs/README.md +0 -85
  397. package/docs-MachinaOs/deployment/docker.mdx +0 -228
  398. package/docs-MachinaOs/deployment/production.mdx +0 -345
  399. package/docs-MachinaOs/docs.json +0 -75
  400. package/docs-MachinaOs/faq.mdx +0 -309
  401. package/docs-MachinaOs/favicon.svg +0 -5
  402. package/docs-MachinaOs/installation.mdx +0 -160
  403. package/docs-MachinaOs/introduction.mdx +0 -114
  404. package/docs-MachinaOs/logo/dark.svg +0 -6
  405. package/docs-MachinaOs/logo/light.svg +0 -6
  406. package/docs-MachinaOs/nodes/ai-agent.mdx +0 -216
  407. package/docs-MachinaOs/nodes/ai-models.mdx +0 -240
  408. package/docs-MachinaOs/nodes/android.mdx +0 -411
  409. package/docs-MachinaOs/nodes/overview.mdx +0 -181
  410. package/docs-MachinaOs/nodes/schedulers.mdx +0 -316
  411. package/docs-MachinaOs/nodes/webhooks.mdx +0 -330
  412. package/docs-MachinaOs/nodes/whatsapp.mdx +0 -305
  413. package/docs-MachinaOs/quickstart.mdx +0 -119
  414. package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +0 -177
  415. package/docs-MachinaOs/tutorials/android-automation.mdx +0 -242
  416. package/docs-MachinaOs/tutorials/first-workflow.mdx +0 -134
  417. package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +0 -185
  418. package/nul +0 -0
  419. package/scripts/check-ports.ps1 +0 -33
  420. package/scripts/kill-port.ps1 +0 -154
@@ -1,375 +1,372 @@
1
- """Node Executor - Single node execution with handler dispatch.
2
-
3
- Uses a registry pattern for clean handler dispatch without if-else chains.
4
- """
5
-
6
- import asyncio
7
- import time
8
- import uuid
9
- from dataclasses import dataclass
10
- from datetime import datetime
11
- from functools import partial
12
- from typing import Dict, Any, Optional, Callable, TYPE_CHECKING
13
-
14
- from core.logging import get_logger
15
- from constants import (
16
- ANDROID_SERVICE_NODE_TYPES,
17
- AI_MODEL_TYPES,
18
- AI_CHAT_MODEL_TYPES,
19
- GOOGLE_MAPS_TYPES,
20
- detect_ai_provider,
21
- )
22
- from models.nodes import validate_node_params
23
- from pydantic import ValidationError
24
- from services import event_waiter
25
- from services.handlers import (
26
- handle_ai_agent, handle_chat_agent, handle_ai_chat_model, handle_simple_memory,
27
- handle_android_device_setup, handle_android_service,
28
- handle_python_executor, handle_javascript_executor,
29
- handle_http_request, handle_webhook_response, handle_trigger_node,
30
- handle_create_map, handle_add_locations, handle_nearby_places,
31
- handle_text_generator, handle_file_handler,
32
- handle_chat_send, handle_chat_history,
33
- handle_start, handle_cron_scheduler, handle_timer, handle_console,
34
- handle_whatsapp_send, handle_whatsapp_connect, handle_whatsapp_db,
35
- handle_http_scraper, handle_file_downloader, handle_document_parser,
36
- handle_text_chunker, handle_embedding_generator, handle_vector_store,
37
- )
38
-
39
- if TYPE_CHECKING:
40
- from core.config import Settings
41
- from core.database import Database
42
- from services.ai import AIService
43
- from services.maps import MapsService
44
- from services.text import TextService
45
- from services.android_service import AndroidService
46
-
47
- logger = get_logger(__name__)
48
-
49
-
50
- @dataclass
51
- class ExecutionResult:
52
- """Standardized execution result."""
53
- success: bool
54
- node_id: str
55
- node_type: str
56
- result: Optional[Dict] = None
57
- error: Optional[str] = None
58
- execution_id: str = ""
59
- execution_time: float = 0.0
60
- timestamp: str = ""
61
-
62
- def to_dict(self) -> Dict[str, Any]:
63
- d = {
64
- "success": self.success,
65
- "node_id": self.node_id,
66
- "node_type": self.node_type,
67
- "execution_id": self.execution_id,
68
- "execution_time": self.execution_time,
69
- "timestamp": self.timestamp or datetime.now().isoformat(),
70
- }
71
- if self.success:
72
- d["result"] = self.result or {}
73
- else:
74
- d["error"] = self.error
75
- return d
76
-
77
-
78
- class NodeExecutor:
79
- """Executes individual workflow nodes using registry-based dispatch."""
80
-
81
- def __init__(
82
- self,
83
- database: "Database",
84
- ai_service: "AIService",
85
- maps_service: "MapsService",
86
- text_service: "TextService",
87
- android_service: "AndroidService",
88
- settings: "Settings",
89
- output_store: Optional[Callable] = None,
90
- ):
91
- self.database = database
92
- self.ai_service = ai_service
93
- self.maps_service = maps_service
94
- self.text_service = text_service
95
- self.android_service = android_service
96
- self.settings = settings
97
- self._output_store = output_store
98
- self._handlers = self._build_handler_registry()
99
-
100
- def _build_handler_registry(self) -> Dict[str, Callable]:
101
- """Build handler registry with service dependencies bound via partial."""
102
- registry = {
103
- # Workflow control
104
- 'start': handle_start,
105
- 'cronScheduler': handle_cron_scheduler,
106
- 'timer': handle_timer,
107
- # AI
108
- 'aiAgent': partial(handle_ai_agent, ai_service=self.ai_service, database=self.database),
109
- 'chatAgent': partial(handle_chat_agent, ai_service=self.ai_service, database=self.database),
110
- 'simpleMemory': handle_simple_memory,
111
- # Maps
112
- 'createMap': partial(handle_create_map, maps_service=self.maps_service),
113
- 'addLocations': partial(handle_add_locations, maps_service=self.maps_service),
114
- 'showNearbyPlaces': partial(handle_nearby_places, maps_service=self.maps_service),
115
- # Text
116
- 'textGenerator': partial(handle_text_generator, text_service=self.text_service),
117
- 'fileHandler': partial(handle_file_handler, text_service=self.text_service),
118
- # WhatsApp
119
- 'whatsappSend': handle_whatsapp_send,
120
- 'whatsappConnect': handle_whatsapp_connect,
121
- 'whatsappDb': handle_whatsapp_db,
122
- # Chat
123
- 'chatSend': handle_chat_send,
124
- 'chatHistory': handle_chat_history,
125
- # HTTP
126
- 'httpRequest': handle_http_request,
127
- # Android setup
128
- 'androidDeviceSetup': partial(handle_android_device_setup, settings=self.settings),
129
- # Document processing
130
- 'httpScraper': handle_http_scraper,
131
- 'fileDownloader': handle_file_downloader,
132
- 'documentParser': handle_document_parser,
133
- 'textChunker': handle_text_chunker,
134
- 'embeddingGenerator': handle_embedding_generator,
135
- 'vectorStore': handle_vector_store,
136
- # Note: 'console' handled in _dispatch with connected_outputs
137
- }
138
-
139
- # Register AI chat models
140
- for node_type in AI_CHAT_MODEL_TYPES:
141
- registry[node_type] = partial(handle_ai_chat_model, ai_service=self.ai_service)
142
-
143
- # Register Android services
144
- for node_type in ANDROID_SERVICE_NODE_TYPES:
145
- registry[node_type] = partial(handle_android_service, android_service=self.android_service)
146
-
147
- return registry
148
-
149
- async def execute(
150
- self,
151
- node_id: str,
152
- node_type: str,
153
- parameters: Dict[str, Any],
154
- context: Dict[str, Any],
155
- resolve_params_fn: Optional[Callable] = None,
156
- ) -> Dict[str, Any]:
157
- """Execute a single workflow node."""
158
- start_time = time.time()
159
- session_id = context.get('session_id', 'default')
160
- execution_id = context.get('execution_id') or str(uuid.uuid4())[:8]
161
-
162
- try:
163
- # Load, validate, enhance parameters
164
- params = await self._prepare_parameters(node_id, node_type, parameters, session_id)
165
-
166
- # Resolve templates if resolver provided
167
- nodes = context.get('nodes')
168
- edges = context.get('edges')
169
- logger.debug(f"[NodeExecutor] Template resolution check: resolve_fn={resolve_params_fn is not None}, nodes={len(nodes) if nodes else 'None'}, edges={len(edges) if edges else 'None'}")
170
-
171
- if resolve_params_fn and nodes is not None and edges is not None:
172
- logger.debug(f"[NodeExecutor] Before resolution: params={list(params.keys())}")
173
- params = await resolve_params_fn(params, node_id, nodes, edges, session_id)
174
- logger.debug(f"[NodeExecutor] After resolution: params keys={list(params.keys())}")
175
-
176
- # Build handler context
177
- handler_ctx = {
178
- **context,
179
- "start_time": start_time,
180
- "execution_id": execution_id,
181
- }
182
- logger.info("NodeExecutor context", node_id=node_id, workflow_id=context.get('workflow_id'))
183
-
184
- # Execute via registry or special handlers
185
- result = await self._dispatch(node_id, node_type, params, handler_ctx)
186
- result['execution_id'] = execution_id
187
-
188
- # Store output if successful
189
- if result.get('success') and self._output_store:
190
- output_data = result.get('result', {})
191
-
192
- # For Android service nodes, extract the nested 'data' field for cleaner template access
193
- # This allows {{batterymonitor.battery_level}} instead of {{batterymonitor.data.battery_level}}
194
- if node_type in ANDROID_SERVICE_NODE_TYPES and isinstance(output_data, dict):
195
- # Flatten: promote 'data' contents to top level while preserving metadata
196
- nested_data = output_data.get('data', {})
197
- if isinstance(nested_data, dict):
198
- # Merge nested data with metadata (service_id, action, timestamp, etc.)
199
- output_data = {**output_data, **nested_data}
200
- logger.debug(f"[NodeExecutor] Flattened Android output for {node_id}: keys={list(output_data.keys())}")
201
-
202
- await self._output_store(session_id, node_id, "output_0", output_data)
203
-
204
- return result
205
-
206
- except asyncio.CancelledError:
207
- return ExecutionResult(False, node_id, node_type, error="Cancelled",
208
- execution_id=execution_id, execution_time=time.time()-start_time).to_dict()
209
- except Exception as e:
210
- logger.error("Node execution error", node_id=node_id, error=str(e))
211
- return ExecutionResult(False, node_id, node_type, error=str(e),
212
- execution_id=execution_id, execution_time=time.time()-start_time).to_dict()
213
-
214
- async def _prepare_parameters(self, node_id: str, node_type: str, params: Dict, session_id: str) -> Dict:
215
- """Load from DB, validate, inject API keys."""
216
- # Merge with DB parameters (DB provides defaults, frontend can override)
217
- db_params = await self.database.get_node_parameters(node_id) or {}
218
- merged = {**db_params, **params} if params else db_params
219
-
220
- # Validate
221
- try:
222
- validated = validate_node_params(node_type, merged)
223
- merged = {**merged, **validated.model_dump(by_alias=True, exclude_unset=True)}
224
- except ValidationError as e:
225
- logger.warning("Validation warning", node_type=node_type, errors=str(e))
226
-
227
- # Inject API keys
228
- return await self._inject_api_keys(node_type, merged)
229
-
230
- async def _inject_api_keys(self, node_type: str, params: Dict) -> Dict:
231
- """Auto-inject API keys for AI and Maps nodes."""
232
- result = params.copy()
233
-
234
- if node_type in AI_MODEL_TYPES:
235
- provider = detect_ai_provider(node_type, params)
236
- if not result.get('api_key') and not result.get('apiKey'):
237
- key = await self.ai_service.auth.get_api_key(provider, "default")
238
- if key:
239
- result['api_key'] = key
240
- if not result.get('model'):
241
- models = await self.ai_service.auth.get_stored_models(provider, "default")
242
- if models:
243
- result['model'] = models[0]
244
-
245
- elif node_type in GOOGLE_MAPS_TYPES:
246
- if not result.get('api_key'):
247
- # Try database first, then fall back to environment variable
248
- key = await self.ai_service.auth.get_api_key("google_maps", "default")
249
- if key:
250
- result['api_key'] = key
251
- elif self.settings.google_maps_api_key:
252
- result['api_key'] = self.settings.google_maps_api_key
253
-
254
- return result
255
-
256
- async def _dispatch(self, node_id: str, node_type: str, params: Dict, context: Dict) -> Dict:
257
- """Dispatch to handler from registry or special handlers."""
258
-
259
- # Check registry first
260
- handler = self._handlers.get(node_type)
261
- if handler:
262
- return await handler(node_id, node_type, params, context)
263
-
264
- # Special handlers needing connected outputs
265
- if node_type in ('pythonExecutor', 'javascriptExecutor', 'webhookResponse', 'console'):
266
- outputs, source_nodes = await self._get_connected_outputs_with_info(context, node_id)
267
- if node_type == 'console':
268
- return await handle_console(node_id, node_type, params, context, outputs, source_nodes)
269
- handlers = {
270
- 'pythonExecutor': handle_python_executor,
271
- 'javascriptExecutor': handle_javascript_executor,
272
- 'webhookResponse': handle_webhook_response,
273
- }
274
- return await handlers[node_type](node_id, node_type, params, context, outputs)
275
-
276
- # Trigger nodes
277
- if event_waiter.is_trigger_node(node_type):
278
- return await handle_trigger_node(node_id, node_type, params, context)
279
-
280
- # Fallback
281
- return {
282
- "success": True,
283
- "node_id": node_id,
284
- "node_type": node_type,
285
- "result": {"message": f"Node {node_id} executed", "parameters": params},
286
- "execution_time": time.time() - context.get('start_time', time.time()),
287
- "timestamp": datetime.now().isoformat()
288
- }
289
-
290
- async def _get_connected_outputs(self, context: Dict, node_id: str) -> Dict[str, Any]:
291
- """Get outputs from connected upstream nodes."""
292
- get_output = context.get('get_output_fn')
293
- if not get_output:
294
- return {}
295
-
296
- nodes = context.get('nodes', [])
297
- edges = context.get('edges', [])
298
- session_id = context.get('session_id', 'default')
299
- result = {}
300
-
301
- for edge in edges:
302
- if edge.get('target') == node_id:
303
- source_id = edge.get('source')
304
- output = await get_output(session_id, source_id, "output_0")
305
- if output:
306
- source = next((n for n in nodes if n.get('id') == source_id), {})
307
- result[source.get('type', 'unknown')] = output
308
-
309
- return result
310
-
311
- def _get_source_nodes_info(self, context: Dict, node_id: str) -> list:
312
- """Get source node info (id, type, label) for edges targeting this node.
313
-
314
- This is used for display purposes (e.g., showing source in Console panel).
315
- Does NOT filter by output availability - just returns edge source info.
316
- """
317
- nodes = context.get('nodes', [])
318
- edges = context.get('edges', [])
319
- source_nodes = []
320
-
321
- for edge in edges:
322
- if edge.get('target') == node_id:
323
- source_id = edge.get('source')
324
- source = next((n for n in nodes if n.get('id') == source_id), {})
325
- source_type = source.get('type', 'unknown')
326
- source_data = source.get('data', {})
327
- source_label = source_data.get('label') or source_type
328
- source_nodes.append({
329
- 'id': source_id,
330
- 'type': source_type,
331
- 'label': source_label
332
- })
333
-
334
- return source_nodes
335
-
336
- async def _get_connected_outputs_with_info(self, context: Dict, node_id: str) -> tuple:
337
- """Get outputs from connected upstream nodes with source node info.
338
-
339
- Returns:
340
- Tuple of (outputs dict, source_nodes list with id/type/label info)
341
- """
342
- get_output = context.get('get_output_fn')
343
- if not get_output:
344
- logger.warning(f"[_get_connected_outputs_with_info] No get_output_fn in context for {node_id}")
345
- return {}, []
346
-
347
- nodes = context.get('nodes', [])
348
- edges = context.get('edges', [])
349
- session_id = context.get('session_id', 'default')
350
- outputs = {}
351
- source_nodes = []
352
-
353
- logger.debug(f"[_get_connected_outputs_with_info] node_id={node_id}, edges={len(edges)}, session={session_id}")
354
-
355
- for edge in edges:
356
- if edge.get('target') == node_id:
357
- source_id = edge.get('source')
358
- logger.debug(f"[_get_connected_outputs_with_info] Found edge from {source_id} to {node_id}")
359
- output = await get_output(session_id, source_id, "output_0")
360
- logger.debug(f"[_get_connected_outputs_with_info] Output from {source_id}: {'FOUND' if output else 'NOT FOUND'}")
361
- if output:
362
- source = next((n for n in nodes if n.get('id') == source_id), {})
363
- source_type = source.get('type', 'unknown')
364
- outputs[source_type] = output
365
- # Get label from node data if available
366
- source_data = source.get('data', {})
367
- source_label = source_data.get('label') or source_type
368
- source_nodes.append({
369
- 'id': source_id,
370
- 'type': source_type,
371
- 'label': source_label
372
- })
373
-
374
- logger.debug(f"[_get_connected_outputs_with_info] Returning {len(outputs)} outputs, {len(source_nodes)} source_nodes")
375
- return outputs, source_nodes
1
+ """Node Executor - Single node execution with handler dispatch.
2
+
3
+ Uses a registry pattern for clean handler dispatch without if-else chains.
4
+ """
5
+
6
+ import asyncio
7
+ import time
8
+ import uuid
9
+ from dataclasses import dataclass
10
+ from datetime import datetime
11
+ from functools import partial
12
+ from typing import Dict, Any, Optional, Callable, TYPE_CHECKING
13
+
14
+ from core.logging import get_logger
15
+ from constants import (
16
+ ANDROID_SERVICE_NODE_TYPES,
17
+ AI_MODEL_TYPES,
18
+ AI_CHAT_MODEL_TYPES,
19
+ GOOGLE_MAPS_TYPES,
20
+ detect_ai_provider,
21
+ )
22
+ from models.nodes import validate_node_params
23
+ from pydantic import ValidationError
24
+ from services import event_waiter
25
+ from services.handlers import (
26
+ handle_ai_agent, handle_chat_agent, handle_ai_chat_model, handle_simple_memory,
27
+ handle_android_service,
28
+ handle_python_executor, handle_javascript_executor,
29
+ handle_http_request, handle_webhook_response, handle_trigger_node,
30
+ handle_create_map, handle_add_locations, handle_nearby_places,
31
+ handle_text_generator, handle_file_handler,
32
+ handle_chat_send, handle_chat_history,
33
+ handle_start, handle_cron_scheduler, handle_timer, handle_console,
34
+ handle_whatsapp_send, handle_whatsapp_db,
35
+ handle_http_scraper, handle_file_downloader, handle_document_parser,
36
+ handle_text_chunker, handle_embedding_generator, handle_vector_store,
37
+ )
38
+
39
+ if TYPE_CHECKING:
40
+ from core.config import Settings
41
+ from core.database import Database
42
+ from services.ai import AIService
43
+ from services.maps import MapsService
44
+ from services.text import TextService
45
+ from services.android_service import AndroidService
46
+
47
+ logger = get_logger(__name__)
48
+
49
+
50
+ @dataclass
51
+ class ExecutionResult:
52
+ """Standardized execution result."""
53
+ success: bool
54
+ node_id: str
55
+ node_type: str
56
+ result: Optional[Dict] = None
57
+ error: Optional[str] = None
58
+ execution_id: str = ""
59
+ execution_time: float = 0.0
60
+ timestamp: str = ""
61
+
62
+ def to_dict(self) -> Dict[str, Any]:
63
+ d = {
64
+ "success": self.success,
65
+ "node_id": self.node_id,
66
+ "node_type": self.node_type,
67
+ "execution_id": self.execution_id,
68
+ "execution_time": self.execution_time,
69
+ "timestamp": self.timestamp or datetime.now().isoformat(),
70
+ }
71
+ if self.success:
72
+ d["result"] = self.result or {}
73
+ else:
74
+ d["error"] = self.error
75
+ return d
76
+
77
+
78
+ class NodeExecutor:
79
+ """Executes individual workflow nodes using registry-based dispatch."""
80
+
81
+ def __init__(
82
+ self,
83
+ database: "Database",
84
+ ai_service: "AIService",
85
+ maps_service: "MapsService",
86
+ text_service: "TextService",
87
+ android_service: "AndroidService",
88
+ settings: "Settings",
89
+ output_store: Optional[Callable] = None,
90
+ ):
91
+ self.database = database
92
+ self.ai_service = ai_service
93
+ self.maps_service = maps_service
94
+ self.text_service = text_service
95
+ self.android_service = android_service
96
+ self.settings = settings
97
+ self._output_store = output_store
98
+ self._handlers = self._build_handler_registry()
99
+
100
+ def _build_handler_registry(self) -> Dict[str, Callable]:
101
+ """Build handler registry with service dependencies bound via partial."""
102
+ registry = {
103
+ # Workflow control
104
+ 'start': handle_start,
105
+ 'cronScheduler': handle_cron_scheduler,
106
+ 'timer': handle_timer,
107
+ # AI
108
+ 'aiAgent': partial(handle_ai_agent, ai_service=self.ai_service, database=self.database),
109
+ 'chatAgent': partial(handle_chat_agent, ai_service=self.ai_service, database=self.database),
110
+ 'simpleMemory': handle_simple_memory,
111
+ # Maps
112
+ 'createMap': partial(handle_create_map, maps_service=self.maps_service),
113
+ 'addLocations': partial(handle_add_locations, maps_service=self.maps_service),
114
+ 'showNearbyPlaces': partial(handle_nearby_places, maps_service=self.maps_service),
115
+ # Text
116
+ 'textGenerator': partial(handle_text_generator, text_service=self.text_service),
117
+ 'fileHandler': partial(handle_file_handler, text_service=self.text_service),
118
+ # WhatsApp
119
+ 'whatsappSend': handle_whatsapp_send,
120
+ 'whatsappDb': handle_whatsapp_db,
121
+ # Chat
122
+ 'chatSend': handle_chat_send,
123
+ 'chatHistory': handle_chat_history,
124
+ # HTTP
125
+ 'httpRequest': handle_http_request,
126
+ # Document processing
127
+ 'httpScraper': handle_http_scraper,
128
+ 'fileDownloader': handle_file_downloader,
129
+ 'documentParser': handle_document_parser,
130
+ 'textChunker': handle_text_chunker,
131
+ 'embeddingGenerator': handle_embedding_generator,
132
+ 'vectorStore': handle_vector_store,
133
+ # Note: 'console' handled in _dispatch with connected_outputs
134
+ }
135
+
136
+ # Register AI chat models
137
+ for node_type in AI_CHAT_MODEL_TYPES:
138
+ registry[node_type] = partial(handle_ai_chat_model, ai_service=self.ai_service)
139
+
140
+ # Register Android services
141
+ for node_type in ANDROID_SERVICE_NODE_TYPES:
142
+ registry[node_type] = partial(handle_android_service, android_service=self.android_service)
143
+
144
+ return registry
145
+
146
+ async def execute(
147
+ self,
148
+ node_id: str,
149
+ node_type: str,
150
+ parameters: Dict[str, Any],
151
+ context: Dict[str, Any],
152
+ resolve_params_fn: Optional[Callable] = None,
153
+ ) -> Dict[str, Any]:
154
+ """Execute a single workflow node."""
155
+ start_time = time.time()
156
+ session_id = context.get('session_id', 'default')
157
+ execution_id = context.get('execution_id') or str(uuid.uuid4())[:8]
158
+
159
+ try:
160
+ # Load, validate, enhance parameters
161
+ params = await self._prepare_parameters(node_id, node_type, parameters, session_id)
162
+
163
+ # Resolve templates if resolver provided
164
+ nodes = context.get('nodes')
165
+ edges = context.get('edges')
166
+ logger.debug(f"[NodeExecutor] Template resolution check: resolve_fn={resolve_params_fn is not None}, nodes={len(nodes) if nodes else 'None'}, edges={len(edges) if edges else 'None'}")
167
+
168
+ if resolve_params_fn and nodes is not None and edges is not None:
169
+ logger.debug(f"[NodeExecutor] Before resolution: params={list(params.keys())}")
170
+ params = await resolve_params_fn(params, node_id, nodes, edges, session_id)
171
+ logger.debug(f"[NodeExecutor] After resolution: params keys={list(params.keys())}")
172
+
173
+ # Build handler context
174
+ handler_ctx = {
175
+ **context,
176
+ "start_time": start_time,
177
+ "execution_id": execution_id,
178
+ }
179
+ logger.info("NodeExecutor context", node_id=node_id, workflow_id=context.get('workflow_id'))
180
+
181
+ # Execute via registry or special handlers
182
+ result = await self._dispatch(node_id, node_type, params, handler_ctx)
183
+ result['execution_id'] = execution_id
184
+
185
+ # Store output if successful
186
+ if result.get('success') and self._output_store:
187
+ output_data = result.get('result', {})
188
+
189
+ # For Android service nodes, extract the nested 'data' field for cleaner template access
190
+ # This allows {{batterymonitor.battery_level}} instead of {{batterymonitor.data.battery_level}}
191
+ if node_type in ANDROID_SERVICE_NODE_TYPES and isinstance(output_data, dict):
192
+ # Flatten: promote 'data' contents to top level while preserving metadata
193
+ nested_data = output_data.get('data', {})
194
+ if isinstance(nested_data, dict):
195
+ # Merge nested data with metadata (service_id, action, timestamp, etc.)
196
+ output_data = {**output_data, **nested_data}
197
+ logger.debug(f"[NodeExecutor] Flattened Android output for {node_id}: keys={list(output_data.keys())}")
198
+
199
+ await self._output_store(session_id, node_id, "output_0", output_data)
200
+
201
+ return result
202
+
203
+ except asyncio.CancelledError:
204
+ return ExecutionResult(False, node_id, node_type, error="Cancelled",
205
+ execution_id=execution_id, execution_time=time.time()-start_time).to_dict()
206
+ except Exception as e:
207
+ logger.error("Node execution error", node_id=node_id, error=str(e))
208
+ return ExecutionResult(False, node_id, node_type, error=str(e),
209
+ execution_id=execution_id, execution_time=time.time()-start_time).to_dict()
210
+
211
+ async def _prepare_parameters(self, node_id: str, node_type: str, params: Dict, session_id: str) -> Dict:
212
+ """Load from DB, validate, inject API keys."""
213
+ # Merge with DB parameters (DB provides defaults, frontend can override)
214
+ db_params = await self.database.get_node_parameters(node_id) or {}
215
+ merged = {**db_params, **params} if params else db_params
216
+
217
+ # Validate
218
+ try:
219
+ validated = validate_node_params(node_type, merged)
220
+ merged = {**merged, **validated.model_dump(by_alias=True, exclude_unset=True)}
221
+ except ValidationError as e:
222
+ logger.warning("Validation warning", node_type=node_type, errors=str(e))
223
+
224
+ # Inject API keys
225
+ return await self._inject_api_keys(node_type, merged)
226
+
227
+ async def _inject_api_keys(self, node_type: str, params: Dict) -> Dict:
228
+ """Auto-inject API keys for AI and Maps nodes."""
229
+ result = params.copy()
230
+
231
+ if node_type in AI_MODEL_TYPES:
232
+ provider = detect_ai_provider(node_type, params)
233
+ if not result.get('api_key') and not result.get('apiKey'):
234
+ key = await self.ai_service.auth.get_api_key(provider, "default")
235
+ if key:
236
+ result['api_key'] = key
237
+ if not result.get('model'):
238
+ models = await self.ai_service.auth.get_stored_models(provider, "default")
239
+ if models:
240
+ result['model'] = models[0]
241
+
242
+ elif node_type in GOOGLE_MAPS_TYPES:
243
+ if not result.get('api_key'):
244
+ # Try database first, then fall back to environment variable
245
+ key = await self.ai_service.auth.get_api_key("google_maps", "default")
246
+ if key:
247
+ result['api_key'] = key
248
+ elif self.settings.google_maps_api_key:
249
+ result['api_key'] = self.settings.google_maps_api_key
250
+
251
+ return result
252
+
253
+ async def _dispatch(self, node_id: str, node_type: str, params: Dict, context: Dict) -> Dict:
254
+ """Dispatch to handler from registry or special handlers."""
255
+
256
+ # Check registry first
257
+ handler = self._handlers.get(node_type)
258
+ if handler:
259
+ return await handler(node_id, node_type, params, context)
260
+
261
+ # Special handlers needing connected outputs
262
+ if node_type in ('pythonExecutor', 'javascriptExecutor', 'webhookResponse', 'console'):
263
+ outputs, source_nodes = await self._get_connected_outputs_with_info(context, node_id)
264
+ if node_type == 'console':
265
+ return await handle_console(node_id, node_type, params, context, outputs, source_nodes)
266
+ handlers = {
267
+ 'pythonExecutor': handle_python_executor,
268
+ 'javascriptExecutor': handle_javascript_executor,
269
+ 'webhookResponse': handle_webhook_response,
270
+ }
271
+ return await handlers[node_type](node_id, node_type, params, context, outputs)
272
+
273
+ # Trigger nodes
274
+ if event_waiter.is_trigger_node(node_type):
275
+ return await handle_trigger_node(node_id, node_type, params, context)
276
+
277
+ # Fallback
278
+ return {
279
+ "success": True,
280
+ "node_id": node_id,
281
+ "node_type": node_type,
282
+ "result": {"message": f"Node {node_id} executed", "parameters": params},
283
+ "execution_time": time.time() - context.get('start_time', time.time()),
284
+ "timestamp": datetime.now().isoformat()
285
+ }
286
+
287
+ async def _get_connected_outputs(self, context: Dict, node_id: str) -> Dict[str, Any]:
288
+ """Get outputs from connected upstream nodes."""
289
+ get_output = context.get('get_output_fn')
290
+ if not get_output:
291
+ return {}
292
+
293
+ nodes = context.get('nodes', [])
294
+ edges = context.get('edges', [])
295
+ session_id = context.get('session_id', 'default')
296
+ result = {}
297
+
298
+ for edge in edges:
299
+ if edge.get('target') == node_id:
300
+ source_id = edge.get('source')
301
+ output = await get_output(session_id, source_id, "output_0")
302
+ if output:
303
+ source = next((n for n in nodes if n.get('id') == source_id), {})
304
+ result[source.get('type', 'unknown')] = output
305
+
306
+ return result
307
+
308
+ def _get_source_nodes_info(self, context: Dict, node_id: str) -> list:
309
+ """Get source node info (id, type, label) for edges targeting this node.
310
+
311
+ This is used for display purposes (e.g., showing source in Console panel).
312
+ Does NOT filter by output availability - just returns edge source info.
313
+ """
314
+ nodes = context.get('nodes', [])
315
+ edges = context.get('edges', [])
316
+ source_nodes = []
317
+
318
+ for edge in edges:
319
+ if edge.get('target') == node_id:
320
+ source_id = edge.get('source')
321
+ source = next((n for n in nodes if n.get('id') == source_id), {})
322
+ source_type = source.get('type', 'unknown')
323
+ source_data = source.get('data', {})
324
+ source_label = source_data.get('label') or source_type
325
+ source_nodes.append({
326
+ 'id': source_id,
327
+ 'type': source_type,
328
+ 'label': source_label
329
+ })
330
+
331
+ return source_nodes
332
+
333
+ async def _get_connected_outputs_with_info(self, context: Dict, node_id: str) -> tuple:
334
+ """Get outputs from connected upstream nodes with source node info.
335
+
336
+ Returns:
337
+ Tuple of (outputs dict, source_nodes list with id/type/label info)
338
+ """
339
+ get_output = context.get('get_output_fn')
340
+ if not get_output:
341
+ logger.warning(f"[_get_connected_outputs_with_info] No get_output_fn in context for {node_id}")
342
+ return {}, []
343
+
344
+ nodes = context.get('nodes', [])
345
+ edges = context.get('edges', [])
346
+ session_id = context.get('session_id', 'default')
347
+ outputs = {}
348
+ source_nodes = []
349
+
350
+ logger.debug(f"[_get_connected_outputs_with_info] node_id={node_id}, edges={len(edges)}, session={session_id}")
351
+
352
+ for edge in edges:
353
+ if edge.get('target') == node_id:
354
+ source_id = edge.get('source')
355
+ logger.debug(f"[_get_connected_outputs_with_info] Found edge from {source_id} to {node_id}")
356
+ output = await get_output(session_id, source_id, "output_0")
357
+ logger.debug(f"[_get_connected_outputs_with_info] Output from {source_id}: {'FOUND' if output else 'NOT FOUND'}")
358
+ if output:
359
+ source = next((n for n in nodes if n.get('id') == source_id), {})
360
+ source_type = source.get('type', 'unknown')
361
+ outputs[source_type] = output
362
+ # Get label from node data if available
363
+ source_data = source.get('data', {})
364
+ source_label = source_data.get('label') or source_type
365
+ source_nodes.append({
366
+ 'id': source_id,
367
+ 'type': source_type,
368
+ 'label': source_label
369
+ })
370
+
371
+ logger.debug(f"[_get_connected_outputs_with_info] Returning {len(outputs)} outputs, {len(source_nodes)} source_nodes")
372
+ return outputs, source_nodes