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
|
@@ -17,7 +17,28 @@ When TEMPORAL_ENABLED=false:
|
|
|
17
17
|
- Falls back to the existing parallel/sequential executor
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
__all__ = [
|
|
20
|
+
__all__ = [
|
|
21
|
+
"TemporalExecutor",
|
|
22
|
+
"TemporalClientWrapper",
|
|
23
|
+
"TemporalServerRuntime",
|
|
24
|
+
"get_temporal_server_runtime",
|
|
25
|
+
]
|
|
21
26
|
|
|
22
27
|
from .executor import TemporalExecutor
|
|
23
28
|
from .client import TemporalClientWrapper
|
|
29
|
+
from ._runtime import (
|
|
30
|
+
TemporalServerRuntime,
|
|
31
|
+
get_temporal_server_runtime,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# ---- self-registration (Wave 11 plugin-folder pattern) -------------------
|
|
35
|
+
# WS handlers (temporal_status / _start / _stop) + WS-connect refresh
|
|
36
|
+
# (broadcasts current Temporal snapshot). Registries are idempotent on
|
|
37
|
+
# re-import; same callable for same key is a no-op.
|
|
38
|
+
from ._handlers import WS_HANDLERS as _WS_HANDLERS
|
|
39
|
+
from ._refresh import refresh_temporal_status as _refresh_temporal_status
|
|
40
|
+
from services.ws_handler_registry import register_ws_handlers
|
|
41
|
+
from services.status_broadcaster import register_service_refresh
|
|
42
|
+
|
|
43
|
+
register_ws_handlers(_WS_HANDLERS)
|
|
44
|
+
register_service_refresh(_refresh_temporal_status)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""WebSocket handlers for the Temporal lifecycle commands.
|
|
2
|
+
|
|
3
|
+
Wire keys registered in :mod:`services.ws_handler_registry`:
|
|
4
|
+
|
|
5
|
+
- ``temporal_status`` → status snapshot of the runtime
|
|
6
|
+
- ``temporal_start`` → idempotent start
|
|
7
|
+
- ``temporal_stop`` → idempotent stop
|
|
8
|
+
|
|
9
|
+
All three share the snapshot shape from
|
|
10
|
+
:func:`services.temporal._refresh.temporal_status_snapshot` — the
|
|
11
|
+
single source of truth for the ``{temporal}`` payload that the
|
|
12
|
+
WS-connect refresh callback also emits.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from fastapi import WebSocket
|
|
20
|
+
|
|
21
|
+
from core.logging import get_logger
|
|
22
|
+
from services.temporal._refresh import temporal_status_snapshot
|
|
23
|
+
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def handle_temporal_status(
|
|
28
|
+
data: dict[str, Any],
|
|
29
|
+
websocket: WebSocket,
|
|
30
|
+
) -> dict[str, Any]:
|
|
31
|
+
"""Return the status snapshot for the Temporal runtime."""
|
|
32
|
+
return temporal_status_snapshot()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def handle_temporal_start(
|
|
36
|
+
data: dict[str, Any],
|
|
37
|
+
websocket: WebSocket,
|
|
38
|
+
) -> dict[str, Any]:
|
|
39
|
+
"""Start Temporal. Idempotent — ``.start()`` returns immediately if
|
|
40
|
+
the runtime is already running."""
|
|
41
|
+
from services.temporal._runtime import get_temporal_server_runtime
|
|
42
|
+
|
|
43
|
+
await get_temporal_server_runtime().start()
|
|
44
|
+
return {"ok": True, **temporal_status_snapshot()}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def handle_temporal_stop(
|
|
48
|
+
data: dict[str, Any],
|
|
49
|
+
websocket: WebSocket,
|
|
50
|
+
) -> dict[str, Any]:
|
|
51
|
+
"""Stop Temporal. Idempotent."""
|
|
52
|
+
from services.temporal._runtime import get_temporal_server_runtime
|
|
53
|
+
|
|
54
|
+
await get_temporal_server_runtime().stop()
|
|
55
|
+
return {"ok": True, **temporal_status_snapshot()}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
WS_HANDLERS: dict[str, Any] = {
|
|
59
|
+
"temporal_status": handle_temporal_status,
|
|
60
|
+
"temporal_start": handle_temporal_start,
|
|
61
|
+
"temporal_stop": handle_temporal_stop,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
__all__ = ["WS_HANDLERS"]
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Cross-platform Temporal CLI binary downloader using pooch.
|
|
2
|
+
|
|
3
|
+
Downloads the official ``temporal`` CLI from the URL documented at
|
|
4
|
+
https://docs.temporal.io/develop/python/set-up-your-local-python
|
|
5
|
+
(``temporal.download/cli/archive/latest?platform=<os>&arch=<arch>``).
|
|
6
|
+
|
|
7
|
+
Pooch handles XDG-aware cross-platform caching and archive extraction
|
|
8
|
+
(zip on Windows, tar.gz elsewhere). The ``latest`` URL rotates as new
|
|
9
|
+
CLI versions ship, so the download is unverified (TLS gives transport
|
|
10
|
+
integrity); pooch caches by local filename, not URL contents, so
|
|
11
|
+
re-runs after the first fetch are instant cache hits.
|
|
12
|
+
|
|
13
|
+
The downloaded ``temporal`` CLI powers ``temporal server start-dev``
|
|
14
|
+
(the SQLite/in-memory dev server) and ad-hoc workflow / operator
|
|
15
|
+
commands.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import asyncio
|
|
21
|
+
import platform
|
|
22
|
+
import stat
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
import pooch
|
|
26
|
+
|
|
27
|
+
from core.config import Settings
|
|
28
|
+
from core.logging import get_logger
|
|
29
|
+
|
|
30
|
+
logger = get_logger(__name__)
|
|
31
|
+
|
|
32
|
+
# Official Temporal CLI download URL. Per the docs at
|
|
33
|
+
# https://docs.temporal.io/develop/python/set-up-your-local-python the
|
|
34
|
+
# CLI archive lives at ``temporal.download/cli/archive/latest`` with
|
|
35
|
+
# ``platform`` and ``arch`` query parameters. Contains a single
|
|
36
|
+
# ``temporal`` binary.
|
|
37
|
+
_CLI_BASE_URL = "https://temporal.download/cli/archive/latest"
|
|
38
|
+
_CLI_PLATFORM_MAP: dict[tuple[str, str], tuple[str, str]] = {
|
|
39
|
+
# platform.system, platform.machine -> (URL platform, URL arch)
|
|
40
|
+
("Linux", "x86_64"): ("linux", "amd64"),
|
|
41
|
+
("Linux", "aarch64"): ("linux", "arm64"),
|
|
42
|
+
("Darwin", "x86_64"): ("darwin", "amd64"),
|
|
43
|
+
("Darwin", "arm64"): ("darwin", "arm64"),
|
|
44
|
+
("Windows", "AMD64"): ("windows", "amd64"),
|
|
45
|
+
("Windows", "ARM64"): ("windows", "arm64"),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# pooch cache namespace. Pooch's own cache layout includes the asset
|
|
49
|
+
# URL so versions don't collide.
|
|
50
|
+
_CACHE_NAMESPACE = "machinaos-temporal"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_cached: dict[str, Path] | None = None
|
|
54
|
+
_lock = asyncio.Lock()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def ensure_temporal_binaries(
|
|
58
|
+
settings: Settings | None = None,
|
|
59
|
+
) -> dict[str, Path]:
|
|
60
|
+
"""Return ``{"temporal": Path}`` — the official ``temporal`` CLI binary.
|
|
61
|
+
|
|
62
|
+
Idempotent — first call downloads, subsequent calls hit the pooch
|
|
63
|
+
cache (XDG / OS-conventional dir). Async-locked so concurrent
|
|
64
|
+
callers don't double-download.
|
|
65
|
+
|
|
66
|
+
The ``settings`` argument is accepted for API symmetry but currently
|
|
67
|
+
unused — the official ``latest`` URL has no version slot to override.
|
|
68
|
+
"""
|
|
69
|
+
global _cached
|
|
70
|
+
# settings accepted for API symmetry; no fields consulted (the
|
|
71
|
+
# ``latest`` URL has no version slot).
|
|
72
|
+
_ = settings
|
|
73
|
+
|
|
74
|
+
async with _lock:
|
|
75
|
+
if _cached is not None:
|
|
76
|
+
return _cached
|
|
77
|
+
cli_path = await asyncio.to_thread(_fetch_cli_sync)
|
|
78
|
+
_cached = {"temporal": cli_path}
|
|
79
|
+
logger.info(
|
|
80
|
+
"[Temporal install] binary ready: %s",
|
|
81
|
+
{k: str(v) for k, v in _cached.items()},
|
|
82
|
+
)
|
|
83
|
+
return _cached
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _fetch_cli_sync() -> Path:
|
|
87
|
+
"""Download the official ``temporal`` CLI archive and return the binary path.
|
|
88
|
+
|
|
89
|
+
Uses ``pooch.retrieve`` with ``known_hash=None`` because the
|
|
90
|
+
``temporal.download/cli/archive/latest`` URL rotates as new CLI
|
|
91
|
+
versions ship — pinning a SHA would defeat the "latest" semantics
|
|
92
|
+
the official docs document. TLS gives us transport integrity.
|
|
93
|
+
"""
|
|
94
|
+
key = (platform.system(), platform.machine())
|
|
95
|
+
if key not in _CLI_PLATFORM_MAP:
|
|
96
|
+
raise RuntimeError(f"[Temporal install] Unsupported platform for CLI: {key}. " f"Supported: {sorted(_CLI_PLATFORM_MAP.keys())}")
|
|
97
|
+
url_platform, url_arch = _CLI_PLATFORM_MAP[key]
|
|
98
|
+
url = f"{_CLI_BASE_URL}?platform={url_platform}&arch={url_arch}"
|
|
99
|
+
|
|
100
|
+
is_windows = platform.system() == "Windows"
|
|
101
|
+
fname = f"temporal_cli_latest_{url_platform}_{url_arch}.{'zip' if is_windows else 'tar.gz'}"
|
|
102
|
+
processor = pooch.Unzip() if is_windows else pooch.Untar()
|
|
103
|
+
|
|
104
|
+
extracted = pooch.retrieve(
|
|
105
|
+
url=url,
|
|
106
|
+
known_hash=None,
|
|
107
|
+
path=pooch.os_cache(_CACHE_NAMESPACE),
|
|
108
|
+
fname=fname,
|
|
109
|
+
processor=processor,
|
|
110
|
+
progressbar=True,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
target = "temporal.exe" if is_windows else "temporal"
|
|
114
|
+
match = next((Path(p) for p in extracted if Path(p).name == target), None)
|
|
115
|
+
if match is None:
|
|
116
|
+
raise RuntimeError(
|
|
117
|
+
f"[Temporal install] CLI binary {target!r} not found in archive. " f"Extracted files: {[Path(p).name for p in extracted]}"
|
|
118
|
+
)
|
|
119
|
+
if not is_windows:
|
|
120
|
+
mode = match.stat().st_mode
|
|
121
|
+
match.chmod(mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
122
|
+
return match
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
__all__ = ["ensure_temporal_binaries"]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _main() -> int:
|
|
129
|
+
"""Standalone entry: ``python -m services.temporal._install``.
|
|
130
|
+
|
|
131
|
+
Used by ``machina build`` step [6/6] to materialise the Temporal
|
|
132
|
+
CLI at build time instead of paying the download cost on first
|
|
133
|
+
``machina start``. Fetches, verifies the binary exists on disk,
|
|
134
|
+
prints the resolved location. Non-zero exit on any failure.
|
|
135
|
+
"""
|
|
136
|
+
import sys as _sys
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
paths = asyncio.run(ensure_temporal_binaries())
|
|
140
|
+
except Exception as exc: # noqa: BLE001 — propagate to non-zero exit
|
|
141
|
+
print(f"[Temporal install] {exc}", file=_sys.stderr)
|
|
142
|
+
return 1
|
|
143
|
+
|
|
144
|
+
missing = [name for name, path in paths.items() if not path.exists()]
|
|
145
|
+
if missing:
|
|
146
|
+
print(
|
|
147
|
+
f"[Temporal install] binaries missing after fetch: {missing}",
|
|
148
|
+
file=_sys.stderr,
|
|
149
|
+
)
|
|
150
|
+
return 1
|
|
151
|
+
|
|
152
|
+
for name, path in paths.items():
|
|
153
|
+
print(f" {name}: {path}")
|
|
154
|
+
return 0
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
raise SystemExit(_main())
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""WS-connect status broadcast for the Temporal stack.
|
|
2
|
+
|
|
3
|
+
Registered via ``status_broadcaster.register_service_refresh`` so the
|
|
4
|
+
frontend health indicator stays current — every WebSocket client
|
|
5
|
+
connect triggers ``_refresh_all_services()`` which fans out to every
|
|
6
|
+
registered callback. Same idiom :mod:`nodes.telegram._refresh` uses.
|
|
7
|
+
|
|
8
|
+
Also exposes :func:`temporal_status_snapshot` — the single source of
|
|
9
|
+
truth for the ``{temporal}`` status shape consumed by both this refresh
|
|
10
|
+
callback and ``_handlers.py``'s WS commands.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Any
|
|
16
|
+
|
|
17
|
+
from core.logging import get_logger
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from services.status_broadcaster import StatusBroadcaster
|
|
21
|
+
|
|
22
|
+
logger = get_logger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def temporal_status_snapshot() -> dict[str, Any]:
|
|
26
|
+
"""Return ``{"temporal": {...}}`` snapshot.
|
|
27
|
+
|
|
28
|
+
Exposes the uniform ``{name, running, started_at, last_error,
|
|
29
|
+
...extras}`` shape every ``BaseSupervisor`` subclass returns.
|
|
30
|
+
Shared by the WS-refresh callback below and the
|
|
31
|
+
``temporal_status`` / ``_start`` / ``_stop`` handlers in
|
|
32
|
+
:mod:`services.temporal._handlers`.
|
|
33
|
+
"""
|
|
34
|
+
from services.temporal._runtime import get_temporal_server_runtime
|
|
35
|
+
|
|
36
|
+
return {"temporal": get_temporal_server_runtime().status_snapshot()}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def refresh_temporal_status(broadcaster: "StatusBroadcaster") -> None:
|
|
40
|
+
"""Broadcast the Temporal status snapshot on WS connect.
|
|
41
|
+
|
|
42
|
+
Signature matches the ``register_service_refresh`` callback contract
|
|
43
|
+
— :meth:`StatusBroadcaster._refresh_all_services` invokes every
|
|
44
|
+
registered callback as ``callback(self)`` (passes the broadcaster
|
|
45
|
+
as the sole positional argument). Without the parameter the
|
|
46
|
+
framework's ``TaskGroup`` swallows a ``TypeError: takes 0 positional
|
|
47
|
+
arguments but 1 was given`` on every server boot.
|
|
48
|
+
"""
|
|
49
|
+
await broadcaster.broadcast(
|
|
50
|
+
{
|
|
51
|
+
"type": "temporal_status",
|
|
52
|
+
"data": temporal_status_snapshot(),
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
__all__ = ["refresh_temporal_status", "temporal_status_snapshot"]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Wave 12 D1: shared Temporal RetryPolicy constants.
|
|
2
|
+
|
|
3
|
+
Replaces the duplicated inline ``RetryPolicy(1s, 30s, 3 attempts)``
|
|
4
|
+
declarations that lived in ``services/temporal/workflow.py:246`` and
|
|
5
|
+
``services/temporal/agent_workflow.py:76`` (identical knobs, hand-typed
|
|
6
|
+
twice). Adds the missing ``non_retryable_error_types`` declaration so
|
|
7
|
+
``NodeUserError`` (user-correctable failures: missing required field,
|
|
8
|
+
unknown enum value, bad regex) fails fast instead of burning all three
|
|
9
|
+
retry attempts.
|
|
10
|
+
|
|
11
|
+
Why these specific error types are non-retryable
|
|
12
|
+
------------------------------------------------
|
|
13
|
+
|
|
14
|
+
- ``NodeUserError``: every plugin raises this for user-correctable
|
|
15
|
+
failures (see ``services/plugin/__init__.py``). Retrying just spends
|
|
16
|
+
retry budget — the input won't be different next time. The plugin
|
|
17
|
+
``cls.retry_policy`` default already lists it as non-retryable
|
|
18
|
+
(Wave 12 A2, ``services/plugin/scaling.py``), but the WORKFLOW-side
|
|
19
|
+
``workflow.execute_activity(..., retry_policy=...)`` override
|
|
20
|
+
silently lost that contract when the workflow author hand-typed a
|
|
21
|
+
fresh ``RetryPolicy(...)``. This module re-imposes it at the
|
|
22
|
+
workflow callsite.
|
|
23
|
+
|
|
24
|
+
- ``InvalidEvent``: when ``services/events/dispatch.py:emit`` raises
|
|
25
|
+
on a malformed envelope (missing ``id``, bad ``source``), the
|
|
26
|
+
activity's input is structurally broken and retries can't fix it.
|
|
27
|
+
|
|
28
|
+
Keep this list short. Adding error types here is a runtime contract
|
|
29
|
+
change — every workflow that uses these policies inherits the
|
|
30
|
+
non-retryability. Use ``cls.retry_policy.non_retryable_error_types``
|
|
31
|
+
on a specific plugin class when only ONE plugin's failure should be
|
|
32
|
+
non-retryable.
|
|
33
|
+
|
|
34
|
+
Refs
|
|
35
|
+
----
|
|
36
|
+
- https://docs.temporal.io/encyclopedia/retry-policies
|
|
37
|
+
- https://python.temporal.io/temporalio.common.RetryPolicy.html
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
from datetime import timedelta
|
|
43
|
+
|
|
44
|
+
from temporalio.common import RetryPolicy
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Error types that surface user mistakes / structural payload bugs —
|
|
48
|
+
# retrying makes no difference. Plugin classes that raise their own
|
|
49
|
+
# domain-specific non-retryable errors should override
|
|
50
|
+
# ``cls.retry_policy`` rather than expand this list.
|
|
51
|
+
NON_RETRYABLE_ERROR_TYPES: tuple[str, ...] = (
|
|
52
|
+
"NodeUserError",
|
|
53
|
+
"InvalidEvent",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Default activity policy for the orchestrator (MachinaWorkflow,
|
|
58
|
+
# AgentWorkflow, PollingTriggerWorkflow). 3 attempts with 1s→30s
|
|
59
|
+
# exponential backoff covers transient API blips (DNS, rate limits,
|
|
60
|
+
# brief upstream outages) without holding the whole graph hostage to a
|
|
61
|
+
# persistent failure.
|
|
62
|
+
DEFAULT_ACTIVITY_RETRY: RetryPolicy = RetryPolicy(
|
|
63
|
+
initial_interval=timedelta(seconds=1),
|
|
64
|
+
maximum_interval=timedelta(seconds=30),
|
|
65
|
+
maximum_attempts=3,
|
|
66
|
+
non_retryable_error_types=list(NON_RETRYABLE_ERROR_TYPES),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# Tighter policy for activities that are themselves cheap operations
|
|
71
|
+
# (e.g. ``emit_event_activity``, ``broadcast_progress``). Failing fast
|
|
72
|
+
# here surfaces wiring bugs in tests + ops without retry-budget noise.
|
|
73
|
+
QUICK_ACTIVITY_RETRY: RetryPolicy = RetryPolicy(
|
|
74
|
+
initial_interval=timedelta(milliseconds=250),
|
|
75
|
+
maximum_interval=timedelta(seconds=5),
|
|
76
|
+
maximum_attempts=2,
|
|
77
|
+
non_retryable_error_types=list(NON_RETRYABLE_ERROR_TYPES),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
__all__ = [
|
|
82
|
+
"NON_RETRYABLE_ERROR_TYPES",
|
|
83
|
+
"DEFAULT_ACTIVITY_RETRY",
|
|
84
|
+
"QUICK_ACTIVITY_RETRY",
|
|
85
|
+
]
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Temporal server lifecycle.
|
|
2
|
+
|
|
3
|
+
Single supervisor subclass: :class:`TemporalServerRuntime`. Spawns the
|
|
4
|
+
official ``temporal`` CLI (downloaded by :mod:`services.temporal._install`
|
|
5
|
+
from https://temporal.download/cli/archive/latest) with the
|
|
6
|
+
``server start-dev`` subcommand against a SQLite db. Matches the local
|
|
7
|
+
dev install method documented at
|
|
8
|
+
https://docs.temporal.io/develop/python/set-up-your-local-python.
|
|
9
|
+
|
|
10
|
+
Uses the singleton accessor pattern (``Class.get_instance()``) from
|
|
11
|
+
``BaseSupervisor`` — same idiom :mod:`nodes.whatsapp._runtime` uses for
|
|
12
|
+
``WhatsAppRuntime``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, Optional
|
|
22
|
+
|
|
23
|
+
from core.config import Settings
|
|
24
|
+
from core.logging import get_logger
|
|
25
|
+
from services._supervisor import BaseProcessSupervisor
|
|
26
|
+
|
|
27
|
+
logger = get_logger(__name__)
|
|
28
|
+
|
|
29
|
+
# How long each TCP-readiness probe waits per attempt. Sub-second so a
|
|
30
|
+
# stalled subprocess fails health fast.
|
|
31
|
+
_PROBE_TIMEOUT_SECONDS = 1.0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def _probe_tcp_port(port: int, host: str = "127.0.0.1") -> bool:
|
|
35
|
+
"""Return ``True`` iff a TCP connection to ``host:port`` succeeds
|
|
36
|
+
within :data:`_PROBE_TIMEOUT_SECONDS`. Loopback-friendly readiness
|
|
37
|
+
check used by ``health_check``. Mirrors :func:`cli.tcp.probe_tcp_port`
|
|
38
|
+
but keeps server-side modules independent of the ``cli`` CLI package."""
|
|
39
|
+
try:
|
|
40
|
+
_, writer = await asyncio.wait_for(
|
|
41
|
+
asyncio.open_connection(host, port),
|
|
42
|
+
timeout=_PROBE_TIMEOUT_SECONDS,
|
|
43
|
+
)
|
|
44
|
+
writer.close()
|
|
45
|
+
try:
|
|
46
|
+
await writer.wait_closed()
|
|
47
|
+
except (ConnectionResetError, OSError):
|
|
48
|
+
pass
|
|
49
|
+
return True
|
|
50
|
+
except (asyncio.TimeoutError, ConnectionRefusedError, OSError):
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TemporalServerRuntime(BaseProcessSupervisor):
|
|
55
|
+
"""Temporal dev server, supervised via BaseProcessSupervisor.
|
|
56
|
+
|
|
57
|
+
Spawns ``temporal server start-dev`` against a SQLite db at
|
|
58
|
+
``settings.temporal_sqlite_path``. The ``temporal`` CLI binary is
|
|
59
|
+
downloaded by :func:`services.temporal._install.ensure_temporal_binaries`
|
|
60
|
+
from the official URL at
|
|
61
|
+
https://temporal.download/cli/archive/latest.
|
|
62
|
+
|
|
63
|
+
Path is env-driven via ``settings.temporal_sqlite_path``, resolved
|
|
64
|
+
relative to ``settings.data_dir`` unless already absolute.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
name = "temporal"
|
|
68
|
+
pipe_streams = True
|
|
69
|
+
graceful_shutdown = sys.platform == "win32"
|
|
70
|
+
|
|
71
|
+
def __init__(self, settings: Optional[Settings] = None) -> None:
|
|
72
|
+
super().__init__()
|
|
73
|
+
if settings is None:
|
|
74
|
+
settings = Settings()
|
|
75
|
+
self.settings = settings
|
|
76
|
+
# SIGTERM grace = settings.temporal_graceful_shutdown_seconds —
|
|
77
|
+
# same knob already documented for the embedded Temporal worker.
|
|
78
|
+
self.terminate_grace_seconds = float(
|
|
79
|
+
settings.temporal_graceful_shutdown_seconds,
|
|
80
|
+
)
|
|
81
|
+
self._binaries: Optional[dict[str, Path]] = None
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def _sqlite_path(self) -> Path:
|
|
85
|
+
"""Env-driven SQLite db path (``TEMPORAL_SQLITE_PATH``),
|
|
86
|
+
resolved relative to ``DATA_DIR`` unless absolute."""
|
|
87
|
+
return Path(self.settings._resolve_under_data(self.settings.temporal_sqlite_path))
|
|
88
|
+
|
|
89
|
+
# ---- BaseProcessSupervisor overrides ---------------------------------
|
|
90
|
+
|
|
91
|
+
async def _pre_spawn(self) -> None:
|
|
92
|
+
from services.temporal._install import ensure_temporal_binaries
|
|
93
|
+
|
|
94
|
+
self._binaries = await ensure_temporal_binaries(self.settings)
|
|
95
|
+
# Ensure the parent dir for the SQLite file exists before
|
|
96
|
+
# ``temporal server start-dev`` opens it.
|
|
97
|
+
self._sqlite_path.parent.mkdir(parents=True, exist_ok=True)
|
|
98
|
+
|
|
99
|
+
def binary_path(self) -> Path:
|
|
100
|
+
# ``_pre_spawn`` (called by ``BaseProcessSupervisor._do_start``
|
|
101
|
+
# before this method) populates ``self._binaries`` via the
|
|
102
|
+
# pooch downloader. Loud failure if that contract regresses.
|
|
103
|
+
assert self._binaries is not None, f"[{self.label}] binary_path() called before _pre_spawn() " "populated self._binaries"
|
|
104
|
+
return self._binaries["temporal"]
|
|
105
|
+
|
|
106
|
+
def argv(self) -> list[str]:
|
|
107
|
+
# ``temporal server start-dev`` is the official subcommand for
|
|
108
|
+
# the SQLite-backed dev server. Flags documented at
|
|
109
|
+
# https://docs.temporal.io/cli/server (subset we use):
|
|
110
|
+
# --port frontend gRPC port (gates ready-probe)
|
|
111
|
+
# --ui-port Web UI port (default ``--port + 1000``)
|
|
112
|
+
# --db-filename SQLite file — preserves history across
|
|
113
|
+
# restarts. Running workflows are not
|
|
114
|
+
# auto-resumed; see ``TemporalClientWrapper.
|
|
115
|
+
# terminate_running_workflows`` (run from
|
|
116
|
+
# ``main.py`` lifespan) for the boot-time
|
|
117
|
+
# terminate-but-preserve-history behaviour.
|
|
118
|
+
# --metrics-port 0 disables the Prometheus endpoint
|
|
119
|
+
# --log-level warn keeps the supervisor log readable
|
|
120
|
+
# --namespace default namespace bootstrapped at start
|
|
121
|
+
return [
|
|
122
|
+
str(self.binary_path()),
|
|
123
|
+
"server",
|
|
124
|
+
"start-dev",
|
|
125
|
+
"--port",
|
|
126
|
+
str(self.settings.temporal_frontend_grpc_port),
|
|
127
|
+
"--ui-port",
|
|
128
|
+
str(self.settings.temporal_ui_port),
|
|
129
|
+
"--db-filename",
|
|
130
|
+
str(self._sqlite_path),
|
|
131
|
+
"--metrics-port",
|
|
132
|
+
"0",
|
|
133
|
+
"--log-level",
|
|
134
|
+
"warn",
|
|
135
|
+
"--namespace",
|
|
136
|
+
self.settings.temporal_namespace,
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
def cwd(self) -> Path:
|
|
140
|
+
# Land any incidental output / log files next to the SQLite
|
|
141
|
+
# file (when persisting) or under DATA_DIR (when in-memory) —
|
|
142
|
+
# either way, never the supervisor's own working directory.
|
|
143
|
+
return self._sqlite_path.parent
|
|
144
|
+
|
|
145
|
+
def env(self) -> dict[str, str]:
|
|
146
|
+
# ``temporal server start-dev`` reads everything from argv flags;
|
|
147
|
+
# inherit parent env only.
|
|
148
|
+
return {**os.environ}
|
|
149
|
+
|
|
150
|
+
async def health_check(self) -> bool:
|
|
151
|
+
if not self.is_running():
|
|
152
|
+
return False
|
|
153
|
+
# gRPC frontend port — configured via
|
|
154
|
+
# ``settings.temporal_frontend_grpc_port``. Same shared probe
|
|
155
|
+
# MachinaOS uses for every other supervised TCP service.
|
|
156
|
+
return await _probe_tcp_port(self.settings.temporal_frontend_grpc_port)
|
|
157
|
+
|
|
158
|
+
def _extra_status(self) -> dict[str, Any]:
|
|
159
|
+
base = super()._extra_status()
|
|
160
|
+
return {
|
|
161
|
+
**base,
|
|
162
|
+
"grpc_port": self.settings.temporal_frontend_grpc_port,
|
|
163
|
+
"ui_port": self.settings.temporal_ui_port,
|
|
164
|
+
"sqlite_path": str(self._sqlite_path),
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# ---- module-level singleton accessor ------------------------------------
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def get_temporal_server_runtime(
|
|
172
|
+
settings: Optional[Settings] = None,
|
|
173
|
+
) -> TemporalServerRuntime:
|
|
174
|
+
"""Return the Temporal server runtime singleton."""
|
|
175
|
+
return TemporalServerRuntime.get_instance(settings)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
__all__ = [
|
|
179
|
+
"TemporalServerRuntime",
|
|
180
|
+
"get_temporal_server_runtime",
|
|
181
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Generic bridge: run any ``BaseSupervisor`` singleton from a CLI.
|
|
2
|
+
|
|
3
|
+
The CLI's ``Manager`` supervisor (``cli/supervisor.py``) schedules
|
|
4
|
+
``ServiceSpec`` entries by spawning a subprocess and supervising it.
|
|
5
|
+
``BaseSupervisor``-managed runtimes (e.g. the Temporal CLI dev server)
|
|
6
|
+
don't ship a CLI of their own -- they're Python singletons started via
|
|
7
|
+
``await runtime.start()``.
|
|
8
|
+
|
|
9
|
+
This shim bridges the two: import a runtime factory by dotted path,
|
|
10
|
+
call it, ``await singleton.start()``, then block on a shutdown signal.
|
|
11
|
+
``Manager`` sends SIGTERM during graceful shutdown; we catch it,
|
|
12
|
+
``await singleton.stop()``, then exit cleanly.
|
|
13
|
+
|
|
14
|
+
Module location: this lives inside the ``server`` package because the
|
|
15
|
+
factory targets it resolves
|
|
16
|
+
(``services.temporal._runtime:get_temporal_server_runtime``) are
|
|
17
|
+
server-side singletons. Hosting the shim here keeps the CLI free of
|
|
18
|
+
server-side imports and lets a single ``uv run python -m
|
|
19
|
+
services.temporal._supervised_runtime <factory>`` invocation work
|
|
20
|
+
against the workspace ``.venv`` -- no PYTHONPATH composition, no
|
|
21
|
+
cross-venv plumbing.
|
|
22
|
+
|
|
23
|
+
Invocation (inside ``ServiceSpec.argv``, built via ``cli.run.uv_run``):
|
|
24
|
+
|
|
25
|
+
uv_run("python", "-m",
|
|
26
|
+
"services.temporal._supervised_runtime",
|
|
27
|
+
"services.temporal._runtime:get_temporal_server_runtime")
|
|
28
|
+
|
|
29
|
+
Equivalent to (Python idiom):
|
|
30
|
+
|
|
31
|
+
from services.temporal._runtime import get_temporal_server_runtime
|
|
32
|
+
runtime = get_temporal_server_runtime()
|
|
33
|
+
await runtime.start()
|
|
34
|
+
await asyncio.Event().wait() # block on signal
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import asyncio
|
|
40
|
+
import importlib
|
|
41
|
+
import signal
|
|
42
|
+
import sys
|
|
43
|
+
from typing import Awaitable, Callable
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _resolve(dotted: str) -> Callable[[], Awaitable[None]]:
|
|
47
|
+
"""Resolve ``module.path:attr`` to a callable factory."""
|
|
48
|
+
if ":" not in dotted:
|
|
49
|
+
raise SystemExit(f"factory must be 'module.path:attr', got {dotted!r}")
|
|
50
|
+
mod_path, attr = dotted.split(":", 1)
|
|
51
|
+
mod = importlib.import_module(mod_path)
|
|
52
|
+
factory = getattr(mod, attr, None)
|
|
53
|
+
if factory is None:
|
|
54
|
+
raise SystemExit(f"{mod_path}.{attr} not found")
|
|
55
|
+
return factory
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def _run(factory_dotted: str) -> int:
|
|
59
|
+
factory = _resolve(factory_dotted)
|
|
60
|
+
runtime = factory()
|
|
61
|
+
stop = asyncio.Event()
|
|
62
|
+
|
|
63
|
+
def _on_signal() -> None:
|
|
64
|
+
if not stop.is_set():
|
|
65
|
+
stop.set()
|
|
66
|
+
|
|
67
|
+
loop = asyncio.get_running_loop()
|
|
68
|
+
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
69
|
+
try:
|
|
70
|
+
loop.add_signal_handler(sig, _on_signal)
|
|
71
|
+
except NotImplementedError:
|
|
72
|
+
# Windows: add_signal_handler raises for SIGTERM. The
|
|
73
|
+
# Manager supervisor's tree-kill semantics (psutil-based)
|
|
74
|
+
# work regardless -- we just lose the graceful path on Win.
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
print(f"[supervised_runtime] starting {factory_dotted}", flush=True)
|
|
78
|
+
try:
|
|
79
|
+
await runtime.start()
|
|
80
|
+
print(f"[supervised_runtime] {factory_dotted} ready", flush=True)
|
|
81
|
+
await stop.wait()
|
|
82
|
+
finally:
|
|
83
|
+
print(f"[supervised_runtime] stopping {factory_dotted}", flush=True)
|
|
84
|
+
await runtime.stop()
|
|
85
|
+
return 0
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def main() -> int:
|
|
89
|
+
if len(sys.argv) != 2:
|
|
90
|
+
print(
|
|
91
|
+
"usage: python -m services.temporal._supervised_runtime " "<module.path:factory>",
|
|
92
|
+
file=sys.stderr,
|
|
93
|
+
)
|
|
94
|
+
return 2
|
|
95
|
+
try:
|
|
96
|
+
return asyncio.run(_run(sys.argv[1]))
|
|
97
|
+
except KeyboardInterrupt:
|
|
98
|
+
return 0
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
raise SystemExit(main())
|