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
@@ -1,4 +1,9 @@
1
1
  {
2
+ "agentBuilder": {
3
+ "icon": "🛠️",
4
+ "color": "#ffb86c",
5
+ "skill": "agent-builder-skill"
6
+ },
2
7
  "aiAgent": {
3
8
  "icon": "🤖",
4
9
  "color": "#bd93f9"
@@ -8,7 +13,8 @@
8
13
  "color": "#bd93f9"
9
14
  },
10
15
  "airplaneModeControl": {
11
- "icon": "✈️"
16
+ "icon": "✈️",
17
+ "color": "#50fa7b"
12
18
  },
13
19
  "android_agent": {
14
20
  "icon": "📱",
@@ -25,15 +31,18 @@
25
31
  },
26
32
  "appLauncher": {
27
33
  "icon": "🚀",
28
- "skill": "app-launcher-skill"
34
+ "skill": "app-launcher-skill",
35
+ "color": "#50fa7b"
29
36
  },
30
37
  "appList": {
31
38
  "icon": "📋",
32
- "skill": "app-list-skill"
39
+ "skill": "app-list-skill",
40
+ "color": "#50fa7b"
33
41
  },
34
42
  "audioAutomation": {
35
43
  "icon": "🔊",
36
- "skill": "audio-skill"
44
+ "skill": "audio-skill",
45
+ "color": "#50fa7b"
37
46
  },
38
47
  "autonomous_agent": {
39
48
  "icon": "🎯",
@@ -41,11 +50,13 @@
41
50
  },
42
51
  "batteryMonitor": {
43
52
  "icon": "🔋",
44
- "skill": "battery-skill"
53
+ "skill": "battery-skill",
54
+ "color": "#50fa7b"
45
55
  },
46
56
  "bluetoothAutomation": {
47
57
  "icon": "🔵",
48
- "skill": "bluetooth-skill"
58
+ "skill": "bluetooth-skill",
59
+ "color": "#50fa7b"
49
60
  },
50
61
  "braveSearch": {
51
62
  "icon": "asset:brave",
@@ -63,7 +74,8 @@
63
74
  },
64
75
  "cameraControl": {
65
76
  "icon": "📷",
66
- "skill": "camera-skill"
77
+ "skill": "camera-skill",
78
+ "color": "#50fa7b"
67
79
  },
68
80
  "cerebrasChatModel": {
69
81
  "icon": "lobehub:cerebras",
@@ -89,6 +101,10 @@
89
101
  "icon": "lobehub:Claude",
90
102
  "color": "#8be9fd"
91
103
  },
104
+ "codex_agent": {
105
+ "icon": "lobehub:OpenAI",
106
+ "color": "#ffb86c"
107
+ },
92
108
  "coding_agent": {
93
109
  "icon": "💻",
94
110
  "color": "#8be9fd"
@@ -124,7 +140,8 @@
124
140
  "color": "#8be9fd"
125
141
  },
126
142
  "deviceStateAutomation": {
127
- "icon": "⚙️"
143
+ "icon": "⚙️",
144
+ "color": "#50fa7b"
128
145
  },
129
146
  "documentParser": {
130
147
  "icon": "📄",
@@ -153,7 +170,8 @@
153
170
  },
154
171
  "environmentalSensors": {
155
172
  "icon": "🌡️",
156
- "skill": "environmental-skill"
173
+ "skill": "environmental-skill",
174
+ "color": "#50fa7b"
157
175
  },
158
176
  "fileDownloader": {
159
177
  "icon": "⬇️",
@@ -245,22 +263,29 @@
245
263
  },
246
264
  "javascriptExecutor": {
247
265
  "icon": "asset:javascript",
248
- "skill": "javascript-skill"
266
+ "skill": "javascript-skill",
267
+ "color": "#ffb86c"
249
268
  },
250
269
  "kimiChatModel": {
251
270
  "icon": "lobehub:kimi",
252
271
  "color": "#bd93f9"
253
272
  },
273
+ "lmstudioChatModel": {
274
+ "icon": "lobehub:lmstudio",
275
+ "color": "#4F8EFF"
276
+ },
254
277
  "location": {
255
278
  "icon": "📍",
256
- "skill": "location-skill"
279
+ "skill": "location-skill",
280
+ "color": "#50fa7b"
257
281
  },
258
282
  "masterSkill": {
259
283
  "icon": "🎯",
260
284
  "color": "#f1fa8c"
261
285
  },
262
286
  "mediaControl": {
263
- "icon": "🎵"
287
+ "icon": "🎵",
288
+ "color": "#50fa7b"
264
289
  },
265
290
  "mistralChatModel": {
266
291
  "icon": "lobehub:mistral",
@@ -268,10 +293,16 @@
268
293
  },
269
294
  "motionDetection": {
270
295
  "icon": "📳",
271
- "skill": "motion-skill"
296
+ "skill": "motion-skill",
297
+ "color": "#50fa7b"
272
298
  },
273
299
  "networkMonitor": {
274
- "icon": "📡"
300
+ "icon": "📡",
301
+ "color": "#50fa7b"
302
+ },
303
+ "ollamaChatModel": {
304
+ "icon": "lobehub:ollama",
305
+ "color": "#000000"
275
306
  },
276
307
  "openaiChatModel": {
277
308
  "icon": "lobehub:openai",
@@ -318,7 +349,8 @@
318
349
  },
319
350
  "pythonExecutor": {
320
351
  "icon": "asset:python",
321
- "skill": "python-skill"
352
+ "skill": "python-skill",
353
+ "color": "#ffb86c"
322
354
  },
323
355
  "rlm_agent": {
324
356
  "icon": "🧠",
@@ -326,7 +358,8 @@
326
358
  },
327
359
  "screenControlAutomation": {
328
360
  "icon": "💡",
329
- "skill": "screen-control-skill"
361
+ "skill": "screen-control-skill",
362
+ "color": "#50fa7b"
330
363
  },
331
364
  "serperSearch": {
332
365
  "icon": "asset:google",
@@ -359,7 +392,8 @@
359
392
  "color": "#bd93f9"
360
393
  },
361
394
  "systemInfo": {
362
- "icon": "📱"
395
+ "icon": "📱",
396
+ "color": "#50fa7b"
363
397
  },
364
398
  "taskManager": {
365
399
  "icon": "📋",
@@ -427,7 +461,8 @@
427
461
  "skill": "twitter-user-skill"
428
462
  },
429
463
  "typescriptExecutor": {
430
- "icon": "asset:typescript"
464
+ "icon": "asset:typescript",
465
+ "color": "#ffb86c"
431
466
  },
432
467
  "vectorStore": {
433
468
  "icon": "🗄️",
@@ -461,11 +496,21 @@
461
496
  },
462
497
  "wifiAutomation": {
463
498
  "icon": "📶",
464
- "skill": "wifi-skill"
499
+ "skill": "wifi-skill",
500
+ "color": "#50fa7b"
465
501
  },
466
502
  "writeTodos": {
467
503
  "icon": "📝",
468
504
  "color": "#bd93f9",
469
505
  "skill": "write-todos-skill"
506
+ },
507
+ "stripeAction": {
508
+ "icon": "asset:stripe",
509
+ "color": "#635BFF",
510
+ "skill": "stripe-skill"
511
+ },
512
+ "stripeReceive": {
513
+ "icon": "asset:stripe",
514
+ "color": "#635BFF"
470
515
  }
471
516
  }
@@ -4,20 +4,60 @@ Public surface:
4
4
  WhatsAppRuntime - edgymeow Go binary supervisor
5
5
  get_whatsapp_runtime() - singleton accessor (lazy-init via classmethod)
6
6
 
7
- The runtime self-registers with ``services._supervisor.registry`` on
8
- package import, so ``services._supervisor.shutdown_all_supervisors()``
9
- called from the FastAPI lifespan will tear it down with all other
10
- plugin supervisors. No per-plugin lifespan hooks needed.
7
+ Two self-registrations happen on package import:
8
+
9
+ 1. The Go-binary supervisor (``WhatsAppRuntime``) is registered with
10
+ :mod:`services._supervisor`, so the FastAPI lifespan teardown
11
+ reaches it via ``shutdown_all_supervisors()``.
12
+
13
+ 2. The WebSocket handler dispatch table (``WS_HANDLERS`` in
14
+ :mod:`._handlers`) is registered with
15
+ :mod:`services.ws_handler_registry`, so the central WS router in
16
+ ``routers/websocket.py`` picks up every ``whatsapp_*`` message type
17
+ without per-plugin imports. Same pattern as the telegram reference
18
+ plugin.
11
19
  """
12
20
 
13
21
  from services._supervisor import register_supervisor
22
+ from services.event_waiter import register_filter_builder
23
+ from services.status_broadcaster import register_service_refresh
24
+ from services.ws_handler_registry import (
25
+ register_option_loader,
26
+ register_ws_handlers,
27
+ )
14
28
 
29
+ from ._filters import build_filter as build_whatsapp_filter
30
+ from ._handlers import WS_HANDLERS
31
+ from ._option_loaders import load_channels, load_group_members, load_groups
32
+ from ._refresh import refresh_whatsapp_status
15
33
  from ._runtime import WhatsAppRuntime, get_whatsapp_runtime
16
34
 
17
- # Self-registration: ensures shutdown_all_supervisors() reaches us.
35
+ # Supervisor: ensures shutdown_all_supervisors() reaches us.
18
36
  # get_instance() constructs the singleton once (lazy in spawn, not here).
19
37
  register_supervisor(WhatsAppRuntime.get_instance())
20
38
 
39
+ # WebSocket handlers: 19 message types (status / qr / send / restart /
40
+ # groups / newsletters / chat_history / rate_limit_* / mark_read /
41
+ # typing / presence / diagnostics / stop). Idempotent on re-import.
42
+ register_ws_handlers(WS_HANDLERS)
43
+
44
+ # Service-status refresh callback (Wave 11.I, milestone J) -- the
45
+ # broadcaster fans out to this on lifespan startup instead of
46
+ # hardcoding the call.
47
+ register_service_refresh(refresh_whatsapp_status)
48
+
49
+ # Trigger-event filter builder (Wave 11.I, milestone K) -- moved out
50
+ # of services/event_waiter.py so the central FILTER_BUILDERS table
51
+ # carries no plugin-specific code.
52
+ register_filter_builder("whatsappReceive", build_whatsapp_filter)
53
+
54
+ # loadOptionsMethod loaders (Wave 11.I, milestone M.1) -- self-register
55
+ # into services.ws_handler_registry's option-loader registry, sibling to
56
+ # the WS-handler / router registries.
57
+ register_option_loader("whatsappGroups", load_groups)
58
+ register_option_loader("whatsappChannels", load_channels)
59
+ register_option_loader("whatsappGroupMembers", load_group_members)
60
+
21
61
  __all__ = [
22
62
  "WhatsAppRuntime",
23
63
  "get_whatsapp_runtime",
@@ -1,7 +1,7 @@
1
1
  """WhatsApp send + DB business logic (Wave 11.D.9 inlined).
2
2
 
3
3
  Imported by nodes.whatsapp.whatsapp_send and nodes.whatsapp.whatsapp_db.
4
- RPC dispatch still flows through services.whatsapp_service (HTTP to the Go
4
+ RPC dispatch still flows through nodes.whatsapp._service (HTTP to the Go
5
5
  bridge) — extracting that is a separate refactor, not required for the
6
6
  plugin migration.
7
7
  """
@@ -145,7 +145,7 @@ async def handle_whatsapp_send(
145
145
  Returns:
146
146
  Execution result dict
147
147
  """
148
- from services.whatsapp_service import handle_whatsapp_send as whatsapp_send_handler
148
+ from nodes.whatsapp._service import handle_whatsapp_send as whatsapp_send_handler
149
149
  start_time = time.time()
150
150
 
151
151
  try:
@@ -289,7 +289,7 @@ async def handle_whatsapp_db(
289
289
  Returns:
290
290
  Execution result dict
291
291
  """
292
- from services.whatsapp_service import (
292
+ from nodes.whatsapp._service import (
293
293
  handle_whatsapp_chat_history as whatsapp_chat_history_handler,
294
294
  whatsapp_rpc_call
295
295
  )
@@ -0,0 +1,137 @@
1
+ """WhatsApp event-trigger filter builder (Wave 11.I, milestone K).
2
+
3
+ Moved verbatim from ``services/event_waiter.build_whatsapp_filter``.
4
+ The plugin's ``__init__.py`` self-registers via
5
+ ``event_waiter.register_filter_builder("whatsappReceive", build_filter)``;
6
+ the central event_waiter dispatch table no longer hardcodes this
7
+ plugin's filter.
8
+
9
+ The filter shape mirrors the event payload shipped by the Go RPC's
10
+ ``handleIncomingMessage()`` (see ``service.go``). Snake_case
11
+ parameters throughout — matches the plugin Params with no camelCase
12
+ aliases.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ from typing import Callable, Dict
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ def build_filter(params: Dict) -> Callable[[Dict], bool]:
24
+ """Build filter function for WhatsApp messages.
25
+
26
+ Based on Go RPC ``handleIncomingMessage()`` event fields:
27
+
28
+ - ``message_id`` -- unique message ID
29
+ - ``sender`` -- Sender JID (may be LID for groups)
30
+ - ``sender_phone`` -- RESOLVED phone number (LID already resolved)
31
+ - ``chat_id`` -- Chat JID (same as sender for DMs, group JID for groups)
32
+ - ``timestamp`` -- message timestamp
33
+ - ``is_from_me`` -- true if sent by connected account
34
+ - ``is_group`` -- true if message is in a group chat
35
+ - ``message_type`` -- text / image / video / audio / document /
36
+ sticker / location / contact / contacts
37
+ - ``text`` -- text content (for text messages)
38
+ - ``is_forwarded`` -- true if message is forwarded
39
+ - ``forwarding_score`` -- forwarding count
40
+ - ``group_info`` -- present for group messages, with ``group_jid``
41
+ / ``sender_jid`` / ``sender_phone`` / ``sender_name``
42
+ """
43
+ msg_type = params.get('message_type_filter', 'all')
44
+ sender_filter = params.get('filter', 'all')
45
+ contact_phone = params.get('phone_number', '')
46
+ group_id = params.get('group_id', '')
47
+ sender_number = params.get('sender_number', '')
48
+ keywords = [
49
+ k.strip().lower()
50
+ for k in params.get('keywords', '').split(',')
51
+ if k.strip()
52
+ ]
53
+ ignore_own = params.get('ignore_own_messages', True)
54
+ forwarded_filter = params.get('forwarded_filter', 'all')
55
+
56
+ logger.debug(
57
+ "[WhatsAppFilter] Built: type=%s, filter=%s, group_id=%r, forwarded=%s",
58
+ msg_type, sender_filter, group_id, forwarded_filter,
59
+ )
60
+
61
+ def matches(m: Dict) -> bool:
62
+ msg_chat_id = m.get('chat_id', '')
63
+ is_group = m.get('is_group', False)
64
+ group_info = m.get('group_info', {})
65
+
66
+ # Use sender_phone directly -- Go RPC already resolves LIDs.
67
+ # For group messages, prefer group_info.sender_phone, fall back
68
+ # to root sender_phone.
69
+ if is_group:
70
+ sender_phone = group_info.get('sender_phone', '') or m.get('sender_phone', '')
71
+ else:
72
+ sender_phone = m.get('sender_phone', '')
73
+
74
+ # Fallback: extract phone from sender JID if sender_phone absent.
75
+ if not sender_phone:
76
+ sender = m.get('sender', '')
77
+ sender_phone = sender.split('@')[0] if '@' in sender else sender
78
+
79
+ # Message type filter (schema field: message_type).
80
+ if msg_type != 'all' and m.get('message_type') != msg_type:
81
+ return False
82
+
83
+ # Sender filter -- for ``contact`` filter, use actual phone.
84
+ if sender_filter == 'self':
85
+ # Self-chat (notes-to-self) only -- must be from me AND in
86
+ # a chat with myself.
87
+ if not m.get('is_from_me'):
88
+ return False
89
+ chat_id = m.get('chat_id', '')
90
+ sender = m.get('sender', '')
91
+ if chat_id != sender:
92
+ return False
93
+
94
+ if sender_filter == 'any_contact':
95
+ if is_group:
96
+ return False
97
+
98
+ if sender_filter == 'contact':
99
+ if contact_phone not in sender_phone:
100
+ return False
101
+
102
+ if sender_filter == 'group':
103
+ if not is_group:
104
+ return False
105
+ if msg_chat_id != group_id:
106
+ return False
107
+ # Optional: filter by specific sender within group.
108
+ if sender_number:
109
+ if sender_number not in sender_phone:
110
+ return False
111
+
112
+ if sender_filter == 'channel':
113
+ if not msg_chat_id.endswith('@newsletter'):
114
+ return False
115
+ channel_jid = params.get('channel_jid', '')
116
+ if channel_jid and msg_chat_id != channel_jid:
117
+ return False
118
+
119
+ if sender_filter == 'keywords':
120
+ text = (m.get('text') or '').lower()
121
+ if not any(kw in text for kw in keywords):
122
+ return False
123
+
124
+ # Ignore own messages -- but not when filtering for ``self``.
125
+ if ignore_own and sender_filter != 'self' and m.get('is_from_me'):
126
+ return False
127
+
128
+ is_forwarded = m.get('is_forwarded', False)
129
+ if forwarded_filter == 'only_forwarded' and not is_forwarded:
130
+ return False
131
+ if forwarded_filter == 'ignore_forwarded' and is_forwarded:
132
+ return False
133
+
134
+ logger.debug("[WhatsAppFilter] Matched message from %s", sender_phone)
135
+ return True
136
+
137
+ return matches
@@ -0,0 +1,167 @@
1
+ """WhatsApp WebSocket handlers — plugin-owned dispatch table.
2
+
3
+ Self-registered into the central WS dispatcher via
4
+ ``register_ws_handlers(WS_HANDLERS)`` from this package's
5
+ ``__init__.py``. ``routers/websocket.py`` knows nothing about
6
+ WhatsApp; the message-type strings are wired here so renames /
7
+ additions stay local to the plugin.
8
+
9
+ Each handler is a thin shim over the underlying RPC client in
10
+ :mod:`._service`. Logic lives in the service module; this module is
11
+ the WS surface.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Any, Dict
17
+
18
+ from fastapi import WebSocket
19
+
20
+ from ._service import (
21
+ handle_whatsapp_chat_history as _wa_chat_history,
22
+ handle_whatsapp_connected_phone as _wa_connected_phone,
23
+ handle_whatsapp_group_info as _wa_group_info,
24
+ handle_whatsapp_groups as _wa_groups,
25
+ handle_whatsapp_newsletters as _wa_newsletters,
26
+ handle_whatsapp_qr as _wa_qr,
27
+ handle_whatsapp_restart as _wa_restart,
28
+ handle_whatsapp_send as _wa_send,
29
+ handle_whatsapp_start as _wa_start,
30
+ handle_whatsapp_status as _wa_status,
31
+ whatsapp_rpc_call as _wa_rpc_call,
32
+ )
33
+
34
+
35
+ async def handle_whatsapp_status(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
36
+ return await _wa_status()
37
+
38
+
39
+ async def handle_whatsapp_connected_phone(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
40
+ """Get the connected WhatsApp phone number."""
41
+ return await _wa_connected_phone()
42
+
43
+
44
+ async def handle_whatsapp_qr(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
45
+ return await _wa_qr()
46
+
47
+
48
+ async def handle_whatsapp_send(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
49
+ """Forward all send params to WhatsApp handler — supports all message types."""
50
+ return await _wa_send(data)
51
+
52
+
53
+ async def handle_whatsapp_start(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
54
+ return await _wa_start()
55
+
56
+
57
+ async def handle_whatsapp_restart(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
58
+ return await _wa_restart()
59
+
60
+
61
+ async def handle_whatsapp_groups(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
62
+ return await _wa_groups()
63
+
64
+
65
+ async def handle_whatsapp_newsletters(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
66
+ """Get list of subscribed newsletter channels."""
67
+ return await _wa_newsletters()
68
+
69
+
70
+ async def handle_whatsapp_group_info(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
71
+ """Get group participants with resolved phone numbers."""
72
+ group_id = data.get("group_id", "")
73
+ return await _wa_group_info(group_id)
74
+
75
+
76
+ async def handle_whatsapp_chat_history(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
77
+ """Get chat history from WhatsApp history store."""
78
+ return await _wa_chat_history(data)
79
+
80
+
81
+ # ---- Rate-limit RPC passthroughs ----
82
+
83
+ async def handle_whatsapp_rate_limit_get(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
84
+ """Get rate-limit config and current stats."""
85
+ return await _wa_rpc_call("rate_limit_get", {})
86
+
87
+
88
+ async def handle_whatsapp_rate_limit_set(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
89
+ """Update rate-limit configuration."""
90
+ return await _wa_rpc_call("rate_limit_set", data.get("config", {}))
91
+
92
+
93
+ async def handle_whatsapp_rate_limit_stats(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
94
+ """Get current rate-limit statistics."""
95
+ return await _wa_rpc_call("rate_limit_stats", {})
96
+
97
+
98
+ async def handle_whatsapp_rate_limit_unpause(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
99
+ """Resume rate limiting after an automatic pause."""
100
+ return await _wa_rpc_call("rate_limit_unpause", {})
101
+
102
+
103
+ # ---- Lightweight RPC passthroughs ----
104
+
105
+ async def handle_whatsapp_mark_read(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
106
+ """Mark messages as read. Schema: mark_read({message_ids, chat_jid, sender_jid?})."""
107
+ message_ids = data.get("message_ids", [])
108
+ chat_jid = data.get("chat_jid", "")
109
+ if not message_ids or not chat_jid:
110
+ return {"success": False, "error": "message_ids (array) and chat_jid are required"}
111
+ params: Dict[str, Any] = {"message_ids": message_ids, "chat_jid": chat_jid}
112
+ if sender_jid := data.get("sender_jid"):
113
+ params["sender_jid"] = sender_jid
114
+ return await _wa_rpc_call("mark_read", params)
115
+
116
+
117
+ async def handle_whatsapp_typing(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
118
+ """Send typing indicator. Schema: typing({jid, state: 'composing'|'paused', media?})."""
119
+ jid = data.get("jid", "")
120
+ if not jid:
121
+ return {"success": False, "error": "jid is required"}
122
+ params: Dict[str, Any] = {"jid": jid, "state": data.get("state", "composing")}
123
+ if media := data.get("media"):
124
+ params["media"] = media
125
+ return await _wa_rpc_call("typing", params)
126
+
127
+
128
+ async def handle_whatsapp_presence(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
129
+ """Set online/offline presence. Schema: presence({status: 'available'|'unavailable'})."""
130
+ return await _wa_rpc_call("presence", {"status": data.get("status", "available")})
131
+
132
+
133
+ async def handle_whatsapp_stop(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
134
+ """Graceful WhatsApp shutdown."""
135
+ return await _wa_rpc_call("stop", {})
136
+
137
+
138
+ async def handle_whatsapp_diagnostics(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
139
+ """Get WhatsApp diagnostics / debug info."""
140
+ return await _wa_rpc_call("diagnostics", {})
141
+
142
+
143
+ # Dispatch table consumed by ``services.ws_handler_registry`` —
144
+ # ``__init__.py`` registers this dict on package import. Ordering
145
+ # preserved from the legacy ``MESSAGE_HANDLERS`` block in
146
+ # ``routers/websocket.py`` so the wire surface is byte-identical.
147
+ WS_HANDLERS = {
148
+ "whatsapp_status": handle_whatsapp_status,
149
+ "whatsapp_connected_phone": handle_whatsapp_connected_phone,
150
+ "whatsapp_qr": handle_whatsapp_qr,
151
+ "whatsapp_send": handle_whatsapp_send,
152
+ "whatsapp_start": handle_whatsapp_start,
153
+ "whatsapp_restart": handle_whatsapp_restart,
154
+ "whatsapp_groups": handle_whatsapp_groups,
155
+ "whatsapp_group_info": handle_whatsapp_group_info,
156
+ "whatsapp_chat_history": handle_whatsapp_chat_history,
157
+ "whatsapp_newsletters": handle_whatsapp_newsletters,
158
+ "whatsapp_rate_limit_get": handle_whatsapp_rate_limit_get,
159
+ "whatsapp_rate_limit_set": handle_whatsapp_rate_limit_set,
160
+ "whatsapp_rate_limit_stats": handle_whatsapp_rate_limit_stats,
161
+ "whatsapp_rate_limit_unpause": handle_whatsapp_rate_limit_unpause,
162
+ "whatsapp_mark_read": handle_whatsapp_mark_read,
163
+ "whatsapp_typing": handle_whatsapp_typing,
164
+ "whatsapp_presence": handle_whatsapp_presence,
165
+ "whatsapp_stop": handle_whatsapp_stop,
166
+ "whatsapp_diagnostics": handle_whatsapp_diagnostics,
167
+ }
@@ -0,0 +1,68 @@
1
+ """WhatsApp ``loadOptionsMethod`` loaders.
2
+
3
+ Wave 11.I, milestone M.1. Each function is registered with
4
+ ``services.ws_handler_registry.register_option_loader`` from
5
+ ``__init__.py`` so the central ``dispatch_load_options`` picks them
6
+ up without a plugin-specific import.
7
+
8
+ Adapter shape: turn the existing WS-handler responses
9
+ (``handle_whatsapp_groups`` / ``handle_whatsapp_newsletters`` /
10
+ ``handle_whatsapp_group_info``) into the unified ``[{value, label}]``
11
+ list the frontend ``useLoadOptionsQuery`` consumes. The legacy WS
12
+ message types stay live for back-compat.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Any, Dict, List
18
+
19
+ from ._service import (
20
+ handle_whatsapp_group_info,
21
+ handle_whatsapp_groups,
22
+ handle_whatsapp_newsletters,
23
+ )
24
+
25
+
26
+ async def load_groups(params: Dict[str, Any]) -> List[Dict[str, Any]]:
27
+ """List WhatsApp groups for the ``recipient_type='group'`` selector."""
28
+ response = await handle_whatsapp_groups()
29
+ groups = response.get("groups", []) if isinstance(response, dict) else []
30
+ return [
31
+ {
32
+ "value": g.get("group_jid") or g.get("id") or "",
33
+ "label": g.get("name") or g.get("subject") or g.get("group_jid", ""),
34
+ }
35
+ for g in groups
36
+ ]
37
+
38
+
39
+ async def load_channels(params: Dict[str, Any]) -> List[Dict[str, Any]]:
40
+ """List subscribed newsletter channels for the ``channel_jid`` selector."""
41
+ response = await handle_whatsapp_newsletters()
42
+ channels = response.get("newsletters", []) if isinstance(response, dict) else []
43
+ return [
44
+ {
45
+ "value": c.get("channel_jid") or c.get("id") or "",
46
+ "label": c.get("name") or c.get("channel_jid", ""),
47
+ }
48
+ for c in channels
49
+ ]
50
+
51
+
52
+ async def load_group_members(params: Dict[str, Any]) -> List[Dict[str, Any]]:
53
+ """List members of a specific WhatsApp group for the ``senderNumber``
54
+ selector. Depends on ``params['group_id']``."""
55
+ group_id = params.get("group_id") or ""
56
+ if not group_id:
57
+ return []
58
+ response = await handle_whatsapp_group_info(group_id)
59
+ participants = (
60
+ response.get("participants", []) if isinstance(response, dict) else []
61
+ )
62
+ return [
63
+ {
64
+ "value": p.get("phone") or p.get("jid") or "",
65
+ "label": p.get("name") or p.get("phone") or p.get("jid", ""),
66
+ }
67
+ for p in participants
68
+ ]