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,103 @@
|
|
|
1
|
+
"""Plugin-owned shutdown-hook registry for FastAPI lifespan teardown.
|
|
2
|
+
|
|
3
|
+
Closes the cross-plugin reaches in ``main.py``:
|
|
4
|
+
|
|
5
|
+
- ``from nodes.android._relay.manager import close_relay_client``
|
|
6
|
+
- ``from nodes.browser._service import shutdown_browser_service``
|
|
7
|
+
|
|
8
|
+
Both made the framework's lifespan code know about specific plugins'
|
|
9
|
+
internals. Plugins with cleanup logic now self-register a hook; the
|
|
10
|
+
lifespan awaits all registered hooks via :func:`run_shutdown_hooks`.
|
|
11
|
+
|
|
12
|
+
Distinct from :mod:`services._supervisor`: that registry is for
|
|
13
|
+
managed-subprocess supervisors (Go binaries, etc.) with a well-defined
|
|
14
|
+
``shutdown()`` contract. This registry is the general async-cleanup
|
|
15
|
+
hatch — any coroutine-shaped teardown (close a connection, drain a
|
|
16
|
+
queue, flush a cache, ...) goes here.
|
|
17
|
+
|
|
18
|
+
Hook signature
|
|
19
|
+
--------------
|
|
20
|
+
|
|
21
|
+
::
|
|
22
|
+
|
|
23
|
+
hook() -> Awaitable[None]
|
|
24
|
+
|
|
25
|
+
The hook is named (label) so failures during shutdown surface with a
|
|
26
|
+
plugin identifier instead of just a traceback. Failures DO NOT block
|
|
27
|
+
sibling hooks — every hook runs even if one raises, mirroring the
|
|
28
|
+
``shutdown_all_supervisors`` semantics.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
from typing import Awaitable, Callable, List, Tuple
|
|
34
|
+
|
|
35
|
+
from core.logging import get_logger
|
|
36
|
+
from services.plugin.registry import IdempotentRegistry
|
|
37
|
+
|
|
38
|
+
logger = get_logger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
ShutdownHook = Callable[[], Awaitable[None]]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Registry is keyed by label so each plugin can register exactly one
|
|
45
|
+
# hook under a stable name. The `items` view preserves insertion order
|
|
46
|
+
# (CPython 3.7+ dict ordering guarantee) which gives deterministic
|
|
47
|
+
# shutdown ordering. Plugins that need ordering invariants should
|
|
48
|
+
# document them via their label naming.
|
|
49
|
+
_REGISTRY: IdempotentRegistry[str, ShutdownHook] = IdempotentRegistry("plugin_shutdown_hook")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def register_shutdown_hook(label: str, hook: ShutdownHook) -> None:
|
|
53
|
+
"""Publish a shutdown hook for FastAPI lifespan teardown.
|
|
54
|
+
|
|
55
|
+
Idempotent on re-import (same callable for the same label is a
|
|
56
|
+
no-op). A different callable for an existing label raises
|
|
57
|
+
``ValueError`` to surface plugin namespace collisions at import time.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
label: Stable plugin identifier (``"android_relay"``,
|
|
61
|
+
``"browser_service"``, ...). Surfaces in shutdown logs +
|
|
62
|
+
in the error message if the hook raises.
|
|
63
|
+
hook: Async function taking no arguments. Should be
|
|
64
|
+
idempotent (lifespan may run it multiple times during
|
|
65
|
+
test harness teardown).
|
|
66
|
+
"""
|
|
67
|
+
_REGISTRY.register(label, hook)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def registered_labels() -> Tuple[str, ...]:
|
|
71
|
+
"""Return registered hook labels in insertion order (snapshot)."""
|
|
72
|
+
return tuple(_REGISTRY.keys())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def run_shutdown_hooks() -> None:
|
|
76
|
+
"""Run every registered shutdown hook in registration order.
|
|
77
|
+
|
|
78
|
+
Per-hook failures are caught + logged with the hook's label so
|
|
79
|
+
one slow / broken plugin doesn't strand the rest of the lifespan
|
|
80
|
+
teardown. Mirrors ``services._supervisor.shutdown_all_supervisors``
|
|
81
|
+
semantics.
|
|
82
|
+
"""
|
|
83
|
+
hooks: List[Tuple[str, ShutdownHook]] = list(_REGISTRY.items().items())
|
|
84
|
+
if not hooks:
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
logger.info(f"Running {len(hooks)} plugin shutdown hook(s): " f"{[label for label, _ in hooks]}")
|
|
88
|
+
for label, hook in hooks:
|
|
89
|
+
try:
|
|
90
|
+
await hook()
|
|
91
|
+
except Exception as exc: # noqa: BLE001 — log and continue
|
|
92
|
+
logger.error(
|
|
93
|
+
f"Plugin shutdown hook {label!r} raised; continuing: {exc}",
|
|
94
|
+
exc_info=True,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
__all__ = [
|
|
99
|
+
"register_shutdown_hook",
|
|
100
|
+
"registered_labels",
|
|
101
|
+
"run_shutdown_hooks",
|
|
102
|
+
"ShutdownHook",
|
|
103
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Plugin-owned dispatch registry for the social-messaging facade.
|
|
2
|
+
|
|
3
|
+
Closes the cross-plugin reach from ``nodes/social/_base.py:478`` —
|
|
4
|
+
``from nodes.whatsapp._service import handle_whatsapp_send`` — which
|
|
5
|
+
made the ``social`` plugin depend on the ``whatsapp`` plugin's
|
|
6
|
+
internals and violated the "framework knows no plugin names" rule.
|
|
7
|
+
|
|
8
|
+
Each social platform plugin (whatsapp, telegram, slack, discord, …)
|
|
9
|
+
registers a send handler keyed by the platform identifier from its
|
|
10
|
+
own ``__init__.py``. The social node queries the registry instead of
|
|
11
|
+
importing platform internals — same Wave-11.I plugin-self-registration
|
|
12
|
+
pattern as ``register_filter_builder`` / ``register_poll_coroutine_factory``
|
|
13
|
+
/ ``register_ws_handlers`` / ``register_canary_trigger_type``.
|
|
14
|
+
|
|
15
|
+
Handler signature
|
|
16
|
+
-----------------
|
|
17
|
+
|
|
18
|
+
::
|
|
19
|
+
|
|
20
|
+
handler(params: Dict[str, Any]) -> Awaitable[Dict[str, Any]]
|
|
21
|
+
|
|
22
|
+
The social node maps its own parameter shape onto the platform's
|
|
23
|
+
expected shape and calls the handler. The handler returns the
|
|
24
|
+
platform's native result dict; the social node passes that through.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from typing import Any, Awaitable, Callable, Dict, Optional
|
|
30
|
+
|
|
31
|
+
from services.plugin.registry import IdempotentRegistry
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# (params: Dict) -> Awaitable[Dict]
|
|
35
|
+
SocialSendHandler = Callable[[Dict[str, Any]], Awaitable[Dict[str, Any]]]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
_REGISTRY: IdempotentRegistry[str, SocialSendHandler] = IdempotentRegistry("social_send_handler")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def register_social_send_handler(platform: str, handler: SocialSendHandler) -> None:
|
|
42
|
+
"""Publish a send handler for one social platform.
|
|
43
|
+
|
|
44
|
+
Idempotent on re-import (same callable for the same platform key
|
|
45
|
+
is a no-op). A different callable for an existing platform raises
|
|
46
|
+
``ValueError`` to surface plugin namespace collisions at import time.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
platform: Lower-case platform identifier (``"whatsapp"``,
|
|
50
|
+
``"telegram"``, …). Matches the value the social node's
|
|
51
|
+
``platform`` parameter holds at runtime.
|
|
52
|
+
handler: Async function accepting platform-specific
|
|
53
|
+
``params: Dict`` and returning the platform's native
|
|
54
|
+
result dict. The social node builds the params dict by
|
|
55
|
+
mapping its generic shape onto the platform's keys.
|
|
56
|
+
"""
|
|
57
|
+
_REGISTRY.register(platform, handler)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_social_send_handler(platform: str) -> Optional[SocialSendHandler]:
|
|
61
|
+
"""Return the handler for ``platform``, or ``None`` if unregistered.
|
|
62
|
+
|
|
63
|
+
A ``None`` return surfaces as a clear "unsupported platform" error
|
|
64
|
+
at the social node call site instead of an ``ImportError`` deep
|
|
65
|
+
inside the platform's ``_service.py``.
|
|
66
|
+
"""
|
|
67
|
+
return _REGISTRY.get(platform)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def registered_platforms() -> frozenset[str]:
|
|
71
|
+
"""Return an immutable snapshot of registered platform identifiers."""
|
|
72
|
+
return frozenset(_REGISTRY.keys())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
__all__ = [
|
|
76
|
+
"register_social_send_handler",
|
|
77
|
+
"get_social_send_handler",
|
|
78
|
+
"registered_platforms",
|
|
79
|
+
"SocialSendHandler",
|
|
80
|
+
]
|
|
@@ -64,7 +64,8 @@ def ws_response(handler: WSHandlerFn) -> WSHandlerFn:
|
|
|
64
64
|
|
|
65
65
|
@wraps(handler)
|
|
66
66
|
async def wrapper(
|
|
67
|
-
data: Dict[str, Any],
|
|
67
|
+
data: Dict[str, Any],
|
|
68
|
+
websocket: WebSocket,
|
|
68
69
|
) -> Dict[str, Any]:
|
|
69
70
|
try:
|
|
70
71
|
return await handler(data, websocket)
|
|
@@ -26,10 +26,11 @@ CONFIG_PATH = Path(__file__).parent.parent / "config" / "pricing.json"
|
|
|
26
26
|
@dataclass
|
|
27
27
|
class ModelPricing:
|
|
28
28
|
"""Pricing for a specific LLM model."""
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
input_per_mtok: float # USD per 1M input tokens
|
|
30
31
|
output_per_mtok: float # USD per 1M output tokens
|
|
31
32
|
cache_read_per_mtok: Optional[float] = None # USD per 1M cache read tokens (Anthropic)
|
|
32
|
-
reasoning_per_mtok: Optional[float] = None
|
|
33
|
+
reasoning_per_mtok: Optional[float] = None # USD per 1M reasoning tokens (OpenAI o-series)
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
class PricingService:
|
|
@@ -44,7 +45,7 @@ class PricingService:
|
|
|
44
45
|
"""Load pricing config from JSON file."""
|
|
45
46
|
if CONFIG_PATH.exists():
|
|
46
47
|
try:
|
|
47
|
-
with open(CONFIG_PATH,
|
|
48
|
+
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
|
48
49
|
self._config = json.load(f)
|
|
49
50
|
self._build_llm_registry()
|
|
50
51
|
logger.info(f"[Pricing] Loaded pricing config v{self._config.get('version', 'unknown')}")
|
|
@@ -97,7 +98,7 @@ class PricingService:
|
|
|
97
98
|
# Ensure config directory exists
|
|
98
99
|
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
99
100
|
|
|
100
|
-
with open(CONFIG_PATH,
|
|
101
|
+
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
|
|
101
102
|
json.dump(config, f, indent=2)
|
|
102
103
|
|
|
103
104
|
# Reload the config into memory
|
|
@@ -128,7 +129,7 @@ class PricingService:
|
|
|
128
129
|
ModelPricing with rates per million tokens
|
|
129
130
|
"""
|
|
130
131
|
provider_lower = provider.lower()
|
|
131
|
-
model_lower = model.lower() if model else
|
|
132
|
+
model_lower = model.lower() if model else ""
|
|
132
133
|
|
|
133
134
|
provider_pricing = self._llm_registry.get(provider_lower, {})
|
|
134
135
|
|
|
@@ -138,16 +139,16 @@ class PricingService:
|
|
|
138
139
|
|
|
139
140
|
# Try partial match (model name starts with a known key)
|
|
140
141
|
for model_key, pricing in provider_pricing.items():
|
|
141
|
-
if model_key !=
|
|
142
|
+
if model_key != "_default" and model_lower.startswith(model_key):
|
|
142
143
|
return pricing
|
|
143
144
|
|
|
144
145
|
# Try if any key is contained in the model name
|
|
145
146
|
for model_key, pricing in provider_pricing.items():
|
|
146
|
-
if model_key !=
|
|
147
|
+
if model_key != "_default" and model_key in model_lower:
|
|
147
148
|
return pricing
|
|
148
149
|
|
|
149
150
|
# Fall back to provider default
|
|
150
|
-
default_pricing = provider_pricing.get(
|
|
151
|
+
default_pricing = provider_pricing.get("_default")
|
|
151
152
|
if default_pricing:
|
|
152
153
|
return default_pricing
|
|
153
154
|
|
|
@@ -163,7 +164,7 @@ class PricingService:
|
|
|
163
164
|
output_tokens: int,
|
|
164
165
|
cache_read_tokens: int = 0,
|
|
165
166
|
cache_creation_tokens: int = 0,
|
|
166
|
-
reasoning_tokens: int = 0
|
|
167
|
+
reasoning_tokens: int = 0,
|
|
167
168
|
) -> Dict[str, float]:
|
|
168
169
|
"""Calculate cost for LLM token usage.
|
|
169
170
|
|
|
@@ -206,11 +207,11 @@ class PricingService:
|
|
|
206
207
|
total_cost = input_cost + output_cost + cache_cost + reasoning_cost
|
|
207
208
|
|
|
208
209
|
return {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
"input_cost": round(input_cost, 6),
|
|
211
|
+
"output_cost": round(output_cost, 6),
|
|
212
|
+
"cache_cost": round(cache_cost, 6),
|
|
213
|
+
"reasoning_cost": round(reasoning_cost, 6),
|
|
214
|
+
"total_cost": round(total_cost, 6),
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
def get_all_pricing(self) -> Dict[str, Dict[str, Dict[str, float]]]:
|
|
@@ -224,10 +225,10 @@ class PricingService:
|
|
|
224
225
|
result[provider] = {}
|
|
225
226
|
for model, pricing in models.items():
|
|
226
227
|
result[provider][model] = {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
228
|
+
"input": pricing.input_per_mtok,
|
|
229
|
+
"output": pricing.output_per_mtok,
|
|
230
|
+
"cache_read": pricing.cache_read_per_mtok,
|
|
231
|
+
"reasoning": pricing.reasoning_per_mtok,
|
|
231
232
|
}
|
|
232
233
|
return result
|
|
233
234
|
|
|
@@ -245,7 +246,7 @@ class PricingService:
|
|
|
245
246
|
Returns:
|
|
246
247
|
USD cost per resource/request
|
|
247
248
|
"""
|
|
248
|
-
api_config = self._config.get(
|
|
249
|
+
api_config = self._config.get("api", {})
|
|
249
250
|
service_config = api_config.get(service, {})
|
|
250
251
|
return service_config.get(operation, 0.0)
|
|
251
252
|
|
|
@@ -258,10 +259,10 @@ class PricingService:
|
|
|
258
259
|
Returns:
|
|
259
260
|
Dict of {operation: price} for the service
|
|
260
261
|
"""
|
|
261
|
-
api_config = self._config.get(
|
|
262
|
+
api_config = self._config.get("api", {})
|
|
262
263
|
service_config = api_config.get(service, {})
|
|
263
264
|
# Filter out metadata keys starting with _
|
|
264
|
-
return {k: v for k, v in service_config.items() if not k.startswith(
|
|
265
|
+
return {k: v for k, v in service_config.items() if not k.startswith("_") and isinstance(v, (int, float))}
|
|
265
266
|
|
|
266
267
|
def map_action_to_operation(self, service: str, action: str) -> Optional[str]:
|
|
267
268
|
"""Map handler action to pricing operation.
|
|
@@ -273,16 +274,11 @@ class PricingService:
|
|
|
273
274
|
Returns:
|
|
274
275
|
Pricing operation key or None if no mapping
|
|
275
276
|
"""
|
|
276
|
-
operation_map = self._config.get(
|
|
277
|
+
operation_map = self._config.get("operation_map", {})
|
|
277
278
|
service_map = operation_map.get(service, {})
|
|
278
279
|
return service_map.get(action)
|
|
279
280
|
|
|
280
|
-
def calculate_api_cost(
|
|
281
|
-
self,
|
|
282
|
-
service: str,
|
|
283
|
-
action: str,
|
|
284
|
-
resource_count: int = 1
|
|
285
|
-
) -> Dict[str, Any]:
|
|
281
|
+
def calculate_api_cost(self, service: str, action: str, resource_count: int = 1) -> Dict[str, Any]:
|
|
286
282
|
"""Calculate cost for API usage.
|
|
287
283
|
|
|
288
284
|
Args:
|
|
@@ -299,22 +295,12 @@ class PricingService:
|
|
|
299
295
|
"""
|
|
300
296
|
operation = self.map_action_to_operation(service, action)
|
|
301
297
|
if not operation:
|
|
302
|
-
return {
|
|
303
|
-
'operation': action,
|
|
304
|
-
'unit_cost': 0.0,
|
|
305
|
-
'resource_count': resource_count,
|
|
306
|
-
'total_cost': 0.0
|
|
307
|
-
}
|
|
298
|
+
return {"operation": action, "unit_cost": 0.0, "resource_count": resource_count, "total_cost": 0.0}
|
|
308
299
|
|
|
309
300
|
unit_cost = self.get_api_price(service, operation)
|
|
310
301
|
total_cost = unit_cost * resource_count
|
|
311
302
|
|
|
312
|
-
return {
|
|
313
|
-
'operation': operation,
|
|
314
|
-
'unit_cost': unit_cost,
|
|
315
|
-
'resource_count': resource_count,
|
|
316
|
-
'total_cost': round(total_cost, 6)
|
|
317
|
-
}
|
|
303
|
+
return {"operation": operation, "unit_cost": unit_cost, "resource_count": resource_count, "total_cost": round(total_cost, 6)}
|
|
318
304
|
|
|
319
305
|
|
|
320
306
|
# Singleton instance
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Pricing-domain WS handlers — Wave 13.8 extraction.
|
|
2
|
+
|
|
3
|
+
Sibling module to ``services/pricing.py`` (the PricingService singleton);
|
|
4
|
+
kept flat instead of a subpackage to avoid renaming the existing
|
|
5
|
+
``services.pricing`` import path used across handlers.
|
|
6
|
+
|
|
7
|
+
Side-effect import (from ``main.py``) registers the 3 handlers below
|
|
8
|
+
into ``ws_handler_registry``:
|
|
9
|
+
- ``get_pricing_config`` — return full pricing config for the
|
|
10
|
+
Settings panel.
|
|
11
|
+
- ``save_pricing_config`` — persist edits.
|
|
12
|
+
- ``get_api_usage_summary`` — aggregated API usage / cost by service.
|
|
13
|
+
|
|
14
|
+
Note: ``get_provider_usage_summary`` (token-pricing summary, per
|
|
15
|
+
provider) moved into ``services/settings/handlers.py`` (Wave 13.3) —
|
|
16
|
+
kept there because it's surfaced in the Credentials Modal alongside
|
|
17
|
+
the provider-defaults panel.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Any, Dict
|
|
23
|
+
|
|
24
|
+
from fastapi import WebSocket
|
|
25
|
+
|
|
26
|
+
from core.container import container
|
|
27
|
+
from core.logging import get_logger
|
|
28
|
+
from services.ws_handler_registry import register_ws_handlers, ws_handler
|
|
29
|
+
|
|
30
|
+
logger = get_logger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@ws_handler()
|
|
34
|
+
async def handle_get_pricing_config(
|
|
35
|
+
data: Dict[str, Any],
|
|
36
|
+
websocket: WebSocket,
|
|
37
|
+
) -> Dict[str, Any]:
|
|
38
|
+
"""Get full pricing configuration for display/editing."""
|
|
39
|
+
from services.pricing import get_pricing_service
|
|
40
|
+
|
|
41
|
+
pricing = get_pricing_service()
|
|
42
|
+
return {"success": True, "config": pricing.get_config()}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@ws_handler()
|
|
46
|
+
async def handle_save_pricing_config(
|
|
47
|
+
data: Dict[str, Any],
|
|
48
|
+
websocket: WebSocket,
|
|
49
|
+
) -> Dict[str, Any]:
|
|
50
|
+
"""Save updated pricing configuration."""
|
|
51
|
+
from services.pricing import get_pricing_service
|
|
52
|
+
|
|
53
|
+
config = data.get("config")
|
|
54
|
+
if not config:
|
|
55
|
+
return {"success": False, "error": "No config provided"}
|
|
56
|
+
|
|
57
|
+
pricing = get_pricing_service()
|
|
58
|
+
success = pricing.save_config(config)
|
|
59
|
+
return {"success": success}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@ws_handler()
|
|
63
|
+
async def handle_get_api_usage_summary(
|
|
64
|
+
data: Dict[str, Any],
|
|
65
|
+
websocket: WebSocket,
|
|
66
|
+
) -> Dict[str, Any]:
|
|
67
|
+
"""Get aggregated API usage and cost by service (Twitter, Maps, etc.)."""
|
|
68
|
+
database = container.database()
|
|
69
|
+
service = data.get("service") # Optional filter by service
|
|
70
|
+
services = await database.get_api_usage_summary(service)
|
|
71
|
+
return {"success": True, "services": services}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
WS_HANDLERS: Dict[str, Any] = {
|
|
75
|
+
"get_pricing_config": handle_get_pricing_config,
|
|
76
|
+
"save_pricing_config": handle_save_pricing_config,
|
|
77
|
+
"get_api_usage_summary": handle_get_api_usage_summary,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Self-register on import — ``main.py`` triggers via
|
|
81
|
+
# ``import services.pricing_handlers``.
|
|
82
|
+
register_ws_handlers(WS_HANDLERS)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
__all__ = [
|
|
86
|
+
"WS_HANDLERS",
|
|
87
|
+
"handle_get_api_usage_summary",
|
|
88
|
+
"handle_get_pricing_config",
|
|
89
|
+
"handle_save_pricing_config",
|
|
90
|
+
]
|
|
@@ -13,7 +13,7 @@ import asyncio
|
|
|
13
13
|
import os
|
|
14
14
|
import shlex
|
|
15
15
|
import shutil
|
|
16
|
-
from dataclasses import dataclass
|
|
16
|
+
from dataclasses import dataclass
|
|
17
17
|
from datetime import datetime
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import Any, Awaitable, Callable, Dict, List, Optional
|
|
@@ -96,15 +96,24 @@ class ProcessService:
|
|
|
96
96
|
# Block destructive commands -- file ops should use the sandboxed shell node
|
|
97
97
|
cmd_lower = command.lower().strip()
|
|
98
98
|
blocked = (
|
|
99
|
-
"rm ",
|
|
100
|
-
"
|
|
101
|
-
"
|
|
99
|
+
"rm ",
|
|
100
|
+
"rm\t",
|
|
101
|
+
"rmdir",
|
|
102
|
+
"del ",
|
|
103
|
+
"rd ",
|
|
104
|
+
"remove-item",
|
|
105
|
+
"format ",
|
|
106
|
+
"mkfs",
|
|
107
|
+
"dd if=",
|
|
108
|
+
"shred",
|
|
109
|
+
"> /dev/",
|
|
110
|
+
"chmod 777",
|
|
111
|
+
"chmod -r",
|
|
102
112
|
)
|
|
103
113
|
if any(cmd_lower.startswith(b) or f" {b}" in f" {cmd_lower}" for b in blocked):
|
|
104
114
|
return {
|
|
105
115
|
"success": False,
|
|
106
|
-
"error":
|
|
107
|
-
f"Use shell_execute for file operations (sandboxed, no PATH).",
|
|
116
|
+
"error": "Destructive commands blocked in process_manager. " "Use shell_execute for file operations (sandboxed, no PATH).",
|
|
108
117
|
}
|
|
109
118
|
|
|
110
119
|
name = name or f"proc_{id(command) % 100000}"
|
|
@@ -132,18 +141,16 @@ class ProcessService:
|
|
|
132
141
|
if resolved is None:
|
|
133
142
|
return {
|
|
134
143
|
"success": False,
|
|
135
|
-
"error": (
|
|
136
|
-
f"Command not found: '{argv[0] if argv else ''}'. "
|
|
137
|
-
"Check spelling or ensure the binary is on PATH."
|
|
138
|
-
),
|
|
144
|
+
"error": (f"Command not found: '{argv[0] if argv else ''}'. " "Check spelling or ensure the binary is on PATH."),
|
|
139
145
|
}
|
|
140
146
|
argv[0] = resolved
|
|
141
147
|
env = {**os.environ, "PYTHONUNBUFFERED": "1"}
|
|
142
148
|
from core.config import Settings
|
|
149
|
+
|
|
143
150
|
workspace_base = Path(Settings().workspace_base_resolved).resolve()
|
|
144
151
|
|
|
145
152
|
if not working_directory:
|
|
146
|
-
working_directory = str(workspace_base /
|
|
153
|
+
working_directory = str(workspace_base / "default")
|
|
147
154
|
os.makedirs(working_directory, exist_ok=True)
|
|
148
155
|
cwd = working_directory
|
|
149
156
|
|
|
@@ -232,9 +239,7 @@ class ProcessService:
|
|
|
232
239
|
result = self._info(managed)
|
|
233
240
|
|
|
234
241
|
# Schedule cleanup after 60s to allow output reading
|
|
235
|
-
asyncio.get_event_loop().call_later(
|
|
236
|
-
60, lambda: self._cleanup_completed(workflow_id, name)
|
|
237
|
-
)
|
|
242
|
+
asyncio.get_event_loop().call_later(60, lambda: self._cleanup_completed(workflow_id, name))
|
|
238
243
|
|
|
239
244
|
return {"success": True, "result": result}
|
|
240
245
|
|
|
@@ -272,11 +277,7 @@ class ProcessService:
|
|
|
272
277
|
|
|
273
278
|
def list_processes(self, workflow_id: str = "default") -> List[Dict[str, Any]]:
|
|
274
279
|
"""List all processes for a workflow."""
|
|
275
|
-
return [
|
|
276
|
-
self._info(m)
|
|
277
|
-
for (wid, _), m in self._processes.items()
|
|
278
|
-
if wid == workflow_id
|
|
279
|
-
]
|
|
280
|
+
return [self._info(m) for (wid, _), m in self._processes.items() if wid == workflow_id]
|
|
280
281
|
|
|
281
282
|
def get_output(
|
|
282
283
|
self,
|
|
@@ -378,9 +379,7 @@ class ProcessService:
|
|
|
378
379
|
shutil.rmtree(managed.log_dir, ignore_errors=True)
|
|
379
380
|
self._processes.clear()
|
|
380
381
|
|
|
381
|
-
async def _read_stream(
|
|
382
|
-
self, managed: ManagedProcess, stream: asyncio.StreamReader, stream_name: str
|
|
383
|
-
) -> None:
|
|
382
|
+
async def _read_stream(self, managed: ManagedProcess, stream: asyncio.StreamReader, stream_name: str) -> None:
|
|
384
383
|
"""Background task: read lines, write to log file, broadcast to Terminal."""
|
|
385
384
|
level = "info" if stream_name == "stdout" else "error"
|
|
386
385
|
source = f"process:{managed.name}"
|
|
@@ -405,12 +404,14 @@ class ProcessService:
|
|
|
405
404
|
|
|
406
405
|
# Broadcast to Terminal tab
|
|
407
406
|
if self._broadcaster:
|
|
408
|
-
await self._broadcaster.broadcast_terminal_log(
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
407
|
+
await self._broadcaster.broadcast_terminal_log(
|
|
408
|
+
{
|
|
409
|
+
"timestamp": datetime.now().isoformat(),
|
|
410
|
+
"level": level,
|
|
411
|
+
"message": text,
|
|
412
|
+
"source": source,
|
|
413
|
+
}
|
|
414
|
+
)
|
|
414
415
|
|
|
415
416
|
# Optional framework-level subscriber.
|
|
416
417
|
if managed.line_handler is not None:
|
|
@@ -419,7 +420,9 @@ class ProcessService:
|
|
|
419
420
|
except Exception as cb_err:
|
|
420
421
|
logger.debug(
|
|
421
422
|
"[Process] line_handler %s/%s raised: %s",
|
|
422
|
-
managed.name,
|
|
423
|
+
managed.name,
|
|
424
|
+
stream_name,
|
|
425
|
+
cb_err,
|
|
423
426
|
)
|
|
424
427
|
except asyncio.CancelledError:
|
|
425
428
|
pass
|
|
@@ -436,9 +439,7 @@ class ProcessService:
|
|
|
436
439
|
logger.info("[Process] Exited: %s (exit=%s)", managed.name, managed.exit_code)
|
|
437
440
|
|
|
438
441
|
# Schedule auto-cleanup of log files and process entry after delay
|
|
439
|
-
asyncio.get_event_loop().call_later(
|
|
440
|
-
60, lambda: self._cleanup_completed(managed.workflow_id, managed.name)
|
|
441
|
-
)
|
|
442
|
+
asyncio.get_event_loop().call_later(60, lambda: self._cleanup_completed(managed.workflow_id, managed.name))
|
|
442
443
|
|
|
443
444
|
@staticmethod
|
|
444
445
|
def _info(m: ManagedProcess) -> Dict[str, Any]:
|
|
@@ -7,12 +7,14 @@ from pydantic import BaseModel, Field
|
|
|
7
7
|
|
|
8
8
|
class SessionType(str, Enum):
|
|
9
9
|
"""Proxy session type controlling IP rotation behavior."""
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
ROTATING = "rotating" # New IP per request (default)
|
|
12
|
+
STICKY = "sticky" # Same IP for duration
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class GeoTarget(BaseModel):
|
|
15
16
|
"""Geographic targeting for proxy requests."""
|
|
17
|
+
|
|
16
18
|
country: Optional[str] = Field(default=None, description="ISO 3166-1 alpha-2 country code")
|
|
17
19
|
city: Optional[str] = Field(default=None, description="City name")
|
|
18
20
|
state: Optional[str] = Field(default=None, description="State/region name")
|
|
@@ -20,11 +22,12 @@ class GeoTarget(BaseModel):
|
|
|
20
22
|
|
|
21
23
|
class ProviderConfig(BaseModel):
|
|
22
24
|
"""Configuration for a proxy provider loaded from database."""
|
|
25
|
+
|
|
23
26
|
name: str
|
|
24
27
|
enabled: bool = True
|
|
25
|
-
priority: int = 50
|
|
26
|
-
weight: float = 1.0
|
|
27
|
-
cost_per_gb: float = 0.0
|
|
28
|
+
priority: int = 50 # Lower = preferred
|
|
29
|
+
weight: float = 1.0 # For weighted random selection
|
|
30
|
+
cost_per_gb: float = 0.0 # USD per GB
|
|
28
31
|
gateway_host: str = ""
|
|
29
32
|
gateway_port: int = 0
|
|
30
33
|
geo_coverage: List[str] = Field(default_factory=list) # ISO country codes
|
|
@@ -36,9 +39,10 @@ class ProviderConfig(BaseModel):
|
|
|
36
39
|
|
|
37
40
|
class ProviderStats(BaseModel):
|
|
38
41
|
"""Runtime health statistics for a proxy provider."""
|
|
42
|
+
|
|
39
43
|
name: str
|
|
40
|
-
score: float = 1.0
|
|
41
|
-
success_rate: float = 1.0
|
|
44
|
+
score: float = 1.0 # 0.0 - 1.0 composite score
|
|
45
|
+
success_rate: float = 1.0 # 0.0 - 1.0
|
|
42
46
|
avg_latency_ms: float = 0.0
|
|
43
47
|
total_requests: int = 0
|
|
44
48
|
total_bytes: int = 0
|
|
@@ -47,8 +51,9 @@ class ProviderStats(BaseModel):
|
|
|
47
51
|
|
|
48
52
|
class RoutingRule(BaseModel):
|
|
49
53
|
"""Domain-based routing rule for proxy selection."""
|
|
54
|
+
|
|
50
55
|
id: Optional[int] = None
|
|
51
|
-
domain_pattern: str
|
|
56
|
+
domain_pattern: str # fnmatch glob: "*.linkedin.com", "*"
|
|
52
57
|
preferred_providers: List[str] = Field(default_factory=list)
|
|
53
58
|
required_country: Optional[str] = None
|
|
54
59
|
session_type: SessionType = SessionType.ROTATING
|
|
@@ -56,11 +61,12 @@ class RoutingRule(BaseModel):
|
|
|
56
61
|
max_retries: int = 3
|
|
57
62
|
failover: bool = True
|
|
58
63
|
min_success_rate: float = 0.7
|
|
59
|
-
priority: int = 0
|
|
64
|
+
priority: int = 0 # Lower = evaluated first
|
|
60
65
|
|
|
61
66
|
|
|
62
67
|
class ProxyResult(BaseModel):
|
|
63
68
|
"""Result of a proxied HTTP request, reported back for scoring."""
|
|
69
|
+
|
|
64
70
|
success: bool
|
|
65
71
|
latency_ms: float = 0.0
|
|
66
72
|
bytes_transferred: int = 0
|