machinaos 0.0.1 → 0.0.7

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 (422) hide show
  1. package/.env.template +71 -71
  2. package/LICENSE +21 -21
  3. package/README.md +163 -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/install.ps1 +308 -0
  114. package/install.sh +343 -0
  115. package/package.json +81 -70
  116. package/scripts/build.js +174 -51
  117. package/scripts/clean.js +40 -40
  118. package/scripts/start.js +234 -210
  119. package/scripts/stop.js +301 -325
  120. package/server/.dockerignore +44 -44
  121. package/server/Dockerfile +45 -45
  122. package/server/constants.py +244 -249
  123. package/server/core/cache.py +460 -460
  124. package/server/core/config.py +127 -127
  125. package/server/core/container.py +98 -98
  126. package/server/core/database.py +1296 -1210
  127. package/server/core/logging.py +313 -313
  128. package/server/main.py +288 -288
  129. package/server/middleware/__init__.py +5 -5
  130. package/server/middleware/auth.py +89 -89
  131. package/server/models/auth.py +52 -52
  132. package/server/models/cache.py +24 -24
  133. package/server/models/database.py +235 -210
  134. package/server/models/nodes.py +435 -455
  135. package/server/pyproject.toml +75 -72
  136. package/server/requirements.txt +83 -83
  137. package/server/routers/android.py +294 -294
  138. package/server/routers/auth.py +203 -203
  139. package/server/routers/database.py +150 -150
  140. package/server/routers/maps.py +141 -141
  141. package/server/routers/nodejs_compat.py +288 -288
  142. package/server/routers/webhook.py +90 -90
  143. package/server/routers/websocket.py +2239 -2127
  144. package/server/routers/whatsapp.py +761 -761
  145. package/server/routers/workflow.py +199 -199
  146. package/server/services/ai.py +2444 -2414
  147. package/server/services/android_service.py +588 -588
  148. package/server/services/auth.py +130 -130
  149. package/server/services/chat_client.py +160 -160
  150. package/server/services/deployment/manager.py +706 -706
  151. package/server/services/event_waiter.py +675 -785
  152. package/server/services/execution/executor.py +1351 -1351
  153. package/server/services/execution/models.py +1 -1
  154. package/server/services/handlers/__init__.py +122 -126
  155. package/server/services/handlers/ai.py +390 -355
  156. package/server/services/handlers/android.py +69 -260
  157. package/server/services/handlers/code.py +278 -278
  158. package/server/services/handlers/http.py +193 -193
  159. package/server/services/handlers/tools.py +146 -32
  160. package/server/services/handlers/triggers.py +107 -107
  161. package/server/services/handlers/utility.py +822 -822
  162. package/server/services/handlers/whatsapp.py +423 -476
  163. package/server/services/maps.py +288 -288
  164. package/server/services/memory_store.py +103 -103
  165. package/server/services/node_executor.py +372 -375
  166. package/server/services/scheduler.py +155 -155
  167. package/server/services/skill_loader.py +1 -1
  168. package/server/services/status_broadcaster.py +834 -826
  169. package/server/services/temporal/__init__.py +23 -23
  170. package/server/services/temporal/activities.py +344 -344
  171. package/server/services/temporal/client.py +76 -76
  172. package/server/services/temporal/executor.py +147 -147
  173. package/server/services/temporal/worker.py +251 -251
  174. package/server/services/temporal/workflow.py +355 -355
  175. package/server/services/temporal/ws_client.py +236 -236
  176. package/server/services/text.py +110 -110
  177. package/server/services/user_auth.py +172 -172
  178. package/server/services/websocket_client.py +29 -29
  179. package/server/services/workflow.py +597 -597
  180. package/server/skills/android-skill/SKILL.md +4 -4
  181. package/server/skills/code-skill/SKILL.md +123 -89
  182. package/server/skills/maps-skill/SKILL.md +3 -3
  183. package/server/skills/memory-skill/SKILL.md +1 -1
  184. package/server/skills/web-search-skill/SKILL.md +154 -0
  185. package/server/skills/whatsapp-skill/SKILL.md +3 -3
  186. package/server/uv.lock +461 -100
  187. package/server/whatsapp-rpc/.dockerignore +30 -30
  188. package/server/whatsapp-rpc/Dockerfile +44 -44
  189. package/server/whatsapp-rpc/Dockerfile.web +17 -17
  190. package/server/whatsapp-rpc/README.md +139 -139
  191. package/server/whatsapp-rpc/bin/whatsapp-rpc-server +0 -0
  192. package/server/whatsapp-rpc/cli.js +95 -95
  193. package/server/whatsapp-rpc/configs/config.yaml +6 -6
  194. package/server/whatsapp-rpc/docker-compose.yml +35 -35
  195. package/server/whatsapp-rpc/docs/API.md +410 -410
  196. package/server/whatsapp-rpc/node_modules/.package-lock.json +259 -0
  197. package/server/whatsapp-rpc/node_modules/chalk/license +9 -0
  198. package/server/whatsapp-rpc/node_modules/chalk/package.json +83 -0
  199. package/server/whatsapp-rpc/node_modules/chalk/readme.md +297 -0
  200. package/server/whatsapp-rpc/node_modules/chalk/source/index.d.ts +325 -0
  201. package/server/whatsapp-rpc/node_modules/chalk/source/index.js +225 -0
  202. package/server/whatsapp-rpc/node_modules/chalk/source/utilities.js +33 -0
  203. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  204. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  205. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  206. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  207. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  208. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  209. package/server/whatsapp-rpc/node_modules/commander/LICENSE +22 -0
  210. package/server/whatsapp-rpc/node_modules/commander/Readme.md +1148 -0
  211. package/server/whatsapp-rpc/node_modules/commander/esm.mjs +16 -0
  212. package/server/whatsapp-rpc/node_modules/commander/index.js +26 -0
  213. package/server/whatsapp-rpc/node_modules/commander/lib/argument.js +145 -0
  214. package/server/whatsapp-rpc/node_modules/commander/lib/command.js +2179 -0
  215. package/server/whatsapp-rpc/node_modules/commander/lib/error.js +43 -0
  216. package/server/whatsapp-rpc/node_modules/commander/lib/help.js +462 -0
  217. package/server/whatsapp-rpc/node_modules/commander/lib/option.js +329 -0
  218. package/server/whatsapp-rpc/node_modules/commander/lib/suggestSimilar.js +100 -0
  219. package/server/whatsapp-rpc/node_modules/commander/package-support.json +16 -0
  220. package/server/whatsapp-rpc/node_modules/commander/package.json +80 -0
  221. package/server/whatsapp-rpc/node_modules/commander/typings/esm.d.mts +3 -0
  222. package/server/whatsapp-rpc/node_modules/commander/typings/index.d.ts +884 -0
  223. package/server/whatsapp-rpc/node_modules/cross-spawn/LICENSE +21 -0
  224. package/server/whatsapp-rpc/node_modules/cross-spawn/README.md +89 -0
  225. package/server/whatsapp-rpc/node_modules/cross-spawn/index.js +39 -0
  226. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/enoent.js +59 -0
  227. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/parse.js +91 -0
  228. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/escape.js +47 -0
  229. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/readShebang.js +23 -0
  230. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/resolveCommand.js +52 -0
  231. package/server/whatsapp-rpc/node_modules/cross-spawn/package.json +73 -0
  232. package/server/whatsapp-rpc/node_modules/execa/index.d.ts +955 -0
  233. package/server/whatsapp-rpc/node_modules/execa/index.js +309 -0
  234. package/server/whatsapp-rpc/node_modules/execa/lib/command.js +119 -0
  235. package/server/whatsapp-rpc/node_modules/execa/lib/error.js +87 -0
  236. package/server/whatsapp-rpc/node_modules/execa/lib/kill.js +102 -0
  237. package/server/whatsapp-rpc/node_modules/execa/lib/pipe.js +42 -0
  238. package/server/whatsapp-rpc/node_modules/execa/lib/promise.js +36 -0
  239. package/server/whatsapp-rpc/node_modules/execa/lib/stdio.js +49 -0
  240. package/server/whatsapp-rpc/node_modules/execa/lib/stream.js +133 -0
  241. package/server/whatsapp-rpc/node_modules/execa/lib/verbose.js +19 -0
  242. package/server/whatsapp-rpc/node_modules/execa/license +9 -0
  243. package/server/whatsapp-rpc/node_modules/execa/package.json +90 -0
  244. package/server/whatsapp-rpc/node_modules/execa/readme.md +822 -0
  245. package/server/whatsapp-rpc/node_modules/get-stream/license +9 -0
  246. package/server/whatsapp-rpc/node_modules/get-stream/package.json +53 -0
  247. package/server/whatsapp-rpc/node_modules/get-stream/readme.md +291 -0
  248. package/server/whatsapp-rpc/node_modules/get-stream/source/array-buffer.js +84 -0
  249. package/server/whatsapp-rpc/node_modules/get-stream/source/array.js +32 -0
  250. package/server/whatsapp-rpc/node_modules/get-stream/source/buffer.js +20 -0
  251. package/server/whatsapp-rpc/node_modules/get-stream/source/contents.js +101 -0
  252. package/server/whatsapp-rpc/node_modules/get-stream/source/index.d.ts +119 -0
  253. package/server/whatsapp-rpc/node_modules/get-stream/source/index.js +5 -0
  254. package/server/whatsapp-rpc/node_modules/get-stream/source/string.js +36 -0
  255. package/server/whatsapp-rpc/node_modules/get-stream/source/utils.js +11 -0
  256. package/server/whatsapp-rpc/node_modules/get-them-args/LICENSE +21 -0
  257. package/server/whatsapp-rpc/node_modules/get-them-args/README.md +95 -0
  258. package/server/whatsapp-rpc/node_modules/get-them-args/index.js +97 -0
  259. package/server/whatsapp-rpc/node_modules/get-them-args/package.json +36 -0
  260. package/server/whatsapp-rpc/node_modules/human-signals/LICENSE +201 -0
  261. package/server/whatsapp-rpc/node_modules/human-signals/README.md +168 -0
  262. package/server/whatsapp-rpc/node_modules/human-signals/build/src/core.js +273 -0
  263. package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.d.ts +73 -0
  264. package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.js +70 -0
  265. package/server/whatsapp-rpc/node_modules/human-signals/build/src/realtime.js +16 -0
  266. package/server/whatsapp-rpc/node_modules/human-signals/build/src/signals.js +34 -0
  267. package/server/whatsapp-rpc/node_modules/human-signals/package.json +61 -0
  268. package/server/whatsapp-rpc/node_modules/is-stream/index.d.ts +81 -0
  269. package/server/whatsapp-rpc/node_modules/is-stream/index.js +29 -0
  270. package/server/whatsapp-rpc/node_modules/is-stream/license +9 -0
  271. package/server/whatsapp-rpc/node_modules/is-stream/package.json +44 -0
  272. package/server/whatsapp-rpc/node_modules/is-stream/readme.md +60 -0
  273. package/server/whatsapp-rpc/node_modules/isexe/LICENSE +15 -0
  274. package/server/whatsapp-rpc/node_modules/isexe/README.md +51 -0
  275. package/server/whatsapp-rpc/node_modules/isexe/index.js +57 -0
  276. package/server/whatsapp-rpc/node_modules/isexe/mode.js +41 -0
  277. package/server/whatsapp-rpc/node_modules/isexe/package.json +31 -0
  278. package/server/whatsapp-rpc/node_modules/isexe/test/basic.js +221 -0
  279. package/server/whatsapp-rpc/node_modules/isexe/windows.js +42 -0
  280. package/server/whatsapp-rpc/node_modules/kill-port/.editorconfig +12 -0
  281. package/server/whatsapp-rpc/node_modules/kill-port/.gitattributes +1 -0
  282. package/server/whatsapp-rpc/node_modules/kill-port/LICENSE +21 -0
  283. package/server/whatsapp-rpc/node_modules/kill-port/README.md +140 -0
  284. package/server/whatsapp-rpc/node_modules/kill-port/cli.js +25 -0
  285. package/server/whatsapp-rpc/node_modules/kill-port/example.js +21 -0
  286. package/server/whatsapp-rpc/node_modules/kill-port/index.js +46 -0
  287. package/server/whatsapp-rpc/node_modules/kill-port/logo.png +0 -0
  288. package/server/whatsapp-rpc/node_modules/kill-port/package.json +41 -0
  289. package/server/whatsapp-rpc/node_modules/kill-port/pnpm-lock.yaml +4606 -0
  290. package/server/whatsapp-rpc/node_modules/kill-port/test.js +16 -0
  291. package/server/whatsapp-rpc/node_modules/merge-stream/LICENSE +21 -0
  292. package/server/whatsapp-rpc/node_modules/merge-stream/README.md +78 -0
  293. package/server/whatsapp-rpc/node_modules/merge-stream/index.js +41 -0
  294. package/server/whatsapp-rpc/node_modules/merge-stream/package.json +19 -0
  295. package/server/whatsapp-rpc/node_modules/mimic-fn/index.d.ts +52 -0
  296. package/server/whatsapp-rpc/node_modules/mimic-fn/index.js +71 -0
  297. package/server/whatsapp-rpc/node_modules/mimic-fn/license +9 -0
  298. package/server/whatsapp-rpc/node_modules/mimic-fn/package.json +45 -0
  299. package/server/whatsapp-rpc/node_modules/mimic-fn/readme.md +90 -0
  300. package/server/whatsapp-rpc/node_modules/npm-run-path/index.d.ts +90 -0
  301. package/server/whatsapp-rpc/node_modules/npm-run-path/index.js +52 -0
  302. package/server/whatsapp-rpc/node_modules/npm-run-path/license +9 -0
  303. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.d.ts +31 -0
  304. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.js +12 -0
  305. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/license +9 -0
  306. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/package.json +41 -0
  307. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/readme.md +57 -0
  308. package/server/whatsapp-rpc/node_modules/npm-run-path/package.json +49 -0
  309. package/server/whatsapp-rpc/node_modules/npm-run-path/readme.md +104 -0
  310. package/server/whatsapp-rpc/node_modules/onetime/index.d.ts +59 -0
  311. package/server/whatsapp-rpc/node_modules/onetime/index.js +41 -0
  312. package/server/whatsapp-rpc/node_modules/onetime/license +9 -0
  313. package/server/whatsapp-rpc/node_modules/onetime/package.json +45 -0
  314. package/server/whatsapp-rpc/node_modules/onetime/readme.md +94 -0
  315. package/server/whatsapp-rpc/node_modules/path-key/index.d.ts +40 -0
  316. package/server/whatsapp-rpc/node_modules/path-key/index.js +16 -0
  317. package/server/whatsapp-rpc/node_modules/path-key/license +9 -0
  318. package/server/whatsapp-rpc/node_modules/path-key/package.json +39 -0
  319. package/server/whatsapp-rpc/node_modules/path-key/readme.md +61 -0
  320. package/server/whatsapp-rpc/node_modules/shebang-command/index.js +19 -0
  321. package/server/whatsapp-rpc/node_modules/shebang-command/license +9 -0
  322. package/server/whatsapp-rpc/node_modules/shebang-command/package.json +34 -0
  323. package/server/whatsapp-rpc/node_modules/shebang-command/readme.md +34 -0
  324. package/server/whatsapp-rpc/node_modules/shebang-regex/index.d.ts +22 -0
  325. package/server/whatsapp-rpc/node_modules/shebang-regex/index.js +2 -0
  326. package/server/whatsapp-rpc/node_modules/shebang-regex/license +9 -0
  327. package/server/whatsapp-rpc/node_modules/shebang-regex/package.json +35 -0
  328. package/server/whatsapp-rpc/node_modules/shebang-regex/readme.md +33 -0
  329. package/server/whatsapp-rpc/node_modules/shell-exec/LICENSE +21 -0
  330. package/server/whatsapp-rpc/node_modules/shell-exec/README.md +60 -0
  331. package/server/whatsapp-rpc/node_modules/shell-exec/index.js +47 -0
  332. package/server/whatsapp-rpc/node_modules/shell-exec/package.json +29 -0
  333. package/server/whatsapp-rpc/node_modules/signal-exit/LICENSE.txt +16 -0
  334. package/server/whatsapp-rpc/node_modules/signal-exit/README.md +74 -0
  335. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts +12 -0
  336. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts.map +1 -0
  337. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js +10 -0
  338. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js.map +1 -0
  339. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts +48 -0
  340. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts.map +1 -0
  341. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js +279 -0
  342. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js.map +1 -0
  343. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/package.json +3 -0
  344. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts +29 -0
  345. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts.map +1 -0
  346. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js +42 -0
  347. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js.map +1 -0
  348. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts +12 -0
  349. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts.map +1 -0
  350. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js +4 -0
  351. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js.map +1 -0
  352. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts +48 -0
  353. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts.map +1 -0
  354. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js +275 -0
  355. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js.map +1 -0
  356. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/package.json +3 -0
  357. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts +29 -0
  358. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts.map +1 -0
  359. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js +39 -0
  360. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js.map +1 -0
  361. package/server/whatsapp-rpc/node_modules/signal-exit/package.json +106 -0
  362. package/server/whatsapp-rpc/node_modules/strip-final-newline/index.js +14 -0
  363. package/server/whatsapp-rpc/node_modules/strip-final-newline/license +9 -0
  364. package/server/whatsapp-rpc/node_modules/strip-final-newline/package.json +43 -0
  365. package/server/whatsapp-rpc/node_modules/strip-final-newline/readme.md +35 -0
  366. package/server/whatsapp-rpc/node_modules/which/CHANGELOG.md +166 -0
  367. package/server/whatsapp-rpc/node_modules/which/LICENSE +15 -0
  368. package/server/whatsapp-rpc/node_modules/which/README.md +54 -0
  369. package/server/whatsapp-rpc/node_modules/which/bin/node-which +52 -0
  370. package/server/whatsapp-rpc/node_modules/which/package.json +43 -0
  371. package/server/whatsapp-rpc/node_modules/which/which.js +125 -0
  372. package/server/whatsapp-rpc/package-lock.json +272 -0
  373. package/server/whatsapp-rpc/package.json +30 -30
  374. package/server/whatsapp-rpc/schema.json +1294 -1294
  375. package/server/whatsapp-rpc/scripts/clean.cjs +66 -66
  376. package/server/whatsapp-rpc/scripts/cli.js +162 -162
  377. package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -166
  378. package/server/whatsapp-rpc/src/python/pyproject.toml +15 -15
  379. package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -4
  380. package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -427
  381. package/server/whatsapp-rpc/web/app.py +609 -609
  382. package/server/whatsapp-rpc/web/requirements.txt +6 -6
  383. package/server/whatsapp-rpc/web/rpc_client.py +427 -427
  384. package/server/whatsapp-rpc/web/static/openapi.yaml +59 -59
  385. package/server/whatsapp-rpc/web/templates/base.html +149 -149
  386. package/server/whatsapp-rpc/web/templates/contacts.html +240 -240
  387. package/server/whatsapp-rpc/web/templates/dashboard.html +319 -319
  388. package/server/whatsapp-rpc/web/templates/groups.html +328 -328
  389. package/server/whatsapp-rpc/web/templates/messages.html +465 -465
  390. package/server/whatsapp-rpc/web/templates/messaging.html +680 -680
  391. package/server/whatsapp-rpc/web/templates/send.html +258 -258
  392. package/server/whatsapp-rpc/web/templates/settings.html +459 -459
  393. package/client/src/components/ui/AndroidSettingsPanel.tsx +0 -401
  394. package/client/src/components/ui/WhatsAppSettingsPanel.tsx +0 -345
  395. package/client/src/nodeDefinitions/androidDeviceNodes.ts +0 -140
  396. package/docker-compose.prod.yml +0 -107
  397. package/docker-compose.yml +0 -104
  398. package/docs-MachinaOs/README.md +0 -85
  399. package/docs-MachinaOs/deployment/docker.mdx +0 -228
  400. package/docs-MachinaOs/deployment/production.mdx +0 -345
  401. package/docs-MachinaOs/docs.json +0 -75
  402. package/docs-MachinaOs/faq.mdx +0 -309
  403. package/docs-MachinaOs/favicon.svg +0 -5
  404. package/docs-MachinaOs/installation.mdx +0 -160
  405. package/docs-MachinaOs/introduction.mdx +0 -114
  406. package/docs-MachinaOs/logo/dark.svg +0 -6
  407. package/docs-MachinaOs/logo/light.svg +0 -6
  408. package/docs-MachinaOs/nodes/ai-agent.mdx +0 -216
  409. package/docs-MachinaOs/nodes/ai-models.mdx +0 -240
  410. package/docs-MachinaOs/nodes/android.mdx +0 -411
  411. package/docs-MachinaOs/nodes/overview.mdx +0 -181
  412. package/docs-MachinaOs/nodes/schedulers.mdx +0 -316
  413. package/docs-MachinaOs/nodes/webhooks.mdx +0 -330
  414. package/docs-MachinaOs/nodes/whatsapp.mdx +0 -305
  415. package/docs-MachinaOs/quickstart.mdx +0 -119
  416. package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +0 -177
  417. package/docs-MachinaOs/tutorials/android-automation.mdx +0 -242
  418. package/docs-MachinaOs/tutorials/first-workflow.mdx +0 -134
  419. package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +0 -185
  420. package/nul +0 -0
  421. package/scripts/check-ports.ps1 +0 -33
  422. 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