machinaos 0.0.78 → 0.0.80
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/.env.template +74 -5
- package/{workflows/AI Assistant_workflow-1778504793388-ou1m1tz2x.json → .machina/workflows/AI Assistant_example_workflow-1779017037684-e2e5da7a.json } +164 -105
- package/{workflows/AI Employee_example_workflow-1777720598005-u4cm858dv.json → .machina/workflows/AI Employee_example_workflow-1779102911870-cbc76c82.json } +582 -328
- package/{workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json → .machina/workflows/Claude Assistant_example_workflow-1779095939967-2369cff4.json } +152 -83
- package/README.md +5 -2
- package/bin/cli.js +2 -2
- package/{machina → cli}/__main__.py +11 -7
- package/cli/_common.py +122 -0
- package/cli/buildenv.py +40 -0
- package/cli/cli.py +204 -0
- package/{machina → cli}/colors.py +10 -2
- package/cli/commands/__init__.py +1 -0
- package/cli/commands/_temporal_specs.py +59 -0
- package/{machina → cli}/commands/build.py +35 -45
- package/cli/commands/clean.py +141 -0
- package/cli/commands/daemon/__init__.py +47 -0
- package/cli/commands/daemon/_state.py +97 -0
- package/cli/commands/daemon/restart.py +14 -0
- package/cli/commands/daemon/start.py +49 -0
- package/cli/commands/daemon/status.py +20 -0
- package/cli/commands/daemon/stop.py +22 -0
- package/{machina → cli}/commands/dev.py +32 -42
- package/{machina → cli}/commands/docs.py +13 -11
- package/{machina → cli}/commands/start.py +69 -62
- package/{machina → cli}/commands/stop.py +7 -10
- package/{machina → cli}/commands/version.py +12 -6
- package/cli/config.py +170 -0
- package/cli/platform_.py +169 -0
- package/{machina → cli}/ports.py +42 -3
- package/{machina → cli}/run.py +29 -2
- package/{machina → cli}/supervisor.py +29 -12
- package/{machina → cli}/tcp.py +6 -2
- package/{machina → cli}/tree.py +38 -11
- package/client/dist/assets/{ActionBar-Du2MSFSz.js → ActionBar-Cjr3TF7g.js} +1 -1
- package/client/dist/assets/{ApiKeyInput-k2LBmBjb.js → ApiKeyInput-DIJE2PVA.js} +1 -1
- package/client/dist/assets/{ApiKeyPanel-C_bV9U0X.js → ApiKeyPanel-CPmye7uh.js} +1 -1
- package/client/dist/assets/{ApiUsageSection-CmVfwZzL.js → ApiUsageSection-TF_7gH2D.js} +1 -1
- package/client/dist/assets/{EmailPanel-CeKIMGu-.js → EmailPanel-Bs-xvbKR.js} +1 -1
- package/client/dist/assets/{OAuthPanel-KA3t3Q2K.js → OAuthPanel-BDtVJhAV.js} +1 -1
- package/client/dist/assets/{QrPairingPanel-NgNpJNuk.js → QrPairingPanel-BwJehTuZ.js} +1 -1
- package/client/dist/assets/{RateLimitSection-Du5YNVIA.js → RateLimitSection-CfNOoPIS.js} +1 -1
- package/client/dist/assets/{StatusCard-DNLyayXc.js → StatusCard-DkwIrgdP.js} +1 -1
- package/client/dist/assets/index-P2FzntoL.js +165 -0
- package/client/dist/index.html +1 -1
- package/client/package.json +1 -1
- package/client/src/Dashboard.tsx +128 -76
- package/client/src/adapters/nodeSpecToDescription.ts +7 -0
- package/client/src/assets/icons/index.test.ts +10 -0
- package/client/src/assets/icons/index.ts +16 -3
- package/client/src/components/AIAgentNode.tsx +8 -8
- package/client/src/components/ParameterRenderer.tsx +6 -3
- package/client/src/components/SkillEditorModal.tsx +1 -0
- package/client/src/components/credentials/panels/EmailPanel.tsx +2 -0
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +2 -0
- package/client/src/components/credentials/sections/RateLimitSection.tsx +1 -0
- package/client/src/components/icons/AIProviderIcons.tsx +1 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +1 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1 -0
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +1 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +1 -0
- package/client/src/components/ui/ComponentPalette.tsx +1 -0
- package/client/src/components/ui/MapSelector.tsx +1 -0
- package/client/src/components/ui/NodeContextMenu.tsx +3 -3
- package/client/src/components/ui/SettingsPanel.tsx +1 -0
- package/client/src/components/ui/action-button.tsx +1 -0
- package/client/src/components/ui/badge.tsx +1 -0
- package/client/src/components/ui/button.tsx +1 -0
- package/client/src/components/ui/form.tsx +1 -0
- package/client/src/components/ui/tabs.tsx +1 -0
- package/client/src/contexts/AuthContext.tsx +1 -0
- package/client/src/contexts/ThemeContext.tsx +1 -0
- package/client/src/contexts/WebSocketContext.tsx +104 -34
- package/client/src/hooks/__tests__/useApiKeys.test.ts +2 -2
- package/client/src/hooks/useReactFlowNodes.ts +1 -0
- package/client/src/hooks/useWorkflowValidation.ts +142 -0
- package/client/src/lib/nodeSpec.ts +1 -0
- package/client/src/test/providers.tsx +1 -0
- package/client/src/types/__tests__/cloudEvents.test.ts +5 -2
- package/client/src/types/cloudEvents.ts +19 -7
- package/client/src/utils/nodeUtils.ts +1 -1
- package/client/src/utils/workflow.ts +8 -2
- package/client/src/utils/workflowExport.ts +60 -3
- package/package.json +24 -23
- package/scripts/install.js +16 -27
- package/scripts/migrate_icons.py +3 -1
- package/scripts/migrate_skill_icons.py +6 -7
- package/scripts/postinstall.js +11 -9
- package/server/config/ai_cli_providers.json +2 -3
- package/server/config/credential_providers.json +15 -15
- package/server/config/llm_defaults.json +1 -1
- package/server/config/model_registry.json +416 -611
- package/server/constants.py +285 -223
- package/server/core/__init__.py +1 -1
- package/server/core/cache.py +9 -29
- package/server/core/cleanup.py +12 -24
- package/server/core/config.py +148 -24
- package/server/core/container.py +68 -59
- package/server/core/credential_backends.py +5 -13
- package/server/core/credentials_database.py +13 -43
- package/server/core/database.py +292 -353
- package/server/core/health.py +4 -5
- package/server/core/logging.py +241 -87
- package/server/core/paths.py +285 -0
- package/server/core/tracing.py +2 -8
- package/server/gunicorn.conf.py +1 -0
- package/server/main.py +150 -74
- package/server/middleware/auth.py +18 -24
- package/server/models/__init__.py +1 -1
- package/server/models/auth.py +5 -12
- package/server/models/database.py +36 -68
- package/server/models/node_metadata.py +25 -18
- package/server/nodejs/dist/index.js +107 -0
- package/server/nodes/README.md +11 -5
- package/server/nodes/__init__.py +1 -1
- package/server/nodes/_visuals.py +146 -14
- package/server/nodes/agent/_events.py +124 -0
- package/server/nodes/agent/_handles.py +15 -29
- package/server/nodes/agent/_inline.py +28 -25
- package/server/nodes/agent/_specialized.py +30 -15
- package/server/nodes/agent/{ai_agent.py → ai_agent/__init__.py} +33 -17
- package/server/nodes/agent/ai_agent/meta.json +3 -0
- package/server/nodes/agent/{ai_employee.py → ai_employee/__init__.py} +5 -2
- package/server/nodes/agent/ai_employee/meta.json +3 -0
- package/server/nodes/agent/{android_agent.py → android_agent/__init__.py} +1 -1
- package/server/nodes/agent/android_agent/meta.json +3 -0
- package/server/nodes/agent/{autonomous_agent.py → autonomous_agent/__init__.py} +2 -1
- package/server/nodes/agent/autonomous_agent/meta.json +3 -0
- package/server/nodes/agent/{chat_agent.py → chat_agent/__init__.py} +29 -12
- package/server/nodes/agent/chat_agent/meta.json +3 -0
- package/server/nodes/agent/{claude_code_agent.py → claude_code_agent/__init__.py} +192 -95
- package/server/nodes/agent/claude_code_agent/_handlers.py +169 -0
- package/server/{services/claude_oauth.py → nodes/agent/claude_code_agent/_oauth.py} +26 -13
- package/server/nodes/agent/claude_code_agent/_pool.py +1020 -0
- package/server/nodes/agent/claude_code_agent/_provider.py +513 -0
- package/server/nodes/agent/claude_code_agent/_skills.py +245 -0
- package/server/nodes/agent/claude_code_agent/meta.json +3 -0
- package/server/nodes/agent/{codex_agent.py → codex_agent/__init__.py} +26 -35
- package/server/nodes/agent/codex_agent/meta.json +3 -0
- package/server/nodes/agent/{coding_agent.py → coding_agent/__init__.py} +1 -1
- package/server/nodes/agent/coding_agent/meta.json +3 -0
- package/server/nodes/agent/{consumer_agent.py → consumer_agent/__init__.py} +1 -1
- package/server/nodes/agent/consumer_agent/meta.json +3 -0
- package/server/nodes/agent/{orchestrator_agent.py → orchestrator_agent/__init__.py} +5 -2
- package/server/nodes/agent/orchestrator_agent/meta.json +3 -0
- package/server/nodes/agent/{payments_agent.py → payments_agent/__init__.py} +1 -1
- package/server/nodes/agent/payments_agent/meta.json +3 -0
- package/server/nodes/agent/{productivity_agent.py → productivity_agent/__init__.py} +1 -1
- package/server/nodes/agent/productivity_agent/meta.json +3 -0
- package/server/nodes/agent/{rlm_agent.py → rlm_agent/__init__.py} +18 -17
- package/server/nodes/agent/rlm_agent/meta.json +3 -0
- package/server/nodes/agent/{social_agent.py → social_agent/__init__.py} +1 -1
- package/server/nodes/agent/social_agent/meta.json +3 -0
- package/server/nodes/agent/{task_agent.py → task_agent/__init__.py} +1 -1
- package/server/nodes/agent/task_agent/meta.json +3 -0
- package/server/nodes/agent/{tool_agent.py → tool_agent/__init__.py} +1 -1
- package/server/nodes/agent/tool_agent/meta.json +3 -0
- package/server/nodes/agent/{travel_agent.py → travel_agent/__init__.py} +1 -1
- package/server/nodes/agent/travel_agent/meta.json +3 -0
- package/server/nodes/agent/{web_agent.py → web_agent/__init__.py} +1 -1
- package/server/nodes/agent/web_agent/meta.json +3 -0
- package/server/nodes/android/__init__.py +24 -0
- package/server/nodes/android/_base.py +93 -76
- package/server/nodes/android/_dispatcher.py +140 -223
- package/server/nodes/android/_events.py +154 -0
- package/server/nodes/android/_handlers.py +13 -7
- package/server/nodes/android/_option_loaders.py +1 -4
- package/server/nodes/android/_refresh.py +27 -37
- package/server/nodes/android/_relay/broadcaster.py +25 -41
- package/server/nodes/android/_relay/client.py +23 -42
- package/server/nodes/android/_relay/manager.py +1 -0
- package/server/nodes/android/_relay/protocol.py +6 -0
- package/server/nodes/android/_router.py +48 -133
- package/server/nodes/android/{airplane_mode_control.py → airplane_mode_control/__init__.py} +2 -1
- package/server/nodes/android/airplane_mode_control/meta.json +3 -0
- package/server/nodes/android/{app_launcher.py → app_launcher/__init__.py} +2 -1
- package/server/nodes/android/app_launcher/meta.json +3 -0
- package/server/nodes/android/{app_list.py → app_list/__init__.py} +2 -1
- package/server/nodes/android/app_list/meta.json +3 -0
- package/server/nodes/android/{audio_automation.py → audio_automation/__init__.py} +2 -1
- package/server/nodes/android/audio_automation/meta.json +3 -0
- package/server/nodes/android/{battery_monitor.py → battery_monitor/__init__.py} +2 -1
- package/server/nodes/android/battery_monitor/meta.json +3 -0
- package/server/nodes/android/{bluetooth_automation.py → bluetooth_automation/__init__.py} +2 -1
- package/server/nodes/android/bluetooth_automation/meta.json +3 -0
- package/server/nodes/android/{camera_control.py → camera_control/__init__.py} +2 -1
- package/server/nodes/android/camera_control/meta.json +3 -0
- package/server/nodes/android/{device_state_automation.py → device_state_automation/__init__.py} +2 -1
- package/server/nodes/android/device_state_automation/meta.json +3 -0
- package/server/nodes/android/{environmental_sensors.py → environmental_sensors/__init__.py} +2 -1
- package/server/nodes/android/environmental_sensors/meta.json +3 -0
- package/server/nodes/android/{location.py → location/__init__.py} +2 -1
- package/server/nodes/android/location/meta.json +3 -0
- package/server/nodes/android/{media_control.py → media_control/__init__.py} +2 -1
- package/server/nodes/android/media_control/meta.json +3 -0
- package/server/nodes/android/{motion_detection.py → motion_detection/__init__.py} +2 -1
- package/server/nodes/android/motion_detection/meta.json +3 -0
- package/server/nodes/android/{network_monitor.py → network_monitor/__init__.py} +2 -1
- package/server/nodes/android/network_monitor/meta.json +3 -0
- package/server/nodes/android/{screen_control_automation.py → screen_control_automation/__init__.py} +2 -1
- package/server/nodes/android/screen_control_automation/meta.json +3 -0
- package/server/nodes/android/{system_info.py → system_info/__init__.py} +2 -1
- package/server/nodes/android/system_info/meta.json +3 -0
- package/server/nodes/android/{wifi_automation.py → wifi_automation/__init__.py} +2 -1
- package/server/nodes/android/wifi_automation/meta.json +3 -0
- package/server/nodes/browser/__init__.py +22 -1
- package/server/nodes/browser/_install.py +63 -0
- package/server/nodes/browser/_service.py +21 -25
- package/server/nodes/browser/{browser.py → browser/__init__.py} +58 -25
- package/server/nodes/browser/browser/meta.json +3 -0
- package/server/nodes/chat/{chat_history.py → chat_history/__init__.py} +2 -4
- package/server/nodes/chat/chat_history/meta.json +3 -0
- package/server/nodes/chat/{chat_send.py → chat_send/__init__.py} +2 -4
- package/server/nodes/chat/chat_send/icon.svg +1 -0
- package/server/nodes/chat/chat_send/meta.json +3 -0
- package/server/nodes/code/_base.py +1 -1
- package/server/nodes/code/{javascript_executor.py → javascript_executor/__init__.py} +5 -5
- package/server/nodes/code/javascript_executor/meta.json +3 -0
- package/server/nodes/code/{python_executor.py → python_executor/__init__.py} +32 -14
- package/server/nodes/code/python_executor/meta.json +3 -0
- package/server/nodes/code/{typescript_executor.py → typescript_executor/__init__.py} +5 -5
- package/server/nodes/code/typescript_executor/meta.json +3 -0
- package/server/nodes/document/{document_parser.py → document_parser/__init__.py} +26 -15
- package/server/nodes/document/document_parser/meta.json +3 -0
- package/server/nodes/document/{embedding_generator.py → embedding_generator/__init__.py} +16 -9
- package/server/nodes/document/embedding_generator/meta.json +3 -0
- package/server/nodes/document/{file_downloader.py → file_downloader/__init__.py} +30 -20
- package/server/nodes/document/file_downloader/meta.json +3 -0
- package/server/nodes/document/{http_scraper.py → http_scraper/__init__.py} +31 -21
- package/server/nodes/document/http_scraper/meta.json +3 -0
- package/server/nodes/document/{text_chunker.py → text_chunker/__init__.py} +17 -12
- package/server/nodes/document/text_chunker/meta.json +3 -0
- package/server/nodes/document/{vector_store.py → vector_store/__init__.py} +88 -72
- package/server/nodes/document/vector_store/meta.json +3 -0
- package/server/nodes/email/__init__.py +9 -2
- package/server/nodes/email/_events.py +54 -0
- package/server/nodes/email/_filters.py +3 -3
- package/server/nodes/email/_himalaya.py +95 -50
- package/server/nodes/email/_service.py +23 -13
- package/server/nodes/email/{email_read.py → email_read/__init__.py} +23 -11
- package/server/nodes/email/email_read/icon.svg +6 -0
- package/server/nodes/email/email_read/meta.json +3 -0
- package/server/nodes/email/{email_receive.py → email_receive/__init__.py} +45 -23
- package/server/nodes/email/email_receive/meta.json +3 -0
- package/server/nodes/email/{email_send.py → email_send/__init__.py} +13 -7
- package/server/nodes/email/email_send/meta.json +3 -0
- package/server/nodes/filesystem/_backend.py +1 -5
- package/server/nodes/filesystem/{file_modify.py → file_modify/__init__.py} +10 -5
- package/server/nodes/filesystem/file_modify/meta.json +3 -0
- package/server/nodes/filesystem/{file_read.py → file_read/__init__.py} +7 -3
- package/server/nodes/filesystem/file_read/meta.json +3 -0
- package/server/nodes/filesystem/{fs_search.py → fs_search/__init__.py} +11 -3
- package/server/nodes/filesystem/fs_search/meta.json +3 -0
- package/server/nodes/filesystem/{shell.py → shell/__init__.py} +12 -5
- package/server/nodes/filesystem/shell/meta.json +3 -0
- package/server/nodes/google/__init__.py +12 -0
- package/server/nodes/google/_auth_helper.py +7 -13
- package/server/nodes/google/_base.py +14 -11
- package/server/nodes/google/_credentials.py +2 -1
- package/server/nodes/google/_events.py +47 -0
- package/server/nodes/google/_filters.py +3 -3
- package/server/nodes/google/_gmail.py +70 -47
- package/server/nodes/google/_handlers.py +3 -1
- package/server/nodes/google/_oauth.py +25 -11
- package/server/nodes/google/_option_loaders.py +9 -30
- package/server/nodes/google/_refresh.py +8 -12
- package/server/nodes/google/_router.py +4 -5
- package/server/nodes/google/{calendar.py → calendar/__init__.py} +87 -64
- package/server/nodes/google/calendar/meta.json +3 -0
- package/server/nodes/google/{contacts.py → contacts/__init__.py} +84 -72
- package/server/nodes/google/contacts/meta.json +3 -0
- package/server/nodes/google/{drive.py → drive/__init__.py} +87 -72
- package/server/nodes/google/drive/meta.json +3 -0
- package/server/nodes/google/{gmail.py → gmail/__init__.py} +73 -39
- package/server/nodes/google/gmail/meta.json +3 -0
- package/server/nodes/google/{gmail_receive.py → gmail_receive/__init__.py} +31 -24
- package/server/nodes/google/gmail_receive/icon.svg +7 -0
- package/server/nodes/google/gmail_receive/meta.json +3 -0
- package/server/nodes/google/google.svg +7 -0
- package/server/nodes/google/{sheets.py → sheets/__init__.py} +54 -42
- package/server/nodes/google/sheets/meta.json +3 -0
- package/server/nodes/google/{tasks.py → tasks/__init__.py} +56 -43
- package/server/nodes/google/tasks/meta.json +3 -0
- package/server/nodes/groups.py +28 -28
- package/server/nodes/location/__init__.py +31 -1
- package/server/nodes/location/_credentials.py +1 -6
- package/server/nodes/location/_service.py +88 -107
- package/server/nodes/location/{gmaps_create.py → gmaps_create/__init__.py} +6 -6
- package/server/nodes/location/gmaps_create/meta.json +3 -0
- package/server/nodes/location/{gmaps_locations.py → gmaps_locations/__init__.py} +8 -6
- package/server/nodes/location/gmaps_locations/meta.json +3 -0
- package/server/nodes/location/{gmaps_nearby_places.py → gmaps_nearby_places/__init__.py} +8 -6
- package/server/nodes/location/gmaps_nearby_places/meta.json +3 -0
- package/server/nodes/model/_base.py +10 -7
- package/server/nodes/model/_credentials.py +10 -10
- package/server/nodes/model/_local_validator.py +28 -24
- package/server/nodes/model/{anthropic_chat_model.py → anthropic_chat_model/__init__.py} +5 -3
- package/server/nodes/model/anthropic_chat_model/meta.json +3 -0
- package/server/nodes/model/{cerebras_chat_model.py → cerebras_chat_model/__init__.py} +5 -3
- package/server/nodes/model/cerebras_chat_model/meta.json +3 -0
- package/server/nodes/model/{deepseek_chat_model.py → deepseek_chat_model/__init__.py} +8 -4
- package/server/nodes/model/deepseek_chat_model/meta.json +3 -0
- package/server/nodes/model/{gemini_chat_model.py → gemini_chat_model/__init__.py} +5 -3
- package/server/nodes/model/gemini_chat_model/meta.json +3 -0
- package/server/nodes/model/{groq_chat_model.py → groq_chat_model/__init__.py} +2 -2
- package/server/nodes/model/groq_chat_model/meta.json +3 -0
- package/server/nodes/model/{kimi_chat_model.py → kimi_chat_model/__init__.py} +2 -2
- package/server/nodes/model/kimi_chat_model/meta.json +3 -0
- package/server/nodes/model/{lmstudio_chat_model.py → lmstudio_chat_model/__init__.py} +2 -2
- package/server/nodes/model/lmstudio_chat_model/meta.json +3 -0
- package/server/nodes/model/{mistral_chat_model.py → mistral_chat_model/__init__.py} +2 -2
- package/server/nodes/model/mistral_chat_model/meta.json +3 -0
- package/server/nodes/model/{ollama_chat_model.py → ollama_chat_model/__init__.py} +2 -2
- package/server/nodes/model/ollama_chat_model/meta.json +3 -0
- package/server/nodes/model/{openai_chat_model.py → openai_chat_model/__init__.py} +8 -4
- package/server/nodes/model/openai_chat_model/meta.json +3 -0
- package/server/nodes/model/{openrouter_chat_model.py → openrouter_chat_model/__init__.py} +8 -4
- package/server/nodes/model/openrouter_chat_model/meta.json +3 -0
- package/server/nodes/proxy/_usage.py +14 -15
- package/server/nodes/proxy/{proxy_config.py → proxy_config/__init__.py} +39 -30
- package/server/nodes/proxy/proxy_config/meta.json +3 -0
- package/server/nodes/proxy/{proxy_request.py → proxy_request/__init__.py} +30 -16
- package/server/nodes/proxy/proxy_request/meta.json +3 -0
- package/server/nodes/proxy/{proxy_status.py → proxy_status/__init__.py} +2 -0
- package/server/nodes/proxy/proxy_status/meta.json +3 -0
- package/server/nodes/scheduler/{cron_scheduler.py → cron_scheduler/__init__.py} +96 -23
- package/server/nodes/scheduler/cron_scheduler/_workflow.py +155 -0
- package/server/nodes/scheduler/cron_scheduler/meta.json +3 -0
- package/server/nodes/scheduler/{timer.py → timer/__init__.py} +6 -5
- package/server/nodes/scheduler/timer/meta.json +3 -0
- package/server/nodes/scraper/_credentials.py +0 -1
- package/server/nodes/scraper/{apify_actor.py → apify_actor/__init__.py} +44 -35
- package/server/nodes/scraper/apify_actor/icon.svg +5 -0
- package/server/nodes/scraper/apify_actor/meta.json +3 -0
- package/server/nodes/scraper/{crawlee_scraper.py → crawlee_scraper/__init__.py} +96 -57
- package/server/nodes/scraper/crawlee_scraper/meta.json +3 -0
- package/server/nodes/search/{brave_search.py → brave_search/__init__.py} +6 -5
- package/server/nodes/search/brave_search/icon.svg +3 -0
- package/server/nodes/search/brave_search/meta.json +3 -0
- package/server/nodes/search/{duckduckgo_search.py → duckduckgo_search/__init__.py} +17 -6
- package/server/nodes/search/duckduckgo_search/meta.json +3 -0
- package/server/nodes/search/{perplexity_search.py → perplexity_search/__init__.py} +4 -5
- package/server/nodes/search/perplexity_search/icon.svg +3 -0
- package/server/nodes/search/perplexity_search/meta.json +3 -0
- package/server/nodes/search/{serper_search.py → serper_search/__init__.py} +32 -25
- package/server/nodes/search/serper_search/icon.svg +3 -0
- package/server/nodes/search/serper_search/meta.json +3 -0
- package/server/nodes/skill/__init__.py +21 -1
- package/server/nodes/skill/_expander.py +75 -0
- package/server/nodes/skill/{master_skill.py → master_skill/__init__.py} +2 -8
- package/server/nodes/skill/master_skill/_events.py +84 -0
- package/server/nodes/skill/master_skill/meta.json +3 -0
- package/server/nodes/skill/{simple_memory.py → simple_memory/__init__.py} +8 -16
- package/server/nodes/skill/simple_memory/meta.json +3 -0
- package/server/nodes/social/_base.py +223 -231
- package/server/nodes/social/{social_receive.py → social_receive/__init__.py} +38 -13
- package/server/nodes/social/social_receive/meta.json +3 -0
- package/server/nodes/social/{social_send.py → social_send/__init__.py} +71 -29
- package/server/nodes/social/social_send/icon.svg +1 -0
- package/server/nodes/social/social_send/meta.json +3 -0
- package/server/nodes/stripe/__init__.py +7 -3
- package/server/nodes/stripe/_credentials.py +0 -1
- package/server/nodes/stripe/_handlers.py +18 -7
- package/server/nodes/stripe/_install.py +14 -15
- package/server/nodes/stripe/_source.py +5 -5
- package/server/nodes/stripe/icon.svg +1 -0
- package/server/nodes/stripe/meta.json +3 -0
- package/server/nodes/stripe/stripe_action.py +4 -4
- package/server/nodes/stripe/stripe_receive.py +6 -9
- package/server/nodes/telegram/__init__.py +13 -0
- package/server/nodes/telegram/_credentials.py +2 -7
- package/server/nodes/telegram/_events.py +167 -0
- package/server/nodes/telegram/_filters.py +3 -11
- package/server/nodes/telegram/_handlers.py +17 -7
- package/server/nodes/telegram/_refresh.py +24 -34
- package/server/nodes/telegram/_service.py +29 -45
- package/server/nodes/telegram/meta.json +3 -0
- package/server/nodes/telegram/telegram.svg +3 -0
- package/server/nodes/telegram/telegram_receive.py +38 -18
- package/server/nodes/telegram/telegram_send.py +21 -19
- package/server/nodes/text/{file_handler.py → file_handler/__init__.py} +7 -1
- package/server/nodes/text/file_handler/meta.json +3 -0
- package/server/nodes/text/{text_generator.py → text_generator/__init__.py} +2 -1
- package/server/nodes/text/text_generator/meta.json +3 -0
- package/server/nodes/tool/{agent_builder.py → agent_builder/__init__.py} +105 -100
- package/server/nodes/tool/agent_builder/_events.py +91 -0
- package/server/nodes/tool/agent_builder/meta.json +3 -0
- package/server/nodes/tool/{calculator_tool.py → calculator_tool/__init__.py} +19 -7
- package/server/nodes/tool/calculator_tool/meta.json +3 -0
- package/server/nodes/tool/{current_time_tool.py → current_time_tool/__init__.py} +6 -4
- package/server/nodes/tool/current_time_tool/meta.json +3 -0
- package/server/nodes/tool/{task_manager.py → task_manager/__init__.py} +17 -18
- package/server/nodes/tool/task_manager/meta.json +3 -0
- package/server/nodes/tool/{write_todos.py → write_todos/__init__.py} +20 -6
- package/server/nodes/tool/write_todos/meta.json +3 -0
- package/server/nodes/trigger/{chat_trigger.py → chat_trigger/__init__.py} +11 -7
- package/server/nodes/trigger/chat_trigger/_events.py +53 -0
- package/server/nodes/trigger/chat_trigger/meta.json +3 -0
- package/server/nodes/trigger/{task_trigger.py → task_trigger/__init__.py} +10 -7
- package/server/nodes/trigger/task_trigger/meta.json +3 -0
- package/server/nodes/trigger/{webhook_trigger.py → webhook_trigger/__init__.py} +10 -7
- package/server/nodes/trigger/webhook_trigger/_events.py +54 -0
- package/server/nodes/trigger/webhook_trigger/meta.json +3 -0
- package/server/nodes/twitter/__init__.py +7 -1
- package/server/nodes/twitter/_base.py +86 -61
- package/server/nodes/twitter/_credentials.py +7 -5
- package/server/nodes/twitter/_events.py +101 -0
- package/server/nodes/twitter/_filters.py +9 -9
- package/server/nodes/twitter/_handlers.py +3 -1
- package/server/nodes/twitter/_oauth.py +1 -2
- package/server/nodes/twitter/_refresh.py +8 -12
- package/server/nodes/twitter/{twitter_receive.py → twitter_receive/__init__.py} +7 -7
- package/server/nodes/twitter/twitter_receive/icon.svg +1 -0
- package/server/nodes/twitter/twitter_receive/meta.json +3 -0
- package/server/nodes/twitter/{twitter_search.py → twitter_search/__init__.py} +16 -11
- package/server/nodes/twitter/twitter_search/icon.svg +1 -0
- package/server/nodes/twitter/twitter_search/meta.json +3 -0
- package/server/nodes/twitter/{twitter_send.py → twitter_send/__init__.py} +60 -27
- package/server/nodes/twitter/twitter_send/icon.svg +1 -0
- package/server/nodes/twitter/twitter_send/meta.json +3 -0
- package/server/nodes/twitter/{twitter_user.py → twitter_user/__init__.py} +34 -19
- package/server/nodes/twitter/twitter_user/icon.svg +1 -0
- package/server/nodes/twitter/twitter_user/meta.json +3 -0
- package/server/nodes/utility/{console.py → console/__init__.py} +17 -22
- package/server/nodes/utility/console/meta.json +3 -0
- package/server/nodes/utility/{http_request.py → http_request/__init__.py} +9 -6
- package/server/nodes/utility/http_request/meta.json +3 -0
- package/server/nodes/utility/{process_manager.py → process_manager/__init__.py} +10 -6
- package/server/nodes/utility/process_manager/meta.json +3 -0
- package/server/nodes/utility/team_monitor/meta.json +3 -0
- package/server/nodes/utility/{webhook_response.py → webhook_response/__init__.py} +12 -7
- package/server/nodes/utility/webhook_response/meta.json +3 -0
- package/server/nodes/visuals.json +69 -251
- package/server/nodes/whatsapp/__init__.py +24 -0
- package/server/nodes/whatsapp/_base.py +283 -338
- package/server/nodes/whatsapp/_credentials.py +44 -0
- package/server/nodes/whatsapp/_events.py +277 -0
- package/server/nodes/whatsapp/_filters.py +36 -37
- package/server/nodes/whatsapp/_handlers.py +2 -0
- package/server/nodes/whatsapp/_option_loaders.py +1 -3
- package/server/nodes/whatsapp/_refresh.py +13 -18
- package/server/nodes/whatsapp/_runtime.py +9 -6
- package/server/nodes/whatsapp/_service.py +89 -152
- package/server/nodes/whatsapp/meta.json +3 -0
- package/server/nodes/whatsapp/whatsapp_db.py +116 -54
- package/server/nodes/whatsapp/whatsapp_receive.py +30 -13
- package/server/nodes/whatsapp/whatsapp_send.py +60 -37
- package/server/nodes/workflow/{start.py → start/__init__.py} +1 -4
- package/server/nodes/workflow/start/meta.json +3 -0
- package/server/package-lock.json +3 -3
- package/server/package.json +3 -0
- package/server/pyproject.toml +39 -10
- package/server/requirements.txt +3 -5
- package/server/routers/__init__.py +1 -1
- package/server/routers/auth.py +16 -56
- package/server/routers/database.py +27 -50
- package/server/routers/nodejs_compat.py +25 -87
- package/server/routers/schemas.py +66 -2
- package/server/routers/webhook.py +12 -12
- package/server/routers/websocket.py +312 -1716
- package/server/routers/workflow.py +28 -53
- package/server/scripts/smoke_test_skills.py +178 -0
- package/server/services/__init__.py +1 -1
- package/server/services/_supervisor/process.py +9 -3
- package/server/services/_supervisor/registry.py +3 -3
- package/server/services/_supervisor/util.py +1 -1
- package/server/services/agent_team.py +15 -43
- package/server/services/agent_teams/__init__.py +17 -0
- package/server/services/agent_teams/handlers.py +195 -0
- package/server/services/ai.py +853 -1108
- package/server/services/auth.py +10 -34
- package/server/services/chat_client.py +5 -34
- package/server/services/circuit_breaker.py +2 -6
- package/server/services/cli_agent/__init__.py +28 -4
- package/server/services/cli_agent/_cli_auth.py +61 -0
- package/server/services/cli_agent/_handlers.py +24 -183
- package/server/services/cli_agent/config.py +5 -8
- package/server/services/cli_agent/factory.py +168 -22
- package/server/services/cli_agent/jsonl_watcher.py +380 -0
- package/server/services/cli_agent/lockfile.py +9 -2
- package/server/services/cli_agent/mcp_server.py +110 -34
- package/server/services/cli_agent/protocol.py +37 -19
- package/server/services/cli_agent/providers/__init__.py +8 -4
- package/server/services/cli_agent/providers/google_gemini.py +11 -5
- package/server/services/cli_agent/providers/openai_codex.py +34 -34
- package/server/services/cli_agent/service.py +245 -83
- package/server/services/cli_agent/session.py +409 -229
- package/server/services/cli_agent/transports/__init__.py +47 -0
- package/server/services/cli_agent/transports/base.py +111 -0
- package/server/services/cli_agent/transports/posix.py +196 -0
- package/server/services/cli_agent/transports/windows.py +189 -0
- package/server/services/cli_agent/types.py +45 -18
- package/server/services/cli_agent/workflow_tools.py +28 -15
- package/server/services/compaction.py +68 -52
- package/server/services/credential_registry.py +6 -20
- package/server/services/credentials/__init__.py +18 -0
- package/server/services/credentials/handlers.py +196 -0
- package/server/services/deployment/__init__.py +12 -1
- package/server/services/deployment/canary_registry.py +137 -0
- package/server/services/deployment/handlers.py +382 -0
- package/server/services/deployment/manager.py +653 -163
- package/server/services/deployment/poll_registry.py +2 -6
- package/server/services/deployment/state.py +2 -0
- package/server/services/deployment/triggers.py +87 -93
- package/server/services/event_waiter.py +47 -54
- package/server/services/events/__init__.py +11 -0
- package/server/services/events/admin_handlers.py +368 -0
- package/server/services/events/daemon.py +3 -1
- package/server/services/events/dispatch.py +188 -0
- package/server/services/events/envelope.py +264 -45
- package/server/services/events/oauth_lifecycle.py +98 -42
- package/server/services/events/triggers.py +3 -13
- package/server/services/events/verifiers/hmac_basic.py +1 -1
- package/server/services/events/verifiers/standard_webhooks.py +2 -4
- package/server/services/events/webhook.py +2 -3
- package/server/services/example_loader.py +73 -15
- package/server/services/execution/cache.py +36 -76
- package/server/services/execution/conditions.py +7 -20
- package/server/services/execution/dlq.py +20 -24
- package/server/services/execution/executor.py +234 -265
- package/server/services/execution/models.py +40 -46
- package/server/services/execution/recovery.py +23 -46
- package/server/services/handlers/__init__.py +12 -16
- package/server/services/handlers/todo.py +3 -6
- package/server/services/handlers/tools.py +143 -194
- package/server/services/handlers/triggers.py +24 -23
- package/server/services/llm/config.py +10 -1
- package/server/services/llm/factory.py +16 -4
- package/server/services/llm/messages.py +1 -5
- package/server/services/llm/protocol.py +9 -1
- package/server/services/llm/providers/anthropic.py +23 -12
- package/server/services/llm/providers/gemini.py +43 -22
- package/server/services/llm/providers/openai.py +14 -6
- package/server/services/llm/providers/openrouter.py +6 -1
- package/server/services/markdown_formatter.py +1 -2
- package/server/services/memory/__init__.py +2 -2
- package/server/services/memory/jsonl.py +6 -2
- package/server/services/memory/markdown.py +6 -6
- package/server/services/memory/state.py +6 -5
- package/server/services/memory_store.py +8 -12
- package/server/services/model_registry.py +22 -20
- package/server/services/node_executor.py +85 -80
- package/server/services/node_output_schemas.py +4 -7
- package/server/services/node_registry.py +40 -4
- package/server/services/node_spec.py +3 -7
- package/server/services/nodejs_client.py +4 -14
- package/server/services/oauth_utils.py +11 -7
- package/server/services/parameter_resolver.py +30 -36
- package/server/services/plugin/base.py +321 -38
- package/server/services/plugin/connection.py +12 -7
- package/server/services/plugin/credential.py +80 -22
- package/server/services/plugin/edge_walker.py +128 -105
- package/server/services/plugin/identifiers.py +48 -0
- package/server/services/plugin/interceptor.py +1 -1
- package/server/services/plugin/oauth.py +25 -21
- package/server/services/plugin/operation.py +1 -1
- package/server/services/plugin/polling.py +151 -26
- package/server/services/plugin/registry.py +52 -4
- package/server/services/plugin/routing.py +6 -9
- package/server/services/plugin/scaling.py +36 -18
- package/server/services/plugin/service_factories.py +95 -0
- package/server/services/plugin/shutdown_hooks.py +103 -0
- package/server/services/plugin/social_provider_registry.py +80 -0
- package/server/services/plugin/ws.py +2 -1
- package/server/services/pricing.py +26 -40
- package/server/services/pricing_handlers.py +90 -0
- package/server/services/process_service.py +33 -32
- package/server/services/proxy/models.py +15 -9
- package/server/services/proxy/service.py +26 -40
- package/server/services/rlm/adapters.py +43 -40
- package/server/services/rlm/constants.py +9 -9
- package/server/services/rlm/service.py +57 -45
- package/server/services/scheduler.py +8 -39
- package/server/services/settings/__init__.py +16 -0
- package/server/services/settings/handlers.py +275 -0
- package/server/services/skill_loader.py +53 -45
- package/server/services/skill_prompt.py +8 -6
- package/server/services/skills/__init__.py +23 -0
- package/server/services/skills/handlers.py +479 -0
- package/server/services/status_broadcaster.py +314 -291
- package/server/services/temporal/__init__.py +22 -1
- package/server/services/temporal/_handlers.py +65 -0
- package/server/services/temporal/_install.py +158 -0
- package/server/services/temporal/_refresh.py +57 -0
- package/server/services/temporal/_retry_policies.py +85 -0
- package/server/services/temporal/_runtime.py +181 -0
- package/server/services/temporal/_supervised_runtime.py +102 -0
- package/server/services/temporal/activities.py +168 -11
- package/server/services/temporal/agent_activities.py +683 -0
- package/server/services/temporal/agent_workflow.py +601 -0
- package/server/services/temporal/client.py +58 -13
- package/server/services/temporal/executor.py +2 -3
- package/server/services/temporal/plugin_activities.py +37 -2
- package/server/services/temporal/plugin_registry.py +82 -0
- package/server/services/temporal/polling_trigger_workflow.py +267 -0
- package/server/services/temporal/schedules.py +220 -0
- package/server/services/temporal/search_attributes.py +177 -0
- package/server/services/temporal/trigger_listener_workflow.py +378 -0
- package/server/services/temporal/worker.py +111 -18
- package/server/services/temporal/workflow.py +259 -40
- package/server/services/temporal/ws_client.py +22 -11
- package/server/services/text.py +14 -28
- package/server/services/tracked_http.py +29 -49
- package/server/services/user_auth.py +7 -21
- package/server/services/workflow.py +28 -20
- package/server/services/workflow_import.py +351 -0
- package/server/services/workflow_ops.py +4 -0
- package/server/services/workflow_storage/__init__.py +18 -0
- package/server/services/workflow_storage/handlers.py +132 -0
- package/server/services/workflow_validator.py +209 -0
- package/server/services/ws_handler_registry.py +80 -9
- package/server/skills/assistant/agent-builder-skill/SKILL.md +6 -6
- package/server/tests/conftest.py +54 -3
- package/server/tests/credentials/test_auth_service.py +9 -21
- package/server/tests/credentials/test_credential_broadcasts.py +116 -22
- package/server/tests/credentials/test_credentials_database.py +12 -38
- package/server/tests/credentials/test_encryption.py +3 -9
- package/server/tests/credentials/test_google_oauth.py +1 -3
- package/server/tests/credentials/test_oauth_utils.py +31 -38
- package/server/tests/credentials/test_twitter_oauth.py +1 -3
- package/server/tests/credentials/test_websocket_handlers.py +37 -72
- package/server/tests/fixtures/tool_names_snapshot.json +78 -0
- package/server/tests/llm/test_factory.py +12 -4
- package/server/tests/llm/test_providers.py +25 -32
- package/server/tests/llm/test_wiring.py +27 -22
- package/server/tests/nodes/_compat.py +4 -5
- package/server/tests/nodes/_harness.py +31 -24
- package/server/tests/nodes/_mocks.py +2 -6
- package/server/tests/nodes/test_agent_builder.py +43 -35
- package/server/tests/nodes/test_ai_agents.py +29 -24
- package/server/tests/nodes/test_ai_chat_models.py +3 -9
- package/server/tests/nodes/test_ai_tools.py +29 -24
- package/server/tests/nodes/test_android.py +34 -64
- package/server/tests/nodes/test_chat_utility.py +2 -2
- package/server/tests/nodes/test_code_fs_process.py +26 -84
- package/server/tests/nodes/test_document.py +23 -47
- package/server/tests/nodes/test_email.py +88 -51
- package/server/tests/nodes/test_google_workspace.py +26 -20
- package/server/tests/nodes/test_http_proxy.py +43 -89
- package/server/tests/nodes/test_search.py +3 -9
- package/server/tests/nodes/test_specialized_agents.py +58 -162
- package/server/tests/nodes/test_stripe_plugin.py +25 -5
- package/server/tests/nodes/test_telegram_social.py +33 -37
- package/server/tests/nodes/test_twitter.py +59 -150
- package/server/tests/nodes/test_web_automation.py +21 -51
- package/server/tests/nodes/test_whatsapp.py +13 -19
- package/server/tests/nodes/test_workflow_triggers.py +16 -45
- package/server/tests/services/cli_agent/test_claude_session_events.py +201 -0
- package/server/tests/services/cli_agent/test_jsonl_watcher.py +190 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +67 -29
- package/server/tests/services/cli_agent/test_providers.py +236 -47
- package/server/tests/services/cli_agent/test_service.py +9 -7
- package/server/tests/services/memory/test_jsonl.py +30 -25
- package/server/tests/services/test_events.py +26 -7
- package/server/tests/services/test_identifiers.py +122 -0
- package/server/tests/services/test_process_lifecycle.py +129 -0
- package/server/tests/services/test_supervisor.py +0 -1
- package/server/tests/temporal/__init__.py +0 -0
- package/server/tests/temporal/test_agent_workflow.py +215 -0
- package/server/tests/temporal/test_dispatch.py +231 -0
- package/server/tests/test_admin_handlers.py +394 -0
- package/server/tests/test_auto_skill.py +4 -2
- package/server/tests/test_canary_registry.py +310 -0
- package/server/tests/test_chat_trigger_canary_producer.py +101 -0
- package/server/tests/test_cloudevents_node_parameters.py +129 -0
- package/server/tests/test_credential_icon.py +115 -0
- package/server/tests/test_cron_canary.py +511 -0
- package/server/tests/test_deployment_canary_listener.py +692 -0
- package/server/tests/test_event_framework_phase_a.py +537 -0
- package/server/tests/test_no_raw_prints.py +131 -0
- package/server/tests/test_node_spec.py +196 -103
- package/server/tests/test_parameter_resolver.py +20 -20
- package/server/tests/test_plugin_contract.py +76 -49
- package/server/tests/test_plugin_helpers.py +0 -1
- package/server/tests/test_plugin_self_containment.py +40 -47
- package/server/tests/test_polling_trigger_workflow.py +572 -0
- package/server/tests/test_retry_policies.py +146 -0
- package/server/tests/test_service_factories.py +168 -0
- package/server/tests/test_shutdown_hooks.py +199 -0
- package/server/tests/test_social_provider_registry.py +177 -0
- package/server/tests/test_status_broadcasts.py +214 -63
- package/server/tests/test_task_trigger_canary_producer.py +131 -0
- package/server/tests/test_telegram_trigger_canary_producer.py +113 -0
- package/server/tests/test_tool_registry.py +110 -0
- package/server/tests/test_trigger_listener_workflow.py +365 -0
- package/server/tests/test_whatsapp_trigger_canary_producer.py +164 -0
- package/server/tests/test_workflow_ops.py +1 -3
- package/server/tests/test_workflow_validator.py +791 -0
- package/server/uv.lock +3539 -0
- package/client/dist/assets/index-DQ0nwhec.js +0 -257
- package/client/src/assets/icons/apify/index.ts +0 -19
- package/client/src/assets/icons/browser/index.ts +0 -17
- package/client/src/assets/icons/email/index.ts +0 -22
- package/client/src/assets/icons/google/index.ts +0 -34
- package/client/src/assets/icons/llm/deepseek.svg +0 -1
- package/client/src/assets/icons/llm/index.ts +0 -18
- package/client/src/assets/icons/llm/kimi.svg +0 -1
- package/client/src/assets/icons/llm/mistral.svg +0 -1
- package/client/src/assets/icons/search/index.ts +0 -28
- package/client/src/assets/icons/telegram/index.ts +0 -19
- package/machina/buildenv.py +0 -44
- package/machina/cli.py +0 -55
- package/machina/commands/__init__.py +0 -1
- package/machina/commands/clean.py +0 -80
- package/machina/commands/daemon.py +0 -150
- package/machina/config.py +0 -93
- package/machina/platform_.py +0 -37
- package/machina/pyproject.toml +0 -33
- package/server/nodes/agent/deep_agent.py +0 -103
- package/server/services/agents/__init__.py +0 -9
- package/server/services/agents/adapters.py +0 -199
- package/server/services/agents/constants.py +0 -10
- package/server/services/agents/service.py +0 -297
- package/server/services/cli_agent/providers/anthropic_claude.py +0 -419
- /package/{machina → cli}/README.md +0 -0
- /package/{machina → cli}/__init__.py +0 -0
- /package/{client/src/assets/icons/apify → server/credentials/icons}/apify.svg +0 -0
- /package/{client/src/assets/icons/search/brave.svg → server/credentials/icons/brave_search.svg} +0 -0
- /package/{client/src/assets/icons/email/read.svg → server/credentials/icons/email_himalaya.svg} +0 -0
- /package/{client/src/assets/icons/search → server/credentials/icons}/perplexity.svg +0 -0
- /package/{client/src/assets/icons/search/google.svg → server/credentials/icons/serper.svg} +0 -0
- /package/{client/src/assets → server/credentials}/icons/stripe.svg +0 -0
- /package/{client/src/assets/icons/twitter/x.svg → server/credentials/icons/twitter.svg} +0 -0
- /package/{client/src/assets/icons/browser/chrome.svg → server/nodes/browser/browser/icon.svg} +0 -0
- /package/{client/src/assets/icons/chat/chat.svg → server/nodes/chat/chat_history/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/javascript.svg → server/nodes/code/javascript_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/python.svg → server/nodes/code/python_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/code/typescript.svg → server/nodes/code/typescript_executor/icon.svg} +0 -0
- /package/{client/src/assets/icons/email/receive.svg → server/nodes/email/email_receive/icon.svg} +0 -0
- /package/{client/src/assets/icons/email/send.svg → server/nodes/email/email_send/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/calendar.svg → server/nodes/google/calendar/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/contacts.svg → server/nodes/google/contacts/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/drive.svg → server/nodes/google/drive/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/gmail.svg → server/nodes/google/gmail/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/sheets.svg → server/nodes/google/sheets/icon.svg} +0 -0
- /package/{client/src/assets/icons/google/tasks.svg → server/nodes/google/tasks/icon.svg} +0 -0
- /package/{client/src/assets/icons/search/duckduckgo.svg → server/nodes/search/duckduckgo_search/icon.svg} +0 -0
- /package/{client/src/assets/icons/social/social.svg → server/nodes/social/social_receive/icon.svg} +0 -0
- /package/{client/src/assets/icons/telegram/telegram.svg → server/nodes/telegram/icon.svg} +0 -0
- /package/server/nodes/utility/{team_monitor.py → team_monitor/__init__.py} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-db.svg → server/nodes/whatsapp/icon_whatsappDb.svg} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-receive.svg → server/nodes/whatsapp/icon_whatsappReceive.svg} +0 -0
- /package/{client/src/assets/icons/whatsapp/whatsapp-send.svg → server/nodes/whatsapp/icon_whatsappSend.svg} +0 -0
- /package/{client/src/assets/icons → server/nodes}/whatsapp/whatsapp.svg +0 -0
|
@@ -35,6 +35,8 @@ fail the callback CSRF check.
|
|
|
35
35
|
|
|
36
36
|
from __future__ import annotations
|
|
37
37
|
|
|
38
|
+
import html
|
|
39
|
+
import json
|
|
38
40
|
from typing import Any, Awaitable, Callable, Dict, Optional, Protocol
|
|
39
41
|
|
|
40
42
|
from fastapi import APIRouter, Query, Request, WebSocket
|
|
@@ -59,7 +61,9 @@ class OAuthLike(Protocol):
|
|
|
59
61
|
"""
|
|
60
62
|
|
|
61
63
|
def generate_authorization_url(
|
|
62
|
-
self,
|
|
64
|
+
self,
|
|
65
|
+
*,
|
|
66
|
+
state_data: Optional[Dict[str, Any]] = None,
|
|
63
67
|
) -> Dict[str, str]: ...
|
|
64
68
|
|
|
65
69
|
async def exchange_code(self, code: str, state: str) -> Dict[str, Any]: ...
|
|
@@ -69,7 +73,9 @@ class OAuthLike(Protocol):
|
|
|
69
73
|
async def refresh_access_token(self, refresh_token: str) -> Dict[str, Any]: ...
|
|
70
74
|
|
|
71
75
|
async def revoke_token(
|
|
72
|
-
self,
|
|
76
|
+
self,
|
|
77
|
+
token: str,
|
|
78
|
+
token_type: str = "access_token",
|
|
73
79
|
) -> Dict[str, Any]: ...
|
|
74
80
|
|
|
75
81
|
|
|
@@ -136,10 +142,7 @@ def make_oauth_lifecycle_handlers(
|
|
|
136
142
|
if not client_id:
|
|
137
143
|
return {
|
|
138
144
|
"success": False,
|
|
139
|
-
"error": (
|
|
140
|
-
f"{provider.capitalize()} Client ID not configured. "
|
|
141
|
-
f"Add your {provider.capitalize()} API credentials first."
|
|
142
|
-
),
|
|
145
|
+
"error": (f"{provider.capitalize()} Client ID not configured. " f"Add your {provider.capitalize()} API credentials first."),
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
redirect_uri = get_redirect_uri(websocket, provider)
|
|
@@ -177,7 +180,8 @@ def make_oauth_lifecycle_handlers(
|
|
|
177
180
|
# cached in memory; pull from DB on demand).
|
|
178
181
|
if not user_info.get("success"):
|
|
179
182
|
refresh_token = await auth_service.get_oauth_refresh_token(
|
|
180
|
-
provider,
|
|
183
|
+
provider,
|
|
184
|
+
customer_id="owner",
|
|
181
185
|
)
|
|
182
186
|
if refresh_token:
|
|
183
187
|
refresh = await oauth.refresh_access_token(refresh_token)
|
|
@@ -216,10 +220,7 @@ def make_oauth_lifecycle_handlers(
|
|
|
216
220
|
auth_service = container.auth_service()
|
|
217
221
|
tokens = await auth_service.get_oauth_tokens(provider, customer_id="owner")
|
|
218
222
|
access_token = tokens.get("access_token") if tokens else None
|
|
219
|
-
refresh_token = (
|
|
220
|
-
await auth_service.get_oauth_refresh_token(provider, customer_id="owner")
|
|
221
|
-
if tokens else None
|
|
222
|
-
)
|
|
223
|
+
refresh_token = await auth_service.get_oauth_refresh_token(provider, customer_id="owner") if tokens else None
|
|
223
224
|
|
|
224
225
|
if access_token or refresh_token:
|
|
225
226
|
redirect_uri = get_redirect_uri(websocket, provider)
|
|
@@ -254,12 +255,14 @@ def make_oauth_lifecycle_handlers(
|
|
|
254
255
|
|
|
255
256
|
|
|
256
257
|
async def _maybe_legacy_broadcast(
|
|
257
|
-
broadcast_type: Optional[str],
|
|
258
|
+
broadcast_type: Optional[str],
|
|
259
|
+
payload: Dict[str, Any],
|
|
258
260
|
) -> None:
|
|
259
261
|
"""Optional legacy status broadcast (Google's ``google_status`` frame)."""
|
|
260
262
|
if not broadcast_type:
|
|
261
263
|
return
|
|
262
264
|
from services.status_broadcaster import get_status_broadcaster
|
|
265
|
+
|
|
263
266
|
await get_status_broadcaster().broadcast({"type": broadcast_type, "data": payload})
|
|
264
267
|
|
|
265
268
|
|
|
@@ -333,8 +336,10 @@ def make_oauth_callback_router(
|
|
|
333
336
|
logger.warning(f"{provider} OAuth denied: {error} - {error_description}")
|
|
334
337
|
return HTMLResponse(
|
|
335
338
|
content=render_oauth_callback_html(
|
|
336
|
-
provider,
|
|
337
|
-
|
|
339
|
+
provider,
|
|
340
|
+
status="error",
|
|
341
|
+
message=error_description or error,
|
|
342
|
+
color_hex=color_hex,
|
|
338
343
|
),
|
|
339
344
|
status_code=200,
|
|
340
345
|
)
|
|
@@ -342,7 +347,8 @@ def make_oauth_callback_router(
|
|
|
342
347
|
if not code or not state:
|
|
343
348
|
return HTMLResponse(
|
|
344
349
|
content=render_oauth_callback_html(
|
|
345
|
-
provider,
|
|
350
|
+
provider,
|
|
351
|
+
status="error",
|
|
346
352
|
message="Missing authorization code or state parameter",
|
|
347
353
|
color_hex=color_hex,
|
|
348
354
|
),
|
|
@@ -365,7 +371,8 @@ def make_oauth_callback_router(
|
|
|
365
371
|
if not result.get("success"):
|
|
366
372
|
return HTMLResponse(
|
|
367
373
|
content=render_oauth_callback_html(
|
|
368
|
-
provider,
|
|
374
|
+
provider,
|
|
375
|
+
status="error",
|
|
369
376
|
message=result.get("error", "Token exchange failed"),
|
|
370
377
|
color_hex=color_hex,
|
|
371
378
|
),
|
|
@@ -384,10 +391,13 @@ def make_oauth_callback_router(
|
|
|
384
391
|
store_overrides: Dict[str, Any] = {}
|
|
385
392
|
redirect_after: Optional[str] = None
|
|
386
393
|
if extra_state_handler is not None:
|
|
387
|
-
override = await extra_state_handler(
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
394
|
+
override = await extra_state_handler(
|
|
395
|
+
{
|
|
396
|
+
"state_data": state_data,
|
|
397
|
+
"user_info": user_info,
|
|
398
|
+
"tokens": result,
|
|
399
|
+
}
|
|
400
|
+
)
|
|
391
401
|
if override:
|
|
392
402
|
redirect_after = override.pop("redirect_after", None)
|
|
393
403
|
store_overrides.update(override)
|
|
@@ -411,13 +421,15 @@ def make_oauth_callback_router(
|
|
|
411
421
|
from services.status_broadcaster import get_status_broadcaster
|
|
412
422
|
|
|
413
423
|
broadcaster = get_status_broadcaster()
|
|
414
|
-
await broadcaster.broadcast(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
"
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
424
|
+
await broadcaster.broadcast(
|
|
425
|
+
{
|
|
426
|
+
"type": f"{provider}_oauth_complete",
|
|
427
|
+
"data": {
|
|
428
|
+
"success": True,
|
|
429
|
+
**{k: v for k, v in user_info.items() if k != "success"},
|
|
430
|
+
},
|
|
431
|
+
}
|
|
432
|
+
)
|
|
421
433
|
await broadcaster.broadcast_credential_event(
|
|
422
434
|
"credential.oauth.connected",
|
|
423
435
|
provider=provider,
|
|
@@ -426,12 +438,15 @@ def make_oauth_callback_router(
|
|
|
426
438
|
|
|
427
439
|
if redirect_after:
|
|
428
440
|
from fastapi.responses import RedirectResponse
|
|
441
|
+
|
|
429
442
|
return RedirectResponse(url=redirect_after, status_code=302)
|
|
430
443
|
|
|
431
444
|
return HTMLResponse(
|
|
432
445
|
content=render_oauth_callback_html(
|
|
433
|
-
provider,
|
|
434
|
-
|
|
446
|
+
provider,
|
|
447
|
+
status="success",
|
|
448
|
+
message=f"Successfully connected as {email}!",
|
|
449
|
+
color_hex=color_hex,
|
|
435
450
|
),
|
|
436
451
|
status_code=200,
|
|
437
452
|
)
|
|
@@ -480,9 +495,9 @@ _HTML_TEMPLATE = """<!DOCTYPE html>
|
|
|
480
495
|
<script>
|
|
481
496
|
if (window.opener) {{
|
|
482
497
|
window.opener.postMessage({{
|
|
483
|
-
type:
|
|
498
|
+
type: {provider_js},
|
|
484
499
|
success: {success_js},
|
|
485
|
-
message:
|
|
500
|
+
message: {message_js},
|
|
486
501
|
}}, '*');
|
|
487
502
|
}}
|
|
488
503
|
setTimeout(function() {{ window.close(); }}, 2000);
|
|
@@ -491,8 +506,7 @@ _HTML_TEMPLATE = """<!DOCTYPE html>
|
|
|
491
506
|
</html>"""
|
|
492
507
|
|
|
493
508
|
_SUCCESS_ICON = (
|
|
494
|
-
"<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' "
|
|
495
|
-
"d='M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'/>"
|
|
509
|
+
"<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' " "d='M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'/>"
|
|
496
510
|
)
|
|
497
511
|
_ERROR_ICON = (
|
|
498
512
|
"<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' "
|
|
@@ -501,7 +515,11 @@ _ERROR_ICON = (
|
|
|
501
515
|
|
|
502
516
|
|
|
503
517
|
def render_oauth_callback_html(
|
|
504
|
-
provider: str,
|
|
518
|
+
provider: str,
|
|
519
|
+
*,
|
|
520
|
+
status: str,
|
|
521
|
+
message: str,
|
|
522
|
+
color_hex: str,
|
|
505
523
|
) -> str:
|
|
506
524
|
"""Render the small auto-closing callback page.
|
|
507
525
|
|
|
@@ -509,22 +527,60 @@ def render_oauth_callback_html(
|
|
|
509
527
|
``{provider}_oauth_callback`` message to ``window.opener`` (the
|
|
510
528
|
popup's parent) and auto-closes after 2 seconds.
|
|
511
529
|
"""
|
|
530
|
+
# ``message`` reaches the page via the OAuth provider's ``?error_description=``
|
|
531
|
+
# query string -- attacker-controllable. Escape for both contexts the
|
|
532
|
+
# template interpolates it into:
|
|
533
|
+
# * ``<p>{message}</p>`` → ``html.escape`` neutralises
|
|
534
|
+
# ``<script>`` / attribute breakouts.
|
|
535
|
+
# * ``postMessage({ message: '{message_js}' })`` → ``json.dumps`` wraps the
|
|
536
|
+
# string in correctly-escaped
|
|
537
|
+
# JS source (matching quotes,
|
|
538
|
+
# ``</script>`` neutralised,
|
|
539
|
+
# unicode literals).
|
|
540
|
+
# CodeQL recognises both as ``py/reflective-xss`` sanitizers.
|
|
541
|
+
# ``message`` reaches the page via the OAuth provider's
|
|
542
|
+
# ``?error_description=`` query string -- attacker-controllable.
|
|
543
|
+
# Two sinks need separate escaping:
|
|
544
|
+
#
|
|
545
|
+
# * ``<p>{message}</p>`` → ``html.escape`` neutralises ``<script>``,
|
|
546
|
+
# attribute breakouts, ampersand-entities.
|
|
547
|
+
# * ``postMessage({message: X})`` → ``json.dumps`` + ``_js_safe`` post-escape.
|
|
548
|
+
# ``json.dumps`` alone does NOT escape ``<``
|
|
549
|
+
# or ``</``: an attacker-controlled message
|
|
550
|
+
# containing ``</script>`` would close the
|
|
551
|
+
# surrounding ``<script>`` tag because the
|
|
552
|
+
# browser tokenizer scans for that pattern
|
|
553
|
+
# before parsing the JS string. Per OWASP
|
|
554
|
+
# guidance we ``\u``-escape ``<`` ``>`` ``&``
|
|
555
|
+
# so the JSON literal is also safe inside
|
|
556
|
+
# a ``<script>`` block.
|
|
512
557
|
is_success = status == "success"
|
|
513
|
-
title = (
|
|
514
|
-
f"{provider.capitalize()} Connected" if is_success
|
|
515
|
-
else "Connection Failed"
|
|
516
|
-
)
|
|
558
|
+
title = f"{provider.capitalize()} Connected" if is_success else "Connection Failed"
|
|
517
559
|
return _HTML_TEMPLATE.format(
|
|
518
|
-
title=title,
|
|
519
|
-
message=message,
|
|
560
|
+
title=html.escape(title),
|
|
561
|
+
message=html.escape(message),
|
|
520
562
|
color_hex=color_hex,
|
|
521
563
|
icon_path=_SUCCESS_ICON if is_success else _ERROR_ICON,
|
|
522
|
-
|
|
564
|
+
provider_js=_js_safe(json.dumps(f"{provider}_oauth_callback")),
|
|
523
565
|
success_js=str(is_success).lower(),
|
|
524
|
-
message_js=
|
|
566
|
+
message_js=_js_safe(json.dumps(message)),
|
|
525
567
|
)
|
|
526
568
|
|
|
527
569
|
|
|
570
|
+
def _js_safe(json_literal: str) -> str:
|
|
571
|
+
"""Post-escape a ``json.dumps`` output for safe inlining inside ``<script>``.
|
|
572
|
+
|
|
573
|
+
``json.dumps`` follows the JSON spec, which doesn't require ``<`` / ``>`` /
|
|
574
|
+
``&`` to be escaped -- but an attacker-controlled string containing
|
|
575
|
+
``</script>`` inside a JSON literal would still close the surrounding
|
|
576
|
+
``<script>`` block (the browser tokenizer hunts for ``</script>`` regardless
|
|
577
|
+
of JS string-literal state). The OWASP Cheat Sheet recommends ``\\u``-encoding
|
|
578
|
+
those three characters before emission. CodeQL recognises this pattern as a
|
|
579
|
+
``py/reflective-xss`` sanitizer.
|
|
580
|
+
"""
|
|
581
|
+
return json_literal.replace("<", "\\u003c").replace(">", "\\u003e").replace("&", "\\u0026")
|
|
582
|
+
|
|
583
|
+
|
|
528
584
|
__all__ = [
|
|
529
585
|
"OAuthLike",
|
|
530
586
|
"OAuthFactory",
|
|
@@ -35,10 +35,7 @@ class BaseTriggerParams(BaseModel):
|
|
|
35
35
|
|
|
36
36
|
event_type_filter: str = Field(
|
|
37
37
|
default="all",
|
|
38
|
-
description=(
|
|
39
|
-
"Event type to match. 'all' for every event, exact name, "
|
|
40
|
-
"or wildcard prefix (e.g. 'foo.*')."
|
|
41
|
-
),
|
|
38
|
+
description=("Event type to match. 'all' for every event, exact name, " "or wildcard prefix (e.g. 'foo.*')."),
|
|
42
39
|
)
|
|
43
40
|
|
|
44
41
|
model_config = ConfigDict(extra="ignore")
|
|
@@ -66,12 +63,7 @@ class WebhookTriggerNode(TriggerNode):
|
|
|
66
63
|
|
|
67
64
|
def build_filter(self, params: BaseModel) -> Callable[[Any], bool]:
|
|
68
65
|
type_filter = (getattr(params, "event_type_filter", "") or "all").strip()
|
|
69
|
-
if (
|
|
70
|
-
type_filter
|
|
71
|
-
and type_filter != "all"
|
|
72
|
-
and self.event_type_prefix
|
|
73
|
-
and not type_filter.startswith(self.event_type_prefix)
|
|
74
|
-
):
|
|
66
|
+
if type_filter and type_filter != "all" and self.event_type_prefix and not type_filter.startswith(self.event_type_prefix):
|
|
75
67
|
type_filter = self.event_type_prefix + type_filter
|
|
76
68
|
extras = self._extra_filter(params)
|
|
77
69
|
|
|
@@ -113,6 +105,4 @@ class WebhookTriggerNode(TriggerNode):
|
|
|
113
105
|
|
|
114
106
|
@Operation("wait")
|
|
115
107
|
async def wait(self, ctx: NodeContext, params: BaseModel):
|
|
116
|
-
raise NotImplementedError(
|
|
117
|
-
"Event triggers return via TriggerNode.execute, not the op body"
|
|
118
|
-
)
|
|
108
|
+
raise NotImplementedError("Event triggers return via TriggerNode.execute, not the op body")
|
|
@@ -26,7 +26,7 @@ class HmacVerifier(WebhookVerifier):
|
|
|
26
26
|
raise ValueError(f"{cls.header_name} header missing")
|
|
27
27
|
if cls.signature_prefix and not sig.startswith(cls.signature_prefix):
|
|
28
28
|
raise ValueError(f"{cls.header_name} missing prefix {cls.signature_prefix!r}")
|
|
29
|
-
provided = sig[len(cls.signature_prefix):]
|
|
29
|
+
provided = sig[len(cls.signature_prefix) :]
|
|
30
30
|
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
|
|
31
31
|
if not hmac.compare_digest(expected, provided):
|
|
32
32
|
raise ValueError(f"{cls.header_name} mismatch")
|
|
@@ -29,14 +29,12 @@ class StandardWebhooksVerifier(WebhookVerifier):
|
|
|
29
29
|
raise ValueError("Standard Webhooks headers missing")
|
|
30
30
|
|
|
31
31
|
if secret.startswith("whsec_"):
|
|
32
|
-
key = base64.b64decode(secret[len("whsec_"):])
|
|
32
|
+
key = base64.b64decode(secret[len("whsec_") :])
|
|
33
33
|
else:
|
|
34
34
|
key = secret.encode()
|
|
35
35
|
|
|
36
36
|
signed = f"{msg_id}.{timestamp}.".encode() + body
|
|
37
|
-
expected = base64.b64encode(
|
|
38
|
-
hmac.new(key, signed, hashlib.sha256).digest()
|
|
39
|
-
).decode()
|
|
37
|
+
expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
|
|
40
38
|
|
|
41
39
|
candidates: list[str] = []
|
|
42
40
|
for part in sig_header.split():
|
|
@@ -37,9 +37,7 @@ def register_webhook_source(source: "WebhookSource") -> None:
|
|
|
37
37
|
"""Idempotent: same instance for the same path is a no-op; conflicts raise."""
|
|
38
38
|
existing = WEBHOOK_SOURCES.get(source.path)
|
|
39
39
|
if existing is not None and existing is not source:
|
|
40
|
-
raise ValueError(
|
|
41
|
-
f"Webhook path {source.path!r} already registered to {type(existing).__name__}"
|
|
42
|
-
)
|
|
40
|
+
raise ValueError(f"Webhook path {source.path!r} already registered to {type(existing).__name__}")
|
|
43
41
|
WEBHOOK_SOURCES[source.path] = source
|
|
44
42
|
|
|
45
43
|
|
|
@@ -101,5 +99,6 @@ class WebhookSource(PushEventSource):
|
|
|
101
99
|
await self.receive(event)
|
|
102
100
|
|
|
103
101
|
from services import event_waiter
|
|
102
|
+
|
|
104
103
|
event_waiter.dispatch(self.type, event)
|
|
105
104
|
return event
|
|
@@ -1,23 +1,35 @@
|
|
|
1
|
-
"""Example workflow loader - reuses existing database.save_workflow()
|
|
1
|
+
"""Example workflow loader - reuses existing database.save_workflow().
|
|
2
|
+
|
|
3
|
+
Most of the import business logic (validation, remap, requirements
|
|
4
|
+
extraction, credential cross-check, name conflict) lives in
|
|
5
|
+
``services.workflow_import`` and is shared with the WS ``import_workflow``
|
|
6
|
+
handler. This module is the first-launch-only convenience wrapper.
|
|
7
|
+
"""
|
|
8
|
+
|
|
2
9
|
import json
|
|
3
10
|
import logging
|
|
4
|
-
from pathlib import Path
|
|
5
11
|
from typing import List, Dict, Any
|
|
6
12
|
|
|
13
|
+
from core.paths import example_workflows_dir
|
|
14
|
+
|
|
7
15
|
logger = logging.getLogger(__name__)
|
|
8
16
|
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
|
|
18
|
+
# Module-level alias so callers (and the workflow-validator contract
|
|
19
|
+
# test in ``tests/test_workflow_validator.py``) can ``glob(EXAMPLES_DIR /
|
|
20
|
+
# "*.json")`` without re-resolving the path on every call.
|
|
21
|
+
EXAMPLES_DIR = example_workflows_dir()
|
|
11
22
|
|
|
12
23
|
|
|
13
24
|
def get_example_workflows() -> List[Dict[str, Any]]:
|
|
14
25
|
"""Load all example workflow JSON files from disk."""
|
|
15
26
|
examples = []
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
examples_dir = example_workflows_dir()
|
|
28
|
+
if not examples_dir.exists():
|
|
29
|
+
logger.warning(f"Examples directory not found: {examples_dir}")
|
|
18
30
|
return examples
|
|
19
31
|
|
|
20
|
-
for file in sorted(
|
|
32
|
+
for file in sorted(examples_dir.glob("*.json")):
|
|
21
33
|
try:
|
|
22
34
|
with open(file, encoding="utf-8") as f:
|
|
23
35
|
workflow = json.load(f)
|
|
@@ -31,10 +43,19 @@ def get_example_workflows() -> List[Dict[str, Any]]:
|
|
|
31
43
|
|
|
32
44
|
|
|
33
45
|
async def import_examples_for_user(database) -> int:
|
|
34
|
-
"""Import all examples using
|
|
46
|
+
"""Import all examples using shared ``services.workflow_import`` helpers.
|
|
47
|
+
|
|
48
|
+
Dry-runs the workflow validator on each example, remaps node ids
|
|
49
|
+
(mandatory: today's two example workflows share 6 node ids), then
|
|
50
|
+
saves the workflow + per-node parameters. Errors skip the example
|
|
51
|
+
(broken examples shipped on disk are bugs); warnings are logged but
|
|
52
|
+
allowed through — first-launch missing credentials are expected.
|
|
35
53
|
|
|
36
|
-
Returns count of workflows imported.
|
|
54
|
+
Returns the count of workflows imported.
|
|
37
55
|
"""
|
|
56
|
+
from services.workflow_import import remap_node_ids
|
|
57
|
+
from services.workflow_validator import validate_workflow
|
|
58
|
+
|
|
38
59
|
examples = get_example_workflows()
|
|
39
60
|
imported = 0
|
|
40
61
|
|
|
@@ -42,23 +63,60 @@ async def import_examples_for_user(database) -> int:
|
|
|
42
63
|
# Use ID from JSON, prefixed with 'example_' for clarity
|
|
43
64
|
workflow_id = f"example_{example.get('id', 'unknown')}"
|
|
44
65
|
|
|
66
|
+
# Rewrite every node id so two examples that share ids don't
|
|
67
|
+
# overwrite each other's parameters in the node_parameters table.
|
|
68
|
+
nodes, edges, node_parameters = remap_node_ids(
|
|
69
|
+
example.get("nodes", []),
|
|
70
|
+
example.get("edges", []),
|
|
71
|
+
example.get("nodeParameters", {}),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Pre-save validation. parameters_by_id is the freshly-remapped
|
|
75
|
+
# map so INVALID_PARAM check sees the configured defaults instead
|
|
76
|
+
# of empty dicts.
|
|
77
|
+
try:
|
|
78
|
+
report = await validate_workflow(
|
|
79
|
+
nodes=nodes,
|
|
80
|
+
edges=edges,
|
|
81
|
+
parameters_by_id=node_parameters,
|
|
82
|
+
)
|
|
83
|
+
except Exception as exc:
|
|
84
|
+
logger.warning(
|
|
85
|
+
"Skipping example %r: validator raised %s",
|
|
86
|
+
example.get("name"),
|
|
87
|
+
exc,
|
|
88
|
+
)
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
if report["errors"]:
|
|
92
|
+
logger.warning(
|
|
93
|
+
"Skipping example %r: %d validation errors %s",
|
|
94
|
+
example.get("name"),
|
|
95
|
+
len(report["errors"]),
|
|
96
|
+
[iss.get("code") for iss in report["errors"]],
|
|
97
|
+
)
|
|
98
|
+
continue
|
|
99
|
+
if report["warnings"]:
|
|
100
|
+
# Expected on first launch — credentials not yet configured.
|
|
101
|
+
logger.info(
|
|
102
|
+
"Example %r has %d warnings (likely first-launch credential gaps)",
|
|
103
|
+
example.get("name"),
|
|
104
|
+
len(report["warnings"]),
|
|
105
|
+
)
|
|
106
|
+
|
|
45
107
|
# Reuse existing save_workflow method
|
|
46
108
|
success = await database.save_workflow(
|
|
47
109
|
workflow_id=workflow_id,
|
|
48
110
|
name=example.get("name", "Example Workflow"),
|
|
49
111
|
description=example.get("description"),
|
|
50
|
-
data={
|
|
51
|
-
"nodes": example.get("nodes", []),
|
|
52
|
-
"edges": example.get("edges", [])
|
|
53
|
-
}
|
|
112
|
+
data={"nodes": nodes, "edges": edges},
|
|
54
113
|
)
|
|
55
114
|
|
|
56
115
|
if success:
|
|
57
116
|
imported += 1
|
|
58
117
|
logger.info(f"Imported example: {example.get('name')}")
|
|
59
118
|
|
|
60
|
-
# Save embedded nodeParameters
|
|
61
|
-
node_parameters = example.get("nodeParameters", {})
|
|
119
|
+
# Save embedded nodeParameters under the remapped node ids.
|
|
62
120
|
for node_id, params in node_parameters.items():
|
|
63
121
|
if params:
|
|
64
122
|
try:
|