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
|
@@ -63,6 +63,22 @@ class _EmptyOutput(BaseModel):
|
|
|
63
63
|
pass
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
# Group memberships that mark a node as auxiliary configuration —
|
|
67
|
+
# its panel inherits the parent's main inputs instead of showing
|
|
68
|
+
# direct inputs. Centralized here so the frontend doesn't need to
|
|
69
|
+
# know any group strings.
|
|
70
|
+
_CONFIG_NODE_GROUPS = frozenset({"memory", "tool"})
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _derive_auto_ui_hints(group: Sequence[str]) -> Dict[str, Any]:
|
|
74
|
+
"""Auto-derived uiHints based on group membership. Plugin-declared
|
|
75
|
+
``ui_hints`` override these — explicit always wins."""
|
|
76
|
+
hints: Dict[str, Any] = {}
|
|
77
|
+
if any(g in _CONFIG_NODE_GROUPS for g in group):
|
|
78
|
+
hints["isConfigNode"] = True
|
|
79
|
+
return hints
|
|
80
|
+
|
|
81
|
+
|
|
66
82
|
class BaseNode:
|
|
67
83
|
"""Abstract plugin node. Do not instantiate directly — subclass
|
|
68
84
|
:class:`ActionNode`, :class:`TriggerNode`, or :class:`ToolNode`.
|
|
@@ -111,6 +127,7 @@ class BaseNode:
|
|
|
111
127
|
handles: ClassVar[Sequence[Dict[str, Any]]] = ()
|
|
112
128
|
visibility: ClassVar[str] = "all"
|
|
113
129
|
hide_output_handle: ClassVar[bool] = False
|
|
130
|
+
hide_input_handle: ClassVar[bool] = False
|
|
114
131
|
ui_hints: ClassVar[Dict[str, Any]] = {}
|
|
115
132
|
annotations: ClassVar[Dict[str, Any]] = {}
|
|
116
133
|
|
|
@@ -140,6 +157,27 @@ class BaseNode:
|
|
|
140
157
|
cls._operations = collect_operations(cls)
|
|
141
158
|
if abstract or not cls.type:
|
|
142
159
|
return
|
|
160
|
+
# Auto-hide default canvas input/output handles for nodes whose
|
|
161
|
+
# primary surface area is the LLM-tool call path. Two cases:
|
|
162
|
+
# * Pure ToolNodes (component_kind="tool" -- calculator,
|
|
163
|
+
# duckduckgoSearch, writeTodos, agentBuilder, ...). They
|
|
164
|
+
# wire through their own output-tool handle; the hardcoded
|
|
165
|
+
# SquareNode input-main + output-main are visual clutter.
|
|
166
|
+
# * Dual-purpose ActionNodes with usable_as_tool=True (gmail,
|
|
167
|
+
# twitter*, brave_search, all 16 android nodes via cascade,
|
|
168
|
+
# code executors via cascade, ...). Same reasoning: the
|
|
169
|
+
# LLM-tool path is the dominant use; default handles confuse
|
|
170
|
+
# the canvas.
|
|
171
|
+
# Subclasses opt out by explicitly setting either flag to False
|
|
172
|
+
# on the class.
|
|
173
|
+
is_tool_oriented = (
|
|
174
|
+
cls.usable_as_tool or cls.component_kind == "tool"
|
|
175
|
+
)
|
|
176
|
+
if is_tool_oriented:
|
|
177
|
+
if "hide_input_handle" not in cls.__dict__:
|
|
178
|
+
cls.hide_input_handle = True
|
|
179
|
+
if "hide_output_handle" not in cls.__dict__:
|
|
180
|
+
cls.hide_output_handle = True
|
|
143
181
|
# Eager registry write — same four registries as @register_node.
|
|
144
182
|
from services.node_registry import register_node, register_node_class
|
|
145
183
|
register_node(
|
|
@@ -185,10 +223,14 @@ class BaseNode:
|
|
|
185
223
|
meta["credentials"] = [c.id for c in cls.credentials]
|
|
186
224
|
if cls.hide_output_handle:
|
|
187
225
|
meta["hideOutputHandle"] = True
|
|
226
|
+
if cls.hide_input_handle:
|
|
227
|
+
meta["hideInputHandle"] = True
|
|
188
228
|
if cls.visibility != "all":
|
|
189
229
|
meta["visibility"] = cls.visibility
|
|
190
|
-
|
|
191
|
-
|
|
230
|
+
ui_hints = _derive_auto_ui_hints(cls.group)
|
|
231
|
+
ui_hints.update(cls.ui_hints)
|
|
232
|
+
if ui_hints:
|
|
233
|
+
meta["uiHints"] = ui_hints
|
|
192
234
|
return meta
|
|
193
235
|
|
|
194
236
|
# ---- legacy handler adapter ------------------------------------------
|
|
@@ -13,16 +13,134 @@ Two concrete bases:
|
|
|
13
13
|
|
|
14
14
|
Both are discovered at import time via :data:`CREDENTIAL_REGISTRY`
|
|
15
15
|
(populated by ``__init_subclass__``). Nothing else needs to wire them.
|
|
16
|
+
|
|
17
|
+
Validation is also a base-class concern. Every "validate this key"
|
|
18
|
+
flow shares the same wiring (read api_key + session_id from request,
|
|
19
|
+
call provider probe, store on success, broadcast status, return
|
|
20
|
+
envelope). Only the probe itself varies. That common scaffold lives on
|
|
21
|
+
:meth:`Credential.validate`; subclasses override the lighter
|
|
22
|
+
:meth:`Credential._probe` to supply the per-provider call. See
|
|
23
|
+
:class:`ProbeResult` for the typed return shape.
|
|
16
24
|
"""
|
|
17
25
|
|
|
18
26
|
from __future__ import annotations
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
import logging
|
|
29
|
+
import time
|
|
30
|
+
from dataclasses import dataclass, field
|
|
31
|
+
from typing import Any, ClassVar, Dict, List, Literal, Optional, Sequence
|
|
32
|
+
|
|
33
|
+
import httpx
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
21
36
|
|
|
22
37
|
|
|
23
38
|
CREDENTIAL_REGISTRY: Dict[str, type] = {}
|
|
24
39
|
|
|
25
40
|
|
|
41
|
+
@dataclass
|
|
42
|
+
class ProbeResult:
|
|
43
|
+
"""Outcome of a per-provider validation probe.
|
|
44
|
+
|
|
45
|
+
Carries the common ``valid`` / ``message`` flags every validator
|
|
46
|
+
returns, plus optional fields a few providers extend with
|
|
47
|
+
(``models`` for LLM ``/v1/models`` lists, ``model_params`` for
|
|
48
|
+
Ollama / LM Studio per-model context, ``extra`` for
|
|
49
|
+
provider-specific extensions like Apify's ``username`` / ``email``
|
|
50
|
+
/ ``plan``). The base ``Credential.validate`` reads these fields
|
|
51
|
+
when wiring storage / broadcast / response envelope so subclasses
|
|
52
|
+
don't repeat any of that scaffolding.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
valid: bool
|
|
56
|
+
message: str = ""
|
|
57
|
+
models: List[str] = field(default_factory=list)
|
|
58
|
+
model_params: Optional[Dict[str, Dict[str, Any]]] = None
|
|
59
|
+
# Free-form passthrough fields for provider-specific extensions
|
|
60
|
+
# (Apify returns username / email / plan; Twitter returns user id;
|
|
61
|
+
# Maps returns nothing). Merged into the response envelope.
|
|
62
|
+
extra: Dict[str, Any] = field(default_factory=dict)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def classify_credential_error(
|
|
66
|
+
exc: BaseException, *, display_name: str
|
|
67
|
+
) -> ProbeResult:
|
|
68
|
+
"""Map a transport / SDK exception to a typed ``ProbeResult``.
|
|
69
|
+
|
|
70
|
+
Single source of truth for credential-error → user-message mapping.
|
|
71
|
+
Used by the base ``Credential.validate`` after a ``_probe`` call
|
|
72
|
+
raises, and by the local-LLM probe directly. Catches the documented
|
|
73
|
+
``httpx`` / ``openai`` exception hierarchy so operator logs see
|
|
74
|
+
"HTTP 401" / "connect-refused" / "timeout" instead of an opaque
|
|
75
|
+
repr.
|
|
76
|
+
|
|
77
|
+
Returns a ``ProbeResult(valid=False, message=...)`` carrying the
|
|
78
|
+
user-facing string. Operator logs are emitted at WARN by the
|
|
79
|
+
caller — this helper just classifies, doesn't log.
|
|
80
|
+
"""
|
|
81
|
+
import openai # local import: openai is a heavy SDK
|
|
82
|
+
|
|
83
|
+
if isinstance(exc, httpx.TimeoutException) or isinstance(exc, openai.APITimeoutError):
|
|
84
|
+
return ProbeResult(
|
|
85
|
+
valid=False,
|
|
86
|
+
message=f"Request to {display_name} timed out — try again or check the network.",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if isinstance(exc, httpx.ConnectError) or isinstance(exc, openai.APIConnectionError):
|
|
90
|
+
return ProbeResult(
|
|
91
|
+
valid=False,
|
|
92
|
+
message=f"Could not reach {display_name}. Is the server running?",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if isinstance(exc, httpx.HTTPStatusError):
|
|
96
|
+
status = exc.response.status_code
|
|
97
|
+
return _classify_status(status, display_name)
|
|
98
|
+
|
|
99
|
+
if isinstance(exc, openai.AuthenticationError):
|
|
100
|
+
return _classify_status(401, display_name)
|
|
101
|
+
if isinstance(exc, openai.PermissionDeniedError):
|
|
102
|
+
return _classify_status(403, display_name)
|
|
103
|
+
if isinstance(exc, openai.NotFoundError):
|
|
104
|
+
return _classify_status(404, display_name)
|
|
105
|
+
if isinstance(exc, openai.RateLimitError):
|
|
106
|
+
return _classify_status(429, display_name)
|
|
107
|
+
if isinstance(exc, openai.APIStatusError):
|
|
108
|
+
status = getattr(getattr(exc, "response", None), "status_code", 500)
|
|
109
|
+
return _classify_status(status, display_name)
|
|
110
|
+
|
|
111
|
+
# Unknown exception: surface the type name so the operator log line
|
|
112
|
+
# is still useful, but don't dump a stacktrace into the user's toast.
|
|
113
|
+
return ProbeResult(
|
|
114
|
+
valid=False,
|
|
115
|
+
message=f"Could not validate {display_name}: {type(exc).__name__}: {exc}",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _classify_status(status: int, display_name: str) -> ProbeResult:
|
|
120
|
+
"""Map an HTTP status code to a user-facing message."""
|
|
121
|
+
if status in (401, 403):
|
|
122
|
+
return ProbeResult(valid=False, message=f"{display_name} rejected the API key.")
|
|
123
|
+
if status == 404:
|
|
124
|
+
return ProbeResult(
|
|
125
|
+
valid=False,
|
|
126
|
+
message=f"{display_name} returned 404 — endpoint not found. Check the URL.",
|
|
127
|
+
)
|
|
128
|
+
if status == 429:
|
|
129
|
+
return ProbeResult(
|
|
130
|
+
valid=False,
|
|
131
|
+
message=f"{display_name} rate-limited the request — try again shortly.",
|
|
132
|
+
)
|
|
133
|
+
if 500 <= status < 600:
|
|
134
|
+
return ProbeResult(
|
|
135
|
+
valid=False,
|
|
136
|
+
message=f"{display_name} returned HTTP {status} — try again later.",
|
|
137
|
+
)
|
|
138
|
+
return ProbeResult(
|
|
139
|
+
valid=False,
|
|
140
|
+
message=f"{display_name} returned HTTP {status}.",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
26
144
|
class Credential:
|
|
27
145
|
"""Base class for provider credentials.
|
|
28
146
|
|
|
@@ -73,6 +191,105 @@ class Credential:
|
|
|
73
191
|
"""
|
|
74
192
|
return request
|
|
75
193
|
|
|
194
|
+
# ---- Validation -------------------------------------------------
|
|
195
|
+
#
|
|
196
|
+
# Every "validate this credential" flow follows the same wiring:
|
|
197
|
+
# read api_key + session_id from the request, run a per-provider
|
|
198
|
+
# probe, store on success, broadcast status, return the standard
|
|
199
|
+
# response envelope. Only the probe call genuinely varies — so the
|
|
200
|
+
# scaffolding lives on the base and subclasses override the lighter
|
|
201
|
+
# :meth:`_probe` hook. The dispatch entry point (called by the WS
|
|
202
|
+
# router) is :meth:`validate`; subclasses with non-standard
|
|
203
|
+
# side-effect ordering (e.g. local-LLM credentials store under TWO
|
|
204
|
+
# keys) override :meth:`validate` directly.
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
async def validate(cls, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
208
|
+
"""Run the credential validation probe and wire side effects.
|
|
209
|
+
|
|
210
|
+
The shared scaffold:
|
|
211
|
+
1. Read ``api_key`` + ``session_id`` from the request data.
|
|
212
|
+
2. Guard: missing key → fast-fail envelope.
|
|
213
|
+
3. Call :meth:`_probe` (subclass-supplied; provider-specific).
|
|
214
|
+
4. On exception, classify via :func:`classify_credential_error`.
|
|
215
|
+
5. On valid result, persist via ``auth_service.store_api_key``.
|
|
216
|
+
6. Broadcast the new status via
|
|
217
|
+
``StatusBroadcaster.update_api_key_status`` (always — invalid
|
|
218
|
+
results clear stale state on connected clients).
|
|
219
|
+
7. Return the standard response envelope.
|
|
220
|
+
|
|
221
|
+
Subclasses with non-standard storage / broadcast (e.g. local-LLM
|
|
222
|
+
credentials that store both URL and placeholder key) override
|
|
223
|
+
this method directly.
|
|
224
|
+
"""
|
|
225
|
+
from core.container import container
|
|
226
|
+
from services.status_broadcaster import get_status_broadcaster
|
|
227
|
+
|
|
228
|
+
api_key = (data.get("api_key") or "").strip()
|
|
229
|
+
session_id = data.get("session_id", "default")
|
|
230
|
+
if not api_key:
|
|
231
|
+
return {
|
|
232
|
+
"success": False,
|
|
233
|
+
"valid": False,
|
|
234
|
+
"error": f"{cls.id} api_key required",
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
result = await cls._probe(api_key)
|
|
239
|
+
except Exception as exc: # noqa: BLE001 — classified below
|
|
240
|
+
display = cls.display_name or cls.id
|
|
241
|
+
result = classify_credential_error(exc, display_name=display)
|
|
242
|
+
logger.warning(
|
|
243
|
+
"[%s] credential probe failed: %s",
|
|
244
|
+
cls.id,
|
|
245
|
+
result.message,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
broadcaster = get_status_broadcaster()
|
|
249
|
+
auth_service = container.auth_service()
|
|
250
|
+
|
|
251
|
+
if result.valid:
|
|
252
|
+
await auth_service.store_api_key(
|
|
253
|
+
provider=cls.id,
|
|
254
|
+
api_key=api_key,
|
|
255
|
+
models=result.models,
|
|
256
|
+
session_id=session_id,
|
|
257
|
+
model_params=result.model_params,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
await broadcaster.update_api_key_status(
|
|
261
|
+
provider=cls.id,
|
|
262
|
+
valid=result.valid,
|
|
263
|
+
message=result.message,
|
|
264
|
+
has_key=result.valid,
|
|
265
|
+
models=result.models,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
"success": True,
|
|
270
|
+
"provider": cls.id,
|
|
271
|
+
"valid": result.valid,
|
|
272
|
+
"message": result.message,
|
|
273
|
+
"models": result.models,
|
|
274
|
+
"timestamp": time.time(),
|
|
275
|
+
**result.extra,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@classmethod
|
|
279
|
+
async def _probe(cls, api_key: str) -> ProbeResult:
|
|
280
|
+
"""Run the per-provider validation probe.
|
|
281
|
+
|
|
282
|
+
Subclass override point — the probe MUST NOT do storage,
|
|
283
|
+
broadcasts, or registry mutation. It returns a
|
|
284
|
+
:class:`ProbeResult` describing what the upstream returned;
|
|
285
|
+
:meth:`validate` does the rest. Raise ``httpx.*`` /
|
|
286
|
+
``openai.OpenAIError`` to let the base map them to a typed
|
|
287
|
+
message via :func:`classify_credential_error`.
|
|
288
|
+
"""
|
|
289
|
+
raise NotImplementedError(
|
|
290
|
+
f"Credential subclass {cls.__name__} must override _probe()"
|
|
291
|
+
)
|
|
292
|
+
|
|
76
293
|
|
|
77
294
|
class OAuth2Credential(Credential):
|
|
78
295
|
"""OAuth 2.0 with refresh-token support.
|
|
@@ -130,6 +347,15 @@ class ApiKeyCredential(Credential):
|
|
|
130
347
|
category = "Search"
|
|
131
348
|
key_name = "X-Subscription-Token"
|
|
132
349
|
key_location = "header"
|
|
350
|
+
probe_url = "https://api.search.brave.com/res/v1/web/search"
|
|
351
|
+
probe_params = {"q": "ping", "count": 1}
|
|
352
|
+
|
|
353
|
+
Subclasses get a default :meth:`_probe` for free as long as they
|
|
354
|
+
declare a ``probe_url``. Auth attaches via :meth:`inject` (header /
|
|
355
|
+
query / bearer). Subclasses with bespoke validation (URL with
|
|
356
|
+
embedded token, SDK-based probe, custom 200-with-error envelope)
|
|
357
|
+
override :meth:`_probe` directly — see ``TelegramCredential``,
|
|
358
|
+
``GoogleMapsCredential``, ``ApifyCredential``.
|
|
133
359
|
"""
|
|
134
360
|
|
|
135
361
|
auth: ClassVar[Literal["api_key"]] = "api_key"
|
|
@@ -139,6 +365,20 @@ class ApiKeyCredential(Credential):
|
|
|
139
365
|
# Extra fields stored alongside (e.g. "apify_account_id" for Apify)
|
|
140
366
|
extra_fields: ClassVar[Sequence[str]] = ()
|
|
141
367
|
|
|
368
|
+
# ---- Declarative HTTP probe ------------------------------------
|
|
369
|
+
#
|
|
370
|
+
# The default :meth:`_probe` builds a request from these attributes,
|
|
371
|
+
# runs it through :meth:`inject` to attach the key, sends via httpx,
|
|
372
|
+
# and calls :meth:`_handle_probe_response` on a 2xx response.
|
|
373
|
+
# ``httpx.HTTPStatusError`` / ``TimeoutException`` / ``ConnectError``
|
|
374
|
+
# propagate to :meth:`Credential.validate`, where
|
|
375
|
+
# :func:`classify_credential_error` produces the user-facing message.
|
|
376
|
+
probe_url: ClassVar[str] = "" # set to enable the default probe
|
|
377
|
+
probe_method: ClassVar[str] = "GET"
|
|
378
|
+
probe_params: ClassVar[Dict[str, Any]] = {}
|
|
379
|
+
probe_json: ClassVar[Optional[Dict[str, Any]]] = None
|
|
380
|
+
probe_timeout_seconds: ClassVar[float] = 10.0
|
|
381
|
+
|
|
142
382
|
@classmethod
|
|
143
383
|
async def resolve(cls, *, user_id: str = "owner") -> Dict[str, Any]:
|
|
144
384
|
from core.container import container
|
|
@@ -173,3 +413,50 @@ class ApiKeyCredential(Credential):
|
|
|
173
413
|
qs[name] = api_key
|
|
174
414
|
request = {**request, "params": qs}
|
|
175
415
|
return request
|
|
416
|
+
|
|
417
|
+
@classmethod
|
|
418
|
+
async def _probe(cls, api_key: str) -> ProbeResult:
|
|
419
|
+
"""Default declarative HTTP probe.
|
|
420
|
+
|
|
421
|
+
Subclasses set ``probe_url`` (and optionally ``probe_method``,
|
|
422
|
+
``probe_params``, ``probe_json``); auth attaches via
|
|
423
|
+
:meth:`inject`. Override :meth:`_handle_probe_response` to
|
|
424
|
+
extract provider-specific metadata or detect API-level failures
|
|
425
|
+
embedded in a 2xx response (e.g. Telegram's ``{ok: false}``
|
|
426
|
+
envelope, Maps' ``status: REQUEST_DENIED``).
|
|
427
|
+
"""
|
|
428
|
+
if not cls.probe_url:
|
|
429
|
+
raise NotImplementedError(
|
|
430
|
+
f"Credential subclass {cls.__name__} must override _probe() "
|
|
431
|
+
f"or set the `probe_url` class attribute"
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
request = cls.inject(
|
|
435
|
+
{"api_key": api_key},
|
|
436
|
+
{"headers": {}, "params": dict(cls.probe_params)},
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
async with httpx.AsyncClient(timeout=cls.probe_timeout_seconds) as client:
|
|
440
|
+
response = await client.request(
|
|
441
|
+
cls.probe_method,
|
|
442
|
+
cls.probe_url,
|
|
443
|
+
headers=request.get("headers") or None,
|
|
444
|
+
params=request.get("params") or None,
|
|
445
|
+
json=cls.probe_json,
|
|
446
|
+
)
|
|
447
|
+
response.raise_for_status()
|
|
448
|
+
return cls._handle_probe_response(response)
|
|
449
|
+
|
|
450
|
+
@classmethod
|
|
451
|
+
def _handle_probe_response(cls, response: httpx.Response) -> ProbeResult:
|
|
452
|
+
"""Translate a 2xx probe response to a :class:`ProbeResult`.
|
|
453
|
+
|
|
454
|
+
Default: success message only. Override to inspect the body —
|
|
455
|
+
for example to surface bot identity (Telegram ``getMe``) or to
|
|
456
|
+
catch wrapped failures that arrive as 200s (Telegram
|
|
457
|
+
``{ok: false}``).
|
|
458
|
+
"""
|
|
459
|
+
return ProbeResult(
|
|
460
|
+
valid=True,
|
|
461
|
+
message=f"{cls.display_name or cls.id} API key is valid",
|
|
462
|
+
)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Lazy dependency-injection helpers (Wave 11.I, milestone T-residual).
|
|
2
|
+
|
|
3
|
+
Plugin packages need lazy imports of singletons from
|
|
4
|
+
:mod:`core.container` to avoid the
|
|
5
|
+
``nodes/<plugin>/__init__ -> core.container -> nodes.android._dispatcher
|
|
6
|
+
-> nodes.android.__init__ -> _router`` import cycle that bites at
|
|
7
|
+
package load time. Pre-T-residual the codebase had 53 inline
|
|
8
|
+
``from core.container import container`` statements scattered across
|
|
9
|
+
plugin handlers + services. These helpers consolidate the pattern
|
|
10
|
+
into one place.
|
|
11
|
+
|
|
12
|
+
**Important: NOT memoised.** Both helpers re-resolve the singleton on
|
|
13
|
+
every call. Test fixtures swap the auth-service / database instances
|
|
14
|
+
mid-test via the dependency-injection container's ``override``
|
|
15
|
+
mechanism; a memoised cache would lock in the originally-resolved
|
|
16
|
+
instance and the override would be silently ignored. The lookup is
|
|
17
|
+
a dict access -- caching adds nothing measurable.
|
|
18
|
+
|
|
19
|
+
Usage::
|
|
20
|
+
|
|
21
|
+
from services.plugin.deps import get_auth_service
|
|
22
|
+
|
|
23
|
+
async def handle_thing(data, websocket):
|
|
24
|
+
auth_service = get_auth_service()
|
|
25
|
+
client_id = await auth_service.get_api_key("twitter_client_id")
|
|
26
|
+
...
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from typing import TYPE_CHECKING, Any
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from core.cache import CacheService
|
|
35
|
+
from core.database import Database
|
|
36
|
+
from services.ai import AIService
|
|
37
|
+
from services.auth import AuthService
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_auth_service() -> "AuthService":
|
|
41
|
+
"""Resolve the singleton :class:`services.auth.AuthService`.
|
|
42
|
+
|
|
43
|
+
Lazy import + call-time lookup. Test monkeypatching depends on
|
|
44
|
+
the container override being read at call time, not at module
|
|
45
|
+
import time -- do NOT memoise.
|
|
46
|
+
"""
|
|
47
|
+
from core.container import container
|
|
48
|
+
|
|
49
|
+
return container.auth_service()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_database() -> "Database":
|
|
53
|
+
"""Resolve the singleton :class:`core.database.Database`.
|
|
54
|
+
|
|
55
|
+
Same NOT-memoised contract as :func:`get_auth_service`.
|
|
56
|
+
"""
|
|
57
|
+
from core.container import container
|
|
58
|
+
|
|
59
|
+
return container.database()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_cache() -> "CacheService":
|
|
63
|
+
"""Resolve the singleton :class:`core.cache.CacheService`."""
|
|
64
|
+
from core.container import container
|
|
65
|
+
|
|
66
|
+
return container.cache()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_ai_service() -> "AIService":
|
|
70
|
+
"""Resolve the singleton :class:`services.ai.AIService`."""
|
|
71
|
+
from core.container import container
|
|
72
|
+
|
|
73
|
+
return container.ai_service()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_text_service() -> Any:
|
|
77
|
+
"""Resolve the singleton ``TextService`` (text generation)."""
|
|
78
|
+
from core.container import container
|
|
79
|
+
|
|
80
|
+
return container.text_service()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_maps_service() -> Any:
|
|
84
|
+
"""Resolve the singleton :class:`nodes.location._service.MapsService`."""
|
|
85
|
+
from core.container import container
|
|
86
|
+
|
|
87
|
+
return container.maps_service()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_android_service() -> Any:
|
|
91
|
+
"""Resolve the singleton :class:`nodes.android._dispatcher.AndroidService`."""
|
|
92
|
+
from core.container import container
|
|
93
|
+
|
|
94
|
+
return container.android_service()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
__all__ = [
|
|
98
|
+
"get_auth_service",
|
|
99
|
+
"get_database",
|
|
100
|
+
"get_cache",
|
|
101
|
+
"get_ai_service",
|
|
102
|
+
"get_text_service",
|
|
103
|
+
"get_maps_service",
|
|
104
|
+
"get_android_service",
|
|
105
|
+
]
|
|
@@ -168,11 +168,19 @@ async def _build_memory_entry(
|
|
|
168
168
|
),
|
|
169
169
|
"long_term_enabled": memory_params.get("long_term_enabled", False),
|
|
170
170
|
"retrieval_count": int(memory_params.get("retrieval_count", 3)),
|
|
171
|
+
# Claude Code CLI session continuity: the UUID claude returned on
|
|
172
|
+
# the previous successful run. claude_code_agent passes this as
|
|
173
|
+
# `--resume <UUID>` so claude finds its own JSONL transcript at
|
|
174
|
+
# `<CLAUDE_CONFIG_DIR>/projects/<cwd-encoded>/<UUID>.jsonl`.
|
|
175
|
+
"last_session_id": memory_params.get("last_session_id"),
|
|
171
176
|
}
|
|
172
|
-
logger.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
logger.info(
|
|
178
|
+
"%s Connected memory node: node=%s session=%s (auto=%s) "
|
|
179
|
+
"content_length=%d last_session_id=%s",
|
|
180
|
+
log_prefix, memory_node_id, session_id,
|
|
181
|
+
not configured_session or configured_session == "default",
|
|
182
|
+
len(entry["memory_content"]),
|
|
183
|
+
entry["last_session_id"],
|
|
176
184
|
)
|
|
177
185
|
return entry
|
|
178
186
|
|