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,77 +0,0 @@
|
|
|
1
|
-
"""Generic loadOptionsMethod dispatch registry.
|
|
2
|
-
|
|
3
|
-
Wave 6 Phase 4. Generalises the WhatsApp-only dynamic-options pattern
|
|
4
|
-
to a registry mirroring services/node_executor._build_handler_registry().
|
|
5
|
-
|
|
6
|
-
The editor calls a single endpoint:
|
|
7
|
-
|
|
8
|
-
POST /api/nodes/options/{method}
|
|
9
|
-
|
|
10
|
-
with body ``{"node_type": "...", "params": {...}}`` and the dispatcher
|
|
11
|
-
looks up the registered async loader, invokes it, and returns the
|
|
12
|
-
resulting ``[{"value": ..., "label": ...}]`` list. WS mirror at
|
|
13
|
-
``load_options`` matches the Wave 3 handler convention.
|
|
14
|
-
|
|
15
|
-
Registering a new dynamic-option loader: define an async function in
|
|
16
|
-
its own module under this package, import it here, and add the entry
|
|
17
|
-
to ``LOAD_OPTIONS_REGISTRY``. No frontend change needed - the
|
|
18
|
-
``loadOptionsMethod`` string in the Pydantic ``Field(json_schema_extra=)``
|
|
19
|
-
is what wires it through.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
from typing import Any, Awaitable, Callable, Optional
|
|
23
|
-
|
|
24
|
-
from .android_loaders import load_android_service_actions
|
|
25
|
-
from .google_loaders import (
|
|
26
|
-
load_gmail_labels,
|
|
27
|
-
load_google_calendar_list,
|
|
28
|
-
load_google_drive_folders,
|
|
29
|
-
load_google_tasklists,
|
|
30
|
-
)
|
|
31
|
-
from .whatsapp_loaders import (
|
|
32
|
-
load_whatsapp_channels,
|
|
33
|
-
load_whatsapp_group_members,
|
|
34
|
-
load_whatsapp_groups,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# Async loader signature: (params: dict) -> list of {value, label, ...}
|
|
39
|
-
LoadOptionsFn = Callable[[dict[str, Any]], Awaitable[list[dict[str, Any]]]]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
LOAD_OPTIONS_REGISTRY: dict[str, LoadOptionsFn] = {
|
|
43
|
-
# WhatsApp - groups, channels, group members.
|
|
44
|
-
"whatsappGroups": load_whatsapp_groups,
|
|
45
|
-
"whatsappChannels": load_whatsapp_channels,
|
|
46
|
-
"whatsappGroupMembers": load_whatsapp_group_members,
|
|
47
|
-
# Google Workspace - Gmail labels, Calendar list, Drive folders, Tasks lists.
|
|
48
|
-
"gmailLabels": load_gmail_labels,
|
|
49
|
-
"googleCalendarList": load_google_calendar_list,
|
|
50
|
-
"googleDriveFolders": load_google_drive_folders,
|
|
51
|
-
"googleTasklists": load_google_tasklists,
|
|
52
|
-
# Android - service actions enum loaded from the connected bridge.
|
|
53
|
-
"getAndroidServiceActions": load_android_service_actions,
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
async def dispatch_load_options(
|
|
58
|
-
method: str, params: Optional[dict[str, Any]] = None
|
|
59
|
-
) -> list[dict[str, Any]]:
|
|
60
|
-
"""Look up and invoke a registered loader.
|
|
61
|
-
|
|
62
|
-
Returns an empty list when the method isn't registered (matches
|
|
63
|
-
n8n's tolerant fallback - the dropdown stays empty rather than
|
|
64
|
-
erroring out)."""
|
|
65
|
-
|
|
66
|
-
loader = LOAD_OPTIONS_REGISTRY.get(method)
|
|
67
|
-
if loader is None:
|
|
68
|
-
return []
|
|
69
|
-
return await loader(params or {})
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def list_load_options_methods() -> list[str]:
|
|
73
|
-
"""Stable alphabetised list of registered method names. The editor
|
|
74
|
-
prefetches this once on boot so it knows which ``loadOptionsMethod``
|
|
75
|
-
values are wired."""
|
|
76
|
-
|
|
77
|
-
return sorted(LOAD_OPTIONS_REGISTRY.keys())
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"""Android dynamic option loaders.
|
|
2
|
-
|
|
3
|
-
Returns the list of actions supported by each Android service node.
|
|
4
|
-
The frontend passes ``node_type`` in params; the loader maps it to the
|
|
5
|
-
service's ``action`` enum advertised by the running Android bridge.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async def load_android_service_actions(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
14
|
-
"""Return ``[{value, label}]`` for the given Android service node.
|
|
15
|
-
|
|
16
|
-
Kept minimal — each service's `execute_action` endpoint validates
|
|
17
|
-
the action at call time; the dropdown is convenience UX, not a
|
|
18
|
-
correctness boundary. The frontend falls back to a free-text input
|
|
19
|
-
if the loader returns an empty list.
|
|
20
|
-
"""
|
|
21
|
-
node_type = params.get("node_type") or ""
|
|
22
|
-
service_id = _ANDROID_SERVICE_MAP.get(node_type)
|
|
23
|
-
if not service_id:
|
|
24
|
-
return []
|
|
25
|
-
|
|
26
|
-
try:
|
|
27
|
-
from core.container import container
|
|
28
|
-
|
|
29
|
-
android_svc = container.android_service()
|
|
30
|
-
actions = await android_svc.list_actions(service_id) # type: ignore[attr-defined]
|
|
31
|
-
return [{"value": a, "label": a.replace("_", " ").title()} for a in actions or []]
|
|
32
|
-
except Exception:
|
|
33
|
-
# Service offline / discovery failed — dropdown stays empty, user
|
|
34
|
-
# can still type the action name manually.
|
|
35
|
-
return []
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
_ANDROID_SERVICE_MAP = {
|
|
39
|
-
"batteryMonitor": "battery",
|
|
40
|
-
"networkMonitor": "network",
|
|
41
|
-
"systemInfo": "system_info",
|
|
42
|
-
"location": "location",
|
|
43
|
-
"appLauncher": "app_launcher",
|
|
44
|
-
"appList": "app_list",
|
|
45
|
-
"wifiAutomation": "wifi_automation",
|
|
46
|
-
"bluetoothAutomation": "bluetooth_automation",
|
|
47
|
-
"audioAutomation": "audio_automation",
|
|
48
|
-
"deviceStateAutomation": "device_state",
|
|
49
|
-
"screenControlAutomation": "screen_control",
|
|
50
|
-
"airplaneModeControl": "airplane_mode",
|
|
51
|
-
"motionDetection": "motion_detection",
|
|
52
|
-
"environmentalSensors": "environmental_sensors",
|
|
53
|
-
"cameraControl": "camera_control",
|
|
54
|
-
"mediaControl": "media_control",
|
|
55
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"""Google Workspace loadOptions adapters.
|
|
2
|
-
|
|
3
|
-
Wave 6 Phase 4b. Adds dynamic-option loaders for the Gmail label
|
|
4
|
-
picker and Calendar list picker. Reuses
|
|
5
|
-
services/handlers/google_auth.get_google_credentials() so the OAuth
|
|
6
|
-
dance is identical to the workflow-execution path.
|
|
7
|
-
|
|
8
|
-
Adding more Google loaders (e.g. Drive folder picker, Sheets
|
|
9
|
-
spreadsheet picker) = one async function + one entry in this file's
|
|
10
|
-
exports + one line in services/node_option_loaders/__init__.py.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import asyncio
|
|
14
|
-
from typing import Any
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
async def _google_service(api: str, version: str, params: dict[str, Any]):
|
|
18
|
-
"""Build a googleapiclient service under the right OAuth credentials.
|
|
19
|
-
`params` may carry ``account_mode`` / ``customer_id`` (customer-mode
|
|
20
|
-
multi-tenant) — falls back to owner tokens otherwise."""
|
|
21
|
-
|
|
22
|
-
from googleapiclient.discovery import build
|
|
23
|
-
|
|
24
|
-
from services.handlers.google_auth import get_google_credentials
|
|
25
|
-
|
|
26
|
-
creds = await get_google_credentials(params, {})
|
|
27
|
-
loop = asyncio.get_event_loop()
|
|
28
|
-
return await loop.run_in_executor(None, lambda: build(api, version, credentials=creds))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
async def load_gmail_labels(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
32
|
-
"""Gmail labels for the label-filter selector on gmailReceive and
|
|
33
|
-
gmail (search)."""
|
|
34
|
-
|
|
35
|
-
service = await _google_service("gmail", "v1", params)
|
|
36
|
-
loop = asyncio.get_event_loop()
|
|
37
|
-
response = await loop.run_in_executor(None, lambda: service.users().labels().list(userId="me").execute())
|
|
38
|
-
labels = response.get("labels", [])
|
|
39
|
-
# Stable sort: system labels alphabetical first, then user labels.
|
|
40
|
-
labels.sort(key=lambda l: (l.get("type") != "system", (l.get("name") or "").lower()))
|
|
41
|
-
return [
|
|
42
|
-
{"value": l["id"], "label": l.get("name") or l["id"]}
|
|
43
|
-
for l in labels
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
async def load_google_calendar_list(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
48
|
-
"""Calendar list for the calendarId picker on calendar CRUD."""
|
|
49
|
-
|
|
50
|
-
service = await _google_service("calendar", "v3", params)
|
|
51
|
-
loop = asyncio.get_event_loop()
|
|
52
|
-
response = await loop.run_in_executor(None, lambda: service.calendarList().list().execute())
|
|
53
|
-
entries = response.get("items", [])
|
|
54
|
-
# Primary first, rest alphabetised.
|
|
55
|
-
entries.sort(key=lambda c: (not c.get("primary", False), (c.get("summary") or "").lower()))
|
|
56
|
-
return [
|
|
57
|
-
{
|
|
58
|
-
"value": c.get("id", ""),
|
|
59
|
-
"label": c.get("summary") or c.get("id", ""),
|
|
60
|
-
"description": "Primary" if c.get("primary") else c.get("description", ""),
|
|
61
|
-
}
|
|
62
|
-
for c in entries
|
|
63
|
-
]
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
async def load_google_drive_folders(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
67
|
-
"""Drive folders for the folderId picker on drive upload/list."""
|
|
68
|
-
|
|
69
|
-
service = await _google_service("drive", "v3", params)
|
|
70
|
-
loop = asyncio.get_event_loop()
|
|
71
|
-
query = "mimeType='application/vnd.google-apps.folder' and trashed=false"
|
|
72
|
-
response = await loop.run_in_executor(
|
|
73
|
-
None,
|
|
74
|
-
lambda: service.files()
|
|
75
|
-
.list(q=query, fields="files(id, name, parents)", pageSize=200)
|
|
76
|
-
.execute(),
|
|
77
|
-
)
|
|
78
|
-
folders = response.get("files", [])
|
|
79
|
-
folders.sort(key=lambda f: (f.get("name") or "").lower())
|
|
80
|
-
return [
|
|
81
|
-
{"value": f.get("id", ""), "label": f.get("name") or f.get("id", "")}
|
|
82
|
-
for f in folders
|
|
83
|
-
]
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
async def load_google_tasklists(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
87
|
-
"""Task lists for the tasklistId picker on tasks CRUD."""
|
|
88
|
-
|
|
89
|
-
service = await _google_service("tasks", "v1", params)
|
|
90
|
-
loop = asyncio.get_event_loop()
|
|
91
|
-
response = await loop.run_in_executor(None, lambda: service.tasklists().list().execute())
|
|
92
|
-
lists = response.get("items", [])
|
|
93
|
-
lists.sort(key=lambda l: (l.get("title") or "").lower())
|
|
94
|
-
return [
|
|
95
|
-
{"value": l.get("id", ""), "label": l.get("title") or l.get("id", "")}
|
|
96
|
-
for l in lists
|
|
97
|
-
]
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"""WhatsApp loadOptions adapters.
|
|
2
|
-
|
|
3
|
-
Wave 6 Phase 4. Thin wrappers around the existing WhatsApp WS handlers
|
|
4
|
-
that normalise the response into the unified ``[{value, label}]`` shape
|
|
5
|
-
the frontend ``useLoadOptionsQuery`` consumes.
|
|
6
|
-
|
|
7
|
-
Today the editor talks to ``handle_whatsapp_groups`` /
|
|
8
|
-
``handle_whatsapp_newsletters`` / ``handle_whatsapp_group_info``
|
|
9
|
-
directly via dedicated WS message types. These adapters expose the
|
|
10
|
-
same data through the unified ``load_options`` dispatcher so the
|
|
11
|
-
``loadOptionsMethod`` schema field wires straight through with no
|
|
12
|
-
per-method WS handler. The legacy WS handlers stay live for back-compat.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from typing import Any
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
async def load_whatsapp_groups(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
19
|
-
"""List WhatsApp groups for the recipient_type='group' selector."""
|
|
20
|
-
|
|
21
|
-
from services.whatsapp_service import handle_whatsapp_groups as _wa_groups
|
|
22
|
-
|
|
23
|
-
response = await _wa_groups()
|
|
24
|
-
groups = response.get("groups", []) if isinstance(response, dict) else []
|
|
25
|
-
return [
|
|
26
|
-
{
|
|
27
|
-
"value": g.get("group_jid") or g.get("id") or "",
|
|
28
|
-
"label": g.get("name") or g.get("subject") or g.get("group_jid", ""),
|
|
29
|
-
}
|
|
30
|
-
for g in groups
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
async def load_whatsapp_channels(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
35
|
-
"""List subscribed newsletter channels for the channel_jid selector."""
|
|
36
|
-
|
|
37
|
-
from services.whatsapp_service import handle_whatsapp_newsletters as _wa_newsletters
|
|
38
|
-
|
|
39
|
-
response = await _wa_newsletters()
|
|
40
|
-
channels = response.get("newsletters", []) if isinstance(response, dict) else []
|
|
41
|
-
return [
|
|
42
|
-
{
|
|
43
|
-
"value": c.get("channel_jid") or c.get("id") or "",
|
|
44
|
-
"label": c.get("name") or c.get("channel_jid", ""),
|
|
45
|
-
}
|
|
46
|
-
for c in channels
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
async def load_whatsapp_group_members(params: dict[str, Any]) -> list[dict[str, Any]]:
|
|
51
|
-
"""List members of a specific WhatsApp group for the senderNumber
|
|
52
|
-
selector. Depends on ``params['group_id']``."""
|
|
53
|
-
|
|
54
|
-
from services.whatsapp_service import handle_whatsapp_group_info as _wa_group_info
|
|
55
|
-
|
|
56
|
-
group_id = params.get("group_id") or ""
|
|
57
|
-
if not group_id:
|
|
58
|
-
return []
|
|
59
|
-
response = await _wa_group_info(group_id)
|
|
60
|
-
participants = (
|
|
61
|
-
response.get("participants", []) if isinstance(response, dict) else []
|
|
62
|
-
)
|
|
63
|
-
return [
|
|
64
|
-
{
|
|
65
|
-
"value": p.get("phone") or p.get("jid") or "",
|
|
66
|
-
"label": p.get("name") or p.get("phone") or p.get("jid", ""),
|
|
67
|
-
}
|
|
68
|
-
for p in participants
|
|
69
|
-
]
|