machinaos 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/.env.template +71 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/bin/cli.js +159 -0
  5. package/client/.dockerignore +45 -0
  6. package/client/Dockerfile +68 -0
  7. package/client/eslint.config.js +29 -0
  8. package/client/index.html +13 -0
  9. package/client/nginx.conf +66 -0
  10. package/client/package.json +48 -0
  11. package/client/src/App.tsx +27 -0
  12. package/client/src/Dashboard.tsx +1173 -0
  13. package/client/src/ParameterPanel.tsx +301 -0
  14. package/client/src/components/AIAgentNode.tsx +321 -0
  15. package/client/src/components/APIKeyValidator.tsx +118 -0
  16. package/client/src/components/ClaudeChatModelNode.tsx +18 -0
  17. package/client/src/components/ConditionalEdge.tsx +189 -0
  18. package/client/src/components/CredentialsModal.tsx +306 -0
  19. package/client/src/components/EdgeConditionEditor.tsx +443 -0
  20. package/client/src/components/GeminiChatModelNode.tsx +18 -0
  21. package/client/src/components/GenericNode.tsx +357 -0
  22. package/client/src/components/LocationParameterPanel.tsx +154 -0
  23. package/client/src/components/ModelNode.tsx +286 -0
  24. package/client/src/components/OpenAIChatModelNode.tsx +18 -0
  25. package/client/src/components/OutputPanel.tsx +471 -0
  26. package/client/src/components/ParameterRenderer.tsx +1874 -0
  27. package/client/src/components/SkillEditorModal.tsx +417 -0
  28. package/client/src/components/SquareNode.tsx +797 -0
  29. package/client/src/components/StartNode.tsx +250 -0
  30. package/client/src/components/ToolkitNode.tsx +365 -0
  31. package/client/src/components/TriggerNode.tsx +463 -0
  32. package/client/src/components/auth/LoginPage.tsx +247 -0
  33. package/client/src/components/auth/ProtectedRoute.tsx +59 -0
  34. package/client/src/components/base/BaseChatModelNode.tsx +271 -0
  35. package/client/src/components/icons/AIProviderIcons.tsx +50 -0
  36. package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
  37. package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
  38. package/client/src/components/maps/index.ts +26 -0
  39. package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
  40. package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
  41. package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
  42. package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
  43. package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
  44. package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
  45. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
  46. package/client/src/components/parameterPanel/index.ts +42 -0
  47. package/client/src/components/shared/DataPanel.tsx +142 -0
  48. package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
  49. package/client/src/components/ui/AIResultModal.tsx +204 -0
  50. package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
  51. package/client/src/components/ui/CodeEditor.tsx +81 -0
  52. package/client/src/components/ui/CollapsibleSection.tsx +88 -0
  53. package/client/src/components/ui/ComponentItem.tsx +154 -0
  54. package/client/src/components/ui/ComponentPalette.tsx +321 -0
  55. package/client/src/components/ui/ConsolePanel.tsx +1074 -0
  56. package/client/src/components/ui/ErrorBoundary.tsx +196 -0
  57. package/client/src/components/ui/InputNodesPanel.tsx +204 -0
  58. package/client/src/components/ui/MapSelector.tsx +314 -0
  59. package/client/src/components/ui/Modal.tsx +149 -0
  60. package/client/src/components/ui/NodeContextMenu.tsx +192 -0
  61. package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
  62. package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
  63. package/client/src/components/ui/SettingsPanel.tsx +243 -0
  64. package/client/src/components/ui/TopToolbar.tsx +736 -0
  65. package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
  66. package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
  67. package/client/src/config/antdTheme.ts +186 -0
  68. package/client/src/config/api.ts +54 -0
  69. package/client/src/contexts/AuthContext.tsx +221 -0
  70. package/client/src/contexts/ThemeContext.tsx +42 -0
  71. package/client/src/contexts/WebSocketContext.tsx +1971 -0
  72. package/client/src/factories/baseChatModelFactory.ts +256 -0
  73. package/client/src/hooks/useAndroidOperations.ts +164 -0
  74. package/client/src/hooks/useApiKeyValidation.ts +107 -0
  75. package/client/src/hooks/useApiKeys.ts +238 -0
  76. package/client/src/hooks/useAppTheme.ts +17 -0
  77. package/client/src/hooks/useComponentPalette.ts +51 -0
  78. package/client/src/hooks/useCopyPaste.ts +155 -0
  79. package/client/src/hooks/useDragAndDrop.ts +124 -0
  80. package/client/src/hooks/useDragVariable.ts +88 -0
  81. package/client/src/hooks/useExecution.ts +313 -0
  82. package/client/src/hooks/useParameterPanel.ts +176 -0
  83. package/client/src/hooks/useReactFlowNodes.ts +189 -0
  84. package/client/src/hooks/useToolSchema.ts +209 -0
  85. package/client/src/hooks/useWhatsApp.ts +196 -0
  86. package/client/src/hooks/useWorkflowManagement.ts +46 -0
  87. package/client/src/index.css +315 -0
  88. package/client/src/main.tsx +19 -0
  89. package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
  90. package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
  91. package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
  92. package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
  93. package/client/src/nodeDefinitions/chatNodes.ts +135 -0
  94. package/client/src/nodeDefinitions/codeNodes.ts +54 -0
  95. package/client/src/nodeDefinitions/documentNodes.ts +379 -0
  96. package/client/src/nodeDefinitions/index.ts +15 -0
  97. package/client/src/nodeDefinitions/locationNodes.ts +463 -0
  98. package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
  99. package/client/src/nodeDefinitions/skillNodes.ts +211 -0
  100. package/client/src/nodeDefinitions/toolNodes.ts +198 -0
  101. package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
  102. package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
  103. package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
  104. package/client/src/nodeDefinitions.ts +104 -0
  105. package/client/src/schemas/workflowSchema.ts +264 -0
  106. package/client/src/services/dynamicParameterService.ts +96 -0
  107. package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
  108. package/client/src/services/executionService.ts +232 -0
  109. package/client/src/services/workflowApi.ts +91 -0
  110. package/client/src/store/useAppStore.ts +582 -0
  111. package/client/src/styles/theme.ts +508 -0
  112. package/client/src/styles/zIndex.ts +17 -0
  113. package/client/src/types/ComponentTypes.ts +39 -0
  114. package/client/src/types/EdgeCondition.ts +231 -0
  115. package/client/src/types/INodeProperties.ts +288 -0
  116. package/client/src/types/NodeTypes.ts +28 -0
  117. package/client/src/utils/formatters.ts +33 -0
  118. package/client/src/utils/googleMapsLoader.ts +140 -0
  119. package/client/src/utils/locationUtils.ts +85 -0
  120. package/client/src/utils/nodeUtils.ts +31 -0
  121. package/client/src/utils/workflow.ts +30 -0
  122. package/client/src/utils/workflowExport.ts +120 -0
  123. package/client/src/vite-env.d.ts +12 -0
  124. package/client/tailwind.config.js +60 -0
  125. package/client/tsconfig.json +25 -0
  126. package/client/tsconfig.node.json +11 -0
  127. package/client/vite.config.js +35 -0
  128. package/docker-compose.prod.yml +107 -0
  129. package/docker-compose.yml +104 -0
  130. package/docs-MachinaOs/README.md +85 -0
  131. package/docs-MachinaOs/deployment/docker.mdx +228 -0
  132. package/docs-MachinaOs/deployment/production.mdx +345 -0
  133. package/docs-MachinaOs/docs.json +75 -0
  134. package/docs-MachinaOs/faq.mdx +309 -0
  135. package/docs-MachinaOs/favicon.svg +5 -0
  136. package/docs-MachinaOs/installation.mdx +160 -0
  137. package/docs-MachinaOs/introduction.mdx +114 -0
  138. package/docs-MachinaOs/logo/dark.svg +6 -0
  139. package/docs-MachinaOs/logo/light.svg +6 -0
  140. package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
  141. package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
  142. package/docs-MachinaOs/nodes/android.mdx +411 -0
  143. package/docs-MachinaOs/nodes/overview.mdx +181 -0
  144. package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
  145. package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
  146. package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
  147. package/docs-MachinaOs/quickstart.mdx +119 -0
  148. package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
  149. package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
  150. package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
  151. package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
  152. package/nul +0 -0
  153. package/package.json +70 -0
  154. package/scripts/build.js +158 -0
  155. package/scripts/check-ports.ps1 +33 -0
  156. package/scripts/clean.js +40 -0
  157. package/scripts/docker.js +93 -0
  158. package/scripts/kill-port.ps1 +154 -0
  159. package/scripts/start.js +210 -0
  160. package/scripts/stop.js +325 -0
  161. package/server/.dockerignore +44 -0
  162. package/server/Dockerfile +45 -0
  163. package/server/constants.py +249 -0
  164. package/server/core/__init__.py +1 -0
  165. package/server/core/cache.py +461 -0
  166. package/server/core/config.py +128 -0
  167. package/server/core/container.py +99 -0
  168. package/server/core/database.py +1211 -0
  169. package/server/core/logging.py +314 -0
  170. package/server/main.py +289 -0
  171. package/server/middleware/__init__.py +5 -0
  172. package/server/middleware/auth.py +89 -0
  173. package/server/models/__init__.py +1 -0
  174. package/server/models/auth.py +52 -0
  175. package/server/models/cache.py +24 -0
  176. package/server/models/database.py +211 -0
  177. package/server/models/nodes.py +455 -0
  178. package/server/package.json +9 -0
  179. package/server/pyproject.toml +72 -0
  180. package/server/requirements.txt +83 -0
  181. package/server/routers/__init__.py +1 -0
  182. package/server/routers/android.py +294 -0
  183. package/server/routers/auth.py +203 -0
  184. package/server/routers/database.py +151 -0
  185. package/server/routers/maps.py +142 -0
  186. package/server/routers/nodejs_compat.py +289 -0
  187. package/server/routers/webhook.py +90 -0
  188. package/server/routers/websocket.py +2127 -0
  189. package/server/routers/whatsapp.py +761 -0
  190. package/server/routers/workflow.py +200 -0
  191. package/server/services/__init__.py +1 -0
  192. package/server/services/ai.py +2415 -0
  193. package/server/services/android/__init__.py +27 -0
  194. package/server/services/android/broadcaster.py +114 -0
  195. package/server/services/android/client.py +608 -0
  196. package/server/services/android/manager.py +78 -0
  197. package/server/services/android/protocol.py +165 -0
  198. package/server/services/android_service.py +588 -0
  199. package/server/services/auth.py +131 -0
  200. package/server/services/chat_client.py +160 -0
  201. package/server/services/deployment/__init__.py +12 -0
  202. package/server/services/deployment/manager.py +706 -0
  203. package/server/services/deployment/state.py +47 -0
  204. package/server/services/deployment/triggers.py +275 -0
  205. package/server/services/event_waiter.py +785 -0
  206. package/server/services/execution/__init__.py +77 -0
  207. package/server/services/execution/cache.py +769 -0
  208. package/server/services/execution/conditions.py +373 -0
  209. package/server/services/execution/dlq.py +132 -0
  210. package/server/services/execution/executor.py +1351 -0
  211. package/server/services/execution/models.py +531 -0
  212. package/server/services/execution/recovery.py +235 -0
  213. package/server/services/handlers/__init__.py +126 -0
  214. package/server/services/handlers/ai.py +355 -0
  215. package/server/services/handlers/android.py +260 -0
  216. package/server/services/handlers/code.py +278 -0
  217. package/server/services/handlers/document.py +598 -0
  218. package/server/services/handlers/http.py +193 -0
  219. package/server/services/handlers/polyglot.py +105 -0
  220. package/server/services/handlers/tools.py +845 -0
  221. package/server/services/handlers/triggers.py +107 -0
  222. package/server/services/handlers/utility.py +822 -0
  223. package/server/services/handlers/whatsapp.py +476 -0
  224. package/server/services/maps.py +289 -0
  225. package/server/services/memory_store.py +103 -0
  226. package/server/services/node_executor.py +375 -0
  227. package/server/services/parameter_resolver.py +218 -0
  228. package/server/services/polyglot_client.py +169 -0
  229. package/server/services/scheduler.py +155 -0
  230. package/server/services/skill_loader.py +417 -0
  231. package/server/services/status_broadcaster.py +826 -0
  232. package/server/services/temporal/__init__.py +23 -0
  233. package/server/services/temporal/activities.py +344 -0
  234. package/server/services/temporal/client.py +76 -0
  235. package/server/services/temporal/executor.py +147 -0
  236. package/server/services/temporal/worker.py +251 -0
  237. package/server/services/temporal/workflow.py +355 -0
  238. package/server/services/temporal/ws_client.py +236 -0
  239. package/server/services/text.py +111 -0
  240. package/server/services/user_auth.py +172 -0
  241. package/server/services/websocket_client.py +29 -0
  242. package/server/services/workflow.py +597 -0
  243. package/server/skills/android-skill/SKILL.md +82 -0
  244. package/server/skills/assistant-personality/SKILL.md +45 -0
  245. package/server/skills/code-skill/SKILL.md +140 -0
  246. package/server/skills/http-skill/SKILL.md +161 -0
  247. package/server/skills/maps-skill/SKILL.md +170 -0
  248. package/server/skills/memory-skill/SKILL.md +154 -0
  249. package/server/skills/scheduler-skill/SKILL.md +84 -0
  250. package/server/skills/whatsapp-skill/SKILL.md +283 -0
  251. package/server/uv.lock +2916 -0
  252. package/server/whatsapp-rpc/.dockerignore +30 -0
  253. package/server/whatsapp-rpc/Dockerfile +44 -0
  254. package/server/whatsapp-rpc/Dockerfile.web +17 -0
  255. package/server/whatsapp-rpc/README.md +139 -0
  256. package/server/whatsapp-rpc/cli.js +95 -0
  257. package/server/whatsapp-rpc/configs/config.yaml +7 -0
  258. package/server/whatsapp-rpc/docker-compose.yml +35 -0
  259. package/server/whatsapp-rpc/docs/API.md +410 -0
  260. package/server/whatsapp-rpc/go.mod +67 -0
  261. package/server/whatsapp-rpc/go.sum +203 -0
  262. package/server/whatsapp-rpc/package.json +30 -0
  263. package/server/whatsapp-rpc/schema.json +1294 -0
  264. package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
  265. package/server/whatsapp-rpc/scripts/cli.js +162 -0
  266. package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
  267. package/server/whatsapp-rpc/src/go/config/config.go +49 -0
  268. package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
  269. package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
  270. package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
  271. package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
  272. package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
  273. package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
  274. package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
  275. package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
  276. package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
  277. package/server/whatsapp-rpc/web/app.py +609 -0
  278. package/server/whatsapp-rpc/web/requirements.txt +6 -0
  279. package/server/whatsapp-rpc/web/rpc_client.py +427 -0
  280. package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
  281. package/server/whatsapp-rpc/web/templates/base.html +150 -0
  282. package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
  283. package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
  284. package/server/whatsapp-rpc/web/templates/groups.html +328 -0
  285. package/server/whatsapp-rpc/web/templates/messages.html +465 -0
  286. package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
  287. package/server/whatsapp-rpc/web/templates/send.html +259 -0
  288. package/server/whatsapp-rpc/web/templates/settings.html +459 -0
@@ -0,0 +1,235 @@
1
+ """Recovery sweeper for crash recovery.
2
+
3
+ Runs as background task to:
4
+ - Detect abandoned executions (running but no heartbeat)
5
+ - Resume interrupted workflows
6
+ - Clean up stale data
7
+ """
8
+
9
+ import asyncio
10
+ import time
11
+ from typing import Dict, Any, List, Optional, Callable, Awaitable
12
+
13
+ from core.logging import get_logger
14
+ from .models import TaskStatus, WorkflowStatus
15
+ from .cache import ExecutionCache
16
+
17
+ logger = get_logger(__name__)
18
+
19
+
20
+ class RecoverySweeper:
21
+ """Background task that recovers abandoned workflow executions.
22
+
23
+ Conductor's sweeper pattern:
24
+ - Periodically scans for stuck executions
25
+ - Detects nodes with stale heartbeats
26
+ - Resets stuck nodes to PENDING for retry
27
+ - Triggers workflow_decide to resume
28
+ """
29
+
30
+ def __init__(self, cache: ExecutionCache,
31
+ heartbeat_timeout: int = 300, # 5 minutes
32
+ sweep_interval: int = 60, # 1 minute
33
+ max_retries: int = 3):
34
+ """Initialize recovery sweeper.
35
+
36
+ Args:
37
+ cache: ExecutionCache for Redis access
38
+ heartbeat_timeout: Seconds before node is considered stuck
39
+ sweep_interval: Seconds between sweep runs
40
+ max_retries: Max retry attempts per node
41
+ """
42
+ self.cache = cache
43
+ self.heartbeat_timeout = heartbeat_timeout
44
+ self.sweep_interval = sweep_interval
45
+ self.max_retries = max_retries
46
+ self._running = False
47
+ self._task: Optional[asyncio.Task] = None
48
+
49
+ # Recovery callbacks (set by workflow service)
50
+ self._on_recovery: Optional[Callable[[str], Awaitable[None]]] = None
51
+
52
+ def set_recovery_callback(self, callback: Callable[[str], Awaitable[None]]) -> None:
53
+ """Set callback to invoke when execution needs recovery.
54
+
55
+ Args:
56
+ callback: Async function that takes execution_id
57
+ """
58
+ self._on_recovery = callback
59
+
60
+ async def start(self) -> None:
61
+ """Start the recovery sweeper background task."""
62
+ if self._running:
63
+ logger.warning("Recovery sweeper already running")
64
+ return
65
+
66
+ self._running = True
67
+ self._task = asyncio.create_task(self._sweep_loop())
68
+ logger.info("Recovery sweeper started",
69
+ heartbeat_timeout=self.heartbeat_timeout,
70
+ sweep_interval=self.sweep_interval)
71
+
72
+ async def stop(self) -> None:
73
+ """Stop the recovery sweeper."""
74
+ self._running = False
75
+ if self._task:
76
+ self._task.cancel()
77
+ try:
78
+ await self._task
79
+ except asyncio.CancelledError:
80
+ pass
81
+ logger.info("Recovery sweeper stopped")
82
+
83
+ async def _sweep_loop(self) -> None:
84
+ """Main sweep loop - runs continuously."""
85
+ while self._running:
86
+ try:
87
+ await self._sweep_once()
88
+ except Exception as e:
89
+ logger.error("Sweep iteration failed", error=str(e))
90
+
91
+ # Wait before next sweep
92
+ await asyncio.sleep(self.sweep_interval)
93
+
94
+ async def _sweep_once(self) -> None:
95
+ """Single sweep iteration - check all active executions."""
96
+ # Get all active executions
97
+ active_ids = await self.cache.get_active_executions()
98
+
99
+ if not active_ids:
100
+ return
101
+
102
+ logger.debug("Sweeping active executions", count=len(active_ids))
103
+
104
+ for execution_id in active_ids:
105
+ try:
106
+ await self._check_execution(execution_id)
107
+ except Exception as e:
108
+ logger.error("Failed to check execution",
109
+ execution_id=execution_id, error=str(e))
110
+
111
+ async def _check_execution(self, execution_id: str) -> None:
112
+ """Check single execution for stuck nodes.
113
+
114
+ Args:
115
+ execution_id: Execution to check
116
+ """
117
+ # Load execution state
118
+ ctx = await self.cache.load_execution_state(execution_id)
119
+ if not ctx:
120
+ # Execution not found - remove from active set
121
+ logger.warning("Orphan execution in active set",
122
+ execution_id=execution_id)
123
+ if self.cache.cache.is_redis_available():
124
+ await self.cache.cache.redis.srem("executions:active", execution_id)
125
+ return
126
+
127
+ # Check if already complete
128
+ if ctx.status in (WorkflowStatus.COMPLETED, WorkflowStatus.FAILED,
129
+ WorkflowStatus.CANCELLED):
130
+ # Should not be in active set
131
+ if self.cache.cache.is_redis_available():
132
+ await self.cache.cache.redis.srem("executions:active", execution_id)
133
+ return
134
+
135
+ # Check for stuck nodes
136
+ needs_recovery = False
137
+ current_time = time.time()
138
+
139
+ for node_id, node_exec in ctx.node_executions.items():
140
+ if node_exec.status == TaskStatus.RUNNING:
141
+ # Check heartbeat
142
+ last_heartbeat = await self.cache.get_heartbeat(execution_id, node_id)
143
+
144
+ if last_heartbeat is None:
145
+ # No heartbeat - node is stuck
146
+ stuck_duration = current_time - (node_exec.started_at or current_time)
147
+ if stuck_duration > self.heartbeat_timeout:
148
+ logger.warning("Node stuck (no heartbeat)",
149
+ execution_id=execution_id,
150
+ node_id=node_id,
151
+ stuck_seconds=stuck_duration)
152
+ needs_recovery = True
153
+
154
+ elif current_time - last_heartbeat > self.heartbeat_timeout:
155
+ # Heartbeat too old
156
+ logger.warning("Node stuck (stale heartbeat)",
157
+ execution_id=execution_id,
158
+ node_id=node_id,
159
+ heartbeat_age=current_time - last_heartbeat)
160
+ needs_recovery = True
161
+
162
+ if needs_recovery and self._on_recovery:
163
+ logger.info("Triggering recovery", execution_id=execution_id)
164
+ try:
165
+ await self._on_recovery(execution_id)
166
+ except Exception as e:
167
+ logger.error("Recovery callback failed",
168
+ execution_id=execution_id, error=str(e))
169
+
170
+ async def scan_on_startup(self) -> List[str]:
171
+ """Scan for executions that need recovery on server startup.
172
+
173
+ Returns:
174
+ List of execution IDs that need recovery
175
+ """
176
+ needs_recovery = []
177
+
178
+ # Get all active executions
179
+ active_ids = await self.cache.get_active_executions()
180
+
181
+ logger.info("Startup scan for incomplete executions",
182
+ active_count=len(active_ids))
183
+
184
+ for execution_id in active_ids:
185
+ ctx = await self.cache.load_execution_state(execution_id)
186
+ if not ctx:
187
+ continue
188
+
189
+ # Check if execution was interrupted
190
+ if ctx.status == WorkflowStatus.RUNNING:
191
+ # Check how long it's been stuck
192
+ if ctx.updated_at:
193
+ age = time.time() - ctx.updated_at
194
+ if age > self.heartbeat_timeout:
195
+ logger.info("Found interrupted execution",
196
+ execution_id=execution_id,
197
+ age_seconds=age)
198
+ needs_recovery.append(execution_id)
199
+
200
+ return needs_recovery
201
+
202
+ async def cleanup_old_executions(self, max_age_hours: int = 24) -> int:
203
+ """Clean up completed executions older than max_age.
204
+
205
+ Args:
206
+ max_age_hours: Maximum age in hours
207
+
208
+ Returns:
209
+ Number of executions cleaned up
210
+ """
211
+ cleaned = 0
212
+ max_age_seconds = max_age_hours * 3600
213
+ current_time = time.time()
214
+
215
+ # This would need a scan of all execution keys
216
+ # For now, we rely on Redis TTL for cleanup
217
+ logger.info("Cleanup skipped - using Redis TTL",
218
+ max_age_hours=max_age_hours)
219
+
220
+ return cleaned
221
+
222
+
223
+ # Global sweeper instance (initialized by main.py)
224
+ _sweeper: Optional[RecoverySweeper] = None
225
+
226
+
227
+ def get_recovery_sweeper() -> Optional[RecoverySweeper]:
228
+ """Get global recovery sweeper instance."""
229
+ return _sweeper
230
+
231
+
232
+ def set_recovery_sweeper(sweeper: RecoverySweeper) -> None:
233
+ """Set global recovery sweeper instance."""
234
+ global _sweeper
235
+ _sweeper = sweeper
@@ -0,0 +1,126 @@
1
+ """Node handlers package - extracted from workflow.py for maintainability.
2
+
3
+ This package contains all node execution handlers organized by category:
4
+ - ai.py: AI Agent, AI Chat Model, Simple Memory
5
+ - android.py: Android Device Setup, Android Services
6
+ - code.py: Python Executor, JavaScript Executor
7
+ - document.py: HTTP Scraper, File Downloader, Document Parser, Text Chunker, Embedding Generator, Vector Store
8
+ - http.py: HTTP Request, Webhook Response
9
+ - tools.py: Tool execution handlers for AI Agent tool calling
10
+ - triggers.py: Generic trigger node handler
11
+ - utility.py: Maps, Text, Chat, Cron, Start
12
+ - whatsapp.py: WhatsApp Send, WhatsApp Connect
13
+ - polyglot.py: Polyglot server integration (standalone, not auto-imported)
14
+ """
15
+
16
+ # AI handlers
17
+ from .ai import (
18
+ handle_ai_agent,
19
+ handle_chat_agent,
20
+ handle_ai_chat_model,
21
+ handle_simple_memory,
22
+ )
23
+
24
+ # Android handlers
25
+ from .android import (
26
+ handle_android_device_setup,
27
+ handle_android_service,
28
+ )
29
+
30
+ # Code execution handlers
31
+ from .code import (
32
+ handle_python_executor,
33
+ handle_javascript_executor,
34
+ )
35
+
36
+ # HTTP handlers
37
+ from .http import (
38
+ handle_http_request,
39
+ handle_webhook_response,
40
+ )
41
+
42
+ # Trigger handlers
43
+ from .triggers import (
44
+ handle_trigger_node,
45
+ )
46
+
47
+ # Utility handlers
48
+ from .utility import (
49
+ handle_create_map,
50
+ handle_add_locations,
51
+ handle_nearby_places,
52
+ handle_text_generator,
53
+ handle_file_handler,
54
+ handle_chat_send,
55
+ handle_chat_history,
56
+ handle_start,
57
+ handle_cron_scheduler,
58
+ handle_timer,
59
+ handle_console,
60
+ )
61
+
62
+ # WhatsApp handlers
63
+ from .whatsapp import (
64
+ handle_whatsapp_send,
65
+ handle_whatsapp_connect,
66
+ handle_whatsapp_db,
67
+ )
68
+
69
+ # Document processing handlers
70
+ from .document import (
71
+ handle_http_scraper,
72
+ handle_file_downloader,
73
+ handle_document_parser,
74
+ handle_text_chunker,
75
+ handle_embedding_generator,
76
+ handle_vector_store,
77
+ )
78
+
79
+ # Tool execution handlers (for AI Agent tool calling)
80
+ from .tools import (
81
+ execute_tool,
82
+ )
83
+
84
+ __all__ = [
85
+ # AI
86
+ 'handle_ai_agent',
87
+ 'handle_chat_agent',
88
+ 'handle_ai_chat_model',
89
+ 'handle_simple_memory',
90
+ # Android
91
+ 'handle_android_device_setup',
92
+ 'handle_android_service',
93
+ # Code
94
+ 'handle_python_executor',
95
+ 'handle_javascript_executor',
96
+ # HTTP
97
+ 'handle_http_request',
98
+ 'handle_webhook_response',
99
+ # Triggers
100
+ 'handle_trigger_node',
101
+ # Utility
102
+ 'handle_create_map',
103
+ 'handle_add_locations',
104
+ 'handle_nearby_places',
105
+ 'handle_text_generator',
106
+ 'handle_file_handler',
107
+ 'handle_chat_send',
108
+ 'handle_chat_history',
109
+ 'handle_start',
110
+ 'handle_cron_scheduler',
111
+ 'handle_timer',
112
+ 'handle_console',
113
+ # WhatsApp
114
+ 'handle_whatsapp_send',
115
+ 'handle_whatsapp_connect',
116
+ 'handle_whatsapp_db',
117
+ # Document processing
118
+ 'handle_http_scraper',
119
+ 'handle_file_downloader',
120
+ 'handle_document_parser',
121
+ 'handle_text_chunker',
122
+ 'handle_embedding_generator',
123
+ 'handle_vector_store',
124
+ # Tools
125
+ 'execute_tool',
126
+ ]