machinaos 0.0.76 → 0.0.78
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/README.md +143 -107
- package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
- package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
- package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
- package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
- package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
- package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
- package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
- package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
- package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
- package/client/dist/assets/index-DQ0nwhec.js +257 -0
- package/client/dist/assets/index-DxmbVskS.css +1 -0
- package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
- package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
- package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
- package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
- package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
- package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
- package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
- package/client/dist/index.html +37 -3
- package/client/index.html +28 -1
- package/client/package.json +44 -40
- package/client/src/App.tsx +2 -0
- package/client/src/Dashboard.tsx +157 -45
- package/client/src/ParameterPanel.tsx +3 -5
- package/client/src/adapters/nodeSpecToDescription.ts +1 -0
- package/client/src/assets/icons/NodeIcon.tsx +32 -0
- package/client/src/assets/icons/index.ts +4 -0
- package/client/src/assets/icons/stripe.svg +1 -0
- package/client/src/assets/icons/themedGlyphs.ts +404 -0
- package/client/src/components/AIAgentNode.tsx +77 -53
- package/client/src/components/GenericNode.tsx +34 -52
- package/client/src/components/OutputPanel.tsx +64 -147
- package/client/src/components/ParameterRenderer.tsx +5 -3
- package/client/src/components/SkillEditorModal.tsx +9 -18
- package/client/src/components/SquareNode.tsx +97 -115
- package/client/src/components/StartNode.tsx +32 -42
- package/client/src/components/SvgFilterDefs.tsx +54 -0
- package/client/src/components/TeamMonitorNode.tsx +12 -14
- package/client/src/components/ToolkitNode.tsx +35 -60
- package/client/src/components/TriggerNode.tsx +43 -77
- package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
- package/client/src/components/credentials/CredentialsModal.tsx +98 -30
- package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
- package/client/src/components/credentials/catalogueAdapter.ts +17 -1
- package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
- package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
- package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
- package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
- package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
- package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
- package/client/src/components/credentials/types.ts +12 -2
- package/client/src/components/credentials/useCredentialPanel.ts +43 -19
- package/client/src/components/icons/AIProviderIcons.tsx +16 -0
- package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
- package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
- package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
- package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
- package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
- package/client/src/components/parameterPanel/InputSection.tsx +9 -7
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
- package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
- package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
- package/client/src/components/ui/AIResultModal.tsx +1 -1
- package/client/src/components/ui/CollapsibleSection.tsx +9 -5
- package/client/src/components/ui/CommandPalette.tsx +147 -0
- package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
- package/client/src/components/ui/ComponentItem.tsx +13 -7
- package/client/src/components/ui/ComponentPalette.tsx +24 -13
- package/client/src/components/ui/ConsolePanel.tsx +19 -11
- package/client/src/components/ui/DropCap.tsx +28 -0
- package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
- package/client/src/components/ui/InputNodesPanel.tsx +1 -1
- package/client/src/components/ui/Modal.tsx +38 -6
- package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
- package/client/src/components/ui/SettingsPanel.tsx +42 -13
- package/client/src/components/ui/StatusBar.tsx +108 -0
- package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
- package/client/src/components/ui/TopToolbar.tsx +42 -25
- package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
- package/client/src/components/ui/action-button.tsx +40 -15
- package/client/src/components/ui/button.tsx +24 -1
- package/client/src/components/ui/dropdown-menu.tsx +24 -2
- package/client/src/components/ui/input.tsx +19 -2
- package/client/src/components/ui/select.tsx +15 -0
- package/client/src/components/ui/textarea.tsx +15 -2
- package/client/src/contexts/AuthContext.tsx +148 -109
- package/client/src/contexts/ThemeContext.tsx +93 -17
- package/client/src/contexts/WebSocketContext.tsx +373 -206
- package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
- package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
- package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
- package/client/src/hooks/useAppTheme.ts +209 -7
- package/client/src/hooks/useAutoSkillEdges.ts +7 -2
- package/client/src/hooks/useCatalogueQuery.ts +67 -1
- package/client/src/hooks/useDragVariable.ts +1 -1
- package/client/src/hooks/useNodeAllowlist.ts +115 -8
- package/client/src/hooks/useOnboarding.ts +20 -8
- package/client/src/hooks/useParameterPanel.ts +2 -1
- package/client/src/hooks/useReactFlowNodes.ts +2 -1
- package/client/src/hooks/useSound.ts +185 -0
- package/client/src/hooks/useWorkflowManagement.ts +6 -8
- package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
- package/client/src/index.css +65 -3
- package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
- package/client/src/lib/aiModelProviders.ts +8 -0
- package/client/src/lib/connectionConfig.ts +107 -0
- package/client/src/lib/queryPersist.ts +13 -5
- package/client/src/lib/sound.ts +393 -0
- package/client/src/main.tsx +20 -0
- package/client/src/store/useAppStore.ts +26 -0
- package/client/src/styles/canvasAnimations.ts +37 -36
- package/client/src/styles/theme.ts +36 -20
- package/client/src/test/setup.ts +1 -0
- package/client/src/themes/atomic.css +253 -0
- package/client/src/themes/base.css +373 -0
- package/client/src/themes/cyber.css +890 -0
- package/client/src/themes/dark.css +70 -0
- package/client/src/themes/edo.css +246 -0
- package/client/src/themes/greek.css +293 -0
- package/client/src/themes/light.css +78 -0
- package/client/src/themes/plague.css +253 -0
- package/client/src/themes/renaissance.css +727 -0
- package/client/src/themes/rot.css +249 -0
- package/client/src/themes/steampunk.css +272 -0
- package/client/src/themes/surveillance.css +289 -0
- package/client/src/themes/wasteland.css +250 -0
- package/client/src/types/INodeProperties.ts +5 -0
- package/client/src/types/NodeTypes.ts +11 -1
- package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
- package/client/src/types/cloudEvents.ts +78 -0
- package/client/src/vite-env.d.ts +7 -0
- package/client/tsconfig.json +1 -1
- package/client/vite.config.js +62 -2
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/machina/commands/build.py +51 -7
- package/machina/pyproject.toml +4 -0
- package/machina/supervisor.py +12 -2
- package/machina/tree.py +71 -21
- package/package.json +4 -4
- package/scripts/install.js +16 -1
- package/server/config/ai_cli_providers.json +54 -0
- package/server/config/credential_providers.json +109 -2
- package/server/config/llm_defaults.json +24 -0
- package/server/config/model_registry.json +338 -499
- package/server/config/node_allowlist.json +16 -1
- package/server/config/pricing.json +8 -0
- package/server/constants.py +38 -15
- package/server/core/container.py +2 -2
- package/server/core/credentials_database.py +35 -2
- package/server/core/logging.py +4 -3
- package/server/main.py +99 -13
- package/server/models/node_metadata.py +1 -0
- package/server/nodejs/package.json +8 -6
- package/server/nodejs/src/index.ts +22 -5
- package/server/nodes/README.md +31 -4
- package/server/nodes/agent/_inline.py +2 -0
- package/server/nodes/agent/_specialized.py +6 -3
- package/server/nodes/agent/ai_agent.py +13 -3
- package/server/nodes/agent/chat_agent.py +6 -3
- package/server/nodes/agent/claude_code_agent.py +287 -75
- package/server/nodes/agent/codex_agent.py +239 -0
- package/server/nodes/agent/deep_agent.py +3 -3
- package/server/nodes/agent/rlm_agent.py +3 -3
- package/server/nodes/android/__init__.py +31 -1
- package/server/nodes/android/_base.py +9 -5
- package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
- package/server/nodes/android/_handlers.py +154 -0
- package/server/nodes/android/_option_loaders.py +44 -0
- package/server/nodes/android/_refresh.py +127 -0
- package/server/{services/android → nodes/android/_relay}/client.py +4 -4
- package/server/{routers/android.py → nodes/android/_router.py} +27 -8
- package/server/nodes/browser/browser.py +2 -2
- package/server/nodes/code/_base.py +6 -2
- package/server/nodes/code/_claude_code.py +134 -0
- package/server/nodes/document/embedding_generator.py +3 -3
- package/server/nodes/document/http_scraper.py +3 -3
- package/server/nodes/document/vector_store.py +5 -5
- package/server/nodes/email/__init__.py +11 -1
- package/server/nodes/email/_filters.py +21 -0
- package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
- package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
- package/server/nodes/email/email_read.py +1 -1
- package/server/nodes/email/email_receive.py +54 -5
- package/server/nodes/email/email_send.py +1 -1
- package/server/nodes/filesystem/shell.py +24 -1
- package/server/nodes/google/__init__.py +55 -1
- package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
- package/server/nodes/google/_base.py +2 -2
- package/server/nodes/google/_credentials.py +5 -5
- package/server/nodes/google/_filters.py +25 -0
- package/server/nodes/google/_handlers.py +57 -0
- package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
- package/server/nodes/google/_option_loaders.py +107 -0
- package/server/nodes/google/_refresh.py +66 -0
- package/server/nodes/google/_router.py +131 -0
- package/server/nodes/google/gmail_receive.py +41 -4
- package/server/nodes/groups.py +1 -0
- package/server/nodes/location/_credentials.py +45 -1
- package/server/{services/maps.py → nodes/location/_service.py} +18 -3
- package/server/nodes/location/gmaps_create.py +4 -4
- package/server/nodes/location/gmaps_locations.py +4 -4
- package/server/nodes/location/gmaps_nearby_places.py +4 -4
- package/server/nodes/model/_base.py +8 -3
- package/server/nodes/model/_credentials.py +96 -8
- package/server/nodes/model/_local_validator.py +345 -0
- package/server/nodes/model/lmstudio_chat_model.py +23 -0
- package/server/nodes/model/ollama_chat_model.py +25 -0
- package/server/nodes/proxy/_usage.py +2 -2
- package/server/nodes/proxy/proxy_config.py +14 -14
- package/server/nodes/proxy/proxy_request.py +4 -4
- package/server/nodes/scraper/_credentials.py +29 -1
- package/server/nodes/scraper/apify_actor.py +9 -9
- package/server/nodes/scraper/crawlee_scraper.py +5 -5
- package/server/nodes/search/brave_search.py +4 -0
- package/server/nodes/search/perplexity_search.py +9 -0
- package/server/nodes/search/serper_search.py +3 -0
- package/server/nodes/skill/simple_memory.py +12 -0
- package/server/nodes/social/_base.py +2 -2
- package/server/nodes/stripe/__init__.py +46 -0
- package/server/nodes/stripe/_credentials.py +33 -0
- package/server/nodes/stripe/_handlers.py +270 -0
- package/server/nodes/stripe/_install.py +127 -0
- package/server/nodes/stripe/_source.py +174 -0
- package/server/nodes/stripe/stripe_action.py +81 -0
- package/server/nodes/stripe/stripe_receive.py +92 -0
- package/server/nodes/telegram/_credentials.py +52 -1
- package/server/nodes/telegram/_handlers.py +19 -18
- package/server/nodes/telegram/_service.py +134 -32
- package/server/nodes/telegram/telegram_send.py +5 -6
- package/server/nodes/text/file_handler.py +2 -2
- package/server/nodes/text/text_generator.py +2 -2
- package/server/nodes/tool/agent_builder.py +630 -0
- package/server/nodes/tool/task_manager.py +144 -2
- package/server/nodes/twitter/__init__.py +38 -1
- package/server/nodes/twitter/_base.py +7 -7
- package/server/nodes/twitter/_credentials.py +1 -1
- package/server/nodes/twitter/_filters.py +37 -0
- package/server/nodes/twitter/_handlers.py +77 -0
- package/server/nodes/twitter/_oauth.py +124 -0
- package/server/nodes/twitter/_refresh.py +78 -0
- package/server/nodes/twitter/_router.py +29 -0
- package/server/nodes/twitter/twitter_receive.py +4 -0
- package/server/nodes/visuals.json +64 -19
- package/server/nodes/whatsapp/__init__.py +45 -5
- package/server/nodes/whatsapp/_base.py +3 -3
- package/server/nodes/whatsapp/_filters.py +137 -0
- package/server/nodes/whatsapp/_handlers.py +167 -0
- package/server/nodes/whatsapp/_option_loaders.py +68 -0
- package/server/nodes/whatsapp/_refresh.py +62 -0
- package/server/nodes/whatsapp/_runtime.py +1 -1
- package/server/pyproject.toml +29 -7
- package/server/routers/schemas.py +2 -2
- package/server/routers/webhook.py +26 -9
- package/server/routers/websocket.py +149 -810
- package/server/services/ai.py +89 -8
- package/server/services/auth.py +220 -43
- package/server/services/claude_oauth.py +126 -100
- package/server/services/cli_agent/__init__.py +78 -0
- package/server/services/cli_agent/_handlers.py +237 -0
- package/server/services/cli_agent/config.py +112 -0
- package/server/services/cli_agent/factory.py +48 -0
- package/server/services/cli_agent/lockfile.py +141 -0
- package/server/services/cli_agent/mcp_server.py +482 -0
- package/server/services/cli_agent/protocol.py +173 -0
- package/server/services/cli_agent/providers/__init__.py +9 -0
- package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
- package/server/services/cli_agent/providers/google_gemini.py +80 -0
- package/server/services/cli_agent/providers/openai_codex.py +310 -0
- package/server/services/cli_agent/service.py +607 -0
- package/server/services/cli_agent/session.py +618 -0
- package/server/services/cli_agent/types.py +227 -0
- package/server/services/cli_agent/workflow_tools.py +233 -0
- package/server/services/credential_registry.py +26 -1
- package/server/services/deployment/manager.py +26 -145
- package/server/services/deployment/poll_registry.py +59 -0
- package/server/services/event_waiter.py +76 -246
- package/server/services/events/__init__.py +54 -0
- package/server/services/events/cli.py +78 -0
- package/server/services/events/daemon.py +163 -0
- package/server/services/events/envelope.py +281 -0
- package/server/services/events/lifecycle.py +99 -0
- package/server/services/events/oauth_lifecycle.py +534 -0
- package/server/services/events/polling.py +60 -0
- package/server/services/events/push.py +36 -0
- package/server/services/events/source.py +63 -0
- package/server/services/events/triggers.py +118 -0
- package/server/services/events/verifiers/__init__.py +25 -0
- package/server/services/events/verifiers/base.py +28 -0
- package/server/services/events/verifiers/github.py +25 -0
- package/server/services/events/verifiers/hmac_basic.py +32 -0
- package/server/services/events/verifiers/standard_webhooks.py +47 -0
- package/server/services/events/verifiers/stripe.py +42 -0
- package/server/services/events/webhook.py +105 -0
- package/server/services/handlers/tools.py +28 -186
- package/server/services/llm/config.py +7 -0
- package/server/services/llm/factory.py +8 -2
- package/server/services/memory/__init__.py +52 -0
- package/server/services/memory/jsonl.py +80 -0
- package/server/services/memory/markdown.py +65 -0
- package/server/services/memory/state.py +112 -0
- package/server/services/memory/vector_store.py +40 -0
- package/server/services/model_registry.py +76 -0
- package/server/services/node_allowlist.py +71 -15
- package/server/services/node_executor.py +2 -2
- package/server/services/node_output_schemas.py +21 -10
- package/server/services/node_spec.py +1 -1
- package/server/services/oauth_utils.py +1 -1
- package/server/services/plugin/__init__.py +2 -0
- package/server/services/plugin/base.py +44 -2
- package/server/services/plugin/credential.py +288 -1
- package/server/services/plugin/deps.py +105 -0
- package/server/services/plugin/edge_walker.py +12 -4
- package/server/services/plugin/oauth.py +381 -0
- package/server/services/plugin/polling.py +247 -0
- package/server/services/plugin/registry.py +145 -0
- package/server/services/plugin/singleton.py +65 -0
- package/server/services/plugin/ws.py +81 -0
- package/server/services/process_service.py +31 -2
- package/server/services/status_broadcaster.py +155 -238
- package/server/services/temporal/workflow.py +7 -7
- package/server/services/workflow.py +21 -3
- package/server/services/ws_handler_registry.py +111 -28
- package/server/skills/GUIDE.md +16 -1
- package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
- package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
- package/server/tests/credentials/test_auth_service.py +16 -9
- package/server/tests/credentials/test_credential_broadcasts.py +219 -0
- package/server/tests/credentials/test_google_oauth.py +6 -6
- package/server/tests/credentials/test_oauth_utils.py +1 -1
- package/server/tests/credentials/test_twitter_oauth.py +2 -2
- package/server/tests/credentials/test_websocket_handlers.py +44 -20
- package/server/tests/llm/test_factory.py +1 -0
- package/server/tests/llm/test_wiring.py +5 -1
- package/server/tests/nodes/_compat.py +24 -24
- package/server/tests/nodes/test_agent_builder.py +439 -0
- package/server/tests/nodes/test_ai_tools.py +18 -14
- package/server/tests/nodes/test_code_fs_process.py +17 -8
- package/server/tests/nodes/test_email.py +10 -9
- package/server/tests/nodes/test_google_workspace.py +2 -2
- package/server/tests/nodes/test_specialized_agents.py +100 -53
- package/server/tests/nodes/test_stripe_plugin.py +293 -0
- package/server/tests/nodes/test_telegram_social.py +4 -4
- package/server/tests/nodes/test_twitter.py +1 -1
- package/server/tests/nodes/test_web_automation.py +2 -2
- package/server/tests/nodes/test_whatsapp.py +9 -9
- package/server/tests/services/cli_agent/__init__.py +0 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
- package/server/tests/services/cli_agent/test_providers.py +358 -0
- package/server/tests/services/cli_agent/test_service.py +298 -0
- package/server/tests/services/memory/__init__.py +0 -0
- package/server/tests/services/memory/test_jsonl.py +188 -0
- package/server/tests/services/test_events.py +333 -0
- package/server/tests/test_node_spec.py +56 -16
- package/server/tests/test_plugin_helpers.py +116 -0
- package/server/tests/test_plugin_self_containment.py +486 -0
- package/server/tests/test_status_broadcasts.py +425 -0
- package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
- package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
- package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
- package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
- package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
- package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
- package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
- package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
- package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
- package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
- package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
- package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
- package/client/dist/assets/index-D9tZfgvi.js +0 -363
- package/client/dist/assets/index-al7snTkG.css +0 -1
- package/client/src/components/credentials/providers.tsx +0 -177
- package/server/routers/google.py +0 -277
- package/server/routers/maps.py +0 -142
- package/server/routers/twitter.py +0 -365
- package/server/services/claude_code_service.py +0 -106
- package/server/services/memory.py +0 -159
- package/server/services/node_option_loaders/__init__.py +0 -77
- package/server/services/node_option_loaders/android_loaders.py +0 -55
- package/server/services/node_option_loaders/google_loaders.py +0 -97
- package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
- package/server/services/twitter_oauth.py +0 -411
- package/server/services/websocket_client.py +0 -29
- /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
- /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
- /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
- /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
|
@@ -49,6 +49,8 @@ from telegram.error import BadRequest, NetworkError
|
|
|
49
49
|
from telegram.ext import Application, ContextTypes, MessageHandler, filters
|
|
50
50
|
from telegram.helpers import escape_markdown
|
|
51
51
|
|
|
52
|
+
from services.plugin.singleton import ServiceSingleton
|
|
53
|
+
|
|
52
54
|
from ._credentials import TelegramCredential
|
|
53
55
|
|
|
54
56
|
logger = logging.getLogger(__name__)
|
|
@@ -99,10 +101,14 @@ def _split_text(text: str, limit: int) -> list[str]:
|
|
|
99
101
|
return chunks
|
|
100
102
|
|
|
101
103
|
|
|
102
|
-
class TelegramService:
|
|
103
|
-
"""Singleton service for Telegram bot operations.
|
|
104
|
+
class TelegramService(ServiceSingleton):
|
|
105
|
+
"""Singleton service for Telegram bot operations.
|
|
106
|
+
|
|
107
|
+
Inherits :meth:`ServiceSingleton.instance` for the lazy accessor.
|
|
108
|
+
Overrides :meth:`reset_instance` because telegram has a side effect
|
|
109
|
+
on reset — it must drain the bot's polling task before clearing.
|
|
110
|
+
"""
|
|
104
111
|
|
|
105
|
-
_instance: Optional["TelegramService"] = None
|
|
106
112
|
_lock = asyncio.Lock()
|
|
107
113
|
|
|
108
114
|
def __init__(self):
|
|
@@ -115,15 +121,10 @@ class TelegramService:
|
|
|
115
121
|
self._owner_chat_id: Optional[int] = None
|
|
116
122
|
|
|
117
123
|
@classmethod
|
|
118
|
-
def
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
@classmethod
|
|
124
|
-
async def reset_instance(cls):
|
|
125
|
-
if cls._instance:
|
|
126
|
-
await cls._instance.disconnect()
|
|
124
|
+
async def reset_instance(cls): # type: ignore[override]
|
|
125
|
+
existing = cls.__dict__.get("_instance")
|
|
126
|
+
if existing is not None:
|
|
127
|
+
await existing.disconnect()
|
|
127
128
|
cls._instance = None
|
|
128
129
|
|
|
129
130
|
@property
|
|
@@ -256,12 +257,11 @@ class TelegramService:
|
|
|
256
257
|
}
|
|
257
258
|
logger.info(f"[Telegram] Bot validated: @{me.username} (ID: {me.id})")
|
|
258
259
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
# Hydrate runtime owner_chat_id from stored credential.
|
|
260
|
+
# Hydrate runtime owner_chat_id from stored credential
|
|
261
|
+
# BEFORE polling starts so the pre-poll peek below can
|
|
262
|
+
# short-circuit when we already know the owner. (Was
|
|
263
|
+
# previously after polling -- race-prone with the
|
|
264
|
+
# capture path in _on_message_received.)
|
|
265
265
|
saved_owner = secrets.get("telegram_owner_chat_id")
|
|
266
266
|
if saved_owner is None:
|
|
267
267
|
try:
|
|
@@ -276,6 +276,28 @@ class TelegramService:
|
|
|
276
276
|
except (TypeError, ValueError):
|
|
277
277
|
pass
|
|
278
278
|
|
|
279
|
+
# Pre-poll peek for historical owner capture.
|
|
280
|
+
# Polling starts with drop_pending_updates=True
|
|
281
|
+
# (correct hygiene for operational events -- we do
|
|
282
|
+
# not want stale group spam replaying on every
|
|
283
|
+
# restart) but that ALSO drops any DM the user sent
|
|
284
|
+
# before the bot connected. Without this peek the
|
|
285
|
+
# only path to owner capture is "DM the bot AFTER
|
|
286
|
+
# polling has started", which is the surprising UX
|
|
287
|
+
# that produces "Bot owner not detected" on every
|
|
288
|
+
# fresh setup. get_updates() with no offset returns
|
|
289
|
+
# the same pending queue without advancing it, so
|
|
290
|
+
# the subsequent start_polling drop-pass is a no-op
|
|
291
|
+
# for these messages -- they were going to be
|
|
292
|
+
# dropped anyway, we just inspect them first.
|
|
293
|
+
if self._owner_chat_id is None:
|
|
294
|
+
await self._capture_owner_from_pending_updates()
|
|
295
|
+
|
|
296
|
+
self._polling_task = asyncio.create_task(self._run_polling())
|
|
297
|
+
self._connected = True
|
|
298
|
+
|
|
299
|
+
await self._broadcast_status()
|
|
300
|
+
|
|
279
301
|
logger.info(f"[Telegram] Connected and polling started for @{me.username}")
|
|
280
302
|
return {
|
|
281
303
|
"success": True,
|
|
@@ -354,36 +376,116 @@ class TelegramService:
|
|
|
354
376
|
self._connected = False
|
|
355
377
|
await self._broadcast_status()
|
|
356
378
|
|
|
379
|
+
async def _capture_owner_from_pending_updates(self) -> None:
|
|
380
|
+
"""Inspect the bot's pending update queue (24h Telegram retention)
|
|
381
|
+
for any private message and capture the owner if found. Called
|
|
382
|
+
from connect() before polling starts -- after polling begins the
|
|
383
|
+
``drop_pending_updates=True`` flag throws these away unread.
|
|
384
|
+
|
|
385
|
+
Uses the same atomic-write-through invariant as
|
|
386
|
+
``_on_message_received``: persist FIRST, set in-memory only on
|
|
387
|
+
successful persist, so a restart can re-capture cleanly if the
|
|
388
|
+
DB write fails.
|
|
389
|
+
"""
|
|
390
|
+
try:
|
|
391
|
+
# offset=None / 0 returns the queue without advancing the
|
|
392
|
+
# confirmed offset. allowed_updates=["message"] skips
|
|
393
|
+
# callback_query / poll / inline_query payloads we don't
|
|
394
|
+
# care about for owner capture.
|
|
395
|
+
pending = await self._bot.get_updates(
|
|
396
|
+
timeout=0,
|
|
397
|
+
allowed_updates=["message"],
|
|
398
|
+
)
|
|
399
|
+
except Exception as e:
|
|
400
|
+
logger.warning(
|
|
401
|
+
f"[Telegram] Pre-poll owner-capture peek failed "
|
|
402
|
+
f"({type(e).__name__}): {e}. Owner capture will fall "
|
|
403
|
+
f"back to live polling once the user DMs the bot.",
|
|
404
|
+
)
|
|
405
|
+
return
|
|
406
|
+
|
|
407
|
+
for upd in pending:
|
|
408
|
+
m = upd.message
|
|
409
|
+
if not (m and m.chat and m.chat.type == "private" and m.from_user):
|
|
410
|
+
continue
|
|
411
|
+
captured_id = m.from_user.id
|
|
412
|
+
try:
|
|
413
|
+
from services.plugin.deps import get_auth_service
|
|
414
|
+
await get_auth_service().store_api_key(
|
|
415
|
+
"telegram_owner_chat_id",
|
|
416
|
+
str(captured_id),
|
|
417
|
+
models=[],
|
|
418
|
+
)
|
|
419
|
+
except Exception as persist_err:
|
|
420
|
+
logger.error(
|
|
421
|
+
f"[Telegram] FAILED to persist retroactively-captured "
|
|
422
|
+
f"owner {captured_id}: {persist_err}. Workaround: "
|
|
423
|
+
f"set TELEGRAM_OWNER_CHAT_ID={captured_id} in .env",
|
|
424
|
+
exc_info=True,
|
|
425
|
+
)
|
|
426
|
+
return
|
|
427
|
+
self._owner_chat_id = captured_id
|
|
428
|
+
logger.info(
|
|
429
|
+
f"[Telegram] Owner retroactively captured from pending "
|
|
430
|
+
f"updates: @{m.from_user.username} (ID: {captured_id})"
|
|
431
|
+
)
|
|
432
|
+
return
|
|
433
|
+
|
|
357
434
|
async def _on_message_received(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|
358
435
|
try:
|
|
359
436
|
if not update.message:
|
|
360
437
|
return
|
|
361
438
|
msg = update.message
|
|
362
439
|
|
|
363
|
-
# Auto-capture bot owner from first private message
|
|
440
|
+
# Auto-capture bot owner from first private message.
|
|
441
|
+
# ATOMIC WRITE-THROUGH: persist to credentials DB FIRST, set
|
|
442
|
+
# in-memory only on success. The previous order
|
|
443
|
+
# (in-memory then await persist, with WARNING-level logging
|
|
444
|
+
# of failures) had a silent failure mode: if the persist
|
|
445
|
+
# raised, the rest of the process lifetime worked correctly,
|
|
446
|
+
# then the next restart wiped in-memory and DB had nothing
|
|
447
|
+
# -- subsequent telegramSend(recipient_type=self) failed
|
|
448
|
+
# forever with "Bot owner not detected" and no trace of
|
|
449
|
+
# what went wrong. The new order preserves the invariant
|
|
450
|
+
# "in-memory has owner => DB has owner" so a restart can
|
|
451
|
+
# always re-capture from the next inbound private message.
|
|
364
452
|
if (
|
|
365
453
|
self._owner_chat_id is None
|
|
366
454
|
and msg.chat.type == "private"
|
|
367
455
|
and msg.from_user
|
|
368
456
|
):
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
f"[Telegram] Owner detected: @{msg.from_user.username} "
|
|
372
|
-
f"(ID: {msg.from_user.id})"
|
|
373
|
-
)
|
|
457
|
+
captured_id = msg.from_user.id
|
|
458
|
+
persist_ok = False
|
|
374
459
|
try:
|
|
375
|
-
from
|
|
376
|
-
|
|
377
|
-
await auth.store_api_key(
|
|
460
|
+
from services.plugin.deps import get_auth_service
|
|
461
|
+
await get_auth_service().store_api_key(
|
|
378
462
|
"telegram_owner_chat_id",
|
|
379
|
-
str(
|
|
463
|
+
str(captured_id),
|
|
380
464
|
models=[],
|
|
381
465
|
)
|
|
466
|
+
persist_ok = True
|
|
382
467
|
except Exception as persist_err:
|
|
383
|
-
|
|
384
|
-
|
|
468
|
+
# ERROR-level so the failure surfaces in default
|
|
469
|
+
# uvicorn output, not just DEBUG. Includes the
|
|
470
|
+
# captured id so the user can manually set
|
|
471
|
+
# TELEGRAM_OWNER_CHAT_ID in .env as an immediate
|
|
472
|
+
# workaround. exc_info=True so the underlying
|
|
473
|
+
# cause (encryption not initialised, DB locked,
|
|
474
|
+
# disk full, ...) is visible.
|
|
475
|
+
logger.error(
|
|
476
|
+
f"[Telegram] FAILED to persist owner chat_id "
|
|
477
|
+
f"{captured_id}: {persist_err}. The bot will not "
|
|
478
|
+
f"recover this binding on restart. Workaround: "
|
|
479
|
+
f"set TELEGRAM_OWNER_CHAT_ID={captured_id} in .env",
|
|
480
|
+
exc_info=True,
|
|
385
481
|
)
|
|
386
|
-
|
|
482
|
+
if persist_ok:
|
|
483
|
+
self._owner_chat_id = captured_id
|
|
484
|
+
logger.info(
|
|
485
|
+
f"[Telegram] Owner detected and persisted: "
|
|
486
|
+
f"@{msg.from_user.username} (ID: {captured_id})"
|
|
487
|
+
)
|
|
488
|
+
await self._broadcast_status()
|
|
387
489
|
|
|
388
490
|
event_data = self._format_message(msg)
|
|
389
491
|
logger.info(
|
|
@@ -727,4 +829,4 @@ class TelegramService:
|
|
|
727
829
|
|
|
728
830
|
def get_telegram_service() -> TelegramService:
|
|
729
831
|
"""Get TelegramService singleton instance."""
|
|
730
|
-
return TelegramService.
|
|
832
|
+
return TelegramService.instance()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Telegram Send — Wave 11.C migration.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
lives in ``auth_service`` under ``telegram_bot_token``. Plugin
|
|
3
|
+
Workflow-only ActionNode (no AI-tool exposure). The Telegram bot
|
|
4
|
+
token lives in ``auth_service`` under ``telegram_bot_token``. Plugin
|
|
5
5
|
delegates to the legacy ``handle_telegram_send`` handler during
|
|
6
6
|
thin-migration; 11.E converts to a declarative ``TelegramCredential``.
|
|
7
7
|
"""
|
|
@@ -148,7 +148,7 @@ class TelegramSendNode(ActionNode):
|
|
|
148
148
|
type = "telegramSend"
|
|
149
149
|
display_name = "Telegram Send"
|
|
150
150
|
subtitle = "Send Message"
|
|
151
|
-
group = ("social",
|
|
151
|
+
group = ("social",)
|
|
152
152
|
description = "Send text, photo, document, location, or contact via Telegram bot"
|
|
153
153
|
component_kind = "square"
|
|
154
154
|
handles = (
|
|
@@ -160,7 +160,6 @@ class TelegramSendNode(ActionNode):
|
|
|
160
160
|
annotations = {"destructive": False, "readonly": False, "open_world": True}
|
|
161
161
|
credentials = (TelegramCredential,)
|
|
162
162
|
task_queue = TaskQueue.MESSAGING
|
|
163
|
-
usable_as_tool = True
|
|
164
163
|
|
|
165
164
|
Params = TelegramSendParams
|
|
166
165
|
Output = TelegramSendOutput
|
|
@@ -183,8 +182,8 @@ class TelegramSendNode(ActionNode):
|
|
|
183
182
|
chat_id = service.owner_chat_id
|
|
184
183
|
if not chat_id:
|
|
185
184
|
try:
|
|
186
|
-
from
|
|
187
|
-
saved = await
|
|
185
|
+
from services.plugin.deps import get_auth_service
|
|
186
|
+
saved = await get_auth_service().get_api_key("telegram_owner_chat_id")
|
|
188
187
|
if saved:
|
|
189
188
|
owner_id = int(saved)
|
|
190
189
|
await service.set_owner(owner_id)
|
|
@@ -63,9 +63,9 @@ class FileHandlerNode(ActionNode):
|
|
|
63
63
|
|
|
64
64
|
@Operation("wrap")
|
|
65
65
|
async def wrap(self, ctx: NodeContext, params: FileHandlerParams) -> Any:
|
|
66
|
-
from
|
|
66
|
+
from services.plugin.deps import get_text_service
|
|
67
67
|
|
|
68
|
-
text_service =
|
|
68
|
+
text_service = get_text_service()
|
|
69
69
|
# Pass schema-canonical snake_case through to the service. The
|
|
70
70
|
# service's *output* dict still carries camelCase keys
|
|
71
71
|
# (`fileName` / `fileType`) by historical wire contract -- the
|
|
@@ -44,9 +44,9 @@ class TextGeneratorNode(ActionNode):
|
|
|
44
44
|
|
|
45
45
|
@Operation("generate")
|
|
46
46
|
async def generate(self, ctx: NodeContext, params: TextGeneratorParams) -> Any:
|
|
47
|
-
from
|
|
47
|
+
from services.plugin.deps import get_text_service
|
|
48
48
|
|
|
49
|
-
text_service =
|
|
49
|
+
text_service = get_text_service()
|
|
50
50
|
response = await text_service.execute_text_generator(
|
|
51
51
|
ctx.node_id, params.model_dump(),
|
|
52
52
|
)
|