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.
- package/README.md +143 -107
- package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
- package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
- package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
- package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
- package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
- package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
- package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
- package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
- package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
- package/client/dist/assets/index-DQ0nwhec.js +257 -0
- package/client/dist/assets/index-DxmbVskS.css +1 -0
- package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
- package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
- package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
- package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
- package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
- package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
- package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
- package/client/dist/index.html +37 -3
- package/client/index.html +28 -1
- package/client/package.json +44 -40
- package/client/src/App.tsx +2 -0
- package/client/src/Dashboard.tsx +157 -45
- package/client/src/ParameterPanel.tsx +3 -5
- package/client/src/adapters/nodeSpecToDescription.ts +1 -0
- package/client/src/assets/icons/NodeIcon.tsx +32 -0
- package/client/src/assets/icons/index.ts +4 -0
- package/client/src/assets/icons/stripe.svg +1 -0
- package/client/src/assets/icons/themedGlyphs.ts +404 -0
- package/client/src/components/AIAgentNode.tsx +77 -53
- package/client/src/components/GenericNode.tsx +34 -52
- package/client/src/components/OutputPanel.tsx +64 -147
- package/client/src/components/ParameterRenderer.tsx +5 -3
- package/client/src/components/SkillEditorModal.tsx +9 -18
- package/client/src/components/SquareNode.tsx +97 -115
- package/client/src/components/StartNode.tsx +32 -42
- package/client/src/components/SvgFilterDefs.tsx +54 -0
- package/client/src/components/TeamMonitorNode.tsx +12 -14
- package/client/src/components/ToolkitNode.tsx +35 -60
- package/client/src/components/TriggerNode.tsx +43 -77
- package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
- package/client/src/components/credentials/CredentialsModal.tsx +98 -30
- package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
- package/client/src/components/credentials/catalogueAdapter.ts +17 -1
- package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
- package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
- package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
- package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
- package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
- package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
- package/client/src/components/credentials/types.ts +12 -2
- package/client/src/components/credentials/useCredentialPanel.ts +43 -19
- package/client/src/components/icons/AIProviderIcons.tsx +16 -0
- package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
- package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
- package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
- package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
- package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
- package/client/src/components/parameterPanel/InputSection.tsx +9 -7
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
- package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
- package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
- package/client/src/components/ui/AIResultModal.tsx +1 -1
- package/client/src/components/ui/CollapsibleSection.tsx +9 -5
- package/client/src/components/ui/CommandPalette.tsx +147 -0
- package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
- package/client/src/components/ui/ComponentItem.tsx +13 -7
- package/client/src/components/ui/ComponentPalette.tsx +24 -13
- package/client/src/components/ui/ConsolePanel.tsx +19 -11
- package/client/src/components/ui/DropCap.tsx +28 -0
- package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
- package/client/src/components/ui/InputNodesPanel.tsx +1 -1
- package/client/src/components/ui/Modal.tsx +38 -6
- package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
- package/client/src/components/ui/SettingsPanel.tsx +42 -13
- package/client/src/components/ui/StatusBar.tsx +108 -0
- package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
- package/client/src/components/ui/TopToolbar.tsx +42 -25
- package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
- package/client/src/components/ui/action-button.tsx +40 -15
- package/client/src/components/ui/button.tsx +24 -1
- package/client/src/components/ui/dropdown-menu.tsx +24 -2
- package/client/src/components/ui/input.tsx +19 -2
- package/client/src/components/ui/select.tsx +15 -0
- package/client/src/components/ui/textarea.tsx +15 -2
- package/client/src/contexts/AuthContext.tsx +148 -109
- package/client/src/contexts/ThemeContext.tsx +93 -17
- package/client/src/contexts/WebSocketContext.tsx +373 -206
- package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
- package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
- package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
- package/client/src/hooks/useAppTheme.ts +209 -7
- package/client/src/hooks/useAutoSkillEdges.ts +7 -2
- package/client/src/hooks/useCatalogueQuery.ts +67 -1
- package/client/src/hooks/useDragVariable.ts +1 -1
- package/client/src/hooks/useNodeAllowlist.ts +115 -8
- package/client/src/hooks/useOnboarding.ts +20 -8
- package/client/src/hooks/useParameterPanel.ts +2 -1
- package/client/src/hooks/useReactFlowNodes.ts +2 -1
- package/client/src/hooks/useSound.ts +185 -0
- package/client/src/hooks/useWorkflowManagement.ts +6 -8
- package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
- package/client/src/index.css +65 -3
- package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
- package/client/src/lib/aiModelProviders.ts +8 -0
- package/client/src/lib/connectionConfig.ts +107 -0
- package/client/src/lib/queryPersist.ts +13 -5
- package/client/src/lib/sound.ts +393 -0
- package/client/src/main.tsx +20 -0
- package/client/src/store/useAppStore.ts +26 -0
- package/client/src/styles/canvasAnimations.ts +37 -36
- package/client/src/styles/theme.ts +36 -20
- package/client/src/test/setup.ts +1 -0
- package/client/src/themes/atomic.css +253 -0
- package/client/src/themes/base.css +373 -0
- package/client/src/themes/cyber.css +890 -0
- package/client/src/themes/dark.css +70 -0
- package/client/src/themes/edo.css +246 -0
- package/client/src/themes/greek.css +293 -0
- package/client/src/themes/light.css +78 -0
- package/client/src/themes/plague.css +253 -0
- package/client/src/themes/renaissance.css +727 -0
- package/client/src/themes/rot.css +249 -0
- package/client/src/themes/steampunk.css +272 -0
- package/client/src/themes/surveillance.css +289 -0
- package/client/src/themes/wasteland.css +250 -0
- package/client/src/types/INodeProperties.ts +5 -0
- package/client/src/types/NodeTypes.ts +11 -1
- package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
- package/client/src/types/cloudEvents.ts +78 -0
- package/client/src/vite-env.d.ts +7 -0
- package/client/tsconfig.json +1 -1
- package/client/vite.config.js +62 -2
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/machina/commands/build.py +51 -7
- package/machina/pyproject.toml +4 -0
- package/machina/supervisor.py +12 -2
- package/machina/tree.py +71 -21
- package/package.json +4 -4
- package/scripts/install.js +16 -1
- package/server/config/ai_cli_providers.json +54 -0
- package/server/config/credential_providers.json +109 -2
- package/server/config/llm_defaults.json +24 -0
- package/server/config/model_registry.json +338 -499
- package/server/config/node_allowlist.json +16 -1
- package/server/config/pricing.json +8 -0
- package/server/constants.py +38 -15
- package/server/core/container.py +2 -2
- package/server/core/credentials_database.py +35 -2
- package/server/core/logging.py +4 -3
- package/server/main.py +99 -13
- package/server/models/node_metadata.py +1 -0
- package/server/nodejs/package.json +8 -6
- package/server/nodejs/src/index.ts +22 -5
- package/server/nodes/README.md +31 -4
- package/server/nodes/agent/_inline.py +2 -0
- package/server/nodes/agent/_specialized.py +6 -3
- package/server/nodes/agent/ai_agent.py +13 -3
- package/server/nodes/agent/chat_agent.py +6 -3
- package/server/nodes/agent/claude_code_agent.py +287 -75
- package/server/nodes/agent/codex_agent.py +239 -0
- package/server/nodes/agent/deep_agent.py +3 -3
- package/server/nodes/agent/rlm_agent.py +3 -3
- package/server/nodes/android/__init__.py +31 -1
- package/server/nodes/android/_base.py +9 -5
- package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
- package/server/nodes/android/_handlers.py +154 -0
- package/server/nodes/android/_option_loaders.py +44 -0
- package/server/nodes/android/_refresh.py +127 -0
- package/server/{services/android → nodes/android/_relay}/client.py +4 -4
- package/server/{routers/android.py → nodes/android/_router.py} +27 -8
- package/server/nodes/browser/browser.py +2 -2
- package/server/nodes/code/_base.py +6 -2
- package/server/nodes/code/_claude_code.py +134 -0
- package/server/nodes/document/embedding_generator.py +3 -3
- package/server/nodes/document/http_scraper.py +3 -3
- package/server/nodes/document/vector_store.py +5 -5
- package/server/nodes/email/__init__.py +11 -1
- package/server/nodes/email/_filters.py +21 -0
- package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
- package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
- package/server/nodes/email/email_read.py +1 -1
- package/server/nodes/email/email_receive.py +54 -5
- package/server/nodes/email/email_send.py +1 -1
- package/server/nodes/filesystem/shell.py +24 -1
- package/server/nodes/google/__init__.py +55 -1
- package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
- package/server/nodes/google/_base.py +2 -2
- package/server/nodes/google/_credentials.py +5 -5
- package/server/nodes/google/_filters.py +25 -0
- package/server/nodes/google/_handlers.py +57 -0
- package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
- package/server/nodes/google/_option_loaders.py +107 -0
- package/server/nodes/google/_refresh.py +66 -0
- package/server/nodes/google/_router.py +131 -0
- package/server/nodes/google/gmail_receive.py +41 -4
- package/server/nodes/groups.py +1 -0
- package/server/nodes/location/_credentials.py +45 -1
- package/server/{services/maps.py → nodes/location/_service.py} +18 -3
- package/server/nodes/location/gmaps_create.py +4 -4
- package/server/nodes/location/gmaps_locations.py +4 -4
- package/server/nodes/location/gmaps_nearby_places.py +4 -4
- package/server/nodes/model/_base.py +8 -3
- package/server/nodes/model/_credentials.py +96 -8
- package/server/nodes/model/_local_validator.py +345 -0
- package/server/nodes/model/lmstudio_chat_model.py +23 -0
- package/server/nodes/model/ollama_chat_model.py +25 -0
- package/server/nodes/proxy/_usage.py +2 -2
- package/server/nodes/proxy/proxy_config.py +14 -14
- package/server/nodes/proxy/proxy_request.py +4 -4
- package/server/nodes/scraper/_credentials.py +29 -1
- package/server/nodes/scraper/apify_actor.py +9 -9
- package/server/nodes/scraper/crawlee_scraper.py +5 -5
- package/server/nodes/search/brave_search.py +4 -0
- package/server/nodes/search/perplexity_search.py +9 -0
- package/server/nodes/search/serper_search.py +3 -0
- package/server/nodes/skill/simple_memory.py +12 -0
- package/server/nodes/social/_base.py +2 -2
- package/server/nodes/stripe/__init__.py +46 -0
- package/server/nodes/stripe/_credentials.py +33 -0
- package/server/nodes/stripe/_handlers.py +270 -0
- package/server/nodes/stripe/_install.py +127 -0
- package/server/nodes/stripe/_source.py +174 -0
- package/server/nodes/stripe/stripe_action.py +81 -0
- package/server/nodes/stripe/stripe_receive.py +92 -0
- package/server/nodes/telegram/_credentials.py +52 -1
- package/server/nodes/telegram/_handlers.py +19 -18
- package/server/nodes/telegram/_service.py +134 -32
- package/server/nodes/telegram/telegram_send.py +5 -6
- package/server/nodes/text/file_handler.py +2 -2
- package/server/nodes/text/text_generator.py +2 -2
- package/server/nodes/tool/agent_builder.py +630 -0
- package/server/nodes/tool/task_manager.py +144 -2
- package/server/nodes/twitter/__init__.py +38 -1
- package/server/nodes/twitter/_base.py +7 -7
- package/server/nodes/twitter/_credentials.py +1 -1
- package/server/nodes/twitter/_filters.py +37 -0
- package/server/nodes/twitter/_handlers.py +77 -0
- package/server/nodes/twitter/_oauth.py +124 -0
- package/server/nodes/twitter/_refresh.py +78 -0
- package/server/nodes/twitter/_router.py +29 -0
- package/server/nodes/twitter/twitter_receive.py +4 -0
- package/server/nodes/visuals.json +64 -19
- package/server/nodes/whatsapp/__init__.py +45 -5
- package/server/nodes/whatsapp/_base.py +3 -3
- package/server/nodes/whatsapp/_filters.py +137 -0
- package/server/nodes/whatsapp/_handlers.py +167 -0
- package/server/nodes/whatsapp/_option_loaders.py +68 -0
- package/server/nodes/whatsapp/_refresh.py +62 -0
- package/server/nodes/whatsapp/_runtime.py +1 -1
- package/server/pyproject.toml +29 -7
- package/server/routers/schemas.py +2 -2
- package/server/routers/webhook.py +26 -9
- package/server/routers/websocket.py +149 -810
- package/server/services/ai.py +89 -8
- package/server/services/auth.py +220 -43
- package/server/services/claude_oauth.py +126 -100
- package/server/services/cli_agent/__init__.py +78 -0
- package/server/services/cli_agent/_handlers.py +237 -0
- package/server/services/cli_agent/config.py +112 -0
- package/server/services/cli_agent/factory.py +48 -0
- package/server/services/cli_agent/lockfile.py +141 -0
- package/server/services/cli_agent/mcp_server.py +482 -0
- package/server/services/cli_agent/protocol.py +173 -0
- package/server/services/cli_agent/providers/__init__.py +9 -0
- package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
- package/server/services/cli_agent/providers/google_gemini.py +80 -0
- package/server/services/cli_agent/providers/openai_codex.py +310 -0
- package/server/services/cli_agent/service.py +607 -0
- package/server/services/cli_agent/session.py +618 -0
- package/server/services/cli_agent/types.py +227 -0
- package/server/services/cli_agent/workflow_tools.py +233 -0
- package/server/services/credential_registry.py +26 -1
- package/server/services/deployment/manager.py +26 -145
- package/server/services/deployment/poll_registry.py +59 -0
- package/server/services/event_waiter.py +76 -246
- package/server/services/events/__init__.py +54 -0
- package/server/services/events/cli.py +78 -0
- package/server/services/events/daemon.py +163 -0
- package/server/services/events/envelope.py +281 -0
- package/server/services/events/lifecycle.py +99 -0
- package/server/services/events/oauth_lifecycle.py +534 -0
- package/server/services/events/polling.py +60 -0
- package/server/services/events/push.py +36 -0
- package/server/services/events/source.py +63 -0
- package/server/services/events/triggers.py +118 -0
- package/server/services/events/verifiers/__init__.py +25 -0
- package/server/services/events/verifiers/base.py +28 -0
- package/server/services/events/verifiers/github.py +25 -0
- package/server/services/events/verifiers/hmac_basic.py +32 -0
- package/server/services/events/verifiers/standard_webhooks.py +47 -0
- package/server/services/events/verifiers/stripe.py +42 -0
- package/server/services/events/webhook.py +105 -0
- package/server/services/handlers/tools.py +28 -186
- package/server/services/llm/config.py +7 -0
- package/server/services/llm/factory.py +8 -2
- package/server/services/memory/__init__.py +52 -0
- package/server/services/memory/jsonl.py +80 -0
- package/server/services/memory/markdown.py +65 -0
- package/server/services/memory/state.py +112 -0
- package/server/services/memory/vector_store.py +40 -0
- package/server/services/model_registry.py +76 -0
- package/server/services/node_allowlist.py +71 -15
- package/server/services/node_executor.py +2 -2
- package/server/services/node_output_schemas.py +21 -10
- package/server/services/node_spec.py +1 -1
- package/server/services/oauth_utils.py +1 -1
- package/server/services/plugin/__init__.py +2 -0
- package/server/services/plugin/base.py +44 -2
- package/server/services/plugin/credential.py +288 -1
- package/server/services/plugin/deps.py +105 -0
- package/server/services/plugin/edge_walker.py +12 -4
- package/server/services/plugin/oauth.py +381 -0
- package/server/services/plugin/polling.py +247 -0
- package/server/services/plugin/registry.py +145 -0
- package/server/services/plugin/singleton.py +65 -0
- package/server/services/plugin/ws.py +81 -0
- package/server/services/process_service.py +31 -2
- package/server/services/status_broadcaster.py +155 -238
- package/server/services/temporal/workflow.py +7 -7
- package/server/services/workflow.py +21 -3
- package/server/services/ws_handler_registry.py +111 -28
- package/server/skills/GUIDE.md +16 -1
- package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
- package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
- package/server/tests/credentials/test_auth_service.py +16 -9
- package/server/tests/credentials/test_credential_broadcasts.py +219 -0
- package/server/tests/credentials/test_google_oauth.py +6 -6
- package/server/tests/credentials/test_oauth_utils.py +1 -1
- package/server/tests/credentials/test_twitter_oauth.py +2 -2
- package/server/tests/credentials/test_websocket_handlers.py +44 -20
- package/server/tests/llm/test_factory.py +1 -0
- package/server/tests/llm/test_wiring.py +5 -1
- package/server/tests/nodes/_compat.py +24 -24
- package/server/tests/nodes/test_agent_builder.py +439 -0
- package/server/tests/nodes/test_ai_tools.py +18 -14
- package/server/tests/nodes/test_code_fs_process.py +17 -8
- package/server/tests/nodes/test_email.py +10 -9
- package/server/tests/nodes/test_google_workspace.py +2 -2
- package/server/tests/nodes/test_specialized_agents.py +100 -53
- package/server/tests/nodes/test_stripe_plugin.py +293 -0
- package/server/tests/nodes/test_telegram_social.py +4 -4
- package/server/tests/nodes/test_twitter.py +1 -1
- package/server/tests/nodes/test_web_automation.py +2 -2
- package/server/tests/nodes/test_whatsapp.py +9 -9
- package/server/tests/services/cli_agent/__init__.py +0 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
- package/server/tests/services/cli_agent/test_providers.py +358 -0
- package/server/tests/services/cli_agent/test_service.py +298 -0
- package/server/tests/services/memory/__init__.py +0 -0
- package/server/tests/services/memory/test_jsonl.py +188 -0
- package/server/tests/services/test_events.py +333 -0
- package/server/tests/test_node_spec.py +56 -16
- package/server/tests/test_plugin_helpers.py +116 -0
- package/server/tests/test_plugin_self_containment.py +486 -0
- package/server/tests/test_status_broadcasts.py +425 -0
- package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
- package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
- package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
- package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
- package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
- package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
- package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
- package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
- package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
- package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
- package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
- package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
- package/client/dist/assets/index-D9tZfgvi.js +0 -363
- package/client/dist/assets/index-al7snTkG.css +0 -1
- package/client/src/components/credentials/providers.tsx +0 -177
- package/server/routers/google.py +0 -277
- package/server/routers/maps.py +0 -142
- package/server/routers/twitter.py +0 -365
- package/server/services/claude_code_service.py +0 -106
- package/server/services/memory.py +0 -159
- package/server/services/node_option_loaders/__init__.py +0 -77
- package/server/services/node_option_loaders/android_loaders.py +0 -55
- package/server/services/node_option_loaders/google_loaders.py +0 -97
- package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
- package/server/services/twitter_oauth.py +0 -411
- package/server/services/websocket_client.py +0 -29
- /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
- /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
- /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
- /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
|
@@ -1,34 +1,53 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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()
|
package/server/skills/GUIDE.md
CHANGED
|
@@ -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
|
|
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.
|