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,34 +1,53 @@
1
- """WebSocket handler registry for plugin-owned messages.
2
-
3
- Mirrors the pattern used by :data:`services.node_registry._HANDLER_REGISTRY`
4
- (plugin nodes self-register via ``BaseNode.__init_subclass__``) but for
5
- side-channel WebSocket commands like ``telegram_connect``,
6
- ``whatsapp_status``, etc.
7
-
8
- Each ``nodes/<group>/__init__.py`` calls :func:`register_ws_handlers`
9
- with a dict of ``message_type -> async handler``; the central WebSocket
10
- router reads :func:`get_ws_handlers` at dispatch time and merges the
11
- result into its own legacy ``MESSAGE_HANDLERS`` table.
12
-
13
- No hardcoded plugin names anywhere in the router. Adding a new
14
- plugin's WS surface is a one-line registration call inside that
15
- plugin's package.
1
+ """Plugin self-registration registries.
2
+
3
+ Three sibling concerns share this file because they're the same pattern
4
+ (idempotent dict + collision check) for the same audience (plugin
5
+ ``__init__.py`` modules wiring themselves into the framework):
6
+
7
+ 1. **WebSocket handlers** — ``register_ws_handlers({type: handler})``
8
+ for side-channel commands like ``telegram_connect``,
9
+ ``whatsapp_status``. Read by ``routers/websocket.py`` at dispatch
10
+ time and merged into ``MESSAGE_HANDLERS``.
11
+
12
+ 2. **HTTP routers** — ``register_router(APIRouter, name="<plugin>")``
13
+ for plugin-owned routes (OAuth callbacks, webhook receivers,
14
+ direct-API endpoints). Read by ``server.main`` after plugin discovery
15
+ and ``app.include_router(...)``'d.
16
+
17
+ 3. **Option loaders** — ``register_option_loader(method_name, fn)`` for
18
+ dynamic-options dropdowns wired through the
19
+ ``loadOptionsMethod`` Pydantic field metadata. Read by
20
+ :func:`dispatch_load_options` (here in this module) and the matching
21
+ WS handler in ``routers/websocket.py``.
22
+
23
+ No hardcoded plugin names anywhere in the central router or main.py.
24
+ Adding a new plugin's WS / HTTP / option-loader surface is one
25
+ registration call inside that plugin's package.
16
26
  """
17
27
 
18
28
  from __future__ import annotations
19
29
 
20
30
  import logging
21
- from typing import Any, Awaitable, Callable, Dict
31
+ from typing import Any, Awaitable, Callable, Dict, List
32
+
33
+ from fastapi import APIRouter, WebSocket
22
34
 
23
- from fastapi import WebSocket
35
+ from services.plugin.registry import IdempotentRegistry
24
36
 
25
37
  logger = logging.getLogger(__name__)
26
38
 
27
39
  WSHandler = Callable[[Dict[str, Any], WebSocket], Awaitable[Dict[str, Any]]]
40
+ LoadOptionsFn = Callable[[Dict[str, Any]], Awaitable[List[Dict[str, Any]]]]
28
41
 
29
- _REGISTRY: Dict[str, WSHandler] = {}
42
+ _WS_REGISTRY: IdempotentRegistry[str, WSHandler] = IdempotentRegistry("ws_handler")
43
+ _ROUTER_REGISTRY: IdempotentRegistry[str, APIRouter] = IdempotentRegistry("router")
44
+ _OPTION_LOADER_REGISTRY: IdempotentRegistry[str, LoadOptionsFn] = IdempotentRegistry(
45
+ "option_loader"
46
+ )
30
47
 
31
48
 
49
+ # ---- WebSocket handlers --------------------------------------------------
50
+
32
51
  def register_ws_handlers(handlers: Dict[str, WSHandler]) -> None:
33
52
  """Publish a batch of ``message_type -> handler`` mappings.
34
53
 
@@ -37,14 +56,7 @@ def register_ws_handlers(handlers: Dict[str, WSHandler]) -> None:
37
56
  ``ValueError`` to surface plugin namespace collisions early.
38
57
  """
39
58
  for msg_type, handler in handlers.items():
40
- existing = _REGISTRY.get(msg_type)
41
- if existing is not None and existing is not handler:
42
- raise ValueError(
43
- f"WS handler for message_type '{msg_type}' is already registered "
44
- f"by {existing.__module__}.{existing.__qualname__}; refusing to "
45
- f"overwrite with {handler.__module__}.{handler.__qualname__}"
46
- )
47
- _REGISTRY[msg_type] = handler
59
+ _WS_REGISTRY.register(msg_type, handler)
48
60
 
49
61
 
50
62
  def get_ws_handlers() -> Dict[str, WSHandler]:
@@ -53,9 +65,80 @@ def get_ws_handlers() -> Dict[str, WSHandler]:
53
65
  Returns a fresh dict so callers can mutate without affecting the
54
66
  registry (e.g. ``MESSAGE_HANDLERS = {**core, **get_ws_handlers()}``).
55
67
  """
56
- return dict(_REGISTRY)
68
+ return dict(_WS_REGISTRY.items())
57
69
 
58
70
 
59
71
  def list_registered_types() -> list[str]:
60
72
  """For diagnostics / startup logging."""
61
- return sorted(_REGISTRY.keys())
73
+ return sorted(_WS_REGISTRY.keys())
74
+
75
+
76
+ # ---- HTTP routers --------------------------------------------------------
77
+
78
+ def register_router(router: APIRouter, *, name: str) -> None:
79
+ """Publish a plugin-owned ``APIRouter`` for inclusion at app startup.
80
+
81
+ ``name`` is the plugin folder name — used for diagnostics and
82
+ collision detection. Same idempotency contract as the WS side: the
83
+ same router for the same name is a no-op; a different router for an
84
+ existing name raises ``ValueError`` so plugin-name collisions fail
85
+ at import time, not request time.
86
+ """
87
+ _ROUTER_REGISTRY.register(name, router)
88
+
89
+
90
+ def get_routers() -> List[APIRouter]:
91
+ """Snapshot of registered routers in registration order."""
92
+ return _ROUTER_REGISTRY.values()
93
+
94
+
95
+ def list_registered_routers() -> List[str]:
96
+ """For diagnostics / startup logging."""
97
+ return sorted(_ROUTER_REGISTRY.keys())
98
+
99
+
100
+ # ---- Option loaders ------------------------------------------------------
101
+
102
+ def register_option_loader(method_name: str, fn: LoadOptionsFn) -> None:
103
+ """Publish a ``loadOptionsMethod`` async loader.
104
+
105
+ ``method_name`` is the string declared on Pydantic ``Field`` metadata
106
+ via ``json_schema_extra={"loadOptionsMethod": "..."}`` (e.g.
107
+ ``gmailLabels``, ``whatsappGroups``). Same idempotency contract as
108
+ the WS / router registries: the same callable for the same method
109
+ is a no-op; a different callable raises ``ValueError`` so plugin
110
+ namespace clashes fail at import time.
111
+ """
112
+ _OPTION_LOADER_REGISTRY.register(method_name, fn)
113
+
114
+
115
+ def get_option_loader(method_name: str) -> LoadOptionsFn | None:
116
+ """Look up a registered loader. ``None`` when the method is unknown."""
117
+ return _OPTION_LOADER_REGISTRY.get(method_name)
118
+
119
+
120
+ def list_registered_option_methods() -> List[str]:
121
+ """For diagnostics / startup logging."""
122
+ return sorted(_OPTION_LOADER_REGISTRY.keys())
123
+
124
+
125
+ async def dispatch_load_options(
126
+ method: str, params: Dict[str, Any] | None = None
127
+ ) -> List[Dict[str, Any]]:
128
+ """Look up and invoke a registered loader.
129
+
130
+ Returns an empty list when the method isn't registered (matches
131
+ n8n's tolerant fallback -- the dropdown stays empty rather than
132
+ erroring out).
133
+ """
134
+ loader = _OPTION_LOADER_REGISTRY.get(method)
135
+ if loader is None:
136
+ return []
137
+ return await loader(params or {})
138
+
139
+
140
+ def list_load_options_methods() -> List[str]:
141
+ """Stable alphabetised list of registered method names. The editor
142
+ prefetches this once on boot so it knows which ``loadOptionsMethod``
143
+ values are wired."""
144
+ return list_registered_option_methods()
@@ -110,7 +110,7 @@ Provide usage guidelines, examples, and constraints.
110
110
  |-------|----------|-------------|
111
111
  | `name` | Yes | Lowercase, hyphens only. Must match pattern `^[a-z0-9]+(-[a-z0-9]+)*$` |
112
112
  | `description` | Yes | One-line summary shown in skill lists and to the LLM |
113
- | `allowed-tools` | No | Space-delimited list of tool names. The first that resolves to a known node is the skill's visual source — its icon/color come from `server/nodes/visuals.json` via the `_visuals` handler. |
113
+ | `allowed-tools` | No | Space-delimited list of LLM-facing tool names (snake_case, e.g. `stripe_action`, `apify_actor`, `whatsapp_send`). The first token whose snake→camel conversion (`stripe_action` → `stripeAction`) matches a node `type` in `server/nodes/visuals.json` becomes the skill's visual source — its icon/color come from that entry via the `_visuals` handler. **Convention:** the LLM tool name MUST be the snake_case form of the node type (e.g. `stripeAction` → `stripe_action`). The same string goes into `services/ai.py` `DEFAULT_TOOL_NAMES` so the LLM and the skill agree on the name. Mismatching the snake_case (e.g. `stripe_cli` for a `stripeAction` node) silently breaks icon resolution. |
114
114
  | `metadata` | No | Arbitrary key-value pairs (author, version, category). **Don't set `icon`/`color` for skills that target a node** — those are resolved from the target node's `visuals.json` entry so the skill always mirrors the canvas. Skills with no node target (personality skills, memory operators, autonomous patterns) **may** set inline `icon` and `color` — they're the only visual source for those. |
115
115
 
116
116
  ### Name Format Rules
@@ -121,6 +121,21 @@ Provide usage guidelines, examples, and constraints.
121
121
  - Examples: `my-skill`, `web-search-skill`, `code-skill`
122
122
  - Invalid: `My_Skill`, `my--skill`, `MySkill`
123
123
 
124
+ ### Tool naming — snake_case ↔ camelCase contract
125
+
126
+ Three places have to agree for a skill's icon to render correctly:
127
+
128
+ | Place | Form | Example |
129
+ |---|---|---|
130
+ | Plugin node `type` (in `<plugin>.py`) | camelCase | `stripeAction` |
131
+ | `server/nodes/visuals.json` key | camelCase (= node type) | `"stripeAction": { "icon": "asset:stripe", ... }` |
132
+ | `server/services/ai.py` `DEFAULT_TOOL_NAMES` value | snake_case (LLM tool name) | `"stripeAction": "stripe_action"` |
133
+ | Skill `allowed-tools` | snake_case (matches the LLM tool name) | `allowed-tools: "stripe_action"` |
134
+
135
+ The skill resolver (`SkillLoader._parse_skill_metadata`) takes each `allowed-tools` token, runs snake→camel (`stripe_action` → `stripeAction`), and looks it up in `visuals.json`. If the conversion doesn't equal the node type, no icon resolves and the skill renders without one.
136
+
137
+ **Common pitfall:** picking a "creative" LLM tool name that doesn't snake-back to the node type. For example, calling a `stripeAction` node's tool `stripe_cli` (because it wraps a CLI) breaks the convention — `stripe_cli` snakes to `stripeCli`, not `stripeAction`, so `visuals.json` lookup fails. Stick to `<node_type_in_snake_case>` unless you're prepared to also add an alias entry to `visuals.json`.
138
+
124
139
  ## Optional Supporting Files
125
140
 
126
141
  Skills can include additional files that are loaded alongside the main instructions:
@@ -0,0 +1,166 @@
1
+ ---
2
+ name: agent-builder-skill
3
+ description: How to use the Agent Builder tool's five canvas-mutation operations to inspect and grow your own toolset / skills / teammates / workflows mid-execution
4
+ allowed-tools: "agentBuilder"
5
+ metadata:
6
+ author: machina
7
+ version: "2.0"
8
+ category: autonomous
9
+ ---
10
+
11
+ # Agent Builder
12
+
13
+ You are connected to an **Agent Builder** node. It exposes ONE
14
+ LLM-callable tool, `agentBuilder`, which dispatches to FIVE
15
+ canvas-mutation operations via the `operation` field:
16
+
17
+ | `operation` value | Purpose |
18
+ |---|---|
19
+ | `inspect_canvas` | Read-only view of nodes, edges, and what's already wired to you. ALWAYS call this first. |
20
+ | `add_tool` | Spawn a tool node + wire it to your `input-tools` handle. |
21
+ | `add_skill` | Toggle a skill on your Master Skill (auto-creates one if missing). |
22
+ | `add_subagent` | Team-leads only -- spawn a delegate agent + wire to your `input-teammates`. |
23
+ | `create_workflow` | Persist a fresh empty workflow + return its id. Doesn't switch the user's view. |
24
+
25
+ Every call is `agentBuilder({operation: "<one-of-above>", ...op-specific-fields})`. There are no separate tools.
26
+
27
+ ## The cardinal rule
28
+
29
+ **Call `agentBuilder({operation: "inspect_canvas"})` BEFORE any
30
+ mutation operation.** Without it you cannot tell whether a tool /
31
+ skill / subagent is already wired and will create duplicates.
32
+ Workflow becomes cluttered and your tool list confused.
33
+
34
+ The `inspect_canvas` summary tells you:
35
+ - which tools are already connected to your `input-tools`
36
+ - whether a Master Skill is wired to your `input-skill` (and what
37
+ skills it has enabled)
38
+ - whether you have any teammate delegates (`input-teammates`)
39
+
40
+ Pick the smallest action that solves the problem. If the tool you
41
+ need is already on your canvas, USE IT -- don't add a duplicate.
42
+
43
+ ## Soft-reload semantics (important)
44
+
45
+ LangGraph binds your tool list at the start of each invocation.
46
+ Tools / skills / teammates added mid-run **do NOT become callable
47
+ in the current run**. They become available on your **next
48
+ invocation** (next chat message, next workflow trigger, next call).
49
+
50
+ Each mutation returns a summary ending in "available on your next
51
+ turn". Take this literally. Do NOT loop trying to call something
52
+ you just added -- it isn't there yet. Tell the user what you added
53
+ and that they can use it on the next message.
54
+
55
+ ## Operation reference
56
+
57
+ ### `operation: "inspect_canvas"`
58
+
59
+ No additional fields. Returns:
60
+ ```json
61
+ {
62
+ "operation": "inspect_canvas",
63
+ "nodes": [{ "id", "type", "label", "key_params" }, ...],
64
+ "edges": [{ "source", "target", "source_handle", "target_handle" }, ...],
65
+ "you": {
66
+ "node_id": "agent-1",
67
+ "incoming": [...], "outgoing": [...]
68
+ },
69
+ "summary": "5 nodes, 1 tool(s) wired to you (httpRequest), no skills."
70
+ }
71
+ ```
72
+
73
+ API keys, prompts, and other secrets are **stripped** from
74
+ `key_params`. Only safe planner-relevant fields surface
75
+ (`provider`, `model`, `operation`, `url`, `query`).
76
+
77
+ ### `operation: "add_tool"`
78
+
79
+ Required field: `node_type` (string).
80
+
81
+ Spawns a tool node and wires it to your `input-tools`. Allowed
82
+ types are anything registered with `component_kind="tool"` --
83
+ typically things like `httpRequest`, `braveSearch`,
84
+ `pythonExecutor`, etc. (NOT `agentBuilder` itself; NOT
85
+ `masterSkill`, which is managed via `add_skill` instead.)
86
+
87
+ If the tool has a paired teaching skill, the auto-add-skill
88
+ handler enables it too -- no separate `add_skill` call needed.
89
+
90
+ ### `operation: "add_skill"`
91
+
92
+ Required field: `skill_folder` (string).
93
+
94
+ Enables a skill on your Master Skill. If you don't have a Master
95
+ Skill connected to `input-skill` yet, one is created and wired for
96
+ you. The skill folder must exist under `server/skills/**`
97
+ (e.g. `http-request-skill`, `memory-skill`, `python-skill`).
98
+
99
+ The skill's `SKILL.md` instructions become part of your system
100
+ message on your next turn.
101
+
102
+ ### `operation: "add_subagent"`
103
+
104
+ Required field: `agent_type` (string).
105
+
106
+ **Team-leads only** (`orchestrator_agent`, `ai_employee`,
107
+ `deep_agent`). Spawns a specialized agent (`coding_agent`,
108
+ `web_agent`, `task_agent`, etc.) and wires it to your
109
+ `input-teammates` handle. The new agent appears as a
110
+ `delegate_to_<name>` tool on your next turn.
111
+
112
+ The new agent starts with empty configuration -- the user will
113
+ need to set its provider/model after the run. Mention this in your
114
+ response.
115
+
116
+ ### `operation: "create_workflow"`
117
+
118
+ Required field: `workflow_name` (string).
119
+ Optional field: `workflow_description` (string).
120
+
121
+ Persists a fresh empty workflow with a single Start node and
122
+ returns its `workflow_id`. The user's current view does NOT
123
+ change; the frontend toasts a "Switch" link.
124
+
125
+ Use sparingly. Prefer mutating the current workflow over creating
126
+ new ones. Good cases for `create_workflow`:
127
+ - Templating / saving a solved pattern as a starting point.
128
+ - Splitting work into a separate workflow when the current one
129
+ is getting too large.
130
+
131
+ ## Worked example
132
+
133
+ User: "Search the web for current weather in Tokyo and tell me."
134
+
135
+ ```
136
+ 1. agentBuilder({operation: "inspect_canvas"})
137
+ -> "1 nodes, no tool(s) wired to you, no skills."
138
+ You see no web-search tool exists. Plan: add one.
139
+
140
+ 2. agentBuilder({operation: "add_tool", node_type: "braveSearch"})
141
+ -> "Added 'braveSearch' as a tool. Available on your next turn."
142
+
143
+ 3. Reply to the user:
144
+ "I've added a Brave Search tool to my toolset. Send me the same
145
+ request on your next message and I'll search for the Tokyo
146
+ weather."
147
+ ```
148
+
149
+ DO NOT try to call `brave_search` in the same turn -- it's not in
150
+ your tool list yet. Just tell the user it's ready for next turn.
151
+
152
+ ## What NOT to do
153
+
154
+ - **Don't skip `inspect_canvas`**. Always run it first; it's
155
+ read-only and cheap.
156
+ - **Don't add a tool you already have**. The inspect summary tells
157
+ you what's wired.
158
+ - **Don't loop on freshly-added tools**. They're not callable until
159
+ next invocation.
160
+ - **Don't add `agentBuilder` to yourself via `add_tool`** (rejected
161
+ -- avoids recursion; `agentBuilder` is excluded from the allowed
162
+ tool types).
163
+ - **Don't spawn another team-lead** as a subagent (rejected --
164
+ team-leads delegate to specialists, not to other team-leads).
165
+ - **Don't call `create_workflow`** unless the user explicitly asks
166
+ to start a fresh workflow.
@@ -0,0 +1,306 @@
1
+ ---
2
+ name: stripe-skill
3
+ description: Process payments, manage customers, issue refunds, and handle subscriptions via the Stripe CLI. Pass any Stripe command (customers, charges, payment_intents, refunds, invoices, products, prices, subscriptions) and the tool runs it for you and returns parsed JSON.
4
+ allowed-tools: "stripe_action"
5
+ metadata:
6
+ author: machina
7
+ version: "1.0"
8
+ category: payments
9
+
10
+ ---
11
+
12
+ # Stripe Skill
13
+
14
+ Pass-through over the official [Stripe CLI](https://stripe.com/docs/cli).
15
+ The tool runs `stripe <command> --api-key <stored-key>` for you and
16
+ returns parsed JSON. Every Stripe resource the CLI supports works
17
+ without code changes — products and prices created tomorrow work the
18
+ same way.
19
+
20
+ ## Tool: stripe_action
21
+
22
+ Single field: `command` — a string identical to what you would type
23
+ after `stripe ` on the terminal.
24
+
25
+ ### Schema
26
+
27
+ | Field | Type | Required | Description |
28
+ |-------|------|----------|-------------|
29
+ | command | string | Yes | Stripe CLI command (e.g. `"customers create --email a@b.com"`). Quote arguments that contain spaces. |
30
+
31
+ ### Response
32
+
33
+ ```json
34
+ {
35
+ "command": "customers create --email a@b.com",
36
+ "success": true,
37
+ "result": {
38
+ "id": "cus_NkXz123",
39
+ "email": "a@b.com",
40
+ "object": "customer",
41
+ "created": 1730000000,
42
+ ...
43
+ },
44
+ "stdout": "<raw CLI stdout>"
45
+ }
46
+ ```
47
+
48
+ On CLI failure the tool raises an error with Stripe's own message
49
+ (e.g. `"No such customer: cus_invalid"`).
50
+
51
+ ## Common commands
52
+
53
+ ### Customers
54
+
55
+ | Command | Purpose |
56
+ |---|---|
57
+ | `customers create --email <addr>` | Create a customer. Optional: `--name`, `--description`, `--phone`, `--metadata key=val`. |
58
+ | `customers retrieve <cus_id>` | Fetch a customer by id. |
59
+ | `customers list --limit 10` | List recent customers. Optional: `--email <addr>` filter. |
60
+ | `customers update <cus_id> --email <new>` | Update fields on a customer. |
61
+ | `customers delete <cus_id>` | Permanently delete a customer. |
62
+
63
+ Search:
64
+
65
+ | Command | Purpose |
66
+ |---|---|
67
+ | `customers search --query "email:'a@b.com'"` | Server-side search across customers. Stripe Search query syntax. |
68
+
69
+ ### PaymentIntents (recommended for charges)
70
+
71
+ | Command | Purpose |
72
+ |---|---|
73
+ | `payment_intents create --amount 2000 --currency usd` | Create a PaymentIntent for $20.00. Amount is in the smallest currency unit (cents for USD). |
74
+ | `payment_intents create --amount 2000 --currency usd --customer cus_123` | Create attached to a saved customer. |
75
+ | `payment_intents create --amount 2000 --currency usd --confirm --payment-method pm_card_visa` | Create + confirm in one shot (most common test path). |
76
+ | `payment_intents retrieve pi_123` | Fetch by id. |
77
+ | `payment_intents list --limit 20` | List recent intents. |
78
+ | `payment_intents cancel pi_123` | Cancel an unconfirmed or processing intent. |
79
+
80
+ ### Charges (legacy direct-charge path)
81
+
82
+ | Command | Purpose |
83
+ |---|---|
84
+ | `charges retrieve ch_123` | Fetch a charge by id. |
85
+ | `charges list --limit 10` | List recent charges. |
86
+ | `charges list --customer cus_123` | List charges for a specific customer. |
87
+
88
+ For new integrations prefer **PaymentIntents** over direct
89
+ `charges create` — Stripe deprecated raw card-token charge creation.
90
+
91
+ ### Refunds
92
+
93
+ | Command | Purpose |
94
+ |---|---|
95
+ | `refunds create --payment-intent pi_123` | Refund a PaymentIntent in full. |
96
+ | `refunds create --payment-intent pi_123 --amount 500` | Partial refund (in smallest currency unit). |
97
+ | `refunds create --charge ch_123` | Refund by charge id (older flow). |
98
+ | `refunds retrieve re_123` | Fetch a refund by id. |
99
+ | `refunds list --limit 10` | List recent refunds. |
100
+
101
+ ### Invoices & Subscriptions
102
+
103
+ | Command | Purpose |
104
+ |---|---|
105
+ | `invoices create --customer cus_123` | Draft an invoice. |
106
+ | `invoices finalize_invoice in_123` | Finalize a draft (locks for sending/charging). |
107
+ | `invoices pay in_123` | Charge the customer for a finalized invoice. |
108
+ | `invoices list --customer cus_123` | List invoices for a customer. |
109
+ | `subscriptions create --customer cus_123 --items='[{"price":"price_xxx"}]'` | Create a subscription. |
110
+ | `subscriptions retrieve sub_123` | Fetch a subscription. |
111
+ | `subscriptions cancel sub_123` | Cancel immediately. |
112
+ | `subscriptions update sub_123 --cancel-at-period-end true` | Cancel at end of current billing period. |
113
+
114
+ ### Products & Prices
115
+
116
+ | Command | Purpose |
117
+ |---|---|
118
+ | `products create --name "Pro Plan"` | Create a product. |
119
+ | `prices create --product prod_123 --unit-amount 2000 --currency usd --recurring='{"interval":"month"}'` | Create a recurring price tied to a product. |
120
+ | `prices list --product prod_123` | List prices for a product. |
121
+
122
+ ### Trigger (synthetic test events)
123
+
124
+ | Command | Purpose |
125
+ |---|---|
126
+ | `trigger payment_intent.succeeded` | Fire a synthetic PaymentIntent succeeded event. |
127
+ | `trigger charge.refunded` | Fire a synthetic refund event. |
128
+ | `trigger customer.subscription.created` | Fire a synthetic subscription event. |
129
+
130
+ `stripe trigger` is invaluable for testing webhook flows — it sends
131
+ the event to Stripe, which sends it back through the same `stripe
132
+ listen` daemon and into your `stripeReceive` trigger node.
133
+
134
+ ## Common workflows
135
+
136
+ ### Create a customer and charge them
137
+
138
+ ```json
139
+ { "command": "customers create --email rosy@sparrow.com --name 'Rosy Sparrow'" }
140
+ ```
141
+ returns `cus_…`. Then:
142
+ ```json
143
+ { "command": "payment_intents create --amount 2000 --currency usd --customer cus_xxx --payment-method pm_card_visa --confirm" }
144
+ ```
145
+
146
+ ### Refund the most recent payment for a customer
147
+
148
+ ```json
149
+ { "command": "payment_intents list --customer cus_123 --limit 1" }
150
+ ```
151
+ read `data[0].id`, then:
152
+ ```json
153
+ { "command": "refunds create --payment-intent pi_xxx" }
154
+ ```
155
+
156
+ ### Set up a recurring subscription
157
+
158
+ ```json
159
+ { "command": "products create --name 'Pro Plan'" } -> prod_xxx
160
+ { "command": "prices create --product prod_xxx --unit-amount 2000 --currency usd --recurring='{\"interval\":\"month\"}'" } -> price_xxx
161
+ { "command": "subscriptions create --customer cus_xxx --items='[{\"price\":\"price_xxx\"}]'" }
162
+ ```
163
+
164
+ ## Quoting and escaping
165
+
166
+ The `command` string is parsed with `shlex.split` before being handed
167
+ to `stripe`. Use single quotes for arguments that contain spaces:
168
+
169
+ | You want to send | Type this in `command` |
170
+ |---|---|
171
+ | `--name=Acme Inc` | `customers create --name 'Acme Inc'` |
172
+ | metadata with spaces | `customers update cus_x --metadata 'plan=Acme Pro'` |
173
+ | a JSON array as a flag value | `subscriptions create --items='[{"price":"price_xxx"}]'` |
174
+
175
+ JSON inside `--items` / `--recurring` etc. should be a single quoted
176
+ JSON string with no internal spaces — the CLI parses it on the
177
+ server side.
178
+
179
+ ## Idempotency
180
+
181
+ For any create/charge that you don't want to duplicate on retry, add
182
+ an idempotency key:
183
+
184
+ ```
185
+ customers create --email a@b.com -H "Idempotency-Key: order_42_user_7"
186
+ ```
187
+
188
+ The CLI accepts custom headers via `-H`. Stripe deduplicates by the
189
+ key for 24 hours.
190
+
191
+ ## Test mode vs live mode
192
+
193
+ The stored API key (set in the Credentials Modal) determines mode:
194
+
195
+ | Key prefix | Mode | Effect |
196
+ |---|---|---|
197
+ | `sk_test_…` | Test | Fake money, fake events, no real charges. Always use this for development. |
198
+ | `sk_live_…` | Live | Real money. Real customers. Use only after thorough testing. |
199
+ | `rk_test_…` / `rk_live_…` | Restricted | Scoped permissions; recommended for production agents that don't need full account access. |
200
+
201
+ The CLI reports `livemode: true/false` on every response object so
202
+ you can verify which environment a result is from.
203
+
204
+ ## Errors
205
+
206
+ Stripe returns structured errors that surface in the tool's error
207
+ field. Common ones:
208
+
209
+ | Error code | Meaning | Recovery |
210
+ |---|---|---|
211
+ | `resource_missing` | The id you passed doesn't exist | Verify the id; list to find the right one |
212
+ | `card_declined` | Card payment failed | Use a different `--payment-method`; in test mode try `pm_card_visa` |
213
+ | `invalid_request_error` | Wrong / missing argument | Check `stripe <resource> create --help` |
214
+ | `authentication_required` | 3DS challenge needed | Use a setup_intent or off-session payment_method |
215
+ | `rate_limit_error` | Too many requests | Pause and retry; Stripe rate limit is 100/sec in live, 25/sec in test |
216
+
217
+ The CLI's stderr always contains the Stripe request id (`req_…`) —
218
+ include it when reporting issues.
219
+
220
+ ## Webhooks (`stripeReceive` trigger)
221
+
222
+ When the Stripe daemon is connected (Credentials Modal → Stripe →
223
+ Connect), every event Stripe sends is forwarded to MachinaOs and
224
+ delivered to any active `stripeReceive` trigger nodes after
225
+ HMAC-SHA256 signature verification (`Stripe-Signature` header).
226
+
227
+ To test event delivery without making real payments, use the
228
+ `trigger` command above. Events fire through the same path as real
229
+ ones.
230
+
231
+ Filters on the trigger node (`event_type_filter`):
232
+
233
+ | Filter | Matches |
234
+ |---|---|
235
+ | `all` | every event |
236
+ | `charge.succeeded` | exact match |
237
+ | `charge.*` | every charge.* event (succeeded, refunded, failed, …) |
238
+ | `payment_intent.*` | every PaymentIntent event |
239
+
240
+ The trigger output mirrors Stripe's event shape: `event_id`,
241
+ `event_type`, `created`, `livemode`, `api_version`, `request_id`,
242
+ `account`, `data`.
243
+
244
+ ## Authentication — `stripe login` (browser OAuth)
245
+
246
+ Authentication is delegated entirely to the Stripe CLI. There is no
247
+ API key to paste into MachinaOs. The Credentials Modal's **Login
248
+ with Stripe** button drives the CLI's two-step machine-friendly
249
+ login:
250
+
251
+ 1. **Browser-side**: the modal opens
252
+ `https://dashboard.stripe.com/stripecli/auth/...` in a new tab and
253
+ shows a verification code. The user signs in to Stripe and
254
+ confirms the code.
255
+ 2. **CLI-side**: the CLI polls Stripe until the user authorises,
256
+ then writes credentials (one restricted key per mode, valid for
257
+ 90 days) to `~/.config/stripe/config.toml` (or
258
+ `$XDG_CONFIG_HOME/stripe/config.toml`).
259
+ 3. **MachinaOs-side**: when login completes, the listen daemon
260
+ starts automatically and captures the webhook signing secret.
261
+
262
+ After login, every `stripe …` command run via this tool reads its
263
+ credentials from the CLI's config file — no `--api-key` flag in any
264
+ command you'd type.
265
+
266
+ **Logout** (Credentials Modal → Disconnect) stops the listen daemon
267
+ and runs `stripe logout --all` to clear the config file.
268
+
269
+ ## Best practices
270
+
271
+ 1. **Restricted keys are auto-issued.** When you `stripe login`, the
272
+ CLI generates restricted keys with CLI-appropriate scopes (one
273
+ per test/live mode) — you don't pick scopes manually.
274
+ 2. **Default to test mode** for development. The CLI distinguishes
275
+ modes; `livemode: true/false` is on every response.
276
+ 3. **Add idempotency keys to all create operations.** Especially for
277
+ charges and refunds — accidental retry without a key duplicates
278
+ the charge.
279
+ 4. **Quote string arguments with spaces** (`--name 'Acme Inc'`).
280
+ 5. **Use PaymentIntents, not raw charges** for new integrations.
281
+ 6. **Surface Stripe error messages verbatim to the user.** They are
282
+ precise and actionable; don't paraphrase them.
283
+ 7. **Don't include `--api-key` in your `command` string.** The CLI
284
+ already has stored credentials; an inline `--api-key` overrides
285
+ them and risks key leakage in process listings and logs.
286
+
287
+ ## Setup checklist
288
+
289
+ 1. **Stripe CLI binary** — auto-downloaded on first **Login with
290
+ Stripe** click if not already on PATH. Resolution order:
291
+ - System install (`brew install stripe/stripe-cli/stripe`,
292
+ `scoop install stripe`, `apt install stripe`, or a direct
293
+ binary from <https://stripe.com/docs/stripe-cli#install>).
294
+ - MachinaOs workspace cache at
295
+ `{workspace_base}/_stripe/bin/stripe[.exe]` (populated
296
+ automatically from GitHub releases — pinned to a known-good
297
+ CLI version).
298
+ 2. Credentials Modal → Stripe → **Login with Stripe** → a browser
299
+ tab opens to the Stripe Dashboard with a pairing code. Authorise.
300
+ The modal flips to "Connected" when the CLI's `login --complete`
301
+ subprocess returns and the listen daemon spins up
302
+ (`webhook_secret_captured: true`).
303
+ 3. The `Stripe` action node is connected to your agent's
304
+ `input-tools` handle.
305
+ 4. (For webhook flows) A `stripeReceive` trigger node is wired into
306
+ your workflow with the event-type filter you care about.