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
|
@@ -98,11 +98,7 @@ class OAuthStateStore:
|
|
|
98
98
|
def cleanup_expired(self) -> int:
|
|
99
99
|
"""Remove states older than ``ttl_seconds``. Returns count removed."""
|
|
100
100
|
now = time.time()
|
|
101
|
-
expired = [
|
|
102
|
-
state
|
|
103
|
-
for state, record in self._states.items()
|
|
104
|
-
if now - record.get("created_at", 0) > self.ttl_seconds
|
|
105
|
-
]
|
|
101
|
+
expired = [state for state, record in self._states.items() if now - record.get("created_at", 0) > self.ttl_seconds]
|
|
106
102
|
for state in expired:
|
|
107
103
|
self._states.pop(state, None)
|
|
108
104
|
if expired:
|
|
@@ -180,9 +176,7 @@ class OAuth2PKCEClient(ABC):
|
|
|
180
176
|
scopes: Optional[List[str]] = None,
|
|
181
177
|
) -> None:
|
|
182
178
|
if not self.provider:
|
|
183
|
-
raise ValueError(
|
|
184
|
-
f"{type(self).__name__} must set the ``provider`` ClassVar"
|
|
185
|
-
)
|
|
179
|
+
raise ValueError(f"{type(self).__name__} must set the ``provider`` ClassVar")
|
|
186
180
|
self.client_id = client_id
|
|
187
181
|
self.client_secret = client_secret
|
|
188
182
|
self.redirect_uri = redirect_uri
|
|
@@ -207,11 +201,14 @@ class OAuth2PKCEClient(ABC):
|
|
|
207
201
|
code_verifier = _generate_code_verifier()
|
|
208
202
|
code_challenge = _generate_code_challenge(code_verifier)
|
|
209
203
|
|
|
210
|
-
self.state_store.put(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
204
|
+
self.state_store.put(
|
|
205
|
+
state,
|
|
206
|
+
{
|
|
207
|
+
"code_verifier": code_verifier,
|
|
208
|
+
"redirect_uri": self.redirect_uri,
|
|
209
|
+
"data": state_data or {},
|
|
210
|
+
},
|
|
211
|
+
)
|
|
215
212
|
|
|
216
213
|
params = {
|
|
217
214
|
"response_type": "code",
|
|
@@ -240,9 +237,7 @@ class OAuth2PKCEClient(ABC):
|
|
|
240
237
|
in the header. Public clients put ``client_id`` in the body.
|
|
241
238
|
"""
|
|
242
239
|
if self.client_secret:
|
|
243
|
-
credentials = base64.b64encode(
|
|
244
|
-
f"{self.client_id}:{self.client_secret}".encode()
|
|
245
|
-
).decode()
|
|
240
|
+
credentials = base64.b64encode(f"{self.client_id}:{self.client_secret}".encode()).decode()
|
|
246
241
|
return {}, {"Authorization": f"Basic {credentials}"}
|
|
247
242
|
return {"client_id": self.client_id}, {}
|
|
248
243
|
|
|
@@ -266,7 +261,9 @@ class OAuth2PKCEClient(ABC):
|
|
|
266
261
|
try:
|
|
267
262
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
268
263
|
response = await client.post(
|
|
269
|
-
self.token_endpoint,
|
|
264
|
+
self.token_endpoint,
|
|
265
|
+
data=body,
|
|
266
|
+
headers=headers,
|
|
270
267
|
)
|
|
271
268
|
except httpx.HTTPError as exc:
|
|
272
269
|
logger.error(f"[{self.provider}] HTTP error during token exchange: {exc}")
|
|
@@ -276,7 +273,8 @@ class OAuth2PKCEClient(ABC):
|
|
|
276
273
|
error_data = response.json() if response.text else {}
|
|
277
274
|
logger.error(
|
|
278
275
|
f"[{self.provider}] Token exchange failed",
|
|
279
|
-
status=response.status_code,
|
|
276
|
+
status=response.status_code,
|
|
277
|
+
error=error_data,
|
|
280
278
|
)
|
|
281
279
|
return {
|
|
282
280
|
"success": False,
|
|
@@ -306,7 +304,9 @@ class OAuth2PKCEClient(ABC):
|
|
|
306
304
|
try:
|
|
307
305
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
308
306
|
response = await client.post(
|
|
309
|
-
self.token_endpoint,
|
|
307
|
+
self.token_endpoint,
|
|
308
|
+
data=body,
|
|
309
|
+
headers=headers,
|
|
310
310
|
)
|
|
311
311
|
except httpx.HTTPError as exc:
|
|
312
312
|
logger.error(f"[{self.provider}] HTTP error during token refresh: {exc}")
|
|
@@ -329,7 +329,9 @@ class OAuth2PKCEClient(ABC):
|
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
async def revoke_token(
|
|
332
|
-
self,
|
|
332
|
+
self,
|
|
333
|
+
token: str,
|
|
334
|
+
token_type: str = "access_token",
|
|
333
335
|
) -> Dict[str, Any]:
|
|
334
336
|
"""Revoke an access or refresh token (best-effort).
|
|
335
337
|
|
|
@@ -348,7 +350,9 @@ class OAuth2PKCEClient(ABC):
|
|
|
348
350
|
try:
|
|
349
351
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
350
352
|
response = await client.post(
|
|
351
|
-
self.revocation_endpoint,
|
|
353
|
+
self.revocation_endpoint,
|
|
354
|
+
data=body,
|
|
355
|
+
headers=headers,
|
|
352
356
|
)
|
|
353
357
|
except httpx.HTTPError as exc:
|
|
354
358
|
logger.error(f"[{self.provider}] HTTP error during token revoke: {exc}")
|
|
@@ -32,7 +32,7 @@ class OperationSpec:
|
|
|
32
32
|
name: str
|
|
33
33
|
method: Callable[..., Awaitable[Any]]
|
|
34
34
|
routing: Optional[Routing] = None
|
|
35
|
-
cost: Optional[Dict[str, Any]] = None
|
|
35
|
+
cost: Optional[Dict[str, Any]] = None # {"usage": "gmail_send", "count": 1}
|
|
36
36
|
annotations: Dict[str, Any] = field(default_factory=dict)
|
|
37
37
|
|
|
38
38
|
|
|
@@ -46,7 +46,7 @@ Out of scope (commit-stage)
|
|
|
46
46
|
from __future__ import annotations
|
|
47
47
|
|
|
48
48
|
import asyncio
|
|
49
|
-
from typing import Any, Callable, ClassVar, Dict,
|
|
49
|
+
from typing import Any, Callable, ClassVar, Dict, Set, Tuple
|
|
50
50
|
|
|
51
51
|
from core.logging import get_logger
|
|
52
52
|
from services.plugin.trigger import TriggerNode
|
|
@@ -97,9 +97,7 @@ class PollingTriggerNode(TriggerNode, abstract=True):
|
|
|
97
97
|
"""
|
|
98
98
|
raise NotImplementedError
|
|
99
99
|
|
|
100
|
-
async def fetch_ids(
|
|
101
|
-
self, service: Any, params: Dict[str, Any]
|
|
102
|
-
) -> Set[str]:
|
|
100
|
+
async def fetch_ids(self, service: Any, params: Dict[str, Any]) -> Set[str]:
|
|
103
101
|
"""Return the current set of visible IDs for one poll cycle.
|
|
104
102
|
|
|
105
103
|
Called once for the baseline pass at loop start, then once per
|
|
@@ -108,9 +106,7 @@ class PollingTriggerNode(TriggerNode, abstract=True):
|
|
|
108
106
|
"""
|
|
109
107
|
raise NotImplementedError
|
|
110
108
|
|
|
111
|
-
async def fetch_detail(
|
|
112
|
-
self, service: Any, msg_id: str, params: Dict[str, Any]
|
|
113
|
-
) -> Dict[str, Any]:
|
|
109
|
+
async def fetch_detail(self, service: Any, msg_id: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
114
110
|
"""Fetch the full event payload for one ID.
|
|
115
111
|
|
|
116
112
|
Called once per new ID per cycle. The returned dict goes
|
|
@@ -119,9 +115,7 @@ class PollingTriggerNode(TriggerNode, abstract=True):
|
|
|
119
115
|
"""
|
|
120
116
|
raise NotImplementedError
|
|
121
117
|
|
|
122
|
-
async def post_emit(
|
|
123
|
-
self, service: Any, msg_id: str, params: Dict[str, Any]
|
|
124
|
-
) -> None:
|
|
118
|
+
async def post_emit(self, service: Any, msg_id: str, params: Dict[str, Any]) -> None:
|
|
125
119
|
"""Optional side effect AFTER the payload was enqueued.
|
|
126
120
|
|
|
127
121
|
Default no-op. Override for "mark-as-read" or similar
|
|
@@ -166,22 +160,131 @@ class PollingTriggerNode(TriggerNode, abstract=True):
|
|
|
166
160
|
value = self.default_poll_interval
|
|
167
161
|
return max(lo, min(hi, value))
|
|
168
162
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
163
|
+
# ---- Wave 12 C2: per-cycle Temporal activity --------------------------
|
|
164
|
+
#
|
|
165
|
+
# The asyncio coroutine version above feeds the legacy
|
|
166
|
+
# ``setup_polling_trigger`` collector/processor task pair (dies on
|
|
167
|
+
# FastAPI restart). The Temporal-durable version below — yielded by
|
|
168
|
+
# :meth:`as_poll_activity` — does ONE cycle and returns events,
|
|
169
|
+
# called per ``workflow.sleep`` tick in
|
|
170
|
+
# :class:`services.temporal.polling_trigger_workflow.PollingTriggerWorkflow`.
|
|
171
|
+
# The seen-id baseline survives via workflow state (carried across
|
|
172
|
+
# ``continueAsNew``) rather than living inside the asyncio task.
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def as_poll_activity(cls):
|
|
176
|
+
"""Return a ``@activity.defn`` wrapping one poll cycle.
|
|
177
|
+
|
|
178
|
+
Stable activity name: ``poll.{type}.v{version}``.
|
|
179
|
+
|
|
180
|
+
Activity payload (in)::
|
|
181
|
+
|
|
182
|
+
{
|
|
183
|
+
"node_id": str,
|
|
184
|
+
"params": Dict[str, Any],
|
|
185
|
+
"seen_ids": List[str], # provider-side IDs from prior cycle
|
|
186
|
+
"baseline_only": bool, # True on the first call after start
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
Activity result (out)::
|
|
190
|
+
|
|
191
|
+
{
|
|
192
|
+
"events": List[Dict], # NEW event payloads this cycle
|
|
193
|
+
"seen_ids": List[str], # union of prior seen + current
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
Determinism: every operation runs inside the activity (network
|
|
197
|
+
I/O, mutable state) — the workflow body only sees the serialised
|
|
198
|
+
result so replay stays deterministic. Per-cycle errors raise
|
|
199
|
+
out of the activity; the workflow's ``RetryPolicy`` / try-except
|
|
200
|
+
owns retry semantics.
|
|
201
|
+
"""
|
|
202
|
+
from temporalio import activity
|
|
203
|
+
|
|
204
|
+
activity_name = f"poll.{cls.type}.v{cls.version}"
|
|
205
|
+
|
|
206
|
+
@activity.defn(name=activity_name)
|
|
207
|
+
async def _poll_cycle_activity(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
208
|
+
node_id = payload.get("node_id", "")
|
|
209
|
+
params = payload.get("params", {}) or {}
|
|
210
|
+
prior_seen: Set[str] = set(payload.get("seen_ids") or [])
|
|
211
|
+
baseline_only = bool(payload.get("baseline_only"))
|
|
212
|
+
|
|
213
|
+
instance = cls()
|
|
214
|
+
try:
|
|
215
|
+
service = await instance.setup_service(params)
|
|
216
|
+
except Exception as exc: # noqa: BLE001
|
|
217
|
+
logger.error(
|
|
218
|
+
"Polling activity setup failed",
|
|
219
|
+
node_id=node_id,
|
|
220
|
+
node_type=cls.type,
|
|
221
|
+
error=str(exc),
|
|
222
|
+
)
|
|
223
|
+
raise
|
|
224
|
+
|
|
225
|
+
current = await instance.fetch_ids(service, params)
|
|
226
|
+
|
|
227
|
+
if baseline_only:
|
|
228
|
+
# First call after workflow start: establish the seen
|
|
229
|
+
# baseline without emitting anything. Matches the
|
|
230
|
+
# pre-Temporal collector's baseline pass.
|
|
231
|
+
return {"events": [], "seen_ids": list(current)}
|
|
232
|
+
|
|
233
|
+
new_ids = current - prior_seen
|
|
234
|
+
events: list[Dict[str, Any]] = []
|
|
235
|
+
for msg_id in new_ids:
|
|
236
|
+
try:
|
|
237
|
+
detail = await instance.fetch_detail(service, msg_id, params)
|
|
238
|
+
except Exception as exc: # noqa: BLE001
|
|
239
|
+
logger.warning(
|
|
240
|
+
"Polling activity fetch_detail failed; skipping id",
|
|
241
|
+
node_id=node_id,
|
|
242
|
+
node_type=cls.type,
|
|
243
|
+
msg_id=msg_id,
|
|
244
|
+
error=str(exc),
|
|
245
|
+
)
|
|
246
|
+
continue
|
|
247
|
+
# The workflow needs a stable event.id for cross-cycle
|
|
248
|
+
# dedup. Fall back to the provider id when fetch_detail
|
|
249
|
+
# doesn't supply one.
|
|
250
|
+
if "id" not in detail:
|
|
251
|
+
detail["id"] = msg_id
|
|
252
|
+
events.append(detail)
|
|
253
|
+
# post_emit side effect (e.g. mark-as-read). Failures
|
|
254
|
+
# MUST NOT block the next emit or kill the cycle —
|
|
255
|
+
# mirror the legacy coroutine semantics.
|
|
256
|
+
try:
|
|
257
|
+
await instance.post_emit(service, msg_id, params)
|
|
258
|
+
except Exception:
|
|
259
|
+
pass
|
|
260
|
+
|
|
261
|
+
# OOM fix: bound ``seen_ids`` to what the provider currently
|
|
262
|
+
# reports as visible — old IDs that have dropped off the
|
|
263
|
+
# provider's window (archived / deleted / aged out) fall
|
|
264
|
+
# out of seen too. Pre-fix this was ``prior_seen | current``
|
|
265
|
+
# which grew forever (every poll appended new IDs; nothing
|
|
266
|
+
# ever got evicted). At Gmail's ~100 msgs/day cadence the
|
|
267
|
+
# set hit ~36K entries in a year — ~1.4MB just for IDs,
|
|
268
|
+
# serialised through Temporal payload on every cycle. Now
|
|
269
|
+
# bounded by the provider's natural window size.
|
|
270
|
+
return {"events": events, "seen_ids": list(current)}
|
|
271
|
+
|
|
272
|
+
return _poll_cycle_activity
|
|
273
|
+
|
|
274
|
+
def _build_poll_coroutine(self, node_id: str, params: Dict[str, Any]) -> Callable[[asyncio.Queue, Callable[[], bool]], Any]:
|
|
172
275
|
"""Return the bound poll coroutine the deployment manager
|
|
173
276
|
consumes. Closes over ``self``, ``node_id``, and ``params``.
|
|
174
277
|
"""
|
|
175
278
|
|
|
176
|
-
async def poll(
|
|
177
|
-
queue: asyncio.Queue, is_running_fn: Callable[[], bool]
|
|
178
|
-
) -> None:
|
|
279
|
+
async def poll(queue: asyncio.Queue, is_running_fn: Callable[[], bool]) -> None:
|
|
179
280
|
try:
|
|
180
281
|
service = await self.setup_service(params)
|
|
181
282
|
except Exception as exc: # noqa: BLE001 -- single setup failure
|
|
182
283
|
logger.error(
|
|
183
284
|
"Polling trigger setup failed",
|
|
184
|
-
node_id=node_id,
|
|
285
|
+
node_id=node_id,
|
|
286
|
+
node_type=self.type,
|
|
287
|
+
error=str(exc),
|
|
185
288
|
)
|
|
186
289
|
return
|
|
187
290
|
|
|
@@ -197,12 +300,16 @@ class PollingTriggerNode(TriggerNode, abstract=True):
|
|
|
197
300
|
seen = await self.fetch_ids(service, params)
|
|
198
301
|
logger.info(
|
|
199
302
|
"Polling trigger baseline established",
|
|
200
|
-
node_id=node_id,
|
|
303
|
+
node_id=node_id,
|
|
304
|
+
node_type=self.type,
|
|
305
|
+
seen=len(seen),
|
|
201
306
|
)
|
|
202
307
|
except Exception as exc: # noqa: BLE001
|
|
203
308
|
logger.warning(
|
|
204
309
|
"Polling trigger baseline failed; treating all as new",
|
|
205
|
-
node_id=node_id,
|
|
310
|
+
node_id=node_id,
|
|
311
|
+
node_type=self.type,
|
|
312
|
+
error=str(exc),
|
|
206
313
|
)
|
|
207
314
|
|
|
208
315
|
cycle = 0
|
|
@@ -215,18 +322,23 @@ class PollingTriggerNode(TriggerNode, abstract=True):
|
|
|
215
322
|
current = await self.fetch_ids(service, params)
|
|
216
323
|
new_ids = current - seen
|
|
217
324
|
if not new_ids:
|
|
325
|
+
# OOM fix: even when nothing new arrives we
|
|
326
|
+
# still rebase ``seen`` to ``current`` so old
|
|
327
|
+
# IDs that dropped off the provider's window
|
|
328
|
+
# don't linger forever.
|
|
329
|
+
seen = set(current)
|
|
218
330
|
continue
|
|
219
331
|
logger.debug(
|
|
220
332
|
"Polling trigger cycle",
|
|
221
|
-
node_id=node_id,
|
|
222
|
-
|
|
333
|
+
node_id=node_id,
|
|
334
|
+
node_type=self.type,
|
|
335
|
+
cycle=cycle,
|
|
336
|
+
current=len(current),
|
|
337
|
+
seen=len(seen),
|
|
223
338
|
new=len(new_ids),
|
|
224
339
|
)
|
|
225
340
|
for msg_id in new_ids:
|
|
226
|
-
|
|
227
|
-
payload = await self.fetch_detail(
|
|
228
|
-
service, msg_id, params
|
|
229
|
-
)
|
|
341
|
+
payload = await self.fetch_detail(service, msg_id, params)
|
|
230
342
|
await queue.put(payload)
|
|
231
343
|
# post_emit failure must NOT block the next emit
|
|
232
344
|
# nor kill the loop; mirrors pre-migration
|
|
@@ -235,12 +347,25 @@ class PollingTriggerNode(TriggerNode, abstract=True):
|
|
|
235
347
|
await self.post_emit(service, msg_id, params)
|
|
236
348
|
except Exception:
|
|
237
349
|
pass
|
|
350
|
+
# OOM fix: rebase ``seen`` to the provider's current
|
|
351
|
+
# window. Pre-fix this added every new ID to ``seen``
|
|
352
|
+
# forever (``seen.add(msg_id)`` inside the loop, no
|
|
353
|
+
# eviction). Long-running pollers (Gmail with the
|
|
354
|
+
# default 60s interval) accumulated tens of thousands
|
|
355
|
+
# of entries over weeks/months. Now bounded by the
|
|
356
|
+
# provider's natural window size — items that have
|
|
357
|
+
# been emitted are still in ``current`` until they
|
|
358
|
+
# age/archive out, at which point dropping them is
|
|
359
|
+
# correct (the provider will never re-surface them
|
|
360
|
+
# under the same filter).
|
|
361
|
+
seen = set(current)
|
|
238
362
|
except asyncio.CancelledError:
|
|
239
363
|
raise
|
|
240
364
|
except Exception as exc: # noqa: BLE001 -- per-cycle isolation
|
|
241
365
|
logger.error(
|
|
242
366
|
"Polling trigger cycle error; retrying next interval",
|
|
243
|
-
node_id=node_id,
|
|
367
|
+
node_id=node_id,
|
|
368
|
+
node_type=self.type,
|
|
244
369
|
error=str(exc),
|
|
245
370
|
)
|
|
246
371
|
|
|
@@ -78,17 +78,65 @@ class IdempotentRegistry(Generic[K, V]):
|
|
|
78
78
|
self._on_register = on_register
|
|
79
79
|
|
|
80
80
|
def register(self, key: K, value: V) -> None:
|
|
81
|
-
"""Add ``key -> value``. Idempotent on
|
|
81
|
+
"""Add ``key -> value``. Idempotent on equality; raises on conflict.
|
|
82
|
+
|
|
83
|
+
Three equivalence checks (any match = idempotent re-register):
|
|
84
|
+
|
|
85
|
+
1. ``existing == value`` — covers strings / ints / dataclasses /
|
|
86
|
+
dicts (content-equal even on reload) and singleton instances
|
|
87
|
+
cached across imports.
|
|
88
|
+
2. **Reload tolerance for callables**: same fully-qualified
|
|
89
|
+
name (``__module__`` + ``__qualname__``) means the same
|
|
90
|
+
source-level function reloaded under fresh identity.
|
|
91
|
+
``importlib.reload(module)`` constructs new function objects
|
|
92
|
+
that compare unequal under Python's identity-based
|
|
93
|
+
``function.__eq__``; without this branch the
|
|
94
|
+
self-containment reload tests would break for every plugin
|
|
95
|
+
that registers a wrapper closure.
|
|
96
|
+
3. **Reload tolerance for classes**: same ``__module__`` +
|
|
97
|
+
``__qualname__`` for class objects (same reason).
|
|
98
|
+
|
|
99
|
+
Genuinely conflicting registrations (different values, different
|
|
100
|
+
names) still raise ``ValueError`` at import time.
|
|
101
|
+
"""
|
|
82
102
|
existing = self._items.get(key)
|
|
83
|
-
if existing is not None and existing
|
|
103
|
+
if existing is not None and not self._values_equivalent(existing, value):
|
|
84
104
|
raise ValueError(
|
|
85
|
-
f"{self._name}: {key!r} is already registered by "
|
|
86
|
-
f"{_qual(existing)}; refusing to overwrite with {_qual(value)}"
|
|
105
|
+
f"{self._name}: {key!r} is already registered by " f"{_qual(existing)}; refusing to overwrite with {_qual(value)}"
|
|
87
106
|
)
|
|
88
107
|
self._items[key] = value
|
|
89
108
|
if self._on_register is not None:
|
|
90
109
|
self._on_register(key, value)
|
|
91
110
|
|
|
111
|
+
@staticmethod
|
|
112
|
+
def _values_equivalent(existing: object, new: object) -> bool:
|
|
113
|
+
"""True iff ``existing`` and ``new`` are the same registration
|
|
114
|
+
for idempotency purposes. See :meth:`register` docstring."""
|
|
115
|
+
# Content equality first (strings, dicts, dataclasses, singletons
|
|
116
|
+
# cached across imports). Catches the bulk of cases.
|
|
117
|
+
try:
|
|
118
|
+
if existing == new:
|
|
119
|
+
return True
|
|
120
|
+
except Exception: # noqa: BLE001 — exotic types with broken __eq__
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
# Reload tolerance: callable / class objects re-defined under a
|
|
124
|
+
# fresh module-level identity still count as the "same"
|
|
125
|
+
# registration when their fully-qualified name matches.
|
|
126
|
+
existing_module = getattr(existing, "__module__", None)
|
|
127
|
+
new_module = getattr(new, "__module__", None)
|
|
128
|
+
existing_qualname = getattr(existing, "__qualname__", None)
|
|
129
|
+
new_qualname = getattr(new, "__qualname__", None)
|
|
130
|
+
if (
|
|
131
|
+
existing_qualname is not None
|
|
132
|
+
and new_qualname is not None
|
|
133
|
+
and existing_module == new_module
|
|
134
|
+
and existing_qualname == new_qualname
|
|
135
|
+
):
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
return False
|
|
139
|
+
|
|
92
140
|
def get(self, key: K) -> Optional[V]:
|
|
93
141
|
return self._items.get(key)
|
|
94
142
|
|
|
@@ -61,6 +61,7 @@ class Routing(BaseModel):
|
|
|
61
61
|
# ---------------------------------------------------------------------------
|
|
62
62
|
# Template interpolation
|
|
63
63
|
|
|
64
|
+
|
|
64
65
|
def _resolve_template(value: Any, env: Dict[str, Any]) -> Any:
|
|
65
66
|
"""Resolve ``={{expr}}`` templates against ``env``. Non-string values
|
|
66
67
|
pass through. Supports dotted paths — ``params.maxResults``."""
|
|
@@ -94,6 +95,7 @@ def _resolve_dict(d: Dict[str, Any], env: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
94
95
|
# ---------------------------------------------------------------------------
|
|
95
96
|
# post_receive strategies
|
|
96
97
|
|
|
98
|
+
|
|
97
99
|
def _strategy_root_property(data: Any, action: PostReceiveAction) -> Any:
|
|
98
100
|
if not action.property or not isinstance(data, dict):
|
|
99
101
|
return data
|
|
@@ -114,9 +116,7 @@ def _strategy_limit(data: Any, action: PostReceiveAction) -> Any:
|
|
|
114
116
|
def _strategy_filter(data: Any, action: PostReceiveAction) -> Any:
|
|
115
117
|
if not isinstance(data, list) or not action.where:
|
|
116
118
|
return data
|
|
117
|
-
return [item for item in data if all(
|
|
118
|
-
isinstance(item, dict) and item.get(k) == v for k, v in action.where.items()
|
|
119
|
-
)]
|
|
119
|
+
return [item for item in data if all(isinstance(item, dict) and item.get(k) == v for k, v in action.where.items())]
|
|
120
120
|
|
|
121
121
|
|
|
122
122
|
def _strategy_set(data: Any, action: PostReceiveAction) -> Any:
|
|
@@ -125,8 +125,7 @@ def _strategy_set(data: Any, action: PostReceiveAction) -> Any:
|
|
|
125
125
|
if isinstance(data, dict):
|
|
126
126
|
return {**data, **action.set_fields}
|
|
127
127
|
if isinstance(data, list):
|
|
128
|
-
return [{**item, **action.set_fields} if isinstance(item, dict) else item
|
|
129
|
-
for item in data]
|
|
128
|
+
return [{**item, **action.set_fields} if isinstance(item, dict) else item for item in data]
|
|
130
129
|
return data
|
|
131
130
|
|
|
132
131
|
|
|
@@ -141,6 +140,7 @@ POST_RECEIVE_STRATEGIES = {
|
|
|
141
140
|
# ---------------------------------------------------------------------------
|
|
142
141
|
# Execution
|
|
143
142
|
|
|
143
|
+
|
|
144
144
|
async def execute_routing(
|
|
145
145
|
routing: Routing,
|
|
146
146
|
*,
|
|
@@ -164,10 +164,7 @@ async def execute_routing(
|
|
|
164
164
|
|
|
165
165
|
kwargs: Dict[str, Any] = {"headers": headers, "params": qs}
|
|
166
166
|
if req.body is not None:
|
|
167
|
-
body_resolved = (
|
|
168
|
-
_resolve_dict(req.body, env) if isinstance(req.body, dict) else
|
|
169
|
-
_resolve_template(req.body, env)
|
|
170
|
-
)
|
|
167
|
+
body_resolved = _resolve_dict(req.body, env) if isinstance(req.body, dict) else _resolve_template(req.body, env)
|
|
171
168
|
if req.body_encoding == "form":
|
|
172
169
|
kwargs["data"] = body_resolved
|
|
173
170
|
else:
|
|
@@ -20,20 +20,29 @@ class TaskQueue:
|
|
|
20
20
|
and provision a worker pool in ``services/temporal/worker.py``.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
DEFAULT = "machina-default"
|
|
24
|
-
REST_API = "rest-api"
|
|
25
|
-
AI_HEAVY = "ai-heavy"
|
|
26
|
-
CODE_EXEC = "code-exec"
|
|
27
|
-
TRIGGERS_POLL = "triggers-poll"
|
|
23
|
+
DEFAULT = "machina-default" # catch-all
|
|
24
|
+
REST_API = "rest-api" # lightweight HTTP calls (gmail, brave, twitter)
|
|
25
|
+
AI_HEAVY = "ai-heavy" # long-running LLM + agent loops
|
|
26
|
+
CODE_EXEC = "code-exec" # python/js/ts sandboxed execution
|
|
27
|
+
TRIGGERS_POLL = "triggers-poll" # polling triggers (long-lived)
|
|
28
28
|
TRIGGERS_EVENT = "triggers-event" # event-waiter triggers (long-lived)
|
|
29
|
-
ANDROID = "android"
|
|
30
|
-
BROWSER = "browser"
|
|
31
|
-
MESSAGING = "messaging"
|
|
29
|
+
ANDROID = "android" # ADB / relay ops
|
|
30
|
+
BROWSER = "browser" # agent-browser, Playwright
|
|
31
|
+
MESSAGING = "messaging" # whatsapp / telegram / twitter send
|
|
32
32
|
|
|
33
|
-
ALL = frozenset(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
ALL = frozenset(
|
|
34
|
+
{
|
|
35
|
+
DEFAULT,
|
|
36
|
+
REST_API,
|
|
37
|
+
AI_HEAVY,
|
|
38
|
+
CODE_EXEC,
|
|
39
|
+
TRIGGERS_POLL,
|
|
40
|
+
TRIGGERS_EVENT,
|
|
41
|
+
ANDROID,
|
|
42
|
+
BROWSER,
|
|
43
|
+
MESSAGING,
|
|
44
|
+
}
|
|
45
|
+
)
|
|
37
46
|
|
|
38
47
|
|
|
39
48
|
@dataclass(frozen=True)
|
|
@@ -47,11 +56,20 @@ class RetryPolicy:
|
|
|
47
56
|
backoff_coefficient: float = 2.0
|
|
48
57
|
maximum_interval: timedelta = timedelta(seconds=60)
|
|
49
58
|
maximum_attempts: int = 3
|
|
50
|
-
non_retryable_error_types: Sequence[str] = field(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
non_retryable_error_types: Sequence[str] = field(
|
|
60
|
+
default_factory=lambda: (
|
|
61
|
+
"ValidationError",
|
|
62
|
+
"PermissionDeniedError",
|
|
63
|
+
"InvalidParametersError",
|
|
64
|
+
# Wave 12 A2: NodeUserError is the canonical "user-correctable
|
|
65
|
+
# failure" exception (missing input, missing credential, API
|
|
66
|
+
# rejected the payload). Retrying it just burns attempts since
|
|
67
|
+
# the underlying input won't fix itself. Plugins that raise
|
|
68
|
+
# NodeUserError fail fast; the framework surfaces the message
|
|
69
|
+
# to the user / agent loop without traceback noise.
|
|
70
|
+
"NodeUserError",
|
|
71
|
+
)
|
|
72
|
+
)
|
|
55
73
|
|
|
56
74
|
def to_temporal(self):
|
|
57
75
|
"""Lazy import — only called inside the Temporal worker."""
|
|
@@ -74,5 +92,5 @@ DEFAULT_RETRY = RetryPolicy()
|
|
|
74
92
|
# Kind-specific defaults — picked up when a subclass doesn't override.
|
|
75
93
|
ACTION_START_TO_CLOSE = timedelta(minutes=10)
|
|
76
94
|
AI_START_TO_CLOSE = timedelta(minutes=30)
|
|
77
|
-
TRIGGER_START_TO_CLOSE = timedelta(hours=24)
|
|
95
|
+
TRIGGER_START_TO_CLOSE = timedelta(hours=24) # long-lived
|
|
78
96
|
CODE_START_TO_CLOSE = timedelta(minutes=5)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Plugin-owned registry of service-factory callables for the DI container.
|
|
2
|
+
|
|
3
|
+
Closes the two remaining cross-plugin top-level imports in
|
|
4
|
+
``core/container.py``:
|
|
5
|
+
|
|
6
|
+
- ``from nodes.location._service import MapsService`` (line 25)
|
|
7
|
+
- ``from nodes.android._dispatcher import AndroidService`` (line 30)
|
|
8
|
+
|
|
9
|
+
Both made the framework's DI container code know about specific
|
|
10
|
+
plugins' implementation classes — direct violation of the
|
|
11
|
+
"framework knows no plugin names" rule.
|
|
12
|
+
|
|
13
|
+
Pattern (matches the established Wave-11.I plugin-self-registration
|
|
14
|
+
shape; same as ``register_filter_builder`` /
|
|
15
|
+
``register_poll_coroutine_factory`` / ``register_ws_handlers`` /
|
|
16
|
+
``register_canary_trigger_type`` / ``register_social_send_handler`` /
|
|
17
|
+
``register_shutdown_hook``):
|
|
18
|
+
|
|
19
|
+
1. Plugin's ``__init__.py`` imports its service class from ``_service.py``
|
|
20
|
+
(intra-plugin, fine) and calls
|
|
21
|
+
``register_service_factory("<name>", ServiceClass)``.
|
|
22
|
+
2. The container's provider declaration references a lazy lookup
|
|
23
|
+
function — :func:`get_service_factory` — instead of the concrete
|
|
24
|
+
class. ``providers.Factory`` / ``providers.Singleton`` calls the
|
|
25
|
+
wrapper function with its declared kwargs; the wrapper looks up
|
|
26
|
+
the registered factory and instantiates.
|
|
27
|
+
3. Lookup happens at **instantiation time** (first ``container.foo()``
|
|
28
|
+
call), not at class-definition time, so plugin import ordering
|
|
29
|
+
isn't a constraint — plugin auto-discovery on startup populates
|
|
30
|
+
the registry before any service is first resolved.
|
|
31
|
+
|
|
32
|
+
Factory signature
|
|
33
|
+
-----------------
|
|
34
|
+
|
|
35
|
+
The "factory" is typically the class itself (calling it constructs
|
|
36
|
+
the instance). Any zero-or-more-keyword-arg callable that returns
|
|
37
|
+
the service instance is acceptable.
|
|
38
|
+
|
|
39
|
+
::
|
|
40
|
+
|
|
41
|
+
factory(**kwargs) -> ServiceInstance
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from __future__ import annotations
|
|
45
|
+
|
|
46
|
+
from typing import Any, Callable, Optional
|
|
47
|
+
|
|
48
|
+
from services.plugin.registry import IdempotentRegistry
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
ServiceFactory = Callable[..., Any]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_REGISTRY: IdempotentRegistry[str, ServiceFactory] = IdempotentRegistry("service_factory")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def register_service_factory(name: str, factory: ServiceFactory) -> None:
|
|
58
|
+
"""Publish a service factory for the DI container.
|
|
59
|
+
|
|
60
|
+
Idempotent on re-import (same callable / class for the same name
|
|
61
|
+
is a no-op). A different factory for an existing name raises
|
|
62
|
+
``ValueError`` to surface plugin namespace collisions at import time.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
name: Lower-case service identifier (``"maps"``, ``"android"``,
|
|
66
|
+
...). The container's provider declaration references this
|
|
67
|
+
name via :func:`get_service_factory`.
|
|
68
|
+
factory: The service class (or any callable taking the
|
|
69
|
+
provider's declared kwargs) that constructs the service
|
|
70
|
+
instance.
|
|
71
|
+
"""
|
|
72
|
+
_REGISTRY.register(name, factory)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_service_factory(name: str) -> Optional[ServiceFactory]:
|
|
76
|
+
"""Return the factory for ``name``, or ``None`` if unregistered.
|
|
77
|
+
|
|
78
|
+
Container providers wrap this in a lazy thunk that surfaces a
|
|
79
|
+
clear "service not registered" error if a required plugin failed
|
|
80
|
+
to load, instead of silently producing ``None``.
|
|
81
|
+
"""
|
|
82
|
+
return _REGISTRY.get(name)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def registered_service_names() -> frozenset[str]:
|
|
86
|
+
"""Return an immutable snapshot of registered service identifiers."""
|
|
87
|
+
return frozenset(_REGISTRY.keys())
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
__all__ = [
|
|
91
|
+
"register_service_factory",
|
|
92
|
+
"get_service_factory",
|
|
93
|
+
"registered_service_names",
|
|
94
|
+
"ServiceFactory",
|
|
95
|
+
]
|