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
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Wave 12 B1: CloudEvents factories + broadcaster wrappers for android.
|
|
2
|
+
|
|
3
|
+
Plugin-specific event emission — replaces the
|
|
4
|
+
``StatusBroadcaster.update_android_status`` method (deleted in this
|
|
5
|
+
commit) and the direct ``broadcaster._status["android"]`` mutations
|
|
6
|
+
inside ``_refresh.py`` / ``_relay/broadcaster.py``.
|
|
7
|
+
|
|
8
|
+
Per RFC plugin_authoring_rfc.md §6.4: plugin-specific factories live in
|
|
9
|
+
the plugin folder. The cross-cutting ``WorkflowEvent.connection_status``
|
|
10
|
+
factory in ``services/events/envelope.py`` is borderline (parametrized
|
|
11
|
+
by plugin but each plugin's payload differs) — Phase B moves the
|
|
12
|
+
plugin-specific shape into per-plugin factories.
|
|
13
|
+
|
|
14
|
+
Wire format (Wave 12 D4 — legacy ``android_status`` raw frame retired):
|
|
15
|
+
- Typed CloudEvents sibling: ``{type: "plugin_connection_status",
|
|
16
|
+
data: <WorkflowEvent envelope>}``
|
|
17
|
+
|
|
18
|
+
The FE handler at ``client/src/contexts/WebSocketContext.tsx::case
|
|
19
|
+
'plugin_connection_status'`` routes by ``envelope.source`` substring to
|
|
20
|
+
the matching Zustand setter.
|
|
21
|
+
|
|
22
|
+
Caller pattern (from ``_refresh.py`` / ``_relay/broadcaster.py``):
|
|
23
|
+
from nodes.android import broadcast_android_status
|
|
24
|
+
await broadcast_android_status(connected=True, device_id="...", ...)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from typing import Any, Dict, List, Optional
|
|
30
|
+
|
|
31
|
+
from services.events.envelope import WorkflowEvent
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Wire-routing key — outer ``type`` field on the WS frame.
|
|
35
|
+
_TYPED_WIRE_KEY = "plugin_connection_status"
|
|
36
|
+
|
|
37
|
+
# Module-level cache mirror — kept in sync with
|
|
38
|
+
# ``StatusBroadcaster._status["android"]`` until the framework's
|
|
39
|
+
# ``get_status()`` snapshot path migrates to a per-plugin getter
|
|
40
|
+
# registry. The plugin owns the SHAPE of this dict; the broadcaster
|
|
41
|
+
# stores it.
|
|
42
|
+
_DEFAULT_STATUS: Dict[str, Any] = {
|
|
43
|
+
"connected": False,
|
|
44
|
+
"paired": False,
|
|
45
|
+
"device_id": None,
|
|
46
|
+
"device_name": None,
|
|
47
|
+
"connected_devices": [],
|
|
48
|
+
"connection_type": None,
|
|
49
|
+
"qr_data": None,
|
|
50
|
+
"session_token": None,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---- Typed factory ---------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def android_connection_status(
|
|
58
|
+
*,
|
|
59
|
+
connected: bool,
|
|
60
|
+
paired: bool = False,
|
|
61
|
+
device_id: Optional[str] = None,
|
|
62
|
+
device_name: Optional[str] = None,
|
|
63
|
+
connected_devices: Optional[List[str]] = None,
|
|
64
|
+
connection_type: Optional[str] = None,
|
|
65
|
+
qr_data: Optional[str] = None,
|
|
66
|
+
session_token: Optional[str] = None,
|
|
67
|
+
) -> WorkflowEvent:
|
|
68
|
+
"""Build a typed CloudEvents envelope describing the android relay
|
|
69
|
+
connection state.
|
|
70
|
+
|
|
71
|
+
``subject`` is the device id so the FE can route per-device updates.
|
|
72
|
+
``data`` carries the full android status payload (mirrors
|
|
73
|
+
``_DEFAULT_STATUS`` shape).
|
|
74
|
+
"""
|
|
75
|
+
return WorkflowEvent(
|
|
76
|
+
source="machinaos://nodes/android",
|
|
77
|
+
type=("com.machinaos.android.connection.opened" if connected else "com.machinaos.android.connection.closed"),
|
|
78
|
+
subject=device_id,
|
|
79
|
+
data={
|
|
80
|
+
"connected": connected,
|
|
81
|
+
"paired": paired,
|
|
82
|
+
"device_id": device_id,
|
|
83
|
+
"device_name": device_name,
|
|
84
|
+
"connected_devices": list(connected_devices or []),
|
|
85
|
+
"connection_type": connection_type,
|
|
86
|
+
"qr_data": qr_data,
|
|
87
|
+
"session_token": session_token,
|
|
88
|
+
},
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# ---- Broadcaster wrapper ---------------------------------------------------
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def broadcast_android_status(
|
|
96
|
+
*,
|
|
97
|
+
connected: bool,
|
|
98
|
+
paired: bool = False,
|
|
99
|
+
device_id: Optional[str] = None,
|
|
100
|
+
device_name: Optional[str] = None,
|
|
101
|
+
connected_devices: Optional[List[str]] = None,
|
|
102
|
+
connection_type: Optional[str] = None,
|
|
103
|
+
qr_data: Optional[str] = None,
|
|
104
|
+
session_token: Optional[str] = None,
|
|
105
|
+
) -> None:
|
|
106
|
+
"""Update the android status cache + emit the typed
|
|
107
|
+
``plugin_connection_status`` CloudEvents envelope.
|
|
108
|
+
|
|
109
|
+
Replaces ``StatusBroadcaster.update_android_status``. The legacy
|
|
110
|
+
raw ``android_status`` frame retired in Wave 12 D4 — FE consumes
|
|
111
|
+
via the envelope-aware ``plugin_connection_status`` case in
|
|
112
|
+
``WebSocketContext.tsx`` (routes by ``envelope.source``).
|
|
113
|
+
"""
|
|
114
|
+
from services.status_broadcaster import get_status_broadcaster
|
|
115
|
+
|
|
116
|
+
broadcaster = get_status_broadcaster()
|
|
117
|
+
|
|
118
|
+
# Status cache lives on the broadcaster (still consumed by
|
|
119
|
+
# ``StatusBroadcaster.get_android_status()`` + the WS-connect
|
|
120
|
+
# initial-status snapshot). Plugin owns the SHAPE of the cache.
|
|
121
|
+
payload: Dict[str, Any] = {
|
|
122
|
+
"connected": connected,
|
|
123
|
+
"paired": paired,
|
|
124
|
+
"device_id": device_id,
|
|
125
|
+
"device_name": device_name,
|
|
126
|
+
"connected_devices": list(connected_devices or []),
|
|
127
|
+
"connection_type": connection_type,
|
|
128
|
+
"qr_data": qr_data,
|
|
129
|
+
"session_token": session_token,
|
|
130
|
+
}
|
|
131
|
+
broadcaster._status["android"] = payload
|
|
132
|
+
|
|
133
|
+
event = android_connection_status(
|
|
134
|
+
connected=connected,
|
|
135
|
+
paired=paired,
|
|
136
|
+
device_id=device_id,
|
|
137
|
+
device_name=device_name,
|
|
138
|
+
connected_devices=connected_devices,
|
|
139
|
+
connection_type=connection_type,
|
|
140
|
+
qr_data=qr_data,
|
|
141
|
+
session_token=session_token,
|
|
142
|
+
)
|
|
143
|
+
await broadcaster.broadcast(
|
|
144
|
+
{
|
|
145
|
+
"type": _TYPED_WIRE_KEY,
|
|
146
|
+
"data": event.model_dump(mode="json"),
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
__all__ = [
|
|
152
|
+
"android_connection_status",
|
|
153
|
+
"broadcast_android_status",
|
|
154
|
+
]
|
|
@@ -29,6 +29,12 @@ from services.status_broadcaster import get_status_broadcaster
|
|
|
29
29
|
logger = get_logger(__name__)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
async def handle_get_android_status(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
|
|
33
|
+
"""Get Android connection status (Wave 13.9 — moved from routers/websocket.py)."""
|
|
34
|
+
broadcaster = get_status_broadcaster()
|
|
35
|
+
return {"type": "android_status", "data": broadcaster.get_android_status()}
|
|
36
|
+
|
|
37
|
+
|
|
32
38
|
async def handle_get_android_devices(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
|
|
33
39
|
"""Get list of connected Android devices."""
|
|
34
40
|
from services.plugin.deps import get_android_service
|
|
@@ -49,16 +55,16 @@ async def handle_execute_android_action(data: Dict[str, Any], websocket: WebSock
|
|
|
49
55
|
|
|
50
56
|
await broadcaster.update_node_status(node_id, "executing")
|
|
51
57
|
result = await android_service.execute_service(
|
|
52
|
-
node_id=node_id,
|
|
58
|
+
node_id=node_id,
|
|
59
|
+
service_id=service_id,
|
|
60
|
+
action=action,
|
|
53
61
|
parameters=data.get("parameters", {}),
|
|
54
62
|
android_host=data.get("android_host", "localhost"),
|
|
55
|
-
android_port=data.get("android_port", 8888)
|
|
63
|
+
android_port=data.get("android_port", 8888),
|
|
56
64
|
)
|
|
57
65
|
|
|
58
66
|
status = "success" if result.get("success") else "error"
|
|
59
|
-
await broadcaster.update_node_status(
|
|
60
|
-
node_id, status, result.get("result") or {"error": result.get("error")}
|
|
61
|
-
)
|
|
67
|
+
await broadcaster.update_node_status(node_id, status, result.get("result") or {"error": result.get("error")})
|
|
62
68
|
return result
|
|
63
69
|
|
|
64
70
|
|
|
@@ -84,8 +90,7 @@ async def handle_android_relay_connect(data: Dict[str, Any], websocket: WebSocke
|
|
|
84
90
|
client, error = await get_relay_client(url, api_key)
|
|
85
91
|
if client:
|
|
86
92
|
logger.info(
|
|
87
|
-
f"[WebSocket] Android relay connect success, qr_data present: "
|
|
88
|
-
f"{bool(client.qr_data)}, session_token: {client.session_token}"
|
|
93
|
+
f"[WebSocket] Android relay connect success, qr_data present: " f"{bool(client.qr_data)}, session_token: {client.session_token}"
|
|
89
94
|
)
|
|
90
95
|
return {
|
|
91
96
|
"success": True,
|
|
@@ -146,6 +151,7 @@ async def handle_android_relay_reconnect(data: Dict[str, Any], websocket: WebSoc
|
|
|
146
151
|
|
|
147
152
|
|
|
148
153
|
WS_HANDLERS = {
|
|
154
|
+
"get_android_status": handle_get_android_status,
|
|
149
155
|
"get_android_devices": handle_get_android_devices,
|
|
150
156
|
"execute_android_action": handle_execute_android_action,
|
|
151
157
|
"android_relay_connect": handle_android_relay_connect,
|
|
@@ -36,9 +36,6 @@ async def load_service_actions(params: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
|
36
36
|
|
|
37
37
|
android_svc = get_android_service()
|
|
38
38
|
actions = await android_svc.list_actions(service_id) # type: ignore[attr-defined]
|
|
39
|
-
return [
|
|
40
|
-
{"value": a, "label": a.replace("_", " ").title()}
|
|
41
|
-
for a in actions or []
|
|
42
|
-
]
|
|
39
|
+
return [{"value": a, "label": a.replace("_", " ").title()} for a in actions or []]
|
|
43
40
|
except Exception:
|
|
44
41
|
return []
|
|
@@ -37,27 +37,27 @@ async def refresh_android_status(broadcaster: "StatusBroadcaster") -> None:
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
async def _auto_reconnect_body(broadcaster: "StatusBroadcaster", span) -> None:
|
|
40
|
+
# Single source of truth for android status emission — owns both
|
|
41
|
+
# the cache update + the dual-emit (legacy raw + typed sibling).
|
|
42
|
+
from ._events import broadcast_android_status
|
|
43
|
+
|
|
40
44
|
try:
|
|
41
45
|
# Already connected? Refresh the cached snapshot and stop.
|
|
42
46
|
from ._relay.manager import get_current_relay_client
|
|
43
47
|
|
|
44
48
|
existing = get_current_relay_client()
|
|
45
49
|
if existing and existing.is_connected():
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
await broadcast_android_status(
|
|
51
|
+
connected=True,
|
|
52
|
+
paired=existing.is_paired(),
|
|
53
|
+
device_id=existing.paired_device_id,
|
|
54
|
+
device_name=existing.paired_device_name,
|
|
55
|
+
connected_devices=list(existing.get_connected_devices()),
|
|
56
|
+
connection_type="relay",
|
|
57
|
+
qr_data=existing.qr_data,
|
|
58
|
+
session_token=existing.session_token,
|
|
59
|
+
)
|
|
56
60
|
logger.debug("[StatusBroadcaster] Android relay already connected")
|
|
57
|
-
await broadcaster.broadcast({
|
|
58
|
-
"type": "android_status",
|
|
59
|
-
"data": broadcaster._status["android"],
|
|
60
|
-
})
|
|
61
61
|
span.set_attribute("path", "already_connected")
|
|
62
62
|
return
|
|
63
63
|
|
|
@@ -77,9 +77,7 @@ async def _auto_reconnect_body(broadcaster: "StatusBroadcaster", span) -> None:
|
|
|
77
77
|
|
|
78
78
|
if not relay_url or not api_key:
|
|
79
79
|
span.set_attribute("path", "session_missing_creds")
|
|
80
|
-
logger.debug(
|
|
81
|
-
"[StatusBroadcaster] Stored session missing relay URL or API key"
|
|
82
|
-
)
|
|
80
|
+
logger.debug("[StatusBroadcaster] Stored session missing relay URL or API key")
|
|
83
81
|
return
|
|
84
82
|
|
|
85
83
|
span.set_attribute("path", "auto_reconnect")
|
|
@@ -98,30 +96,22 @@ async def _auto_reconnect_body(broadcaster: "StatusBroadcaster", span) -> None:
|
|
|
98
96
|
# The relay server creates a new session on each connect, so
|
|
99
97
|
# pairing may be lost -- mirror whatever the new client
|
|
100
98
|
# reports.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
await broadcaster.broadcast({
|
|
112
|
-
"type": "android_status",
|
|
113
|
-
"data": broadcaster._status["android"],
|
|
114
|
-
})
|
|
99
|
+
await broadcast_android_status(
|
|
100
|
+
connected=True,
|
|
101
|
+
paired=client.is_paired(),
|
|
102
|
+
device_id=client.paired_device_id,
|
|
103
|
+
device_name=client.paired_device_name,
|
|
104
|
+
connected_devices=list(client.get_connected_devices()),
|
|
105
|
+
connection_type="relay",
|
|
106
|
+
qr_data=client.qr_data,
|
|
107
|
+
session_token=client.session_token,
|
|
108
|
+
)
|
|
115
109
|
span.set_attribute("reconnect_ok", True)
|
|
116
110
|
else:
|
|
117
111
|
span.set_attribute("reconnect_ok", False)
|
|
118
|
-
logger.warning(
|
|
119
|
-
"[StatusBroadcaster] Failed to reconnect Android relay: %s", error
|
|
120
|
-
)
|
|
112
|
+
logger.warning("[StatusBroadcaster] Failed to reconnect Android relay: %s", error)
|
|
121
113
|
# Stored session is stale; drop it.
|
|
122
114
|
await database.clear_android_relay_session()
|
|
123
115
|
except Exception as exc: # noqa: BLE001 -- mirror pre-migration behaviour
|
|
124
116
|
span.record_exception(exc)
|
|
125
|
-
logger.debug(
|
|
126
|
-
"[StatusBroadcaster] Could not auto-reconnect Android relay: %s", exc
|
|
127
|
-
)
|
|
117
|
+
logger.debug("[StatusBroadcaster] Could not auto-reconnect Android relay: %s", exc)
|
|
@@ -1,39 +1,37 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Android Status Broadcaster
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Relay-specific helpers that translate relay-shaped arguments
|
|
5
|
+
(``devices: Set[str]``, derived ``connection_type="relay"``) into the
|
|
6
|
+
canonical :func:`nodes.android.broadcast_android_status` shape. Wave 12
|
|
7
|
+
B1 — the actual broadcast emission moved into
|
|
8
|
+
:mod:`nodes.android._events` (plugin-owned). This file just adapts
|
|
9
|
+
relay-state to that shape.
|
|
5
10
|
"""
|
|
11
|
+
|
|
6
12
|
from typing import Optional, Set
|
|
7
13
|
import structlog
|
|
8
14
|
|
|
9
15
|
logger = structlog.get_logger()
|
|
10
16
|
|
|
11
17
|
|
|
12
|
-
async def
|
|
18
|
+
async def _emit_relay_status(
|
|
13
19
|
connected: bool,
|
|
14
20
|
paired: bool = False,
|
|
15
21
|
device_id: Optional[str] = None,
|
|
16
22
|
device_name: Optional[str] = None,
|
|
17
23
|
devices: Optional[Set[str]] = None,
|
|
18
24
|
qr_data: Optional[str] = None,
|
|
19
|
-
session_token: Optional[str] = None
|
|
25
|
+
session_token: Optional[str] = None,
|
|
20
26
|
):
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Args:
|
|
25
|
-
connected: Whether connected to relay server
|
|
26
|
-
paired: Whether paired with Android device
|
|
27
|
-
device_id: Paired device ID (if any)
|
|
28
|
-
device_name: Paired device name (if any)
|
|
29
|
-
devices: Set of all connected device IDs
|
|
30
|
-
qr_data: QR code data for pairing (app://pair?token=...)
|
|
31
|
-
session_token: Relay session token
|
|
32
|
-
"""
|
|
27
|
+
"""Translate relay-shaped args and route to the canonical
|
|
28
|
+
plugin-owned broadcaster (``nodes.android._events``)."""
|
|
33
29
|
try:
|
|
34
|
-
from
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
# Import from the plugin package — single source of truth
|
|
31
|
+
# for the android status wire format (Wave 12 B1).
|
|
32
|
+
from nodes.android import broadcast_android_status
|
|
33
|
+
|
|
34
|
+
await broadcast_android_status(
|
|
37
35
|
connected=connected,
|
|
38
36
|
paired=paired,
|
|
39
37
|
device_id=device_id,
|
|
@@ -41,7 +39,7 @@ async def broadcast_android_status(
|
|
|
41
39
|
connected_devices=list(devices) if devices else [],
|
|
42
40
|
connection_type="relay" if connected else None,
|
|
43
41
|
qr_data=qr_data,
|
|
44
|
-
session_token=session_token
|
|
42
|
+
session_token=session_token,
|
|
45
43
|
)
|
|
46
44
|
except Exception as e:
|
|
47
45
|
logger.warning("[Android] Failed to broadcast status", error=str(e))
|
|
@@ -49,20 +47,12 @@ async def broadcast_android_status(
|
|
|
49
47
|
|
|
50
48
|
async def broadcast_connected(device_id: str, device_name: Optional[str] = None):
|
|
51
49
|
"""Broadcast that Android device is paired"""
|
|
52
|
-
await
|
|
53
|
-
connected=True,
|
|
54
|
-
paired=True,
|
|
55
|
-
device_id=device_id,
|
|
56
|
-
device_name=device_name,
|
|
57
|
-
devices={device_id} if device_id else set()
|
|
50
|
+
await _emit_relay_status(
|
|
51
|
+
connected=True, paired=True, device_id=device_id, device_name=device_name, devices={device_id} if device_id else set()
|
|
58
52
|
)
|
|
59
53
|
|
|
60
54
|
|
|
61
|
-
async def broadcast_device_disconnected(
|
|
62
|
-
relay_connected: bool = True,
|
|
63
|
-
qr_data: Optional[str] = None,
|
|
64
|
-
session_token: Optional[str] = None
|
|
65
|
-
):
|
|
55
|
+
async def broadcast_device_disconnected(relay_connected: bool = True, qr_data: Optional[str] = None, session_token: Optional[str] = None):
|
|
66
56
|
"""Broadcast that Android device is disconnected (but relay may still be connected).
|
|
67
57
|
|
|
68
58
|
This is called when the Android device unpairs. The relay connection may still be active.
|
|
@@ -73,26 +63,20 @@ async def broadcast_device_disconnected(
|
|
|
73
63
|
qr_data: QR code data for re-pairing
|
|
74
64
|
session_token: Current session token
|
|
75
65
|
"""
|
|
76
|
-
await
|
|
66
|
+
await _emit_relay_status(
|
|
77
67
|
connected=relay_connected, # Relay may still be connected
|
|
78
68
|
paired=False, # Device is disconnected
|
|
79
69
|
device_id=None,
|
|
80
70
|
device_name=None,
|
|
81
71
|
devices=set(),
|
|
82
72
|
qr_data=qr_data, # Keep QR data for re-pairing
|
|
83
|
-
session_token=session_token
|
|
73
|
+
session_token=session_token,
|
|
84
74
|
)
|
|
85
75
|
|
|
86
76
|
|
|
87
77
|
async def broadcast_relay_disconnected():
|
|
88
78
|
"""Broadcast that relay connection is closed (fully disconnected)"""
|
|
89
|
-
await
|
|
90
|
-
connected=False,
|
|
91
|
-
paired=False,
|
|
92
|
-
device_id=None,
|
|
93
|
-
device_name=None,
|
|
94
|
-
devices=set()
|
|
95
|
-
)
|
|
79
|
+
await _emit_relay_status(connected=False, paired=False, device_id=None, device_name=None, devices=set())
|
|
96
80
|
|
|
97
81
|
|
|
98
82
|
# Legacy alias for backwards compatibility
|
|
@@ -103,12 +87,12 @@ async def broadcast_disconnected():
|
|
|
103
87
|
|
|
104
88
|
async def broadcast_qr_code(qr_data: str, session_token: Optional[str] = None):
|
|
105
89
|
"""Broadcast QR code for pairing"""
|
|
106
|
-
await
|
|
90
|
+
await _emit_relay_status(
|
|
107
91
|
connected=True, # Connected to relay but not paired
|
|
108
92
|
paired=False,
|
|
109
93
|
device_id=None,
|
|
110
94
|
device_name=None,
|
|
111
95
|
devices=set(),
|
|
112
96
|
qr_data=qr_data,
|
|
113
|
-
session_token=session_token
|
|
97
|
+
session_token=session_token,
|
|
114
98
|
)
|
|
@@ -10,6 +10,7 @@ Connection flow:
|
|
|
10
10
|
4. Receive pairing.connected when Android pairs
|
|
11
11
|
5. Exchange messages via relay.send / relay.message
|
|
12
12
|
"""
|
|
13
|
+
|
|
13
14
|
import asyncio
|
|
14
15
|
import json
|
|
15
16
|
import uuid
|
|
@@ -18,12 +19,7 @@ from typing import Optional, Dict, Any, Set, Callable
|
|
|
18
19
|
import structlog
|
|
19
20
|
|
|
20
21
|
from .protocol import RPCResponse, RPCRequestTracker, is_response
|
|
21
|
-
from .broadcaster import
|
|
22
|
-
broadcast_connected,
|
|
23
|
-
broadcast_device_disconnected,
|
|
24
|
-
broadcast_relay_disconnected,
|
|
25
|
-
broadcast_qr_code
|
|
26
|
-
)
|
|
22
|
+
from .broadcaster import broadcast_connected, broadcast_device_disconnected, broadcast_relay_disconnected, broadcast_qr_code
|
|
27
23
|
|
|
28
24
|
logger = structlog.get_logger()
|
|
29
25
|
|
|
@@ -90,7 +86,7 @@ class RelayWebSocketClient:
|
|
|
90
86
|
self.url,
|
|
91
87
|
heartbeat=30,
|
|
92
88
|
autoping=True,
|
|
93
|
-
ssl=True # Explicit SSL for wss://
|
|
89
|
+
ssl=True, # Explicit SSL for wss://
|
|
94
90
|
)
|
|
95
91
|
self.connected = True
|
|
96
92
|
self._running = True
|
|
@@ -111,9 +107,7 @@ class RelayWebSocketClient:
|
|
|
111
107
|
self.session_token = params.get("session_token")
|
|
112
108
|
self.qr_data = params.get("qr_data")
|
|
113
109
|
|
|
114
|
-
logger.info("[Relay] Connection established",
|
|
115
|
-
session_token=self.session_token,
|
|
116
|
-
has_qr=bool(self.qr_data))
|
|
110
|
+
logger.info("[Relay] Connection established", session_token=self.session_token, has_qr=bool(self.qr_data))
|
|
117
111
|
|
|
118
112
|
# Broadcast QR data to frontend
|
|
119
113
|
if self.qr_data:
|
|
@@ -208,6 +202,7 @@ class RelayWebSocketClient:
|
|
|
208
202
|
"""Clear stored pairing session from database."""
|
|
209
203
|
try:
|
|
210
204
|
from services.plugin.deps import get_database
|
|
205
|
+
|
|
211
206
|
database = get_database()
|
|
212
207
|
|
|
213
208
|
await database.clear_android_relay_session()
|
|
@@ -277,11 +272,7 @@ class RelayWebSocketClient:
|
|
|
277
272
|
await asyncio.sleep(25)
|
|
278
273
|
if self._running and self.ws and not self.ws.closed:
|
|
279
274
|
try:
|
|
280
|
-
await self.ws.send_json({
|
|
281
|
-
"jsonrpc": "2.0",
|
|
282
|
-
"method": "ping",
|
|
283
|
-
"params": {}
|
|
284
|
-
})
|
|
275
|
+
await self.ws.send_json({"jsonrpc": "2.0", "method": "ping", "params": {}})
|
|
285
276
|
except Exception as e:
|
|
286
277
|
logger.error("[Relay] Keepalive error", error=str(e))
|
|
287
278
|
self._running = False
|
|
@@ -335,9 +326,7 @@ class RelayWebSocketClient:
|
|
|
335
326
|
self.paired_device_id = params.get("device_id")
|
|
336
327
|
self.paired_device_name = params.get("device_name")
|
|
337
328
|
|
|
338
|
-
logger.info("[Relay] Android paired",
|
|
339
|
-
device_id=self.paired_device_id,
|
|
340
|
-
device_name=self.paired_device_name)
|
|
329
|
+
logger.info("[Relay] Android paired", device_id=self.paired_device_id, device_name=self.paired_device_name)
|
|
341
330
|
|
|
342
331
|
await broadcast_connected(self.paired_device_id, self.paired_device_name)
|
|
343
332
|
|
|
@@ -353,9 +342,9 @@ class RelayWebSocketClient:
|
|
|
353
342
|
self.paired_device_id = params.get("device_id")
|
|
354
343
|
self.paired_device_name = params.get("device_name")
|
|
355
344
|
|
|
356
|
-
logger.info(
|
|
357
|
-
|
|
358
|
-
|
|
345
|
+
logger.info(
|
|
346
|
+
"[Relay] Android pairing restored (auto-reconnect)", device_id=self.paired_device_id, device_name=self.paired_device_name
|
|
347
|
+
)
|
|
359
348
|
|
|
360
349
|
await broadcast_connected(self.paired_device_id, self.paired_device_name)
|
|
361
350
|
|
|
@@ -369,6 +358,7 @@ class RelayWebSocketClient:
|
|
|
369
358
|
"""Save pairing session to database for auto-reconnect."""
|
|
370
359
|
try:
|
|
371
360
|
from services.plugin.deps import get_database
|
|
361
|
+
|
|
372
362
|
database = get_database()
|
|
373
363
|
|
|
374
364
|
await database.save_android_relay_session(
|
|
@@ -376,7 +366,7 @@ class RelayWebSocketClient:
|
|
|
376
366
|
api_key=self.api_key,
|
|
377
367
|
device_id=self.paired_device_id,
|
|
378
368
|
device_name=self.paired_device_name,
|
|
379
|
-
session_token=self.session_token
|
|
369
|
+
session_token=self.session_token,
|
|
380
370
|
)
|
|
381
371
|
logger.debug("[Relay] Pairing session saved for auto-reconnect")
|
|
382
372
|
except Exception as e:
|
|
@@ -396,11 +386,7 @@ class RelayWebSocketClient:
|
|
|
396
386
|
self.paired_device_name = None
|
|
397
387
|
|
|
398
388
|
# Broadcast device disconnection - relay is still connected, pass QR data for re-pairing
|
|
399
|
-
await broadcast_device_disconnected(
|
|
400
|
-
relay_connected=self.is_connected(),
|
|
401
|
-
qr_data=self.qr_data,
|
|
402
|
-
session_token=self.session_token
|
|
403
|
-
)
|
|
389
|
+
await broadcast_device_disconnected(relay_connected=self.is_connected(), qr_data=self.qr_data, session_token=self.session_token)
|
|
404
390
|
|
|
405
391
|
if self.on_pairing_disconnected:
|
|
406
392
|
await self.on_pairing_disconnected(params)
|
|
@@ -414,8 +400,7 @@ class RelayWebSocketClient:
|
|
|
414
400
|
# Schema: params = {"data": {...}}
|
|
415
401
|
data = params.get("data", {})
|
|
416
402
|
|
|
417
|
-
logger.debug("[Relay] relay.message received",
|
|
418
|
-
data_keys=list(data.keys()) if isinstance(data, dict) else "not_dict")
|
|
403
|
+
logger.debug("[Relay] relay.message received", data_keys=list(data.keys()) if isinstance(data, dict) else "not_dict")
|
|
419
404
|
|
|
420
405
|
# Route to service response queue if matching request_id
|
|
421
406
|
# Android app uses "request_id" (underscore), not "requestId" (camelCase)
|
|
@@ -505,7 +490,7 @@ class RelayWebSocketClient:
|
|
|
505
490
|
action: str,
|
|
506
491
|
parameters: Dict[str, Any] = None,
|
|
507
492
|
target_id: Optional[str] = None, # Ignored, kept for compatibility
|
|
508
|
-
timeout: float = 30.0
|
|
493
|
+
timeout: float = 30.0,
|
|
509
494
|
) -> Optional[Dict[str, Any]]:
|
|
510
495
|
"""
|
|
511
496
|
Send service request to paired Android device.
|
|
@@ -535,17 +520,11 @@ class RelayWebSocketClient:
|
|
|
535
520
|
# - action
|
|
536
521
|
# - request_id (not requestId)
|
|
537
522
|
# - params (not parameters)
|
|
538
|
-
await self.relay_send(
|
|
539
|
-
"service": service_id,
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}, timeout=5.0)
|
|
544
|
-
|
|
545
|
-
logger.debug("[Relay] Sent service request",
|
|
546
|
-
request_id=request_id,
|
|
547
|
-
service_id=service_id,
|
|
548
|
-
action=action)
|
|
523
|
+
await self.relay_send(
|
|
524
|
+
{"service": service_id, "action": action, "request_id": request_id, "params": parameters or {}}, timeout=5.0
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
logger.debug("[Relay] Sent service request", request_id=request_id, service_id=service_id, action=action)
|
|
549
528
|
|
|
550
529
|
# Wait for response
|
|
551
530
|
logger.debug("[Relay] Waiting for response", request_id=request_id, timeout=timeout)
|
|
@@ -554,7 +533,9 @@ class RelayWebSocketClient:
|
|
|
554
533
|
return response
|
|
555
534
|
|
|
556
535
|
except asyncio.TimeoutError:
|
|
557
|
-
logger.warning(
|
|
536
|
+
logger.warning(
|
|
537
|
+
"[Relay] Service response timeout", request_id=request_id, timeout=timeout, pending_queues=list(self._service_queues.keys())
|
|
538
|
+
)
|
|
558
539
|
return None
|
|
559
540
|
except Exception as e:
|
|
560
541
|
logger.error("[Relay] Service request error", error=str(e))
|
|
@@ -15,6 +15,7 @@ Error codes:
|
|
|
15
15
|
- -32002: Pairing failed
|
|
16
16
|
- -32003: Relay error
|
|
17
17
|
"""
|
|
18
|
+
|
|
18
19
|
import asyncio
|
|
19
20
|
from dataclasses import dataclass, field
|
|
20
21
|
from typing import Dict, Any, Optional
|
|
@@ -23,6 +24,7 @@ from enum import Enum
|
|
|
23
24
|
|
|
24
25
|
class RPCErrorCode(Enum):
|
|
25
26
|
"""JSON-RPC 2.0 error codes"""
|
|
27
|
+
|
|
26
28
|
INVALID_REQUEST = -32600
|
|
27
29
|
METHOD_NOT_FOUND = -32601
|
|
28
30
|
INVALID_PARAMS = -32602
|
|
@@ -34,6 +36,7 @@ class RPCErrorCode(Enum):
|
|
|
34
36
|
@dataclass
|
|
35
37
|
class RPCRequest:
|
|
36
38
|
"""JSON-RPC 2.0 request"""
|
|
39
|
+
|
|
37
40
|
method: str
|
|
38
41
|
params: Dict[str, Any] = field(default_factory=dict)
|
|
39
42
|
id: Optional[int] = None
|
|
@@ -52,6 +55,7 @@ class RPCRequest:
|
|
|
52
55
|
@dataclass
|
|
53
56
|
class RPCResponse:
|
|
54
57
|
"""JSON-RPC 2.0 response"""
|
|
58
|
+
|
|
55
59
|
id: int
|
|
56
60
|
result: Optional[Any] = None
|
|
57
61
|
error: Optional[Dict[str, Any]] = None
|
|
@@ -72,6 +76,7 @@ class RPCResponse:
|
|
|
72
76
|
@dataclass
|
|
73
77
|
class RPCEvent:
|
|
74
78
|
"""JSON-RPC 2.0 server event (notification without id)"""
|
|
79
|
+
|
|
75
80
|
method: str
|
|
76
81
|
params: Dict[str, Any] = field(default_factory=dict)
|
|
77
82
|
|
|
@@ -142,6 +147,7 @@ class RPCRequestTracker:
|
|
|
142
147
|
|
|
143
148
|
class RPCError(Exception):
|
|
144
149
|
"""JSON-RPC error"""
|
|
150
|
+
|
|
145
151
|
def __init__(self, message: str, code: int = None):
|
|
146
152
|
super().__init__(message)
|
|
147
153
|
self.code = code
|