machinaos 0.0.76 → 0.0.78

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 (393) hide show
  1. package/README.md +143 -107
  2. package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
  3. package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
  4. package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
  5. package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
  6. package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
  7. package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
  8. package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
  9. package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
  10. package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
  11. package/client/dist/assets/index-DQ0nwhec.js +257 -0
  12. package/client/dist/assets/index-DxmbVskS.css +1 -0
  13. package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
  14. package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
  15. package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
  16. package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
  17. package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
  18. package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
  19. package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
  20. package/client/dist/index.html +37 -3
  21. package/client/index.html +28 -1
  22. package/client/package.json +44 -40
  23. package/client/src/App.tsx +2 -0
  24. package/client/src/Dashboard.tsx +157 -45
  25. package/client/src/ParameterPanel.tsx +3 -5
  26. package/client/src/adapters/nodeSpecToDescription.ts +1 -0
  27. package/client/src/assets/icons/NodeIcon.tsx +32 -0
  28. package/client/src/assets/icons/index.ts +4 -0
  29. package/client/src/assets/icons/stripe.svg +1 -0
  30. package/client/src/assets/icons/themedGlyphs.ts +404 -0
  31. package/client/src/components/AIAgentNode.tsx +77 -53
  32. package/client/src/components/GenericNode.tsx +34 -52
  33. package/client/src/components/OutputPanel.tsx +64 -147
  34. package/client/src/components/ParameterRenderer.tsx +5 -3
  35. package/client/src/components/SkillEditorModal.tsx +9 -18
  36. package/client/src/components/SquareNode.tsx +97 -115
  37. package/client/src/components/StartNode.tsx +32 -42
  38. package/client/src/components/SvgFilterDefs.tsx +54 -0
  39. package/client/src/components/TeamMonitorNode.tsx +12 -14
  40. package/client/src/components/ToolkitNode.tsx +35 -60
  41. package/client/src/components/TriggerNode.tsx +43 -77
  42. package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
  43. package/client/src/components/credentials/CredentialsModal.tsx +98 -30
  44. package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
  45. package/client/src/components/credentials/catalogueAdapter.ts +17 -1
  46. package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
  47. package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
  48. package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
  49. package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
  50. package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
  51. package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
  52. package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
  53. package/client/src/components/credentials/types.ts +12 -2
  54. package/client/src/components/credentials/useCredentialPanel.ts +43 -19
  55. package/client/src/components/icons/AIProviderIcons.tsx +16 -0
  56. package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
  57. package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
  58. package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
  59. package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
  60. package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
  61. package/client/src/components/parameterPanel/InputSection.tsx +9 -7
  62. package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
  63. package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
  64. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
  65. package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
  66. package/client/src/components/ui/AIResultModal.tsx +1 -1
  67. package/client/src/components/ui/CollapsibleSection.tsx +9 -5
  68. package/client/src/components/ui/CommandPalette.tsx +147 -0
  69. package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
  70. package/client/src/components/ui/ComponentItem.tsx +13 -7
  71. package/client/src/components/ui/ComponentPalette.tsx +24 -13
  72. package/client/src/components/ui/ConsolePanel.tsx +19 -11
  73. package/client/src/components/ui/DropCap.tsx +28 -0
  74. package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
  75. package/client/src/components/ui/InputNodesPanel.tsx +1 -1
  76. package/client/src/components/ui/Modal.tsx +38 -6
  77. package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
  78. package/client/src/components/ui/SettingsPanel.tsx +42 -13
  79. package/client/src/components/ui/StatusBar.tsx +108 -0
  80. package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
  81. package/client/src/components/ui/TopToolbar.tsx +42 -25
  82. package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
  83. package/client/src/components/ui/action-button.tsx +40 -15
  84. package/client/src/components/ui/button.tsx +24 -1
  85. package/client/src/components/ui/dropdown-menu.tsx +24 -2
  86. package/client/src/components/ui/input.tsx +19 -2
  87. package/client/src/components/ui/select.tsx +15 -0
  88. package/client/src/components/ui/textarea.tsx +15 -2
  89. package/client/src/contexts/AuthContext.tsx +148 -109
  90. package/client/src/contexts/ThemeContext.tsx +93 -17
  91. package/client/src/contexts/WebSocketContext.tsx +373 -206
  92. package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
  93. package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
  94. package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
  95. package/client/src/hooks/useAppTheme.ts +209 -7
  96. package/client/src/hooks/useAutoSkillEdges.ts +7 -2
  97. package/client/src/hooks/useCatalogueQuery.ts +67 -1
  98. package/client/src/hooks/useDragVariable.ts +1 -1
  99. package/client/src/hooks/useNodeAllowlist.ts +115 -8
  100. package/client/src/hooks/useOnboarding.ts +20 -8
  101. package/client/src/hooks/useParameterPanel.ts +2 -1
  102. package/client/src/hooks/useReactFlowNodes.ts +2 -1
  103. package/client/src/hooks/useSound.ts +185 -0
  104. package/client/src/hooks/useWorkflowManagement.ts +6 -8
  105. package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
  106. package/client/src/index.css +65 -3
  107. package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
  108. package/client/src/lib/aiModelProviders.ts +8 -0
  109. package/client/src/lib/connectionConfig.ts +107 -0
  110. package/client/src/lib/queryPersist.ts +13 -5
  111. package/client/src/lib/sound.ts +393 -0
  112. package/client/src/main.tsx +20 -0
  113. package/client/src/store/useAppStore.ts +26 -0
  114. package/client/src/styles/canvasAnimations.ts +37 -36
  115. package/client/src/styles/theme.ts +36 -20
  116. package/client/src/test/setup.ts +1 -0
  117. package/client/src/themes/atomic.css +253 -0
  118. package/client/src/themes/base.css +373 -0
  119. package/client/src/themes/cyber.css +890 -0
  120. package/client/src/themes/dark.css +70 -0
  121. package/client/src/themes/edo.css +246 -0
  122. package/client/src/themes/greek.css +293 -0
  123. package/client/src/themes/light.css +78 -0
  124. package/client/src/themes/plague.css +253 -0
  125. package/client/src/themes/renaissance.css +727 -0
  126. package/client/src/themes/rot.css +249 -0
  127. package/client/src/themes/steampunk.css +272 -0
  128. package/client/src/themes/surveillance.css +289 -0
  129. package/client/src/themes/wasteland.css +250 -0
  130. package/client/src/types/INodeProperties.ts +5 -0
  131. package/client/src/types/NodeTypes.ts +11 -1
  132. package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
  133. package/client/src/types/cloudEvents.ts +78 -0
  134. package/client/src/vite-env.d.ts +7 -0
  135. package/client/tsconfig.json +1 -1
  136. package/client/vite.config.js +62 -2
  137. package/install.ps1 +1 -1
  138. package/install.sh +1 -1
  139. package/machina/commands/build.py +51 -7
  140. package/machina/pyproject.toml +4 -0
  141. package/machina/supervisor.py +12 -2
  142. package/machina/tree.py +71 -21
  143. package/package.json +4 -4
  144. package/scripts/install.js +16 -1
  145. package/server/config/ai_cli_providers.json +54 -0
  146. package/server/config/credential_providers.json +109 -2
  147. package/server/config/llm_defaults.json +24 -0
  148. package/server/config/model_registry.json +338 -499
  149. package/server/config/node_allowlist.json +16 -1
  150. package/server/config/pricing.json +8 -0
  151. package/server/constants.py +38 -15
  152. package/server/core/container.py +2 -2
  153. package/server/core/credentials_database.py +35 -2
  154. package/server/core/logging.py +4 -3
  155. package/server/main.py +99 -13
  156. package/server/models/node_metadata.py +1 -0
  157. package/server/nodejs/package.json +8 -6
  158. package/server/nodejs/src/index.ts +22 -5
  159. package/server/nodes/README.md +31 -4
  160. package/server/nodes/agent/_inline.py +2 -0
  161. package/server/nodes/agent/_specialized.py +6 -3
  162. package/server/nodes/agent/ai_agent.py +13 -3
  163. package/server/nodes/agent/chat_agent.py +6 -3
  164. package/server/nodes/agent/claude_code_agent.py +287 -75
  165. package/server/nodes/agent/codex_agent.py +239 -0
  166. package/server/nodes/agent/deep_agent.py +3 -3
  167. package/server/nodes/agent/rlm_agent.py +3 -3
  168. package/server/nodes/android/__init__.py +31 -1
  169. package/server/nodes/android/_base.py +9 -5
  170. package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
  171. package/server/nodes/android/_handlers.py +154 -0
  172. package/server/nodes/android/_option_loaders.py +44 -0
  173. package/server/nodes/android/_refresh.py +127 -0
  174. package/server/{services/android → nodes/android/_relay}/client.py +4 -4
  175. package/server/{routers/android.py → nodes/android/_router.py} +27 -8
  176. package/server/nodes/browser/browser.py +2 -2
  177. package/server/nodes/code/_base.py +6 -2
  178. package/server/nodes/code/_claude_code.py +134 -0
  179. package/server/nodes/document/embedding_generator.py +3 -3
  180. package/server/nodes/document/http_scraper.py +3 -3
  181. package/server/nodes/document/vector_store.py +5 -5
  182. package/server/nodes/email/__init__.py +11 -1
  183. package/server/nodes/email/_filters.py +21 -0
  184. package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
  185. package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
  186. package/server/nodes/email/email_read.py +1 -1
  187. package/server/nodes/email/email_receive.py +54 -5
  188. package/server/nodes/email/email_send.py +1 -1
  189. package/server/nodes/filesystem/shell.py +24 -1
  190. package/server/nodes/google/__init__.py +55 -1
  191. package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
  192. package/server/nodes/google/_base.py +2 -2
  193. package/server/nodes/google/_credentials.py +5 -5
  194. package/server/nodes/google/_filters.py +25 -0
  195. package/server/nodes/google/_handlers.py +57 -0
  196. package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
  197. package/server/nodes/google/_option_loaders.py +107 -0
  198. package/server/nodes/google/_refresh.py +66 -0
  199. package/server/nodes/google/_router.py +131 -0
  200. package/server/nodes/google/gmail_receive.py +41 -4
  201. package/server/nodes/groups.py +1 -0
  202. package/server/nodes/location/_credentials.py +45 -1
  203. package/server/{services/maps.py → nodes/location/_service.py} +18 -3
  204. package/server/nodes/location/gmaps_create.py +4 -4
  205. package/server/nodes/location/gmaps_locations.py +4 -4
  206. package/server/nodes/location/gmaps_nearby_places.py +4 -4
  207. package/server/nodes/model/_base.py +8 -3
  208. package/server/nodes/model/_credentials.py +96 -8
  209. package/server/nodes/model/_local_validator.py +345 -0
  210. package/server/nodes/model/lmstudio_chat_model.py +23 -0
  211. package/server/nodes/model/ollama_chat_model.py +25 -0
  212. package/server/nodes/proxy/_usage.py +2 -2
  213. package/server/nodes/proxy/proxy_config.py +14 -14
  214. package/server/nodes/proxy/proxy_request.py +4 -4
  215. package/server/nodes/scraper/_credentials.py +29 -1
  216. package/server/nodes/scraper/apify_actor.py +9 -9
  217. package/server/nodes/scraper/crawlee_scraper.py +5 -5
  218. package/server/nodes/search/brave_search.py +4 -0
  219. package/server/nodes/search/perplexity_search.py +9 -0
  220. package/server/nodes/search/serper_search.py +3 -0
  221. package/server/nodes/skill/simple_memory.py +12 -0
  222. package/server/nodes/social/_base.py +2 -2
  223. package/server/nodes/stripe/__init__.py +46 -0
  224. package/server/nodes/stripe/_credentials.py +33 -0
  225. package/server/nodes/stripe/_handlers.py +270 -0
  226. package/server/nodes/stripe/_install.py +127 -0
  227. package/server/nodes/stripe/_source.py +174 -0
  228. package/server/nodes/stripe/stripe_action.py +81 -0
  229. package/server/nodes/stripe/stripe_receive.py +92 -0
  230. package/server/nodes/telegram/_credentials.py +52 -1
  231. package/server/nodes/telegram/_handlers.py +19 -18
  232. package/server/nodes/telegram/_service.py +134 -32
  233. package/server/nodes/telegram/telegram_send.py +5 -6
  234. package/server/nodes/text/file_handler.py +2 -2
  235. package/server/nodes/text/text_generator.py +2 -2
  236. package/server/nodes/tool/agent_builder.py +630 -0
  237. package/server/nodes/tool/task_manager.py +144 -2
  238. package/server/nodes/twitter/__init__.py +38 -1
  239. package/server/nodes/twitter/_base.py +7 -7
  240. package/server/nodes/twitter/_credentials.py +1 -1
  241. package/server/nodes/twitter/_filters.py +37 -0
  242. package/server/nodes/twitter/_handlers.py +77 -0
  243. package/server/nodes/twitter/_oauth.py +124 -0
  244. package/server/nodes/twitter/_refresh.py +78 -0
  245. package/server/nodes/twitter/_router.py +29 -0
  246. package/server/nodes/twitter/twitter_receive.py +4 -0
  247. package/server/nodes/visuals.json +64 -19
  248. package/server/nodes/whatsapp/__init__.py +45 -5
  249. package/server/nodes/whatsapp/_base.py +3 -3
  250. package/server/nodes/whatsapp/_filters.py +137 -0
  251. package/server/nodes/whatsapp/_handlers.py +167 -0
  252. package/server/nodes/whatsapp/_option_loaders.py +68 -0
  253. package/server/nodes/whatsapp/_refresh.py +62 -0
  254. package/server/nodes/whatsapp/_runtime.py +1 -1
  255. package/server/pyproject.toml +29 -7
  256. package/server/routers/schemas.py +2 -2
  257. package/server/routers/webhook.py +26 -9
  258. package/server/routers/websocket.py +149 -810
  259. package/server/services/ai.py +89 -8
  260. package/server/services/auth.py +220 -43
  261. package/server/services/claude_oauth.py +126 -100
  262. package/server/services/cli_agent/__init__.py +78 -0
  263. package/server/services/cli_agent/_handlers.py +237 -0
  264. package/server/services/cli_agent/config.py +112 -0
  265. package/server/services/cli_agent/factory.py +48 -0
  266. package/server/services/cli_agent/lockfile.py +141 -0
  267. package/server/services/cli_agent/mcp_server.py +482 -0
  268. package/server/services/cli_agent/protocol.py +173 -0
  269. package/server/services/cli_agent/providers/__init__.py +9 -0
  270. package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
  271. package/server/services/cli_agent/providers/google_gemini.py +80 -0
  272. package/server/services/cli_agent/providers/openai_codex.py +310 -0
  273. package/server/services/cli_agent/service.py +607 -0
  274. package/server/services/cli_agent/session.py +618 -0
  275. package/server/services/cli_agent/types.py +227 -0
  276. package/server/services/cli_agent/workflow_tools.py +233 -0
  277. package/server/services/credential_registry.py +26 -1
  278. package/server/services/deployment/manager.py +26 -145
  279. package/server/services/deployment/poll_registry.py +59 -0
  280. package/server/services/event_waiter.py +76 -246
  281. package/server/services/events/__init__.py +54 -0
  282. package/server/services/events/cli.py +78 -0
  283. package/server/services/events/daemon.py +163 -0
  284. package/server/services/events/envelope.py +281 -0
  285. package/server/services/events/lifecycle.py +99 -0
  286. package/server/services/events/oauth_lifecycle.py +534 -0
  287. package/server/services/events/polling.py +60 -0
  288. package/server/services/events/push.py +36 -0
  289. package/server/services/events/source.py +63 -0
  290. package/server/services/events/triggers.py +118 -0
  291. package/server/services/events/verifiers/__init__.py +25 -0
  292. package/server/services/events/verifiers/base.py +28 -0
  293. package/server/services/events/verifiers/github.py +25 -0
  294. package/server/services/events/verifiers/hmac_basic.py +32 -0
  295. package/server/services/events/verifiers/standard_webhooks.py +47 -0
  296. package/server/services/events/verifiers/stripe.py +42 -0
  297. package/server/services/events/webhook.py +105 -0
  298. package/server/services/handlers/tools.py +28 -186
  299. package/server/services/llm/config.py +7 -0
  300. package/server/services/llm/factory.py +8 -2
  301. package/server/services/memory/__init__.py +52 -0
  302. package/server/services/memory/jsonl.py +80 -0
  303. package/server/services/memory/markdown.py +65 -0
  304. package/server/services/memory/state.py +112 -0
  305. package/server/services/memory/vector_store.py +40 -0
  306. package/server/services/model_registry.py +76 -0
  307. package/server/services/node_allowlist.py +71 -15
  308. package/server/services/node_executor.py +2 -2
  309. package/server/services/node_output_schemas.py +21 -10
  310. package/server/services/node_spec.py +1 -1
  311. package/server/services/oauth_utils.py +1 -1
  312. package/server/services/plugin/__init__.py +2 -0
  313. package/server/services/plugin/base.py +44 -2
  314. package/server/services/plugin/credential.py +288 -1
  315. package/server/services/plugin/deps.py +105 -0
  316. package/server/services/plugin/edge_walker.py +12 -4
  317. package/server/services/plugin/oauth.py +381 -0
  318. package/server/services/plugin/polling.py +247 -0
  319. package/server/services/plugin/registry.py +145 -0
  320. package/server/services/plugin/singleton.py +65 -0
  321. package/server/services/plugin/ws.py +81 -0
  322. package/server/services/process_service.py +31 -2
  323. package/server/services/status_broadcaster.py +155 -238
  324. package/server/services/temporal/workflow.py +7 -7
  325. package/server/services/workflow.py +21 -3
  326. package/server/services/ws_handler_registry.py +111 -28
  327. package/server/skills/GUIDE.md +16 -1
  328. package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
  329. package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
  330. package/server/tests/credentials/test_auth_service.py +16 -9
  331. package/server/tests/credentials/test_credential_broadcasts.py +219 -0
  332. package/server/tests/credentials/test_google_oauth.py +6 -6
  333. package/server/tests/credentials/test_oauth_utils.py +1 -1
  334. package/server/tests/credentials/test_twitter_oauth.py +2 -2
  335. package/server/tests/credentials/test_websocket_handlers.py +44 -20
  336. package/server/tests/llm/test_factory.py +1 -0
  337. package/server/tests/llm/test_wiring.py +5 -1
  338. package/server/tests/nodes/_compat.py +24 -24
  339. package/server/tests/nodes/test_agent_builder.py +439 -0
  340. package/server/tests/nodes/test_ai_tools.py +18 -14
  341. package/server/tests/nodes/test_code_fs_process.py +17 -8
  342. package/server/tests/nodes/test_email.py +10 -9
  343. package/server/tests/nodes/test_google_workspace.py +2 -2
  344. package/server/tests/nodes/test_specialized_agents.py +100 -53
  345. package/server/tests/nodes/test_stripe_plugin.py +293 -0
  346. package/server/tests/nodes/test_telegram_social.py +4 -4
  347. package/server/tests/nodes/test_twitter.py +1 -1
  348. package/server/tests/nodes/test_web_automation.py +2 -2
  349. package/server/tests/nodes/test_whatsapp.py +9 -9
  350. package/server/tests/services/cli_agent/__init__.py +0 -0
  351. package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
  352. package/server/tests/services/cli_agent/test_providers.py +358 -0
  353. package/server/tests/services/cli_agent/test_service.py +298 -0
  354. package/server/tests/services/memory/__init__.py +0 -0
  355. package/server/tests/services/memory/test_jsonl.py +188 -0
  356. package/server/tests/services/test_events.py +333 -0
  357. package/server/tests/test_node_spec.py +56 -16
  358. package/server/tests/test_plugin_helpers.py +116 -0
  359. package/server/tests/test_plugin_self_containment.py +486 -0
  360. package/server/tests/test_status_broadcasts.py +425 -0
  361. package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
  362. package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
  363. package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
  364. package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
  365. package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
  366. package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
  367. package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
  368. package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
  369. package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
  370. package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
  371. package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
  372. package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
  373. package/client/dist/assets/index-D9tZfgvi.js +0 -363
  374. package/client/dist/assets/index-al7snTkG.css +0 -1
  375. package/client/src/components/credentials/providers.tsx +0 -177
  376. package/server/routers/google.py +0 -277
  377. package/server/routers/maps.py +0 -142
  378. package/server/routers/twitter.py +0 -365
  379. package/server/services/claude_code_service.py +0 -106
  380. package/server/services/memory.py +0 -159
  381. package/server/services/node_option_loaders/__init__.py +0 -77
  382. package/server/services/node_option_loaders/android_loaders.py +0 -55
  383. package/server/services/node_option_loaders/google_loaders.py +0 -97
  384. package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
  385. package/server/services/twitter_oauth.py +0 -411
  386. package/server/services/websocket_client.py +0 -29
  387. /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
  388. /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
  389. /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
  390. /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
  391. /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
  392. /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
  393. /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
@@ -5,9 +5,9 @@ These tests freeze the input -> output behaviour documented in
5
5
  to the Go `whatsapp-rpc` service via a WebSocket RPC client in
6
6
  `routers/whatsapp.py`. We patch the two entry points the handlers use:
7
7
 
8
- - `services.whatsapp_service.handle_whatsapp_send` - sending messages
9
- - `services.whatsapp_service.whatsapp_rpc_call` - generic RPC calls
10
- - `services.whatsapp_service.handle_whatsapp_chat_history` - chat history RPC
8
+ - `nodes.whatsapp._service.handle_whatsapp_send` - sending messages
9
+ - `nodes.whatsapp._service.whatsapp_rpc_call` - generic RPC calls
10
+ - `nodes.whatsapp._service.handle_whatsapp_chat_history` - chat history RPC
11
11
 
12
12
  For `whatsappReceive` we use the same event-waiter stub pattern as
13
13
  `test_telegram_social.py` - patching the module reference imported by both
@@ -32,15 +32,15 @@ pytestmark = pytest.mark.node_contract
32
32
 
33
33
 
34
34
  def _patch_whatsapp_send(return_value):
35
- """Patch services.whatsapp_service.handle_whatsapp_send at the import site."""
35
+ """Patch nodes.whatsapp._service.handle_whatsapp_send at the import site."""
36
36
  return patch(
37
- "services.whatsapp_service.handle_whatsapp_send",
37
+ "nodes.whatsapp._service.handle_whatsapp_send",
38
38
  new=AsyncMock(return_value=return_value),
39
39
  )
40
40
 
41
41
 
42
42
  def _patch_rpc_call(return_value=None, *, side_effect=None):
43
- """Patch services.whatsapp_service.whatsapp_rpc_call.
43
+ """Patch nodes.whatsapp._service.whatsapp_rpc_call.
44
44
 
45
45
  ``return_value`` may be a dict or list. ``side_effect`` may be a callable
46
46
  for per-method routing.
@@ -50,12 +50,12 @@ def _patch_rpc_call(return_value=None, *, side_effect=None):
50
50
  kwargs["side_effect"] = side_effect
51
51
  else:
52
52
  kwargs["return_value"] = return_value if return_value is not None else {}
53
- return patch("services.whatsapp_service.whatsapp_rpc_call", new=AsyncMock(**kwargs))
53
+ return patch("nodes.whatsapp._service.whatsapp_rpc_call", new=AsyncMock(**kwargs))
54
54
 
55
55
 
56
56
  def _patch_chat_history_handler(return_value):
57
57
  return patch(
58
- "services.whatsapp_service.handle_whatsapp_chat_history",
58
+ "nodes.whatsapp._service.handle_whatsapp_chat_history",
59
59
  new=AsyncMock(return_value=return_value),
60
60
  )
61
61
 
@@ -95,7 +95,7 @@ class TestWhatsappSend:
95
95
  return {"success": True}
96
96
 
97
97
  with patch(
98
- "services.whatsapp_service.handle_whatsapp_send",
98
+ "nodes.whatsapp._service.handle_whatsapp_send",
99
99
  new=AsyncMock(side_effect=fake_send),
100
100
  ), patch(
101
101
  "services.markdown_formatter.to_whatsapp",
File without changes
@@ -0,0 +1,432 @@
1
+ """MCP server integration tests.
2
+
3
+ Covers:
4
+ - Bearer-token registry: register/lookup/unregister
5
+ - 401 on missing / malformed / wrong / expired tokens
6
+ - Per-batch scoping: tools see the right `BatchContext`
7
+ - Lockfile format (VSCode-shape) + stale-PID sweep
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import os
14
+ import tempfile
15
+ from pathlib import Path
16
+
17
+ import pytest
18
+ from httpx import ASGITransport, AsyncClient
19
+
20
+ from services.cli_agent.lockfile import (
21
+ list_active_lockfiles,
22
+ lockfile_path,
23
+ remove_ide_lockfile,
24
+ sweep_stale_lockfiles,
25
+ write_ide_lockfile,
26
+ )
27
+ from services.cli_agent.mcp_server import (
28
+ BatchContext,
29
+ _reset_for_tests,
30
+ active_batch_count,
31
+ get_mcp_app,
32
+ issue_token,
33
+ lookup_batch,
34
+ register_batch,
35
+ unregister_batch,
36
+ )
37
+
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Token registry
41
+ # ---------------------------------------------------------------------------
42
+
43
+ class TestTokenRegistry:
44
+ def setup_method(self):
45
+ _reset_for_tests()
46
+
47
+ def test_issue_token_is_random_64_hex(self):
48
+ a = issue_token()
49
+ b = issue_token()
50
+ assert a != b
51
+ assert len(a) == 64
52
+ int(a, 16) # parses as hex
53
+
54
+ def test_register_lookup_unregister(self):
55
+ token = issue_token()
56
+ ctx = BatchContext(
57
+ workflow_id="wf", node_id="n",
58
+ workspace_dir=Path("."),
59
+ )
60
+ assert lookup_batch(token) is None
61
+ register_batch(token, ctx)
62
+ assert lookup_batch(token) is ctx
63
+ assert active_batch_count() == 1
64
+ unregister_batch(token)
65
+ assert lookup_batch(token) is None
66
+ assert active_batch_count() == 0
67
+
68
+ def test_unregister_idempotent(self):
69
+ unregister_batch("nonexistent") # should not raise
70
+
71
+ def test_token_collision_with_different_ctx_raises(self):
72
+ token = issue_token()
73
+ ctx1 = BatchContext(workflow_id="a", node_id="n", workspace_dir=Path("."))
74
+ ctx2 = BatchContext(workflow_id="b", node_id="n", workspace_dir=Path("."))
75
+ register_batch(token, ctx1)
76
+ with pytest.raises(ValueError, match="collision"):
77
+ register_batch(token, ctx2)
78
+
79
+
80
+ # ---------------------------------------------------------------------------
81
+ # ASGI auth middleware
82
+ # ---------------------------------------------------------------------------
83
+
84
+ class TestAuthMiddleware:
85
+ def setup_method(self):
86
+ _reset_for_tests()
87
+
88
+ @pytest.mark.asyncio
89
+ async def test_no_authorization_header_returns_401(self):
90
+ app = get_mcp_app()
91
+ async with AsyncClient(transport=ASGITransport(app=app), base_url="http://t") as c:
92
+ r = await c.post(
93
+ "/mcp/",
94
+ json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
95
+ )
96
+ assert r.status_code == 401
97
+
98
+ @pytest.mark.asyncio
99
+ async def test_malformed_authorization_returns_401(self):
100
+ app = get_mcp_app()
101
+ async with AsyncClient(transport=ASGITransport(app=app), base_url="http://t") as c:
102
+ r = await c.post(
103
+ "/mcp/",
104
+ json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
105
+ headers={"Authorization": "Token abc"},
106
+ )
107
+ assert r.status_code == 401
108
+
109
+ @pytest.mark.asyncio
110
+ async def test_unknown_token_returns_401(self):
111
+ app = get_mcp_app()
112
+ async with AsyncClient(transport=ASGITransport(app=app), base_url="http://t") as c:
113
+ r = await c.post(
114
+ "/mcp/",
115
+ json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
116
+ headers={"Authorization": "Bearer unknown_token"},
117
+ )
118
+ assert r.status_code == 401
119
+
120
+ @pytest.mark.asyncio
121
+ async def test_expired_token_returns_401(self):
122
+ """Token registered then unregistered behaves as unknown."""
123
+ token = issue_token()
124
+ register_batch(token, BatchContext(
125
+ workflow_id="wf", node_id="n", workspace_dir=Path("."),
126
+ ))
127
+ unregister_batch(token)
128
+ app = get_mcp_app()
129
+ async with AsyncClient(transport=ASGITransport(app=app), base_url="http://t") as c:
130
+ r = await c.post(
131
+ "/mcp/",
132
+ json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
133
+ headers={"Authorization": f"Bearer {token}"},
134
+ )
135
+ assert r.status_code == 401
136
+
137
+
138
+ # ---------------------------------------------------------------------------
139
+ # Lockfile format
140
+ # ---------------------------------------------------------------------------
141
+
142
+ class TestLockfile:
143
+ def test_claude_lockfile_path_pid_lock(self, tmp_path):
144
+ path = lockfile_path(
145
+ ide_lockfile_dir=tmp_path,
146
+ pid=12345,
147
+ port=3010,
148
+ ide_name="claude",
149
+ )
150
+ assert path.name == "12345.lock"
151
+
152
+ def test_gemini_lockfile_path_includes_port(self, tmp_path):
153
+ path = lockfile_path(
154
+ ide_lockfile_dir=tmp_path,
155
+ pid=12345,
156
+ port=3010,
157
+ ide_name="gemini",
158
+ )
159
+ assert "12345" in path.name
160
+ assert "3010" in path.name
161
+ assert path.name.endswith(".json")
162
+
163
+ def test_lockfile_payload_matches_vscode_shape(self, tmp_path):
164
+ path = write_ide_lockfile(
165
+ ide_lockfile_dir=tmp_path,
166
+ pid=99999,
167
+ port=3010,
168
+ token="abc123",
169
+ workspace_dir=tmp_path / "ws",
170
+ ide_name="claude",
171
+ )
172
+ payload = json.loads(path.read_text(encoding="utf-8"))
173
+ # VSCode-style fields
174
+ assert payload["port"] == 3010
175
+ assert payload["authToken"] == "abc123"
176
+ assert payload["ideName"] == "claude"
177
+ assert "url" in payload
178
+ assert "workspaceFolders" in payload
179
+ assert payload["transport"] == "http"
180
+ # Our extra: pid for the stale-sweep
181
+ assert payload["pid"] == 99999
182
+
183
+ def test_default_url_constructed(self, tmp_path):
184
+ path = write_ide_lockfile(
185
+ ide_lockfile_dir=tmp_path,
186
+ pid=1, port=3010, token="t",
187
+ workspace_dir=tmp_path, ide_name="claude",
188
+ )
189
+ payload = json.loads(path.read_text(encoding="utf-8"))
190
+ # FastMCP serves at `/mcp` of the sub-app, mounted at `/mcp/ide`.
191
+ # The lockfile must advertise the absolute JSON-RPC endpoint.
192
+ assert payload["url"] == "http://127.0.0.1:3010/mcp/ide/mcp"
193
+
194
+ def test_remove_lockfile_safe_when_missing(self, tmp_path):
195
+ # Should never raise
196
+ remove_ide_lockfile(None)
197
+ remove_ide_lockfile(tmp_path / "does-not-exist.lock")
198
+
199
+ def test_sweep_removes_dead_pid_lockfile(self, tmp_path):
200
+ # Find a guaranteed-dead PID by walking up high. PID 0 is the
201
+ # system idle process on Windows and the swapper on POSIX, so
202
+ # `psutil.pid_exists(0)` returns True everywhere — DON'T use 0.
203
+ import psutil
204
+ dead_pid = 99_999_999
205
+ while psutil.pid_exists(dead_pid):
206
+ dead_pid += 1
207
+ if dead_pid > 999_999_999: # paranoid bound
208
+ pytest.skip("could not find a dead PID")
209
+
210
+ write_ide_lockfile(
211
+ ide_lockfile_dir=tmp_path, pid=dead_pid, port=3010, token="t",
212
+ workspace_dir=tmp_path, ide_name="claude",
213
+ )
214
+ # Write one with the live PID
215
+ live_path = write_ide_lockfile(
216
+ ide_lockfile_dir=tmp_path, pid=os.getpid(), port=3010, token="t",
217
+ workspace_dir=tmp_path, ide_name="claude",
218
+ )
219
+
220
+ before = list_active_lockfiles(tmp_path)
221
+ assert len(before) >= 2
222
+
223
+ n = sweep_stale_lockfiles(tmp_path)
224
+ # Should have removed at least the dead-PID one
225
+ assert n >= 1
226
+
227
+ after = list_active_lockfiles(tmp_path)
228
+ # Live one survives
229
+ assert live_path in after
230
+
231
+ def test_sweep_safe_on_nonexistent_dir(self, tmp_path):
232
+ assert sweep_stale_lockfiles(tmp_path / "no") == 0
233
+ assert sweep_stale_lockfiles(None) == 0
234
+
235
+
236
+ # ---------------------------------------------------------------------------
237
+ # MachinaOs workflow-tool bridge — per-batch dynamic FastMCP exposure.
238
+ # Each wired node lands as `mcp__machinaos__<node_type>`; FastMCP infers
239
+ # the schema from the typed `params` annotation (no custom translation).
240
+ #
241
+ # These tests drive FastMCP's own ``list_tools`` / ``call_tool`` API so
242
+ # we exercise the same code path the spawned ``claude`` CLI hits over
243
+ # JSON-RPC.
244
+ # ---------------------------------------------------------------------------
245
+
246
+ class TestMachinaOsToolBridge:
247
+ def setup_method(self):
248
+ _reset_for_tests()
249
+ import nodes # noqa: F401 — populate plugin registry
250
+
251
+ @staticmethod
252
+ def _ctx(node_type: str, label: str) -> BatchContext:
253
+ return BatchContext(
254
+ workflow_id="wf_test", node_id="claude_code_agent_1",
255
+ workspace_dir=Path("."),
256
+ connected_tools=[{
257
+ "node_id": f"{node_type}_1", "node_type": node_type,
258
+ "label": label, "parameters": {},
259
+ }],
260
+ )
261
+
262
+ @pytest.mark.asyncio
263
+ async def test_register_batch_exposes_tool_on_fastmcp(self):
264
+ """The connected workflow node must surface in ``list_tools`` so
265
+ claude sees ``mcp__machinaos__<node_type>`` on first
266
+ ``tools/list``."""
267
+ from services.cli_agent.mcp_server import _mcp_singleton, get_mcp_app
268
+ get_mcp_app() # ensure FastMCP singleton built
269
+ from services.cli_agent.mcp_server import _mcp_singleton as mcp
270
+ assert mcp is not None
271
+
272
+ ctx = self._ctx("calculatorTool", "Calculator")
273
+ token = issue_token()
274
+ register_batch(token, ctx)
275
+ try:
276
+ tools = await mcp.list_tools()
277
+ names = {t.name for t in tools}
278
+ assert "calculatorTool" in names
279
+ calc = next(t for t in tools if t.name == "calculatorTool")
280
+ # FastMCP infers the inputSchema from the typed `params` arg
281
+ # (Pydantic-v2 → JSON Schema 2020-12 wire format).
282
+ assert {"operation", "a", "b"}.issubset(
283
+ calc.inputSchema["properties"].keys()
284
+ )
285
+ finally:
286
+ unregister_batch(token)
287
+
288
+ # After unregister the tool refcount hits zero and FastMCP drops it.
289
+ tools_after = await mcp.list_tools()
290
+ assert "calculatorTool" not in {t.name for t in tools_after}
291
+
292
+ @pytest.mark.asyncio
293
+ async def test_call_tool_dispatches_via_execute_tool(self):
294
+ """``mcp.call_tool`` for a wired node routes through
295
+ ``services.handlers.tools.execute_tool`` and returns the
296
+ plugin's result envelope."""
297
+ from services.cli_agent.mcp_server import (
298
+ _current_batch, get_mcp_app,
299
+ )
300
+ get_mcp_app()
301
+ from services.cli_agent.mcp_server import _mcp_singleton as mcp
302
+
303
+ ctx = self._ctx("calculatorTool", "Calculator")
304
+ token = issue_token()
305
+ register_batch(token, ctx)
306
+ reset = _current_batch.set(ctx)
307
+ try:
308
+ out = await mcp.call_tool(
309
+ "calculatorTool",
310
+ {"operation": "multiply", "a": 2, "b": 21},
311
+ )
312
+ finally:
313
+ _current_batch.reset(reset)
314
+ unregister_batch(token)
315
+
316
+ # FastMCP's `call_tool` returns either a content sequence or a
317
+ # dict, depending on `structured_output`. Either way the
318
+ # calculator's `result` (42) must be in there.
319
+ text = repr(out)
320
+ assert "42" in text, text
321
+
322
+ @pytest.mark.asyncio
323
+ async def test_unconnected_tool_call_is_blocked_by_batch_scope(self):
324
+ """If a tool is registered globally (because some other batch
325
+ wired it) but the calling batch didn't, the per-handler
326
+ ``_require_batch`` check returns 403."""
327
+ from services.cli_agent.mcp_server import _current_batch, get_mcp_app
328
+ get_mcp_app()
329
+ from services.cli_agent.mcp_server import _mcp_singleton as mcp
330
+
331
+ # Batch A wires both tools — registers them globally.
332
+ token_a = issue_token()
333
+ register_batch(token_a, BatchContext(
334
+ workflow_id="wf_a", node_id="cc_a", workspace_dir=Path("."),
335
+ connected_tools=[
336
+ {"node_id": "c1", "node_type": "calculatorTool", "label": "C", "parameters": {}},
337
+ {"node_id": "h1", "node_type": "httpRequest", "label": "H", "parameters": {}},
338
+ ],
339
+ ))
340
+ # Batch B wires only one. Even though `httpRequest` is exposed
341
+ # globally (refcount=1), batch B's ctx forbids it.
342
+ ctx_b = self._ctx("calculatorTool", "C")
343
+ token_b = issue_token()
344
+ register_batch(token_b, ctx_b)
345
+ reset = _current_batch.set(ctx_b)
346
+ try:
347
+ out = await mcp.call_tool(
348
+ "httpRequest", {"method": "GET", "url": "x"},
349
+ )
350
+ finally:
351
+ _current_batch.reset(reset)
352
+ unregister_batch(token_b)
353
+ unregister_batch(token_a)
354
+
355
+ text = repr(out)
356
+ assert "403" in text or "not connected" in text, text
357
+
358
+ # -- live, real-tool runs (no mocks) ----------------------------------
359
+
360
+ @pytest.mark.asyncio
361
+ async def test_currentTimeTool_real(self):
362
+ from services.cli_agent.mcp_server import _current_batch, get_mcp_app
363
+ get_mcp_app()
364
+ from services.cli_agent.mcp_server import _mcp_singleton as mcp
365
+
366
+ ctx = self._ctx("currentTimeTool", "Time")
367
+ token = issue_token()
368
+ register_batch(token, ctx)
369
+ reset = _current_batch.set(ctx)
370
+ try:
371
+ tools = {t.name for t in await mcp.list_tools()}
372
+ assert "currentTimeTool" in tools
373
+ out = await mcp.call_tool("currentTimeTool", {"timezone": "UTC"})
374
+ finally:
375
+ _current_batch.reset(reset)
376
+ unregister_batch(token)
377
+ text = repr(out)
378
+ assert "UTC" in text, text
379
+
380
+ @pytest.mark.slow
381
+ @pytest.mark.asyncio
382
+ async def test_duckduckgoSearch_real(self):
383
+ from services.cli_agent.mcp_server import _current_batch, get_mcp_app
384
+ get_mcp_app()
385
+ from services.cli_agent.mcp_server import _mcp_singleton as mcp
386
+
387
+ ctx = self._ctx("duckduckgoSearch", "DDG")
388
+ token = issue_token()
389
+ register_batch(token, ctx)
390
+ reset = _current_batch.set(ctx)
391
+ try:
392
+ try:
393
+ out = await mcp.call_tool(
394
+ "duckduckgoSearch",
395
+ {"query": "MachinaOs Anthropic Claude", "max_results": 3},
396
+ )
397
+ except OSError as exc:
398
+ pytest.skip(f"network unavailable: {exc}")
399
+ finally:
400
+ _current_batch.reset(reset)
401
+ unregister_batch(token)
402
+ text = repr(out)
403
+ if "error" in text and "duckduckgo" in text.lower():
404
+ pytest.skip(f"ddgs unavailable: {text[:200]}")
405
+ assert "duckduckgo" in text.lower(), text[:200]
406
+
407
+ @pytest.mark.slow
408
+ @pytest.mark.asyncio
409
+ async def test_httpRequest_real(self):
410
+ from services.cli_agent.mcp_server import _current_batch, get_mcp_app
411
+ get_mcp_app()
412
+ from services.cli_agent.mcp_server import _mcp_singleton as mcp
413
+
414
+ ctx = self._ctx("httpRequest", "HTTP")
415
+ token = issue_token()
416
+ register_batch(token, ctx)
417
+ reset = _current_batch.set(ctx)
418
+ try:
419
+ try:
420
+ out = await mcp.call_tool(
421
+ "httpRequest",
422
+ {"method": "GET", "url": "https://httpbin.org/get?bridge=ok"},
423
+ )
424
+ except OSError as exc:
425
+ pytest.skip(f"network unavailable: {exc}")
426
+ finally:
427
+ _current_batch.reset(reset)
428
+ unregister_batch(token)
429
+ text = repr(out)
430
+ if '"status": 200' not in text and "'status': 200" not in text:
431
+ pytest.skip(f"httpbin unavailable: {text[:200]}")
432
+ assert "bridge" in text and "ok" in text, text[:300]