machinaos 0.0.1
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 +71 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/bin/cli.js +159 -0
- package/client/.dockerignore +45 -0
- package/client/Dockerfile +68 -0
- package/client/eslint.config.js +29 -0
- package/client/index.html +13 -0
- package/client/nginx.conf +66 -0
- package/client/package.json +48 -0
- package/client/src/App.tsx +27 -0
- package/client/src/Dashboard.tsx +1173 -0
- package/client/src/ParameterPanel.tsx +301 -0
- package/client/src/components/AIAgentNode.tsx +321 -0
- package/client/src/components/APIKeyValidator.tsx +118 -0
- package/client/src/components/ClaudeChatModelNode.tsx +18 -0
- package/client/src/components/ConditionalEdge.tsx +189 -0
- package/client/src/components/CredentialsModal.tsx +306 -0
- package/client/src/components/EdgeConditionEditor.tsx +443 -0
- package/client/src/components/GeminiChatModelNode.tsx +18 -0
- package/client/src/components/GenericNode.tsx +357 -0
- package/client/src/components/LocationParameterPanel.tsx +154 -0
- package/client/src/components/ModelNode.tsx +286 -0
- package/client/src/components/OpenAIChatModelNode.tsx +18 -0
- package/client/src/components/OutputPanel.tsx +471 -0
- package/client/src/components/ParameterRenderer.tsx +1874 -0
- package/client/src/components/SkillEditorModal.tsx +417 -0
- package/client/src/components/SquareNode.tsx +797 -0
- package/client/src/components/StartNode.tsx +250 -0
- package/client/src/components/ToolkitNode.tsx +365 -0
- package/client/src/components/TriggerNode.tsx +463 -0
- package/client/src/components/auth/LoginPage.tsx +247 -0
- package/client/src/components/auth/ProtectedRoute.tsx +59 -0
- package/client/src/components/base/BaseChatModelNode.tsx +271 -0
- package/client/src/components/icons/AIProviderIcons.tsx +50 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
- package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
- package/client/src/components/maps/index.ts +26 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
- package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
- package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
- package/client/src/components/parameterPanel/index.ts +42 -0
- package/client/src/components/shared/DataPanel.tsx +142 -0
- package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
- package/client/src/components/ui/AIResultModal.tsx +204 -0
- package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -0
- package/client/src/components/ui/CollapsibleSection.tsx +88 -0
- package/client/src/components/ui/ComponentItem.tsx +154 -0
- package/client/src/components/ui/ComponentPalette.tsx +321 -0
- package/client/src/components/ui/ConsolePanel.tsx +1074 -0
- package/client/src/components/ui/ErrorBoundary.tsx +196 -0
- package/client/src/components/ui/InputNodesPanel.tsx +204 -0
- package/client/src/components/ui/MapSelector.tsx +314 -0
- package/client/src/components/ui/Modal.tsx +149 -0
- package/client/src/components/ui/NodeContextMenu.tsx +192 -0
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
- package/client/src/components/ui/SettingsPanel.tsx +243 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -0
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
- package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
- package/client/src/config/antdTheme.ts +186 -0
- package/client/src/config/api.ts +54 -0
- package/client/src/contexts/AuthContext.tsx +221 -0
- package/client/src/contexts/ThemeContext.tsx +42 -0
- package/client/src/contexts/WebSocketContext.tsx +1971 -0
- package/client/src/factories/baseChatModelFactory.ts +256 -0
- package/client/src/hooks/useAndroidOperations.ts +164 -0
- package/client/src/hooks/useApiKeyValidation.ts +107 -0
- package/client/src/hooks/useApiKeys.ts +238 -0
- package/client/src/hooks/useAppTheme.ts +17 -0
- package/client/src/hooks/useComponentPalette.ts +51 -0
- package/client/src/hooks/useCopyPaste.ts +155 -0
- package/client/src/hooks/useDragAndDrop.ts +124 -0
- package/client/src/hooks/useDragVariable.ts +88 -0
- package/client/src/hooks/useExecution.ts +313 -0
- package/client/src/hooks/useParameterPanel.ts +176 -0
- package/client/src/hooks/useReactFlowNodes.ts +189 -0
- package/client/src/hooks/useToolSchema.ts +209 -0
- package/client/src/hooks/useWhatsApp.ts +196 -0
- package/client/src/hooks/useWorkflowManagement.ts +46 -0
- package/client/src/index.css +315 -0
- package/client/src/main.tsx +19 -0
- package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
- package/client/src/nodeDefinitions/chatNodes.ts +135 -0
- package/client/src/nodeDefinitions/codeNodes.ts +54 -0
- package/client/src/nodeDefinitions/documentNodes.ts +379 -0
- package/client/src/nodeDefinitions/index.ts +15 -0
- package/client/src/nodeDefinitions/locationNodes.ts +463 -0
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
- package/client/src/nodeDefinitions/skillNodes.ts +211 -0
- package/client/src/nodeDefinitions/toolNodes.ts +198 -0
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
- package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
- package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
- package/client/src/nodeDefinitions.ts +104 -0
- package/client/src/schemas/workflowSchema.ts +264 -0
- package/client/src/services/dynamicParameterService.ts +96 -0
- package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
- package/client/src/services/executionService.ts +232 -0
- package/client/src/services/workflowApi.ts +91 -0
- package/client/src/store/useAppStore.ts +582 -0
- package/client/src/styles/theme.ts +508 -0
- package/client/src/styles/zIndex.ts +17 -0
- package/client/src/types/ComponentTypes.ts +39 -0
- package/client/src/types/EdgeCondition.ts +231 -0
- package/client/src/types/INodeProperties.ts +288 -0
- package/client/src/types/NodeTypes.ts +28 -0
- package/client/src/utils/formatters.ts +33 -0
- package/client/src/utils/googleMapsLoader.ts +140 -0
- package/client/src/utils/locationUtils.ts +85 -0
- package/client/src/utils/nodeUtils.ts +31 -0
- package/client/src/utils/workflow.ts +30 -0
- package/client/src/utils/workflowExport.ts +120 -0
- package/client/src/vite-env.d.ts +12 -0
- package/client/tailwind.config.js +60 -0
- package/client/tsconfig.json +25 -0
- package/client/tsconfig.node.json +11 -0
- package/client/vite.config.js +35 -0
- package/docker-compose.prod.yml +107 -0
- package/docker-compose.yml +104 -0
- package/docs-MachinaOs/README.md +85 -0
- package/docs-MachinaOs/deployment/docker.mdx +228 -0
- package/docs-MachinaOs/deployment/production.mdx +345 -0
- package/docs-MachinaOs/docs.json +75 -0
- package/docs-MachinaOs/faq.mdx +309 -0
- package/docs-MachinaOs/favicon.svg +5 -0
- package/docs-MachinaOs/installation.mdx +160 -0
- package/docs-MachinaOs/introduction.mdx +114 -0
- package/docs-MachinaOs/logo/dark.svg +6 -0
- package/docs-MachinaOs/logo/light.svg +6 -0
- package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
- package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
- package/docs-MachinaOs/nodes/android.mdx +411 -0
- package/docs-MachinaOs/nodes/overview.mdx +181 -0
- package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
- package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
- package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
- package/docs-MachinaOs/quickstart.mdx +119 -0
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
- package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
- package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
- package/nul +0 -0
- package/package.json +70 -0
- package/scripts/build.js +158 -0
- package/scripts/check-ports.ps1 +33 -0
- package/scripts/clean.js +40 -0
- package/scripts/docker.js +93 -0
- package/scripts/kill-port.ps1 +154 -0
- package/scripts/start.js +210 -0
- package/scripts/stop.js +325 -0
- package/server/.dockerignore +44 -0
- package/server/Dockerfile +45 -0
- package/server/constants.py +249 -0
- package/server/core/__init__.py +1 -0
- package/server/core/cache.py +461 -0
- package/server/core/config.py +128 -0
- package/server/core/container.py +99 -0
- package/server/core/database.py +1211 -0
- package/server/core/logging.py +314 -0
- package/server/main.py +289 -0
- package/server/middleware/__init__.py +5 -0
- package/server/middleware/auth.py +89 -0
- package/server/models/__init__.py +1 -0
- package/server/models/auth.py +52 -0
- package/server/models/cache.py +24 -0
- package/server/models/database.py +211 -0
- package/server/models/nodes.py +455 -0
- package/server/package.json +9 -0
- package/server/pyproject.toml +72 -0
- package/server/requirements.txt +83 -0
- package/server/routers/__init__.py +1 -0
- package/server/routers/android.py +294 -0
- package/server/routers/auth.py +203 -0
- package/server/routers/database.py +151 -0
- package/server/routers/maps.py +142 -0
- package/server/routers/nodejs_compat.py +289 -0
- package/server/routers/webhook.py +90 -0
- package/server/routers/websocket.py +2127 -0
- package/server/routers/whatsapp.py +761 -0
- package/server/routers/workflow.py +200 -0
- package/server/services/__init__.py +1 -0
- package/server/services/ai.py +2415 -0
- package/server/services/android/__init__.py +27 -0
- package/server/services/android/broadcaster.py +114 -0
- package/server/services/android/client.py +608 -0
- package/server/services/android/manager.py +78 -0
- package/server/services/android/protocol.py +165 -0
- package/server/services/android_service.py +588 -0
- package/server/services/auth.py +131 -0
- package/server/services/chat_client.py +160 -0
- package/server/services/deployment/__init__.py +12 -0
- package/server/services/deployment/manager.py +706 -0
- package/server/services/deployment/state.py +47 -0
- package/server/services/deployment/triggers.py +275 -0
- package/server/services/event_waiter.py +785 -0
- package/server/services/execution/__init__.py +77 -0
- package/server/services/execution/cache.py +769 -0
- package/server/services/execution/conditions.py +373 -0
- package/server/services/execution/dlq.py +132 -0
- package/server/services/execution/executor.py +1351 -0
- package/server/services/execution/models.py +531 -0
- package/server/services/execution/recovery.py +235 -0
- package/server/services/handlers/__init__.py +126 -0
- package/server/services/handlers/ai.py +355 -0
- package/server/services/handlers/android.py +260 -0
- package/server/services/handlers/code.py +278 -0
- package/server/services/handlers/document.py +598 -0
- package/server/services/handlers/http.py +193 -0
- package/server/services/handlers/polyglot.py +105 -0
- package/server/services/handlers/tools.py +845 -0
- package/server/services/handlers/triggers.py +107 -0
- package/server/services/handlers/utility.py +822 -0
- package/server/services/handlers/whatsapp.py +476 -0
- package/server/services/maps.py +289 -0
- package/server/services/memory_store.py +103 -0
- package/server/services/node_executor.py +375 -0
- package/server/services/parameter_resolver.py +218 -0
- package/server/services/polyglot_client.py +169 -0
- package/server/services/scheduler.py +155 -0
- package/server/services/skill_loader.py +417 -0
- package/server/services/status_broadcaster.py +826 -0
- package/server/services/temporal/__init__.py +23 -0
- package/server/services/temporal/activities.py +344 -0
- package/server/services/temporal/client.py +76 -0
- package/server/services/temporal/executor.py +147 -0
- package/server/services/temporal/worker.py +251 -0
- package/server/services/temporal/workflow.py +355 -0
- package/server/services/temporal/ws_client.py +236 -0
- package/server/services/text.py +111 -0
- package/server/services/user_auth.py +172 -0
- package/server/services/websocket_client.py +29 -0
- package/server/services/workflow.py +597 -0
- package/server/skills/android-skill/SKILL.md +82 -0
- package/server/skills/assistant-personality/SKILL.md +45 -0
- package/server/skills/code-skill/SKILL.md +140 -0
- package/server/skills/http-skill/SKILL.md +161 -0
- package/server/skills/maps-skill/SKILL.md +170 -0
- package/server/skills/memory-skill/SKILL.md +154 -0
- package/server/skills/scheduler-skill/SKILL.md +84 -0
- package/server/skills/whatsapp-skill/SKILL.md +283 -0
- package/server/uv.lock +2916 -0
- package/server/whatsapp-rpc/.dockerignore +30 -0
- package/server/whatsapp-rpc/Dockerfile +44 -0
- package/server/whatsapp-rpc/Dockerfile.web +17 -0
- package/server/whatsapp-rpc/README.md +139 -0
- package/server/whatsapp-rpc/cli.js +95 -0
- package/server/whatsapp-rpc/configs/config.yaml +7 -0
- package/server/whatsapp-rpc/docker-compose.yml +35 -0
- package/server/whatsapp-rpc/docs/API.md +410 -0
- package/server/whatsapp-rpc/go.mod +67 -0
- package/server/whatsapp-rpc/go.sum +203 -0
- package/server/whatsapp-rpc/package.json +30 -0
- package/server/whatsapp-rpc/schema.json +1294 -0
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
- package/server/whatsapp-rpc/scripts/cli.js +162 -0
- package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
- package/server/whatsapp-rpc/src/go/config/config.go +49 -0
- package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
- package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
- package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
- package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
- package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
- package/server/whatsapp-rpc/web/app.py +609 -0
- package/server/whatsapp-rpc/web/requirements.txt +6 -0
- package/server/whatsapp-rpc/web/rpc_client.py +427 -0
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
- package/server/whatsapp-rpc/web/templates/base.html +150 -0
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
- package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
- package/server/whatsapp-rpc/web/templates/groups.html +328 -0
- package/server/whatsapp-rpc/web/templates/messages.html +465 -0
- package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
- package/server/whatsapp-rpc/web/templates/send.html +259 -0
- package/server/whatsapp-rpc/web/templates/settings.html +459 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"""AI node handlers - AI Agent, Chat Agent, AI Chat Model, Simple Memory."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Any, List, Optional, TYPE_CHECKING
|
|
4
|
+
from core.logging import get_logger
|
|
5
|
+
from constants import ANDROID_SERVICE_NODE_TYPES
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from services.ai import AIService
|
|
9
|
+
from core.database import Database
|
|
10
|
+
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def handle_ai_agent(
|
|
15
|
+
node_id: str,
|
|
16
|
+
node_type: str,
|
|
17
|
+
parameters: Dict[str, Any],
|
|
18
|
+
context: Dict[str, Any],
|
|
19
|
+
ai_service: "AIService",
|
|
20
|
+
database: "Database"
|
|
21
|
+
) -> Dict[str, Any]:
|
|
22
|
+
"""Handle AI agent node execution with memory and tool support.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
node_id: The node ID
|
|
26
|
+
node_type: The node type (aiAgent)
|
|
27
|
+
parameters: Resolved parameters
|
|
28
|
+
context: Execution context with nodes, edges, session_id, start_time, execution_id
|
|
29
|
+
ai_service: The AI service instance
|
|
30
|
+
database: The database instance
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Execution result dict
|
|
34
|
+
"""
|
|
35
|
+
nodes = context.get('nodes')
|
|
36
|
+
edges = context.get('edges')
|
|
37
|
+
execution_id = context.get('execution_id', 'unknown')
|
|
38
|
+
workflow_id = context.get('workflow_id') # Extract for status broadcasts
|
|
39
|
+
memory_data = None
|
|
40
|
+
tool_data: List[Dict[str, Any]] = [] # Collect connected tool nodes
|
|
41
|
+
|
|
42
|
+
logger.info(f"[AI Agent] Processing node {node_id}, edges={len(edges) if edges else 0}, nodes={len(nodes) if nodes else 0}, workflow_id={workflow_id}")
|
|
43
|
+
|
|
44
|
+
if edges and nodes:
|
|
45
|
+
# Log all edges targeting this AI Agent for debugging
|
|
46
|
+
incoming_edges = [e for e in edges if e.get('target') == node_id]
|
|
47
|
+
logger.info(f"[AI Agent] Incoming edges to {node_id}: {len(incoming_edges)}")
|
|
48
|
+
for e in incoming_edges:
|
|
49
|
+
logger.info(f"[AI Agent] Edge: source={e.get('source')}, targetHandle={e.get('targetHandle')}")
|
|
50
|
+
|
|
51
|
+
# Check for tool edges specifically
|
|
52
|
+
tool_incoming = [e for e in incoming_edges if e.get('targetHandle') == 'input-tools']
|
|
53
|
+
logger.info(f"[AI Agent] Tool edges (input-tools handle): {len(tool_incoming)}")
|
|
54
|
+
|
|
55
|
+
for edge in edges:
|
|
56
|
+
if edge.get('target') != node_id:
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
target_handle = edge.get('targetHandle')
|
|
60
|
+
source_node_id = edge.get('source')
|
|
61
|
+
source_node = next((n for n in nodes if n.get('id') == source_node_id), None)
|
|
62
|
+
|
|
63
|
+
if not source_node:
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
# Memory detection - load markdown content for editing
|
|
67
|
+
if target_handle == 'input-memory':
|
|
68
|
+
if source_node.get('type') == 'simpleMemory':
|
|
69
|
+
memory_params = await database.get_node_parameters(source_node_id) or {}
|
|
70
|
+
memory_session_id = memory_params.get('sessionId', 'default')
|
|
71
|
+
window_size = int(memory_params.get('windowSize', 10))
|
|
72
|
+
memory_content = memory_params.get('memoryContent', '# Conversation History\n\n*No messages yet.*\n')
|
|
73
|
+
long_term_enabled = memory_params.get('longTermEnabled', False)
|
|
74
|
+
retrieval_count = int(memory_params.get('retrievalCount', 3))
|
|
75
|
+
|
|
76
|
+
memory_data = {
|
|
77
|
+
'node_id': source_node_id, # For saving updated content
|
|
78
|
+
'session_id': memory_session_id,
|
|
79
|
+
'window_size': window_size,
|
|
80
|
+
'memory_content': memory_content,
|
|
81
|
+
'long_term_enabled': long_term_enabled,
|
|
82
|
+
'retrieval_count': retrieval_count
|
|
83
|
+
}
|
|
84
|
+
logger.debug("AI Agent connected memory node", memory_session=memory_session_id, content_length=len(memory_content))
|
|
85
|
+
|
|
86
|
+
# Tool detection (new) - any node connected to input-tools becomes a tool
|
|
87
|
+
elif target_handle == 'input-tools':
|
|
88
|
+
tool_type = source_node.get('type')
|
|
89
|
+
logger.info(f"[AI Agent] Found tool connected via input-tools: type={tool_type}, node_id={source_node_id}")
|
|
90
|
+
tool_params = await database.get_node_parameters(source_node_id) or {}
|
|
91
|
+
|
|
92
|
+
# Build base tool entry
|
|
93
|
+
tool_entry = {
|
|
94
|
+
'node_id': source_node_id,
|
|
95
|
+
'node_type': tool_type,
|
|
96
|
+
'parameters': tool_params,
|
|
97
|
+
'label': source_node.get('data', {}).get('label', tool_type)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Special handling for androidTool - discover connected Android services
|
|
101
|
+
# Follows n8n Sub-Node pattern
|
|
102
|
+
if tool_type == 'androidTool':
|
|
103
|
+
connected_services = []
|
|
104
|
+
|
|
105
|
+
# Scan edges for Android nodes connected to this toolkit
|
|
106
|
+
for service_edge in edges:
|
|
107
|
+
# Skip if not targeting this androidTool node
|
|
108
|
+
if service_edge.get('target') != source_node_id:
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
target_handle = service_edge.get('targetHandle')
|
|
112
|
+
# Accept input-main or no handle (ReactFlow may omit handle for single-input nodes)
|
|
113
|
+
if target_handle is not None and target_handle != 'input-main':
|
|
114
|
+
logger.debug(f"[Android Toolkit] Skipping edge with targetHandle: {target_handle}")
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
android_node_id = service_edge.get('source')
|
|
118
|
+
android_node = next((n for n in nodes if n.get('id') == android_node_id), None)
|
|
119
|
+
|
|
120
|
+
if android_node and android_node.get('type') in ANDROID_SERVICE_NODE_TYPES:
|
|
121
|
+
android_params = await database.get_node_parameters(android_node_id) or {}
|
|
122
|
+
connected_services.append({
|
|
123
|
+
'node_id': android_node_id,
|
|
124
|
+
'node_type': android_node.get('type'),
|
|
125
|
+
'service_id': android_params.get('service_id'),
|
|
126
|
+
'action': android_params.get('action'), # Default action
|
|
127
|
+
'parameters': android_params,
|
|
128
|
+
'label': android_node.get('data', {}).get('label', android_node.get('type'))
|
|
129
|
+
})
|
|
130
|
+
logger.debug(f"Android toolkit connected service: {android_params.get('service_id')}")
|
|
131
|
+
|
|
132
|
+
tool_entry['connected_services'] = connected_services
|
|
133
|
+
logger.debug(f"Android toolkit has {len(connected_services)} connected services")
|
|
134
|
+
|
|
135
|
+
tool_data.append(tool_entry)
|
|
136
|
+
logger.debug(f"AI Agent connected tool: {tool_type}")
|
|
137
|
+
|
|
138
|
+
# Log tool data collection results
|
|
139
|
+
logger.info(f"[AI Agent Handler] Collected tools: count={len(tool_data)}, workflow_id={workflow_id}")
|
|
140
|
+
for td in tool_data:
|
|
141
|
+
logger.info(f"[AI Agent Handler] Tool: type={td.get('node_type')}, node_id={td.get('node_id')}")
|
|
142
|
+
|
|
143
|
+
# Get broadcaster for real-time status updates
|
|
144
|
+
from services.status_broadcaster import get_status_broadcaster
|
|
145
|
+
broadcaster = get_status_broadcaster()
|
|
146
|
+
|
|
147
|
+
return await ai_service.execute_agent(
|
|
148
|
+
node_id,
|
|
149
|
+
parameters,
|
|
150
|
+
memory_data=memory_data,
|
|
151
|
+
tool_data=tool_data if tool_data else None,
|
|
152
|
+
broadcaster=broadcaster,
|
|
153
|
+
workflow_id=workflow_id
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
async def handle_chat_agent(
|
|
158
|
+
node_id: str,
|
|
159
|
+
node_type: str,
|
|
160
|
+
parameters: Dict[str, Any],
|
|
161
|
+
context: Dict[str, Any],
|
|
162
|
+
ai_service: "AIService",
|
|
163
|
+
database: "Database"
|
|
164
|
+
) -> Dict[str, Any]:
|
|
165
|
+
"""Handle Chat Agent node execution with skill-based tool calling.
|
|
166
|
+
|
|
167
|
+
Chat Agent supports:
|
|
168
|
+
- Memory (input-memory): SimpleMemory node for conversation history
|
|
169
|
+
- Skills (input-skill): Provide context/instructions via SKILL.md
|
|
170
|
+
- Tools (input-tools): Tool nodes (httpRequest, etc.) for LangGraph tool calling
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
node_id: The node ID
|
|
174
|
+
node_type: The node type (chatAgent)
|
|
175
|
+
parameters: Resolved parameters
|
|
176
|
+
context: Execution context with nodes, edges, session_id, start_time, execution_id
|
|
177
|
+
ai_service: The AI service instance
|
|
178
|
+
database: The database instance
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Execution result dict
|
|
182
|
+
"""
|
|
183
|
+
nodes = context.get('nodes')
|
|
184
|
+
edges = context.get('edges')
|
|
185
|
+
workflow_id = context.get('workflow_id')
|
|
186
|
+
memory_data = None
|
|
187
|
+
skill_data: List[Dict[str, Any]] = []
|
|
188
|
+
tool_data: List[Dict[str, Any]] = [] # Tools for LangGraph
|
|
189
|
+
input_data: Optional[Dict[str, Any]] = None
|
|
190
|
+
|
|
191
|
+
logger.info(f"[Chat Agent] Processing node {node_id}, workflow_id={workflow_id}")
|
|
192
|
+
|
|
193
|
+
if edges and nodes:
|
|
194
|
+
incoming_edges = [e for e in edges if e.get('target') == node_id]
|
|
195
|
+
logger.debug(f"[Chat Agent] Incoming edges: {len(incoming_edges)}")
|
|
196
|
+
|
|
197
|
+
for edge in edges:
|
|
198
|
+
if edge.get('target') != node_id:
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
target_handle = edge.get('targetHandle')
|
|
202
|
+
source_node_id = edge.get('source')
|
|
203
|
+
source_node = next((n for n in nodes if n.get('id') == source_node_id), None)
|
|
204
|
+
|
|
205
|
+
if not source_node:
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
# Memory detection - load markdown content (same as AI Agent)
|
|
209
|
+
if target_handle == 'input-memory':
|
|
210
|
+
if source_node.get('type') == 'simpleMemory':
|
|
211
|
+
memory_params = await database.get_node_parameters(source_node_id) or {}
|
|
212
|
+
memory_session_id = memory_params.get('sessionId', 'default')
|
|
213
|
+
window_size = int(memory_params.get('windowSize', 10))
|
|
214
|
+
memory_content = memory_params.get('memoryContent', '# Conversation History\n\n*No messages yet.*\n')
|
|
215
|
+
long_term_enabled = memory_params.get('longTermEnabled', False)
|
|
216
|
+
retrieval_count = int(memory_params.get('retrievalCount', 3))
|
|
217
|
+
|
|
218
|
+
memory_data = {
|
|
219
|
+
'node_id': source_node_id,
|
|
220
|
+
'session_id': memory_session_id,
|
|
221
|
+
'window_size': window_size,
|
|
222
|
+
'memory_content': memory_content,
|
|
223
|
+
'long_term_enabled': long_term_enabled,
|
|
224
|
+
'retrieval_count': retrieval_count
|
|
225
|
+
}
|
|
226
|
+
logger.info(f"[Chat Agent] Connected memory node: session={memory_session_id}, content_length={len(memory_content)}")
|
|
227
|
+
|
|
228
|
+
# Skill detection - nodes connected to input-skill handle
|
|
229
|
+
elif target_handle == 'input-skill':
|
|
230
|
+
skill_type = source_node.get('type')
|
|
231
|
+
skill_params = await database.get_node_parameters(source_node_id) or {}
|
|
232
|
+
skill_entry = {
|
|
233
|
+
'node_id': source_node_id,
|
|
234
|
+
'node_type': skill_type,
|
|
235
|
+
'skill_name': skill_params.get('skillName', skill_type),
|
|
236
|
+
'parameters': skill_params,
|
|
237
|
+
'label': source_node.get('data', {}).get('label', skill_type)
|
|
238
|
+
}
|
|
239
|
+
skill_data.append(skill_entry)
|
|
240
|
+
logger.debug(f"[Chat Agent] Connected skill: {skill_type}")
|
|
241
|
+
|
|
242
|
+
# Tool detection - nodes connected to input-tools handle (for LangGraph)
|
|
243
|
+
elif target_handle == 'input-tools':
|
|
244
|
+
tool_type = source_node.get('type')
|
|
245
|
+
tool_params = await database.get_node_parameters(source_node_id) or {}
|
|
246
|
+
tool_entry = {
|
|
247
|
+
'node_id': source_node_id,
|
|
248
|
+
'node_type': tool_type,
|
|
249
|
+
'parameters': tool_params,
|
|
250
|
+
'label': source_node.get('data', {}).get('label', tool_type)
|
|
251
|
+
}
|
|
252
|
+
tool_data.append(tool_entry)
|
|
253
|
+
logger.info(f"[Chat Agent] Connected tool: {tool_type} ({tool_entry['label']})")
|
|
254
|
+
|
|
255
|
+
# Input data detection - nodes connected to input-main
|
|
256
|
+
elif target_handle == 'input-main' or target_handle is None:
|
|
257
|
+
source_output = context.get('outputs', {}).get(source_node_id)
|
|
258
|
+
if source_output:
|
|
259
|
+
input_data = source_output
|
|
260
|
+
logger.debug(f"[Chat Agent] Input from {source_node.get('type')}: {list(source_output.keys())}")
|
|
261
|
+
|
|
262
|
+
# Log discovered connections
|
|
263
|
+
logger.info(f"[Chat Agent] Discovered: memory={'yes' if memory_data else 'no'}, {len(skill_data)} skills, {len(tool_data)} tools")
|
|
264
|
+
for td in tool_data:
|
|
265
|
+
logger.debug(f"[Chat Agent] Tool: type={td.get('node_type')}, label={td.get('label')}")
|
|
266
|
+
|
|
267
|
+
# Auto-use input data if prompt is empty (fallback for trigger nodes)
|
|
268
|
+
if not parameters.get('prompt') and input_data:
|
|
269
|
+
prompt = (
|
|
270
|
+
input_data.get('message') or
|
|
271
|
+
input_data.get('text') or
|
|
272
|
+
input_data.get('content') or
|
|
273
|
+
str(input_data)
|
|
274
|
+
)
|
|
275
|
+
parameters = {**parameters, 'prompt': prompt}
|
|
276
|
+
logger.info(f"[Chat Agent] Auto-using input as prompt: {prompt[:100] if len(str(prompt)) > 100 else prompt}...")
|
|
277
|
+
|
|
278
|
+
# Get broadcaster for real-time status updates
|
|
279
|
+
from services.status_broadcaster import get_status_broadcaster
|
|
280
|
+
broadcaster = get_status_broadcaster()
|
|
281
|
+
|
|
282
|
+
# Execute Chat Agent with memory, skills and tools
|
|
283
|
+
return await ai_service.execute_chat_agent(
|
|
284
|
+
node_id,
|
|
285
|
+
parameters,
|
|
286
|
+
memory_data=memory_data,
|
|
287
|
+
skill_data=skill_data if skill_data else None,
|
|
288
|
+
tool_data=tool_data if tool_data else None,
|
|
289
|
+
broadcaster=broadcaster,
|
|
290
|
+
workflow_id=workflow_id
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
async def handle_ai_chat_model(
|
|
295
|
+
node_id: str,
|
|
296
|
+
node_type: str,
|
|
297
|
+
parameters: Dict[str, Any],
|
|
298
|
+
context: Dict[str, Any],
|
|
299
|
+
ai_service: "AIService"
|
|
300
|
+
) -> Dict[str, Any]:
|
|
301
|
+
"""Handle AI chat model node execution.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
node_id: The node ID
|
|
305
|
+
node_type: The node type (openaiChatModel, anthropicChatModel, etc.)
|
|
306
|
+
parameters: Resolved parameters
|
|
307
|
+
context: Execution context
|
|
308
|
+
ai_service: The AI service instance
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Execution result dict
|
|
312
|
+
"""
|
|
313
|
+
return await ai_service.execute_chat(node_id, node_type, parameters)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
async def handle_simple_memory(
|
|
317
|
+
node_id: str,
|
|
318
|
+
node_type: str,
|
|
319
|
+
parameters: Dict[str, Any],
|
|
320
|
+
context: Dict[str, Any]
|
|
321
|
+
) -> Dict[str, Any]:
|
|
322
|
+
"""Handle simple memory node execution.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
node_id: The node ID
|
|
326
|
+
node_type: The node type (simpleMemory)
|
|
327
|
+
parameters: Resolved parameters
|
|
328
|
+
context: Execution context
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
Execution result dict with session info and messages
|
|
332
|
+
"""
|
|
333
|
+
from services.memory_store import get_messages, clear_session
|
|
334
|
+
|
|
335
|
+
session_id = parameters.get('sessionId', 'default')
|
|
336
|
+
memory_type = parameters.get('memoryType', 'buffer')
|
|
337
|
+
window_size = int(parameters.get('windowSize', 10)) if memory_type == 'window' else None
|
|
338
|
+
clear_on_run = parameters.get('clearOnRun', False)
|
|
339
|
+
|
|
340
|
+
if clear_on_run:
|
|
341
|
+
cleared = clear_session(session_id)
|
|
342
|
+
logger.info(f"[Memory] Cleared {cleared} messages from session '{session_id}'")
|
|
343
|
+
|
|
344
|
+
messages = get_messages(session_id, window_size)
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
"success": True,
|
|
348
|
+
"result": {
|
|
349
|
+
"session_id": session_id,
|
|
350
|
+
"messages": messages,
|
|
351
|
+
"message_count": len(messages),
|
|
352
|
+
"memory_type": memory_type,
|
|
353
|
+
"window_size": window_size
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Android node handlers - Device Setup and Android Services."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Dict, Any, TYPE_CHECKING
|
|
7
|
+
from core.logging import get_logger
|
|
8
|
+
from constants import ANDROID_SERVICE_NODE_TYPES
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from services.android_service import AndroidService
|
|
12
|
+
from core.config import Settings
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def handle_android_device_setup(
|
|
18
|
+
node_id: str,
|
|
19
|
+
node_type: str,
|
|
20
|
+
parameters: Dict[str, Any],
|
|
21
|
+
context: Dict[str, Any],
|
|
22
|
+
settings: "Settings"
|
|
23
|
+
) -> Dict[str, Any]:
|
|
24
|
+
"""Handle Android device setup node execution.
|
|
25
|
+
|
|
26
|
+
Supports two connection types:
|
|
27
|
+
- local: ADB connection with port forwarding
|
|
28
|
+
- remote: WebSocket connection to remote proxy
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
node_id: The node ID
|
|
32
|
+
node_type: The node type (androidDeviceSetup)
|
|
33
|
+
parameters: Resolved parameters
|
|
34
|
+
context: Execution context with start_time
|
|
35
|
+
settings: Application settings
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Execution result dict with connection info
|
|
39
|
+
"""
|
|
40
|
+
start_time = context.get('start_time', time.time())
|
|
41
|
+
connection_type = parameters.get('connection_type', 'local')
|
|
42
|
+
device_id = parameters.get('device_id', '')
|
|
43
|
+
websocket_url = parameters.get('websocket_url', settings.websocket_url)
|
|
44
|
+
port = parameters.get('port', 8888)
|
|
45
|
+
auto_forward = parameters.get('auto_forward', True)
|
|
46
|
+
|
|
47
|
+
logger.info("[Android Device Setup] Executing", node_id=node_id, connection_type=connection_type,
|
|
48
|
+
device_id=device_id, websocket_url=websocket_url, port=port, auto_forward=auto_forward)
|
|
49
|
+
|
|
50
|
+
if connection_type == 'local' and auto_forward and device_id:
|
|
51
|
+
# Local ADB connection with port forwarding
|
|
52
|
+
import subprocess
|
|
53
|
+
try:
|
|
54
|
+
cmd = ["adb", "-s", device_id, "forward", f"tcp:{port}", f"tcp:{port}"]
|
|
55
|
+
subprocess_result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=5)
|
|
56
|
+
|
|
57
|
+
if subprocess_result.returncode == 0:
|
|
58
|
+
connection_info = {
|
|
59
|
+
"connection_type": "local",
|
|
60
|
+
"device_id": device_id,
|
|
61
|
+
"port": port,
|
|
62
|
+
"port_forwarding": "active",
|
|
63
|
+
"local_endpoint": f"http://localhost:{port}",
|
|
64
|
+
"message": f"Port forwarding setup: localhost:{port} -> device:{port}",
|
|
65
|
+
"timestamp": datetime.now().isoformat()
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
"success": True,
|
|
69
|
+
"node_id": node_id,
|
|
70
|
+
"node_type": node_type,
|
|
71
|
+
"result": connection_info,
|
|
72
|
+
"execution_time": time.time() - start_time,
|
|
73
|
+
"timestamp": datetime.now().isoformat()
|
|
74
|
+
}
|
|
75
|
+
else:
|
|
76
|
+
error_msg = subprocess_result.stderr.strip() or subprocess_result.stdout.strip()
|
|
77
|
+
return {
|
|
78
|
+
"success": False,
|
|
79
|
+
"node_id": node_id,
|
|
80
|
+
"node_type": node_type,
|
|
81
|
+
"error": f"Port forwarding failed: {error_msg}",
|
|
82
|
+
"execution_time": time.time() - start_time,
|
|
83
|
+
"timestamp": datetime.now().isoformat()
|
|
84
|
+
}
|
|
85
|
+
except Exception as e:
|
|
86
|
+
return {
|
|
87
|
+
"success": False,
|
|
88
|
+
"node_id": node_id,
|
|
89
|
+
"node_type": node_type,
|
|
90
|
+
"error": f"Port forwarding error: {str(e)}",
|
|
91
|
+
"execution_time": time.time() - start_time,
|
|
92
|
+
"timestamp": datetime.now().isoformat()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
elif connection_type == 'remote' and websocket_url:
|
|
96
|
+
# Remote WebSocket connection via Android Services Relay
|
|
97
|
+
try:
|
|
98
|
+
from services.android import get_relay_client
|
|
99
|
+
|
|
100
|
+
api_key = settings.websocket_api_key
|
|
101
|
+
if not api_key:
|
|
102
|
+
raise ValueError("WEBSOCKET_API_KEY not configured in environment")
|
|
103
|
+
|
|
104
|
+
logger.info("[Android Device Setup] Connecting to Relay",
|
|
105
|
+
url=websocket_url, api_key=api_key[:8] + "...")
|
|
106
|
+
|
|
107
|
+
relay_client = await get_relay_client(websocket_url, api_key)
|
|
108
|
+
|
|
109
|
+
if relay_client and relay_client.is_connected():
|
|
110
|
+
# Wait for Android device to pair via QR code
|
|
111
|
+
if not relay_client.is_paired():
|
|
112
|
+
logger.info("[Android Device Setup] Waiting for Android device to pair...")
|
|
113
|
+
paired = await relay_client.wait_for_pairing(timeout=5.0)
|
|
114
|
+
if not paired:
|
|
115
|
+
# Return QR data for pairing
|
|
116
|
+
connection_info = {
|
|
117
|
+
"connection_type": "remote",
|
|
118
|
+
"websocket_url": websocket_url,
|
|
119
|
+
"port": port,
|
|
120
|
+
"status": "waiting_for_pairing",
|
|
121
|
+
"qr_data": relay_client.qr_data,
|
|
122
|
+
"session_token": relay_client.session_token,
|
|
123
|
+
"message": "Scan QR code with Android app to pair",
|
|
124
|
+
"timestamp": datetime.now().isoformat()
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
"success": True,
|
|
128
|
+
"node_id": node_id,
|
|
129
|
+
"node_type": node_type,
|
|
130
|
+
"result": connection_info,
|
|
131
|
+
"execution_time": time.time() - start_time,
|
|
132
|
+
"timestamp": datetime.now().isoformat()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Device is paired
|
|
136
|
+
device_id = relay_client.paired_device_id
|
|
137
|
+
device_name = relay_client.paired_device_name
|
|
138
|
+
|
|
139
|
+
connection_info = {
|
|
140
|
+
"connection_type": "remote",
|
|
141
|
+
"websocket_url": websocket_url,
|
|
142
|
+
"port": port,
|
|
143
|
+
"status": "paired",
|
|
144
|
+
"paired": True,
|
|
145
|
+
"device_id": device_id,
|
|
146
|
+
"device_name": device_name,
|
|
147
|
+
"session_token": relay_client.session_token,
|
|
148
|
+
"message": f"Paired with Android device: {device_name or device_id}",
|
|
149
|
+
"timestamp": datetime.now().isoformat()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
logger.info("[Android Device Setup] Relay connected and paired",
|
|
153
|
+
device_id=device_id, device_name=device_name)
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
"success": True,
|
|
157
|
+
"node_id": node_id,
|
|
158
|
+
"node_type": node_type,
|
|
159
|
+
"result": connection_info,
|
|
160
|
+
"execution_time": time.time() - start_time,
|
|
161
|
+
"timestamp": datetime.now().isoformat()
|
|
162
|
+
}
|
|
163
|
+
else:
|
|
164
|
+
logger.error("[Android Device Setup] Relay connection failed")
|
|
165
|
+
return {
|
|
166
|
+
"success": False,
|
|
167
|
+
"node_id": node_id,
|
|
168
|
+
"node_type": node_type,
|
|
169
|
+
"error": "Failed to connect to relay server",
|
|
170
|
+
"execution_time": time.time() - start_time,
|
|
171
|
+
"timestamp": datetime.now().isoformat()
|
|
172
|
+
}
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.error("[Android Device Setup] Relay error", error=str(e))
|
|
175
|
+
return {
|
|
176
|
+
"success": False,
|
|
177
|
+
"node_id": node_id,
|
|
178
|
+
"node_type": node_type,
|
|
179
|
+
"error": f"Relay connection error: {str(e)}",
|
|
180
|
+
"execution_time": time.time() - start_time,
|
|
181
|
+
"timestamp": datetime.now().isoformat()
|
|
182
|
+
}
|
|
183
|
+
else:
|
|
184
|
+
# Configuration saved without active setup
|
|
185
|
+
connection_info = {
|
|
186
|
+
"connection_type": connection_type,
|
|
187
|
+
"device_id": device_id if connection_type == 'local' else None,
|
|
188
|
+
"websocket_url": websocket_url if connection_type == 'remote' else None,
|
|
189
|
+
"port": port,
|
|
190
|
+
"port_forwarding": "not_setup",
|
|
191
|
+
"message": "Device configuration saved (auto_forward disabled or missing device info)",
|
|
192
|
+
"timestamp": datetime.now().isoformat()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
"success": True,
|
|
197
|
+
"node_id": node_id,
|
|
198
|
+
"node_type": node_type,
|
|
199
|
+
"result": connection_info,
|
|
200
|
+
"execution_time": time.time() - start_time,
|
|
201
|
+
"timestamp": datetime.now().isoformat()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
async def handle_android_service(
|
|
206
|
+
node_id: str,
|
|
207
|
+
node_type: str,
|
|
208
|
+
parameters: Dict[str, Any],
|
|
209
|
+
context: Dict[str, Any],
|
|
210
|
+
android_service: "AndroidService"
|
|
211
|
+
) -> Dict[str, Any]:
|
|
212
|
+
"""Handle Android service node execution.
|
|
213
|
+
|
|
214
|
+
Executes Android service actions like battery status, network info, etc.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
node_id: The node ID
|
|
218
|
+
node_type: The node type (batteryMonitor, networkMonitor, etc.)
|
|
219
|
+
parameters: Resolved parameters
|
|
220
|
+
context: Execution context
|
|
221
|
+
android_service: The Android service instance
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Execution result dict
|
|
225
|
+
"""
|
|
226
|
+
logger.debug(f"[ANDROID DEBUG] Matched! node_type={node_type}")
|
|
227
|
+
|
|
228
|
+
service_id = parameters.get('service_id', 'battery')
|
|
229
|
+
action = parameters.get('action', 'status')
|
|
230
|
+
service_params = parameters.get('parameters', {})
|
|
231
|
+
android_host = parameters.get('android_host', 'localhost')
|
|
232
|
+
android_port = parameters.get('android_port', 8888)
|
|
233
|
+
|
|
234
|
+
# Parse parameters if it's a JSON string
|
|
235
|
+
if isinstance(service_params, str):
|
|
236
|
+
try:
|
|
237
|
+
service_params = json.loads(service_params)
|
|
238
|
+
except json.JSONDecodeError:
|
|
239
|
+
service_params = {}
|
|
240
|
+
|
|
241
|
+
# Extract additional parameters that are at root level (from additionalProperties in node definitions)
|
|
242
|
+
# These include: package_name (appLauncher), and any future custom parameters
|
|
243
|
+
additional_param_keys = ['package_name']
|
|
244
|
+
for key in additional_param_keys:
|
|
245
|
+
if key in parameters and parameters[key]:
|
|
246
|
+
service_params[key] = parameters[key]
|
|
247
|
+
|
|
248
|
+
logger.debug(f"[ANDROID DEBUG] Extracted params: service_id={service_id}, action={action}, host={android_host}, port={android_port}, service_params={service_params}")
|
|
249
|
+
logger.debug(f"[ANDROID DEBUG] About to call android_service.execute_service")
|
|
250
|
+
|
|
251
|
+
result = await android_service.execute_service(
|
|
252
|
+
node_id=node_id,
|
|
253
|
+
service_id=service_id,
|
|
254
|
+
action=action,
|
|
255
|
+
parameters=service_params,
|
|
256
|
+
android_host=android_host,
|
|
257
|
+
android_port=android_port
|
|
258
|
+
)
|
|
259
|
+
logger.debug(f"[ANDROID DEBUG] Got result from android_service")
|
|
260
|
+
return result
|