machinaos 0.0.1 → 0.0.7
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 -71
- package/LICENSE +21 -21
- package/README.md +163 -87
- package/bin/cli.js +62 -106
- package/client/.dockerignore +45 -45
- package/client/Dockerfile +68 -68
- package/client/dist/assets/index-DFSC53FP.css +1 -0
- package/client/dist/assets/index-fJ-1gTf5.js +613 -0
- package/client/dist/index.html +14 -0
- package/client/eslint.config.js +34 -16
- package/client/nginx.conf +66 -66
- package/client/package.json +61 -48
- package/client/src/App.tsx +27 -27
- package/client/src/Dashboard.tsx +1200 -1172
- package/client/src/ParameterPanel.tsx +302 -300
- package/client/src/components/AIAgentNode.tsx +315 -321
- package/client/src/components/APIKeyValidator.tsx +117 -117
- package/client/src/components/ClaudeChatModelNode.tsx +17 -17
- package/client/src/components/CredentialsModal.tsx +1200 -306
- package/client/src/components/GeminiChatModelNode.tsx +17 -17
- package/client/src/components/GenericNode.tsx +356 -356
- package/client/src/components/LocationParameterPanel.tsx +153 -153
- package/client/src/components/ModelNode.tsx +285 -285
- package/client/src/components/OpenAIChatModelNode.tsx +17 -17
- package/client/src/components/OutputPanel.tsx +470 -470
- package/client/src/components/ParameterRenderer.tsx +1873 -1873
- package/client/src/components/SkillEditorModal.tsx +3 -3
- package/client/src/components/SquareNode.tsx +812 -796
- package/client/src/components/ToolkitNode.tsx +365 -365
- package/client/src/components/auth/LoginPage.tsx +247 -247
- package/client/src/components/auth/ProtectedRoute.tsx +59 -59
- package/client/src/components/base/BaseChatModelNode.tsx +270 -270
- package/client/src/components/icons/AIProviderIcons.tsx +50 -50
- package/client/src/components/maps/GoogleMapsPicker.tsx +136 -136
- package/client/src/components/maps/MapsPreviewPanel.tsx +109 -109
- package/client/src/components/maps/index.ts +25 -25
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -1094
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +64 -64
- package/client/src/components/parameterPanel/MapsSection.tsx +91 -91
- package/client/src/components/parameterPanel/MiddleSection.tsx +867 -571
- package/client/src/components/parameterPanel/OutputSection.tsx +80 -80
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +81 -81
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -436
- package/client/src/components/parameterPanel/index.ts +41 -41
- package/client/src/components/shared/DataPanel.tsx +142 -142
- package/client/src/components/shared/JSONTreeRenderer.tsx +105 -105
- package/client/src/components/ui/AIResultModal.tsx +203 -203
- package/client/src/components/ui/ApiKeyInput.tsx +93 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -81
- package/client/src/components/ui/CollapsibleSection.tsx +87 -87
- package/client/src/components/ui/ComponentItem.tsx +153 -153
- package/client/src/components/ui/ComponentPalette.tsx +320 -320
- package/client/src/components/ui/ConsolePanel.tsx +151 -43
- package/client/src/components/ui/ErrorBoundary.tsx +195 -195
- package/client/src/components/ui/InputNodesPanel.tsx +203 -203
- package/client/src/components/ui/MapSelector.tsx +313 -313
- package/client/src/components/ui/Modal.tsx +151 -148
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -1150
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -381
- package/client/src/components/ui/QRCodeDisplay.tsx +182 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -736
- package/client/src/components/ui/WorkflowSidebar.tsx +293 -293
- package/client/src/config/antdTheme.ts +186 -186
- package/client/src/contexts/AuthContext.tsx +221 -221
- package/client/src/contexts/ThemeContext.tsx +42 -42
- package/client/src/contexts/WebSocketContext.tsx +2144 -1971
- package/client/src/factories/baseChatModelFactory.ts +255 -255
- package/client/src/hooks/useAndroidOperations.ts +118 -164
- package/client/src/hooks/useApiKeyValidation.ts +106 -106
- package/client/src/hooks/useApiKeys.ts +238 -238
- package/client/src/hooks/useAppTheme.ts +17 -17
- package/client/src/hooks/useComponentPalette.ts +50 -50
- package/client/src/hooks/useDragAndDrop.ts +123 -123
- package/client/src/hooks/useDragVariable.ts +88 -88
- package/client/src/hooks/useExecution.ts +319 -313
- package/client/src/hooks/useParameterPanel.ts +176 -176
- package/client/src/hooks/useReactFlowNodes.ts +188 -188
- package/client/src/hooks/useToolSchema.ts +209 -209
- package/client/src/hooks/useWhatsApp.ts +196 -196
- package/client/src/hooks/useWorkflowManagement.ts +45 -45
- package/client/src/index.css +314 -314
- package/client/src/nodeDefinitions/aiAgentNodes.ts +335 -335
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -340
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -383
- package/client/src/nodeDefinitions/chatNodes.ts +135 -135
- package/client/src/nodeDefinitions/codeNodes.ts +54 -54
- package/client/src/nodeDefinitions/index.ts +14 -14
- package/client/src/nodeDefinitions/locationNodes.ts +462 -462
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -220
- package/client/src/nodeDefinitions/skillNodes.ts +17 -5
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -284
- package/client/src/nodeDefinitions/whatsappNodes.ts +821 -865
- package/client/src/nodeDefinitions.ts +101 -103
- package/client/src/services/dynamicParameterService.ts +95 -95
- package/client/src/services/execution/aiAgentExecutionService.ts +34 -34
- package/client/src/services/executionService.ts +227 -231
- package/client/src/services/workflowApi.ts +91 -91
- package/client/src/store/useAppStore.ts +578 -581
- package/client/src/styles/theme.ts +513 -508
- package/client/src/styles/zIndex.ts +16 -16
- package/client/src/types/ComponentTypes.ts +38 -38
- package/client/src/types/INodeProperties.ts +287 -287
- package/client/src/types/NodeTypes.ts +27 -27
- package/client/src/utils/formatters.ts +32 -32
- package/client/src/utils/googleMapsLoader.ts +139 -139
- package/client/src/utils/locationUtils.ts +84 -84
- package/client/src/utils/nodeUtils.ts +30 -30
- package/client/src/utils/workflow.ts +29 -29
- package/client/src/vite-env.d.ts +12 -12
- package/client/tailwind.config.js +59 -59
- package/client/tsconfig.json +25 -25
- package/client/vite.config.js +35 -35
- package/install.ps1 +308 -0
- package/install.sh +343 -0
- package/package.json +81 -70
- package/scripts/build.js +174 -51
- package/scripts/clean.js +40 -40
- package/scripts/start.js +234 -210
- package/scripts/stop.js +301 -325
- package/server/.dockerignore +44 -44
- package/server/Dockerfile +45 -45
- package/server/constants.py +244 -249
- package/server/core/cache.py +460 -460
- package/server/core/config.py +127 -127
- package/server/core/container.py +98 -98
- package/server/core/database.py +1296 -1210
- package/server/core/logging.py +313 -313
- package/server/main.py +288 -288
- package/server/middleware/__init__.py +5 -5
- package/server/middleware/auth.py +89 -89
- package/server/models/auth.py +52 -52
- package/server/models/cache.py +24 -24
- package/server/models/database.py +235 -210
- package/server/models/nodes.py +435 -455
- package/server/pyproject.toml +75 -72
- package/server/requirements.txt +83 -83
- package/server/routers/android.py +294 -294
- package/server/routers/auth.py +203 -203
- package/server/routers/database.py +150 -150
- package/server/routers/maps.py +141 -141
- package/server/routers/nodejs_compat.py +288 -288
- package/server/routers/webhook.py +90 -90
- package/server/routers/websocket.py +2239 -2127
- package/server/routers/whatsapp.py +761 -761
- package/server/routers/workflow.py +199 -199
- package/server/services/ai.py +2444 -2414
- package/server/services/android_service.py +588 -588
- package/server/services/auth.py +130 -130
- package/server/services/chat_client.py +160 -160
- package/server/services/deployment/manager.py +706 -706
- package/server/services/event_waiter.py +675 -785
- package/server/services/execution/executor.py +1351 -1351
- package/server/services/execution/models.py +1 -1
- package/server/services/handlers/__init__.py +122 -126
- package/server/services/handlers/ai.py +390 -355
- package/server/services/handlers/android.py +69 -260
- package/server/services/handlers/code.py +278 -278
- package/server/services/handlers/http.py +193 -193
- package/server/services/handlers/tools.py +146 -32
- package/server/services/handlers/triggers.py +107 -107
- package/server/services/handlers/utility.py +822 -822
- package/server/services/handlers/whatsapp.py +423 -476
- package/server/services/maps.py +288 -288
- package/server/services/memory_store.py +103 -103
- package/server/services/node_executor.py +372 -375
- package/server/services/scheduler.py +155 -155
- package/server/services/skill_loader.py +1 -1
- package/server/services/status_broadcaster.py +834 -826
- package/server/services/temporal/__init__.py +23 -23
- package/server/services/temporal/activities.py +344 -344
- package/server/services/temporal/client.py +76 -76
- package/server/services/temporal/executor.py +147 -147
- package/server/services/temporal/worker.py +251 -251
- package/server/services/temporal/workflow.py +355 -355
- package/server/services/temporal/ws_client.py +236 -236
- package/server/services/text.py +110 -110
- package/server/services/user_auth.py +172 -172
- package/server/services/websocket_client.py +29 -29
- package/server/services/workflow.py +597 -597
- package/server/skills/android-skill/SKILL.md +4 -4
- package/server/skills/code-skill/SKILL.md +123 -89
- package/server/skills/maps-skill/SKILL.md +3 -3
- package/server/skills/memory-skill/SKILL.md +1 -1
- package/server/skills/web-search-skill/SKILL.md +154 -0
- package/server/skills/whatsapp-skill/SKILL.md +3 -3
- package/server/uv.lock +461 -100
- package/server/whatsapp-rpc/.dockerignore +30 -30
- package/server/whatsapp-rpc/Dockerfile +44 -44
- package/server/whatsapp-rpc/Dockerfile.web +17 -17
- package/server/whatsapp-rpc/README.md +139 -139
- package/server/whatsapp-rpc/bin/whatsapp-rpc-server +0 -0
- package/server/whatsapp-rpc/cli.js +95 -95
- package/server/whatsapp-rpc/configs/config.yaml +6 -6
- package/server/whatsapp-rpc/docker-compose.yml +35 -35
- package/server/whatsapp-rpc/docs/API.md +410 -410
- package/server/whatsapp-rpc/node_modules/.package-lock.json +259 -0
- package/server/whatsapp-rpc/node_modules/chalk/license +9 -0
- package/server/whatsapp-rpc/node_modules/chalk/package.json +83 -0
- package/server/whatsapp-rpc/node_modules/chalk/readme.md +297 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/index.d.ts +325 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/index.js +225 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/utilities.js +33 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/server/whatsapp-rpc/node_modules/commander/LICENSE +22 -0
- package/server/whatsapp-rpc/node_modules/commander/Readme.md +1148 -0
- package/server/whatsapp-rpc/node_modules/commander/esm.mjs +16 -0
- package/server/whatsapp-rpc/node_modules/commander/index.js +26 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/argument.js +145 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/command.js +2179 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/error.js +43 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/help.js +462 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/option.js +329 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/suggestSimilar.js +100 -0
- package/server/whatsapp-rpc/node_modules/commander/package-support.json +16 -0
- package/server/whatsapp-rpc/node_modules/commander/package.json +80 -0
- package/server/whatsapp-rpc/node_modules/commander/typings/esm.d.mts +3 -0
- package/server/whatsapp-rpc/node_modules/commander/typings/index.d.ts +884 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/README.md +89 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/index.js +39 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/enoent.js +59 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/parse.js +91 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/escape.js +47 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/readShebang.js +23 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/resolveCommand.js +52 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/package.json +73 -0
- package/server/whatsapp-rpc/node_modules/execa/index.d.ts +955 -0
- package/server/whatsapp-rpc/node_modules/execa/index.js +309 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/command.js +119 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/error.js +87 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/kill.js +102 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/pipe.js +42 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/promise.js +36 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/stdio.js +49 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/stream.js +133 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/verbose.js +19 -0
- package/server/whatsapp-rpc/node_modules/execa/license +9 -0
- package/server/whatsapp-rpc/node_modules/execa/package.json +90 -0
- package/server/whatsapp-rpc/node_modules/execa/readme.md +822 -0
- package/server/whatsapp-rpc/node_modules/get-stream/license +9 -0
- package/server/whatsapp-rpc/node_modules/get-stream/package.json +53 -0
- package/server/whatsapp-rpc/node_modules/get-stream/readme.md +291 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/array-buffer.js +84 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/array.js +32 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/buffer.js +20 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/contents.js +101 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/index.d.ts +119 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/index.js +5 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/string.js +36 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/utils.js +11 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/README.md +95 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/index.js +97 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/package.json +36 -0
- package/server/whatsapp-rpc/node_modules/human-signals/LICENSE +201 -0
- package/server/whatsapp-rpc/node_modules/human-signals/README.md +168 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/core.js +273 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.d.ts +73 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.js +70 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/realtime.js +16 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/signals.js +34 -0
- package/server/whatsapp-rpc/node_modules/human-signals/package.json +61 -0
- package/server/whatsapp-rpc/node_modules/is-stream/index.d.ts +81 -0
- package/server/whatsapp-rpc/node_modules/is-stream/index.js +29 -0
- package/server/whatsapp-rpc/node_modules/is-stream/license +9 -0
- package/server/whatsapp-rpc/node_modules/is-stream/package.json +44 -0
- package/server/whatsapp-rpc/node_modules/is-stream/readme.md +60 -0
- package/server/whatsapp-rpc/node_modules/isexe/LICENSE +15 -0
- package/server/whatsapp-rpc/node_modules/isexe/README.md +51 -0
- package/server/whatsapp-rpc/node_modules/isexe/index.js +57 -0
- package/server/whatsapp-rpc/node_modules/isexe/mode.js +41 -0
- package/server/whatsapp-rpc/node_modules/isexe/package.json +31 -0
- package/server/whatsapp-rpc/node_modules/isexe/test/basic.js +221 -0
- package/server/whatsapp-rpc/node_modules/isexe/windows.js +42 -0
- package/server/whatsapp-rpc/node_modules/kill-port/.editorconfig +12 -0
- package/server/whatsapp-rpc/node_modules/kill-port/.gitattributes +1 -0
- package/server/whatsapp-rpc/node_modules/kill-port/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/kill-port/README.md +140 -0
- package/server/whatsapp-rpc/node_modules/kill-port/cli.js +25 -0
- package/server/whatsapp-rpc/node_modules/kill-port/example.js +21 -0
- package/server/whatsapp-rpc/node_modules/kill-port/index.js +46 -0
- package/server/whatsapp-rpc/node_modules/kill-port/logo.png +0 -0
- package/server/whatsapp-rpc/node_modules/kill-port/package.json +41 -0
- package/server/whatsapp-rpc/node_modules/kill-port/pnpm-lock.yaml +4606 -0
- package/server/whatsapp-rpc/node_modules/kill-port/test.js +16 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/README.md +78 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/index.js +41 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/package.json +19 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/index.d.ts +52 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/index.js +71 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/license +9 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/package.json +45 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/readme.md +90 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/index.d.ts +90 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/index.js +52 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/license +9 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.d.ts +31 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.js +12 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/license +9 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/package.json +41 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/readme.md +57 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/package.json +49 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/readme.md +104 -0
- package/server/whatsapp-rpc/node_modules/onetime/index.d.ts +59 -0
- package/server/whatsapp-rpc/node_modules/onetime/index.js +41 -0
- package/server/whatsapp-rpc/node_modules/onetime/license +9 -0
- package/server/whatsapp-rpc/node_modules/onetime/package.json +45 -0
- package/server/whatsapp-rpc/node_modules/onetime/readme.md +94 -0
- package/server/whatsapp-rpc/node_modules/path-key/index.d.ts +40 -0
- package/server/whatsapp-rpc/node_modules/path-key/index.js +16 -0
- package/server/whatsapp-rpc/node_modules/path-key/license +9 -0
- package/server/whatsapp-rpc/node_modules/path-key/package.json +39 -0
- package/server/whatsapp-rpc/node_modules/path-key/readme.md +61 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/index.js +19 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/license +9 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/package.json +34 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/readme.md +34 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/index.d.ts +22 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/index.js +2 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/license +9 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/package.json +35 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/readme.md +33 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/README.md +60 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/index.js +47 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/package.json +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/LICENSE.txt +16 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/README.md +74 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts +12 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js +10 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts +48 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js +279 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/package.json +3 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js +42 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts +12 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js +4 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts +48 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js +275 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/package.json +3 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js +39 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/package.json +106 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/index.js +14 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/license +9 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/package.json +43 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/readme.md +35 -0
- package/server/whatsapp-rpc/node_modules/which/CHANGELOG.md +166 -0
- package/server/whatsapp-rpc/node_modules/which/LICENSE +15 -0
- package/server/whatsapp-rpc/node_modules/which/README.md +54 -0
- package/server/whatsapp-rpc/node_modules/which/bin/node-which +52 -0
- package/server/whatsapp-rpc/node_modules/which/package.json +43 -0
- package/server/whatsapp-rpc/node_modules/which/which.js +125 -0
- package/server/whatsapp-rpc/package-lock.json +272 -0
- package/server/whatsapp-rpc/package.json +30 -30
- package/server/whatsapp-rpc/schema.json +1294 -1294
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -66
- package/server/whatsapp-rpc/scripts/cli.js +162 -162
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -166
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -15
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -4
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -427
- package/server/whatsapp-rpc/web/app.py +609 -609
- package/server/whatsapp-rpc/web/requirements.txt +6 -6
- package/server/whatsapp-rpc/web/rpc_client.py +427 -427
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -59
- package/server/whatsapp-rpc/web/templates/base.html +149 -149
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -240
- package/server/whatsapp-rpc/web/templates/dashboard.html +319 -319
- package/server/whatsapp-rpc/web/templates/groups.html +328 -328
- package/server/whatsapp-rpc/web/templates/messages.html +465 -465
- package/server/whatsapp-rpc/web/templates/messaging.html +680 -680
- package/server/whatsapp-rpc/web/templates/send.html +258 -258
- package/server/whatsapp-rpc/web/templates/settings.html +459 -459
- package/client/src/components/ui/AndroidSettingsPanel.tsx +0 -401
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +0 -345
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +0 -140
- package/docker-compose.prod.yml +0 -107
- package/docker-compose.yml +0 -104
- package/docs-MachinaOs/README.md +0 -85
- package/docs-MachinaOs/deployment/docker.mdx +0 -228
- package/docs-MachinaOs/deployment/production.mdx +0 -345
- package/docs-MachinaOs/docs.json +0 -75
- package/docs-MachinaOs/faq.mdx +0 -309
- package/docs-MachinaOs/favicon.svg +0 -5
- package/docs-MachinaOs/installation.mdx +0 -160
- package/docs-MachinaOs/introduction.mdx +0 -114
- package/docs-MachinaOs/logo/dark.svg +0 -6
- package/docs-MachinaOs/logo/light.svg +0 -6
- package/docs-MachinaOs/nodes/ai-agent.mdx +0 -216
- package/docs-MachinaOs/nodes/ai-models.mdx +0 -240
- package/docs-MachinaOs/nodes/android.mdx +0 -411
- package/docs-MachinaOs/nodes/overview.mdx +0 -181
- package/docs-MachinaOs/nodes/schedulers.mdx +0 -316
- package/docs-MachinaOs/nodes/webhooks.mdx +0 -330
- package/docs-MachinaOs/nodes/whatsapp.mdx +0 -305
- package/docs-MachinaOs/quickstart.mdx +0 -119
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +0 -177
- package/docs-MachinaOs/tutorials/android-automation.mdx +0 -242
- package/docs-MachinaOs/tutorials/first-workflow.mdx +0 -134
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +0 -185
- package/nul +0 -0
- package/scripts/check-ports.ps1 +0 -33
- package/scripts/kill-port.ps1 +0 -154
|
@@ -1,588 +1,588 @@
|
|
|
1
|
-
"""Android System Services integration."""
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
import httpx
|
|
5
|
-
import asyncio
|
|
6
|
-
from datetime import datetime
|
|
7
|
-
from typing import Dict, Any, Optional, List
|
|
8
|
-
|
|
9
|
-
from core.logging import get_logger, log_execution_time
|
|
10
|
-
|
|
11
|
-
logger = get_logger(__name__)
|
|
12
|
-
|
|
13
|
-
# Default parameters for service actions (based on web_tester DEFAULTS)
|
|
14
|
-
SERVICE_DEFAULT_PARAMETERS = {
|
|
15
|
-
('app_launcher', 'launch'): '{"package_name": "com.android.settings"}',
|
|
16
|
-
('wifi_automation', 'enable'): '{}',
|
|
17
|
-
('wifi_automation', 'disable'): '{}',
|
|
18
|
-
('wifi_automation', 'status'): '{}',
|
|
19
|
-
('wifi_automation', 'scan'): '{}',
|
|
20
|
-
('bluetooth_automation', 'enable'): '{}',
|
|
21
|
-
('bluetooth_automation', 'disable'): '{}',
|
|
22
|
-
('bluetooth_automation', 'status'): '{}',
|
|
23
|
-
('audio_automation', 'get_volume'): '{}',
|
|
24
|
-
('audio_automation', 'set_volume'): '{"volume": 50}',
|
|
25
|
-
('audio_automation', 'mute'): '{}',
|
|
26
|
-
('audio_automation', 'unmute'): '{}',
|
|
27
|
-
('media_control', 'volume_control'): '{"action": "get_volume"}',
|
|
28
|
-
('media_control', 'media_control'): '{"action": "play"}',
|
|
29
|
-
('media_control', 'play_media'): '{"url": "https://www.soundjay.com/misc/bell-ringing-05.wav"}',
|
|
30
|
-
('camera_control', 'camera_info'): '{}',
|
|
31
|
-
('camera_control', 'take_photo'): '{"filename": "test_photo.jpg"}',
|
|
32
|
-
('motion_detection', 'current_motion'): '{}',
|
|
33
|
-
('motion_detection', 'shake_detection'): '{}',
|
|
34
|
-
('environmental_sensors', 'ambient_conditions'): '{}',
|
|
35
|
-
('environmental_sensors', 'proximity'): '{}',
|
|
36
|
-
('location', 'current'): '{}',
|
|
37
|
-
('location', 'start_tracking'): '{}',
|
|
38
|
-
('location', 'stop_tracking'): '{}',
|
|
39
|
-
('device_state_automation', 'airplane_mode'): '{"enabled": true}',
|
|
40
|
-
('device_state_automation', 'screen_on'): '{}',
|
|
41
|
-
('device_state_automation', 'screen_off'): '{}',
|
|
42
|
-
('device_state_automation', 'status'): '{}',
|
|
43
|
-
('screen_control_automation', 'brightness'): '{"level": 150}',
|
|
44
|
-
('screen_control_automation', 'wake'): '{}',
|
|
45
|
-
('screen_control_automation', 'status'): '{}',
|
|
46
|
-
('app_list', 'list'): '{"include_system": false, "include_disabled": false}',
|
|
47
|
-
('app_killer', 'kill'): '{"package_name": "com.example.app"}',
|
|
48
|
-
('app_usage', 'stats'): '{}',
|
|
49
|
-
('battery', 'status'): '{}',
|
|
50
|
-
('network', 'status'): '{}',
|
|
51
|
-
('system_info', 'info'): '{}',
|
|
52
|
-
('airplane_mode_control', 'status'): '{}'
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
# Service action mappings based on Android service schemas
|
|
56
|
-
SERVICE_ACTIONS = {
|
|
57
|
-
'battery': [
|
|
58
|
-
{'name': 'Status', 'value': 'status', 'description': 'Get battery status information'}
|
|
59
|
-
],
|
|
60
|
-
'network': [
|
|
61
|
-
{'name': 'Status', 'value': 'status', 'description': 'Get network connectivity status'}
|
|
62
|
-
],
|
|
63
|
-
'system_info': [
|
|
64
|
-
{'name': 'Info', 'value': 'info', 'description': 'Get system and device information'}
|
|
65
|
-
],
|
|
66
|
-
'location': [
|
|
67
|
-
{'name': 'Current', 'value': 'current', 'description': 'Get current location'},
|
|
68
|
-
{'name': 'Start Tracking', 'value': 'start_tracking', 'description': 'Start location tracking'},
|
|
69
|
-
{'name': 'Stop Tracking', 'value': 'stop_tracking', 'description': 'Stop location tracking'}
|
|
70
|
-
],
|
|
71
|
-
'app_launcher': [
|
|
72
|
-
{'name': 'Launch', 'value': 'launch', 'description': 'Launch an application'}
|
|
73
|
-
],
|
|
74
|
-
'app_list': [
|
|
75
|
-
{'name': 'List', 'value': 'list', 'description': 'Get list of installed apps'}
|
|
76
|
-
],
|
|
77
|
-
'app_killer': [
|
|
78
|
-
{'name': 'Kill', 'value': 'kill', 'description': 'Force stop an application'}
|
|
79
|
-
],
|
|
80
|
-
'app_usage': [
|
|
81
|
-
{'name': 'Stats', 'value': 'stats', 'description': 'Get app usage statistics'}
|
|
82
|
-
],
|
|
83
|
-
'wifi_automation': [
|
|
84
|
-
{'name': 'Enable', 'value': 'enable', 'description': 'Enable WiFi'},
|
|
85
|
-
{'name': 'Disable', 'value': 'disable', 'description': 'Disable WiFi'},
|
|
86
|
-
{'name': 'Status', 'value': 'status', 'description': 'Get WiFi status'},
|
|
87
|
-
{'name': 'Scan', 'value': 'scan', 'description': 'Scan for networks'}
|
|
88
|
-
],
|
|
89
|
-
'bluetooth_automation': [
|
|
90
|
-
{'name': 'Enable', 'value': 'enable', 'description': 'Enable Bluetooth'},
|
|
91
|
-
{'name': 'Disable', 'value': 'disable', 'description': 'Disable Bluetooth'},
|
|
92
|
-
{'name': 'Status', 'value': 'status', 'description': 'Get Bluetooth status'}
|
|
93
|
-
],
|
|
94
|
-
'audio_automation': [
|
|
95
|
-
{'name': 'Get Volume', 'value': 'get_volume', 'description': 'Get current volume'},
|
|
96
|
-
{'name': 'Set Volume', 'value': 'set_volume', 'description': 'Set volume level'},
|
|
97
|
-
{'name': 'Mute', 'value': 'mute', 'description': 'Mute audio'},
|
|
98
|
-
{'name': 'Unmute', 'value': 'unmute', 'description': 'Unmute audio'}
|
|
99
|
-
],
|
|
100
|
-
'device_state_automation': [
|
|
101
|
-
{'name': 'Airplane Mode', 'value': 'airplane_mode', 'description': 'Toggle airplane mode'},
|
|
102
|
-
{'name': 'Screen On', 'value': 'screen_on', 'description': 'Turn screen on'},
|
|
103
|
-
{'name': 'Screen Off', 'value': 'screen_off', 'description': 'Turn screen off'},
|
|
104
|
-
{'name': 'Status', 'value': 'status', 'description': 'Get device state'}
|
|
105
|
-
],
|
|
106
|
-
'screen_control_automation': [
|
|
107
|
-
{'name': 'Brightness', 'value': 'brightness', 'description': 'Set brightness level'},
|
|
108
|
-
{'name': 'Wake', 'value': 'wake', 'description': 'Wake up screen'},
|
|
109
|
-
{'name': 'Status', 'value': 'status', 'description': 'Get screen status'}
|
|
110
|
-
],
|
|
111
|
-
'motion_detection': [
|
|
112
|
-
{'name': 'Current Motion', 'value': 'current_motion', 'description': 'Get current motion data'},
|
|
113
|
-
{'name': 'Shake Detection', 'value': 'shake_detection', 'description': 'Detect shake gesture'}
|
|
114
|
-
],
|
|
115
|
-
'environmental_sensors': [
|
|
116
|
-
{'name': 'Ambient Conditions', 'value': 'ambient_conditions', 'description': 'Get temperature, humidity, pressure'},
|
|
117
|
-
{'name': 'Proximity', 'value': 'proximity', 'description': 'Get proximity sensor data'}
|
|
118
|
-
],
|
|
119
|
-
'camera_control': [
|
|
120
|
-
{'name': 'Camera Info', 'value': 'camera_info', 'description': 'Get camera information'},
|
|
121
|
-
{'name': 'Take Photo', 'value': 'take_photo', 'description': 'Capture a photo'}
|
|
122
|
-
],
|
|
123
|
-
'media_control': [
|
|
124
|
-
{'name': 'Volume Control', 'value': 'volume_control', 'description': 'Control media volume'},
|
|
125
|
-
{'name': 'Media Control', 'value': 'media_control', 'description': 'Control playback'},
|
|
126
|
-
{'name': 'Play Media', 'value': 'play_media', 'description': 'Play media file'}
|
|
127
|
-
],
|
|
128
|
-
'airplane_mode_control': [
|
|
129
|
-
{'name': 'Status', 'value': 'status', 'description': 'Get airplane mode status'}
|
|
130
|
-
],
|
|
131
|
-
# Unavailable services on current device (emulator-5554)
|
|
132
|
-
# These services returned "Service not available" errors during testing
|
|
133
|
-
# 'notification_sender': [
|
|
134
|
-
# {'name': 'Send', 'value': 'send', 'description': 'Send notification'}
|
|
135
|
-
# ],
|
|
136
|
-
# 'contact_list': [
|
|
137
|
-
# {'name': 'List', 'value': 'list', 'description': 'Get contacts list'}
|
|
138
|
-
# ],
|
|
139
|
-
# 'clipboard_automation': [
|
|
140
|
-
# {'name': 'Get', 'value': 'get', 'description': 'Get clipboard content'},
|
|
141
|
-
# {'name': 'Set', 'value': 'set', 'description': 'Set clipboard content'}
|
|
142
|
-
# ],
|
|
143
|
-
# 'torch_control': [
|
|
144
|
-
# {'name': 'Status', 'value': 'status', 'description': 'Get torch status'}
|
|
145
|
-
# ],
|
|
146
|
-
# 'vibrator_control': [
|
|
147
|
-
# {'name': 'Vibrate', 'value': 'vibrate', 'description': 'Vibrate device'}
|
|
148
|
-
# ],
|
|
149
|
-
# 'sms_sender': [
|
|
150
|
-
# {'name': 'Send', 'value': 'send', 'description': 'Send SMS'}
|
|
151
|
-
# ],
|
|
152
|
-
# 'call_automation': [
|
|
153
|
-
# {'name': 'Dial', 'value': 'dial', 'description': 'Dial phone number'}
|
|
154
|
-
# ],
|
|
155
|
-
# 'camera_automation': [
|
|
156
|
-
# {'name': 'Status', 'value': 'status', 'description': 'Get camera status'}
|
|
157
|
-
# ],
|
|
158
|
-
# 'flashlight': [
|
|
159
|
-
# {'name': 'Turn On', 'value': 'turn_on', 'description': 'Turn flashlight on'},
|
|
160
|
-
# {'name': 'Turn Off', 'value': 'turn_off', 'description': 'Turn flashlight off'},
|
|
161
|
-
# {'name': 'Toggle', 'value': 'toggle', 'description': 'Toggle flashlight'}
|
|
162
|
-
# ],
|
|
163
|
-
# 'sound_recording': [
|
|
164
|
-
# {'name': 'Start Recording', 'value': 'start_recording', 'description': 'Start audio recording'},
|
|
165
|
-
# {'name': 'Stop Recording', 'value': 'stop_recording', 'description': 'Stop audio recording'}
|
|
166
|
-
# ]
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
class AndroidService:
|
|
171
|
-
"""Android System Services client for device automation."""
|
|
172
|
-
|
|
173
|
-
def __init__(self):
|
|
174
|
-
self.default_timeout = 30.0
|
|
175
|
-
|
|
176
|
-
def get_service_actions(self, service_id: str) -> List[Dict[str, str]]:
|
|
177
|
-
"""Get available actions for a specific Android service.
|
|
178
|
-
|
|
179
|
-
Args:
|
|
180
|
-
service_id: Android service ID
|
|
181
|
-
|
|
182
|
-
Returns:
|
|
183
|
-
List of action options with name, value, and description
|
|
184
|
-
"""
|
|
185
|
-
return SERVICE_ACTIONS.get(service_id, [])
|
|
186
|
-
|
|
187
|
-
def get_default_parameters(self, service_id: str, action: str) -> str:
|
|
188
|
-
"""Get default parameters for a specific service action.
|
|
189
|
-
|
|
190
|
-
Args:
|
|
191
|
-
service_id: Android service ID
|
|
192
|
-
action: Action name
|
|
193
|
-
|
|
194
|
-
Returns:
|
|
195
|
-
Default parameters as JSON string
|
|
196
|
-
"""
|
|
197
|
-
return SERVICE_DEFAULT_PARAMETERS.get((service_id, action), '{}')
|
|
198
|
-
|
|
199
|
-
async def _execute_via_relay(
|
|
200
|
-
self,
|
|
201
|
-
node_id: str,
|
|
202
|
-
service_id: str,
|
|
203
|
-
action: str,
|
|
204
|
-
parameters: Dict[str, Any],
|
|
205
|
-
start_time: float
|
|
206
|
-
) -> Dict[str, Any]:
|
|
207
|
-
"""Execute Android service via Relay WebSocket (remote device).
|
|
208
|
-
|
|
209
|
-
Uses JSON-RPC 2.0 protocol via relay.send to paired Android device.
|
|
210
|
-
|
|
211
|
-
Args:
|
|
212
|
-
node_id: Node identifier
|
|
213
|
-
service_id: Android service ID
|
|
214
|
-
action: Service action
|
|
215
|
-
parameters: Action parameters
|
|
216
|
-
start_time: Execution start time
|
|
217
|
-
|
|
218
|
-
Returns:
|
|
219
|
-
Execution result with service response data
|
|
220
|
-
"""
|
|
221
|
-
try:
|
|
222
|
-
from services.android import get_current_relay_client
|
|
223
|
-
|
|
224
|
-
relay_client = get_current_relay_client()
|
|
225
|
-
if not relay_client:
|
|
226
|
-
raise Exception("Relay client not available - run Android Device Setup first")
|
|
227
|
-
|
|
228
|
-
if not relay_client.is_paired():
|
|
229
|
-
raise Exception("No Android device paired. Scan QR code with Android app to pair.")
|
|
230
|
-
|
|
231
|
-
device_id = relay_client.paired_device_id
|
|
232
|
-
device_name = relay_client.paired_device_name
|
|
233
|
-
|
|
234
|
-
logger.info(
|
|
235
|
-
"[Android Service] Sending relay service request",
|
|
236
|
-
service_id=service_id,
|
|
237
|
-
action=action,
|
|
238
|
-
device_id=device_id,
|
|
239
|
-
device_name=device_name,
|
|
240
|
-
parameters=parameters
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
# Send service request via relay
|
|
244
|
-
response = await relay_client.send_service_request(
|
|
245
|
-
service_id=service_id,
|
|
246
|
-
action=action,
|
|
247
|
-
parameters=parameters,
|
|
248
|
-
timeout=30.0 # Increased timeout for relay communication
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
if not response:
|
|
252
|
-
raise Exception(
|
|
253
|
-
f"No response from Android device (timeout). "
|
|
254
|
-
"Ensure the Android app is running and paired."
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
# Parse response - Android device may return:
|
|
258
|
-
# 1. {"success": true, "data": {...}}
|
|
259
|
-
# 2. {"data": {...}} (success implied)
|
|
260
|
-
# 3. {"success": false, "error": "..."}
|
|
261
|
-
# 4. Raw data object without wrapper
|
|
262
|
-
logger.info("[Android Service] Raw relay response",
|
|
263
|
-
response_keys=list(response.keys()) if isinstance(response, dict) else "not_dict",
|
|
264
|
-
response=response)
|
|
265
|
-
|
|
266
|
-
# Check for explicit success field, otherwise infer from data presence
|
|
267
|
-
if 'success' in response:
|
|
268
|
-
success = response.get('success', False)
|
|
269
|
-
else:
|
|
270
|
-
# No explicit success field - if we got data, consider it success
|
|
271
|
-
success = 'data' in response or ('error' not in response and response)
|
|
272
|
-
|
|
273
|
-
data = response.get('data', response if 'success' not in response and 'error' not in response else {})
|
|
274
|
-
error_msg = response.get('error')
|
|
275
|
-
|
|
276
|
-
logger.info("[Android Service] Relay response parsed",
|
|
277
|
-
success=success,
|
|
278
|
-
has_data=bool(data),
|
|
279
|
-
error=error_msg)
|
|
280
|
-
|
|
281
|
-
if success:
|
|
282
|
-
result = {
|
|
283
|
-
"success": True,
|
|
284
|
-
"node_id": node_id,
|
|
285
|
-
"node_type": "androidService",
|
|
286
|
-
"result": {
|
|
287
|
-
"service_id": service_id,
|
|
288
|
-
"action": action,
|
|
289
|
-
"data": data,
|
|
290
|
-
"response_time": time.time() - start_time,
|
|
291
|
-
"connection_type": "relay",
|
|
292
|
-
"device_id": device_id,
|
|
293
|
-
"device_name": device_name,
|
|
294
|
-
"timestamp": datetime.now().isoformat()
|
|
295
|
-
},
|
|
296
|
-
"execution_time": time.time() - start_time,
|
|
297
|
-
"timestamp": datetime.now().isoformat()
|
|
298
|
-
}
|
|
299
|
-
else:
|
|
300
|
-
result = {
|
|
301
|
-
"success": False,
|
|
302
|
-
"node_id": node_id,
|
|
303
|
-
"node_type": "androidService",
|
|
304
|
-
"error": error_msg or "Service execution failed on Android device",
|
|
305
|
-
"result": {
|
|
306
|
-
"service_id": service_id,
|
|
307
|
-
"action": action,
|
|
308
|
-
"data": data,
|
|
309
|
-
"connection_type": "relay",
|
|
310
|
-
"device_id": device_id,
|
|
311
|
-
"timestamp": datetime.now().isoformat()
|
|
312
|
-
},
|
|
313
|
-
"execution_time": time.time() - start_time,
|
|
314
|
-
"timestamp": datetime.now().isoformat()
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
log_execution_time(logger, f"android_service_{service_id}_{action}_relay", start_time, time.time())
|
|
318
|
-
|
|
319
|
-
logger.info(
|
|
320
|
-
"[Android Service] Relay execution completed",
|
|
321
|
-
node_id=node_id,
|
|
322
|
-
service_id=service_id,
|
|
323
|
-
action=action,
|
|
324
|
-
success=success
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
return result
|
|
328
|
-
|
|
329
|
-
except Exception as e:
|
|
330
|
-
logger.error(
|
|
331
|
-
"[Android Service] Relay execution failed",
|
|
332
|
-
node_id=node_id,
|
|
333
|
-
service_id=service_id,
|
|
334
|
-
action=action,
|
|
335
|
-
error=str(e)
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
return {
|
|
339
|
-
"success": False,
|
|
340
|
-
"node_id": node_id,
|
|
341
|
-
"node_type": "androidService",
|
|
342
|
-
"error": str(e),
|
|
343
|
-
"result": {
|
|
344
|
-
"service_id": service_id,
|
|
345
|
-
"action": action,
|
|
346
|
-
"connection_type": "relay",
|
|
347
|
-
"timestamp": datetime.now().isoformat()
|
|
348
|
-
},
|
|
349
|
-
"execution_time": time.time() - start_time,
|
|
350
|
-
"timestamp": datetime.now().isoformat()
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
async def execute_service(
|
|
354
|
-
self,
|
|
355
|
-
node_id: str,
|
|
356
|
-
service_id: str,
|
|
357
|
-
action: str,
|
|
358
|
-
parameters: Dict[str, Any],
|
|
359
|
-
android_host: str = "localhost",
|
|
360
|
-
android_port: int = 8888
|
|
361
|
-
) -> Dict[str, Any]:
|
|
362
|
-
"""Execute an Android system service action.
|
|
363
|
-
|
|
364
|
-
Args:
|
|
365
|
-
node_id: Node identifier
|
|
366
|
-
service_id: Android service ID (e.g., 'battery', 'network', 'app_launcher')
|
|
367
|
-
action: Service action to perform (e.g., 'status', 'launch', 'list')
|
|
368
|
-
parameters: Action-specific parameters
|
|
369
|
-
android_host: Android device API host
|
|
370
|
-
android_port: Android device API port
|
|
371
|
-
|
|
372
|
-
Returns:
|
|
373
|
-
Execution result with service response data
|
|
374
|
-
"""
|
|
375
|
-
start_time = time.time()
|
|
376
|
-
|
|
377
|
-
try:
|
|
378
|
-
# Check if relay connection is available (remote device)
|
|
379
|
-
from services.android import get_current_relay_client
|
|
380
|
-
relay_client = get_current_relay_client()
|
|
381
|
-
|
|
382
|
-
if relay_client and relay_client.is_paired():
|
|
383
|
-
# Use relay for remote Android device
|
|
384
|
-
logger.info(
|
|
385
|
-
"[Android Service] Using relay connection",
|
|
386
|
-
node_id=node_id,
|
|
387
|
-
service_id=service_id,
|
|
388
|
-
action=action
|
|
389
|
-
)
|
|
390
|
-
return await self._execute_via_relay(
|
|
391
|
-
node_id, service_id, action, parameters, start_time
|
|
392
|
-
)
|
|
393
|
-
else:
|
|
394
|
-
# Use local HTTP connection
|
|
395
|
-
base_url = f"http://{android_host}:{android_port}/api"
|
|
396
|
-
logger.info(
|
|
397
|
-
"[Android Service] Using local HTTP connection",
|
|
398
|
-
node_id=node_id,
|
|
399
|
-
service_id=service_id,
|
|
400
|
-
action=action,
|
|
401
|
-
base_url=base_url
|
|
402
|
-
)
|
|
403
|
-
|
|
404
|
-
# Build request payload
|
|
405
|
-
request_payload = {
|
|
406
|
-
"action": action,
|
|
407
|
-
"parameters": parameters
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
# Make request to Android device API
|
|
411
|
-
async with httpx.AsyncClient(timeout=self.default_timeout) as client:
|
|
412
|
-
response = await client.post(
|
|
413
|
-
f"{base_url}/{service_id}",
|
|
414
|
-
json=request_payload
|
|
415
|
-
)
|
|
416
|
-
|
|
417
|
-
# Parse response
|
|
418
|
-
if response.status_code == 200:
|
|
419
|
-
response_data = response.json()
|
|
420
|
-
|
|
421
|
-
# Check if Android service returned success
|
|
422
|
-
service_success = response_data.get('success', True)
|
|
423
|
-
|
|
424
|
-
result = {
|
|
425
|
-
"success": service_success,
|
|
426
|
-
"node_id": node_id,
|
|
427
|
-
"node_type": "androidService",
|
|
428
|
-
"result": {
|
|
429
|
-
"service_id": service_id,
|
|
430
|
-
"action": action,
|
|
431
|
-
"data": response_data.get('data', response_data),
|
|
432
|
-
"response_time": response.elapsed.total_seconds(),
|
|
433
|
-
"android_host": android_host,
|
|
434
|
-
"android_port": android_port,
|
|
435
|
-
"timestamp": datetime.now().isoformat()
|
|
436
|
-
},
|
|
437
|
-
"execution_time": time.time() - start_time,
|
|
438
|
-
"timestamp": datetime.now().isoformat()
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if not service_success:
|
|
442
|
-
result["result"]["error"] = response_data.get('error', 'Service execution failed')
|
|
443
|
-
|
|
444
|
-
log_execution_time(logger, f"android_service_{service_id}_{action}", start_time, time.time())
|
|
445
|
-
|
|
446
|
-
logger.info(
|
|
447
|
-
"[Android Service] Execution completed",
|
|
448
|
-
node_id=node_id,
|
|
449
|
-
service_id=service_id,
|
|
450
|
-
action=action,
|
|
451
|
-
success=service_success
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
return result
|
|
455
|
-
|
|
456
|
-
else:
|
|
457
|
-
# HTTP error
|
|
458
|
-
error_msg = f"HTTP {response.status_code}: {response.text}"
|
|
459
|
-
logger.error(
|
|
460
|
-
"[Android Service] HTTP error",
|
|
461
|
-
node_id=node_id,
|
|
462
|
-
service_id=service_id,
|
|
463
|
-
status_code=response.status_code,
|
|
464
|
-
error=error_msg
|
|
465
|
-
)
|
|
466
|
-
|
|
467
|
-
return {
|
|
468
|
-
"success": False,
|
|
469
|
-
"node_id": node_id,
|
|
470
|
-
"node_type": "androidService",
|
|
471
|
-
"error": error_msg,
|
|
472
|
-
"result": {
|
|
473
|
-
"service_id": service_id,
|
|
474
|
-
"action": action,
|
|
475
|
-
"status_code": response.status_code,
|
|
476
|
-
"timestamp": datetime.now().isoformat()
|
|
477
|
-
},
|
|
478
|
-
"execution_time": time.time() - start_time,
|
|
479
|
-
"timestamp": datetime.now().isoformat()
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
except httpx.ConnectError as e:
|
|
483
|
-
error_msg = f"Cannot connect to Android device at {android_host}:{android_port}. Ensure ADB port forwarding is active: adb forward tcp:{android_port} tcp:{android_port}"
|
|
484
|
-
logger.error(
|
|
485
|
-
"[Android Service] Connection error",
|
|
486
|
-
node_id=node_id,
|
|
487
|
-
service_id=service_id,
|
|
488
|
-
error=str(e),
|
|
489
|
-
android_host=android_host,
|
|
490
|
-
android_port=android_port
|
|
491
|
-
)
|
|
492
|
-
|
|
493
|
-
return {
|
|
494
|
-
"success": False,
|
|
495
|
-
"node_id": node_id,
|
|
496
|
-
"node_type": "androidService",
|
|
497
|
-
"error": error_msg,
|
|
498
|
-
"result": {
|
|
499
|
-
"service_id": service_id,
|
|
500
|
-
"action": action,
|
|
501
|
-
"connection_error": str(e),
|
|
502
|
-
"timestamp": datetime.now().isoformat()
|
|
503
|
-
},
|
|
504
|
-
"execution_time": time.time() - start_time,
|
|
505
|
-
"timestamp": datetime.now().isoformat()
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
except httpx.TimeoutException as e:
|
|
509
|
-
error_msg = f"Request timeout after {self.default_timeout}s"
|
|
510
|
-
logger.error(
|
|
511
|
-
"[Android Service] Timeout",
|
|
512
|
-
node_id=node_id,
|
|
513
|
-
service_id=service_id,
|
|
514
|
-
timeout=self.default_timeout
|
|
515
|
-
)
|
|
516
|
-
|
|
517
|
-
return {
|
|
518
|
-
"success": False,
|
|
519
|
-
"node_id": node_id,
|
|
520
|
-
"node_type": "androidService",
|
|
521
|
-
"error": error_msg,
|
|
522
|
-
"result": {
|
|
523
|
-
"service_id": service_id,
|
|
524
|
-
"action": action,
|
|
525
|
-
"timeout": self.default_timeout,
|
|
526
|
-
"timestamp": datetime.now().isoformat()
|
|
527
|
-
},
|
|
528
|
-
"execution_time": time.time() - start_time,
|
|
529
|
-
"timestamp": datetime.now().isoformat()
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
except Exception as e:
|
|
533
|
-
logger.error(
|
|
534
|
-
"[Android Service] Unexpected error",
|
|
535
|
-
node_id=node_id,
|
|
536
|
-
service_id=service_id,
|
|
537
|
-
error=str(e),
|
|
538
|
-
exc_info=True
|
|
539
|
-
)
|
|
540
|
-
|
|
541
|
-
return {
|
|
542
|
-
"success": False,
|
|
543
|
-
"node_id": node_id,
|
|
544
|
-
"node_type": "androidService",
|
|
545
|
-
"error": str(e),
|
|
546
|
-
"result": {
|
|
547
|
-
"service_id": service_id,
|
|
548
|
-
"action": action,
|
|
549
|
-
"timestamp": datetime.now().isoformat()
|
|
550
|
-
},
|
|
551
|
-
"execution_time": time.time() - start_time,
|
|
552
|
-
"timestamp": datetime.now().isoformat()
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
async def check_device_status(
|
|
556
|
-
self,
|
|
557
|
-
android_host: str = "localhost",
|
|
558
|
-
android_port: int = 8888
|
|
559
|
-
) -> Dict[str, Any]:
|
|
560
|
-
"""Check if Android device API is reachable.
|
|
561
|
-
|
|
562
|
-
Returns:
|
|
563
|
-
Status check result with online status
|
|
564
|
-
"""
|
|
565
|
-
try:
|
|
566
|
-
base_url = f"http://{android_host}:{android_port}/api"
|
|
567
|
-
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
568
|
-
response = await client.get(f"{base_url}/status")
|
|
569
|
-
|
|
570
|
-
return {
|
|
571
|
-
"online": response.status_code == 200,
|
|
572
|
-
"data": response.json() if response.status_code == 200 else None,
|
|
573
|
-
"android_host": android_host,
|
|
574
|
-
"android_port": android_port
|
|
575
|
-
}
|
|
576
|
-
except Exception as e:
|
|
577
|
-
logger.warning(
|
|
578
|
-
"[Android Service] Device offline",
|
|
579
|
-
android_host=android_host,
|
|
580
|
-
android_port=android_port,
|
|
581
|
-
error=str(e)
|
|
582
|
-
)
|
|
583
|
-
return {
|
|
584
|
-
"online": False,
|
|
585
|
-
"error": str(e),
|
|
586
|
-
"android_host": android_host,
|
|
587
|
-
"android_port": android_port
|
|
588
|
-
}
|
|
1
|
+
"""Android System Services integration."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import httpx
|
|
5
|
+
import asyncio
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Dict, Any, Optional, List
|
|
8
|
+
|
|
9
|
+
from core.logging import get_logger, log_execution_time
|
|
10
|
+
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
# Default parameters for service actions (based on web_tester DEFAULTS)
|
|
14
|
+
SERVICE_DEFAULT_PARAMETERS = {
|
|
15
|
+
('app_launcher', 'launch'): '{"package_name": "com.android.settings"}',
|
|
16
|
+
('wifi_automation', 'enable'): '{}',
|
|
17
|
+
('wifi_automation', 'disable'): '{}',
|
|
18
|
+
('wifi_automation', 'status'): '{}',
|
|
19
|
+
('wifi_automation', 'scan'): '{}',
|
|
20
|
+
('bluetooth_automation', 'enable'): '{}',
|
|
21
|
+
('bluetooth_automation', 'disable'): '{}',
|
|
22
|
+
('bluetooth_automation', 'status'): '{}',
|
|
23
|
+
('audio_automation', 'get_volume'): '{}',
|
|
24
|
+
('audio_automation', 'set_volume'): '{"volume": 50}',
|
|
25
|
+
('audio_automation', 'mute'): '{}',
|
|
26
|
+
('audio_automation', 'unmute'): '{}',
|
|
27
|
+
('media_control', 'volume_control'): '{"action": "get_volume"}',
|
|
28
|
+
('media_control', 'media_control'): '{"action": "play"}',
|
|
29
|
+
('media_control', 'play_media'): '{"url": "https://www.soundjay.com/misc/bell-ringing-05.wav"}',
|
|
30
|
+
('camera_control', 'camera_info'): '{}',
|
|
31
|
+
('camera_control', 'take_photo'): '{"filename": "test_photo.jpg"}',
|
|
32
|
+
('motion_detection', 'current_motion'): '{}',
|
|
33
|
+
('motion_detection', 'shake_detection'): '{}',
|
|
34
|
+
('environmental_sensors', 'ambient_conditions'): '{}',
|
|
35
|
+
('environmental_sensors', 'proximity'): '{}',
|
|
36
|
+
('location', 'current'): '{}',
|
|
37
|
+
('location', 'start_tracking'): '{}',
|
|
38
|
+
('location', 'stop_tracking'): '{}',
|
|
39
|
+
('device_state_automation', 'airplane_mode'): '{"enabled": true}',
|
|
40
|
+
('device_state_automation', 'screen_on'): '{}',
|
|
41
|
+
('device_state_automation', 'screen_off'): '{}',
|
|
42
|
+
('device_state_automation', 'status'): '{}',
|
|
43
|
+
('screen_control_automation', 'brightness'): '{"level": 150}',
|
|
44
|
+
('screen_control_automation', 'wake'): '{}',
|
|
45
|
+
('screen_control_automation', 'status'): '{}',
|
|
46
|
+
('app_list', 'list'): '{"include_system": false, "include_disabled": false}',
|
|
47
|
+
('app_killer', 'kill'): '{"package_name": "com.example.app"}',
|
|
48
|
+
('app_usage', 'stats'): '{}',
|
|
49
|
+
('battery', 'status'): '{}',
|
|
50
|
+
('network', 'status'): '{}',
|
|
51
|
+
('system_info', 'info'): '{}',
|
|
52
|
+
('airplane_mode_control', 'status'): '{}'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Service action mappings based on Android service schemas
|
|
56
|
+
SERVICE_ACTIONS = {
|
|
57
|
+
'battery': [
|
|
58
|
+
{'name': 'Status', 'value': 'status', 'description': 'Get battery status information'}
|
|
59
|
+
],
|
|
60
|
+
'network': [
|
|
61
|
+
{'name': 'Status', 'value': 'status', 'description': 'Get network connectivity status'}
|
|
62
|
+
],
|
|
63
|
+
'system_info': [
|
|
64
|
+
{'name': 'Info', 'value': 'info', 'description': 'Get system and device information'}
|
|
65
|
+
],
|
|
66
|
+
'location': [
|
|
67
|
+
{'name': 'Current', 'value': 'current', 'description': 'Get current location'},
|
|
68
|
+
{'name': 'Start Tracking', 'value': 'start_tracking', 'description': 'Start location tracking'},
|
|
69
|
+
{'name': 'Stop Tracking', 'value': 'stop_tracking', 'description': 'Stop location tracking'}
|
|
70
|
+
],
|
|
71
|
+
'app_launcher': [
|
|
72
|
+
{'name': 'Launch', 'value': 'launch', 'description': 'Launch an application'}
|
|
73
|
+
],
|
|
74
|
+
'app_list': [
|
|
75
|
+
{'name': 'List', 'value': 'list', 'description': 'Get list of installed apps'}
|
|
76
|
+
],
|
|
77
|
+
'app_killer': [
|
|
78
|
+
{'name': 'Kill', 'value': 'kill', 'description': 'Force stop an application'}
|
|
79
|
+
],
|
|
80
|
+
'app_usage': [
|
|
81
|
+
{'name': 'Stats', 'value': 'stats', 'description': 'Get app usage statistics'}
|
|
82
|
+
],
|
|
83
|
+
'wifi_automation': [
|
|
84
|
+
{'name': 'Enable', 'value': 'enable', 'description': 'Enable WiFi'},
|
|
85
|
+
{'name': 'Disable', 'value': 'disable', 'description': 'Disable WiFi'},
|
|
86
|
+
{'name': 'Status', 'value': 'status', 'description': 'Get WiFi status'},
|
|
87
|
+
{'name': 'Scan', 'value': 'scan', 'description': 'Scan for networks'}
|
|
88
|
+
],
|
|
89
|
+
'bluetooth_automation': [
|
|
90
|
+
{'name': 'Enable', 'value': 'enable', 'description': 'Enable Bluetooth'},
|
|
91
|
+
{'name': 'Disable', 'value': 'disable', 'description': 'Disable Bluetooth'},
|
|
92
|
+
{'name': 'Status', 'value': 'status', 'description': 'Get Bluetooth status'}
|
|
93
|
+
],
|
|
94
|
+
'audio_automation': [
|
|
95
|
+
{'name': 'Get Volume', 'value': 'get_volume', 'description': 'Get current volume'},
|
|
96
|
+
{'name': 'Set Volume', 'value': 'set_volume', 'description': 'Set volume level'},
|
|
97
|
+
{'name': 'Mute', 'value': 'mute', 'description': 'Mute audio'},
|
|
98
|
+
{'name': 'Unmute', 'value': 'unmute', 'description': 'Unmute audio'}
|
|
99
|
+
],
|
|
100
|
+
'device_state_automation': [
|
|
101
|
+
{'name': 'Airplane Mode', 'value': 'airplane_mode', 'description': 'Toggle airplane mode'},
|
|
102
|
+
{'name': 'Screen On', 'value': 'screen_on', 'description': 'Turn screen on'},
|
|
103
|
+
{'name': 'Screen Off', 'value': 'screen_off', 'description': 'Turn screen off'},
|
|
104
|
+
{'name': 'Status', 'value': 'status', 'description': 'Get device state'}
|
|
105
|
+
],
|
|
106
|
+
'screen_control_automation': [
|
|
107
|
+
{'name': 'Brightness', 'value': 'brightness', 'description': 'Set brightness level'},
|
|
108
|
+
{'name': 'Wake', 'value': 'wake', 'description': 'Wake up screen'},
|
|
109
|
+
{'name': 'Status', 'value': 'status', 'description': 'Get screen status'}
|
|
110
|
+
],
|
|
111
|
+
'motion_detection': [
|
|
112
|
+
{'name': 'Current Motion', 'value': 'current_motion', 'description': 'Get current motion data'},
|
|
113
|
+
{'name': 'Shake Detection', 'value': 'shake_detection', 'description': 'Detect shake gesture'}
|
|
114
|
+
],
|
|
115
|
+
'environmental_sensors': [
|
|
116
|
+
{'name': 'Ambient Conditions', 'value': 'ambient_conditions', 'description': 'Get temperature, humidity, pressure'},
|
|
117
|
+
{'name': 'Proximity', 'value': 'proximity', 'description': 'Get proximity sensor data'}
|
|
118
|
+
],
|
|
119
|
+
'camera_control': [
|
|
120
|
+
{'name': 'Camera Info', 'value': 'camera_info', 'description': 'Get camera information'},
|
|
121
|
+
{'name': 'Take Photo', 'value': 'take_photo', 'description': 'Capture a photo'}
|
|
122
|
+
],
|
|
123
|
+
'media_control': [
|
|
124
|
+
{'name': 'Volume Control', 'value': 'volume_control', 'description': 'Control media volume'},
|
|
125
|
+
{'name': 'Media Control', 'value': 'media_control', 'description': 'Control playback'},
|
|
126
|
+
{'name': 'Play Media', 'value': 'play_media', 'description': 'Play media file'}
|
|
127
|
+
],
|
|
128
|
+
'airplane_mode_control': [
|
|
129
|
+
{'name': 'Status', 'value': 'status', 'description': 'Get airplane mode status'}
|
|
130
|
+
],
|
|
131
|
+
# Unavailable services on current device (emulator-5554)
|
|
132
|
+
# These services returned "Service not available" errors during testing
|
|
133
|
+
# 'notification_sender': [
|
|
134
|
+
# {'name': 'Send', 'value': 'send', 'description': 'Send notification'}
|
|
135
|
+
# ],
|
|
136
|
+
# 'contact_list': [
|
|
137
|
+
# {'name': 'List', 'value': 'list', 'description': 'Get contacts list'}
|
|
138
|
+
# ],
|
|
139
|
+
# 'clipboard_automation': [
|
|
140
|
+
# {'name': 'Get', 'value': 'get', 'description': 'Get clipboard content'},
|
|
141
|
+
# {'name': 'Set', 'value': 'set', 'description': 'Set clipboard content'}
|
|
142
|
+
# ],
|
|
143
|
+
# 'torch_control': [
|
|
144
|
+
# {'name': 'Status', 'value': 'status', 'description': 'Get torch status'}
|
|
145
|
+
# ],
|
|
146
|
+
# 'vibrator_control': [
|
|
147
|
+
# {'name': 'Vibrate', 'value': 'vibrate', 'description': 'Vibrate device'}
|
|
148
|
+
# ],
|
|
149
|
+
# 'sms_sender': [
|
|
150
|
+
# {'name': 'Send', 'value': 'send', 'description': 'Send SMS'}
|
|
151
|
+
# ],
|
|
152
|
+
# 'call_automation': [
|
|
153
|
+
# {'name': 'Dial', 'value': 'dial', 'description': 'Dial phone number'}
|
|
154
|
+
# ],
|
|
155
|
+
# 'camera_automation': [
|
|
156
|
+
# {'name': 'Status', 'value': 'status', 'description': 'Get camera status'}
|
|
157
|
+
# ],
|
|
158
|
+
# 'flashlight': [
|
|
159
|
+
# {'name': 'Turn On', 'value': 'turn_on', 'description': 'Turn flashlight on'},
|
|
160
|
+
# {'name': 'Turn Off', 'value': 'turn_off', 'description': 'Turn flashlight off'},
|
|
161
|
+
# {'name': 'Toggle', 'value': 'toggle', 'description': 'Toggle flashlight'}
|
|
162
|
+
# ],
|
|
163
|
+
# 'sound_recording': [
|
|
164
|
+
# {'name': 'Start Recording', 'value': 'start_recording', 'description': 'Start audio recording'},
|
|
165
|
+
# {'name': 'Stop Recording', 'value': 'stop_recording', 'description': 'Stop audio recording'}
|
|
166
|
+
# ]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class AndroidService:
|
|
171
|
+
"""Android System Services client for device automation."""
|
|
172
|
+
|
|
173
|
+
def __init__(self):
|
|
174
|
+
self.default_timeout = 30.0
|
|
175
|
+
|
|
176
|
+
def get_service_actions(self, service_id: str) -> List[Dict[str, str]]:
|
|
177
|
+
"""Get available actions for a specific Android service.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
service_id: Android service ID
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
List of action options with name, value, and description
|
|
184
|
+
"""
|
|
185
|
+
return SERVICE_ACTIONS.get(service_id, [])
|
|
186
|
+
|
|
187
|
+
def get_default_parameters(self, service_id: str, action: str) -> str:
|
|
188
|
+
"""Get default parameters for a specific service action.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
service_id: Android service ID
|
|
192
|
+
action: Action name
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Default parameters as JSON string
|
|
196
|
+
"""
|
|
197
|
+
return SERVICE_DEFAULT_PARAMETERS.get((service_id, action), '{}')
|
|
198
|
+
|
|
199
|
+
async def _execute_via_relay(
|
|
200
|
+
self,
|
|
201
|
+
node_id: str,
|
|
202
|
+
service_id: str,
|
|
203
|
+
action: str,
|
|
204
|
+
parameters: Dict[str, Any],
|
|
205
|
+
start_time: float
|
|
206
|
+
) -> Dict[str, Any]:
|
|
207
|
+
"""Execute Android service via Relay WebSocket (remote device).
|
|
208
|
+
|
|
209
|
+
Uses JSON-RPC 2.0 protocol via relay.send to paired Android device.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
node_id: Node identifier
|
|
213
|
+
service_id: Android service ID
|
|
214
|
+
action: Service action
|
|
215
|
+
parameters: Action parameters
|
|
216
|
+
start_time: Execution start time
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Execution result with service response data
|
|
220
|
+
"""
|
|
221
|
+
try:
|
|
222
|
+
from services.android import get_current_relay_client
|
|
223
|
+
|
|
224
|
+
relay_client = get_current_relay_client()
|
|
225
|
+
if not relay_client:
|
|
226
|
+
raise Exception("Relay client not available - run Android Device Setup first")
|
|
227
|
+
|
|
228
|
+
if not relay_client.is_paired():
|
|
229
|
+
raise Exception("No Android device paired. Scan QR code with Android app to pair.")
|
|
230
|
+
|
|
231
|
+
device_id = relay_client.paired_device_id
|
|
232
|
+
device_name = relay_client.paired_device_name
|
|
233
|
+
|
|
234
|
+
logger.info(
|
|
235
|
+
"[Android Service] Sending relay service request",
|
|
236
|
+
service_id=service_id,
|
|
237
|
+
action=action,
|
|
238
|
+
device_id=device_id,
|
|
239
|
+
device_name=device_name,
|
|
240
|
+
parameters=parameters
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Send service request via relay
|
|
244
|
+
response = await relay_client.send_service_request(
|
|
245
|
+
service_id=service_id,
|
|
246
|
+
action=action,
|
|
247
|
+
parameters=parameters,
|
|
248
|
+
timeout=30.0 # Increased timeout for relay communication
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if not response:
|
|
252
|
+
raise Exception(
|
|
253
|
+
f"No response from Android device (timeout). "
|
|
254
|
+
"Ensure the Android app is running and paired."
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Parse response - Android device may return:
|
|
258
|
+
# 1. {"success": true, "data": {...}}
|
|
259
|
+
# 2. {"data": {...}} (success implied)
|
|
260
|
+
# 3. {"success": false, "error": "..."}
|
|
261
|
+
# 4. Raw data object without wrapper
|
|
262
|
+
logger.info("[Android Service] Raw relay response",
|
|
263
|
+
response_keys=list(response.keys()) if isinstance(response, dict) else "not_dict",
|
|
264
|
+
response=response)
|
|
265
|
+
|
|
266
|
+
# Check for explicit success field, otherwise infer from data presence
|
|
267
|
+
if 'success' in response:
|
|
268
|
+
success = response.get('success', False)
|
|
269
|
+
else:
|
|
270
|
+
# No explicit success field - if we got data, consider it success
|
|
271
|
+
success = 'data' in response or ('error' not in response and response)
|
|
272
|
+
|
|
273
|
+
data = response.get('data', response if 'success' not in response and 'error' not in response else {})
|
|
274
|
+
error_msg = response.get('error')
|
|
275
|
+
|
|
276
|
+
logger.info("[Android Service] Relay response parsed",
|
|
277
|
+
success=success,
|
|
278
|
+
has_data=bool(data),
|
|
279
|
+
error=error_msg)
|
|
280
|
+
|
|
281
|
+
if success:
|
|
282
|
+
result = {
|
|
283
|
+
"success": True,
|
|
284
|
+
"node_id": node_id,
|
|
285
|
+
"node_type": "androidService",
|
|
286
|
+
"result": {
|
|
287
|
+
"service_id": service_id,
|
|
288
|
+
"action": action,
|
|
289
|
+
"data": data,
|
|
290
|
+
"response_time": time.time() - start_time,
|
|
291
|
+
"connection_type": "relay",
|
|
292
|
+
"device_id": device_id,
|
|
293
|
+
"device_name": device_name,
|
|
294
|
+
"timestamp": datetime.now().isoformat()
|
|
295
|
+
},
|
|
296
|
+
"execution_time": time.time() - start_time,
|
|
297
|
+
"timestamp": datetime.now().isoformat()
|
|
298
|
+
}
|
|
299
|
+
else:
|
|
300
|
+
result = {
|
|
301
|
+
"success": False,
|
|
302
|
+
"node_id": node_id,
|
|
303
|
+
"node_type": "androidService",
|
|
304
|
+
"error": error_msg or "Service execution failed on Android device",
|
|
305
|
+
"result": {
|
|
306
|
+
"service_id": service_id,
|
|
307
|
+
"action": action,
|
|
308
|
+
"data": data,
|
|
309
|
+
"connection_type": "relay",
|
|
310
|
+
"device_id": device_id,
|
|
311
|
+
"timestamp": datetime.now().isoformat()
|
|
312
|
+
},
|
|
313
|
+
"execution_time": time.time() - start_time,
|
|
314
|
+
"timestamp": datetime.now().isoformat()
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
log_execution_time(logger, f"android_service_{service_id}_{action}_relay", start_time, time.time())
|
|
318
|
+
|
|
319
|
+
logger.info(
|
|
320
|
+
"[Android Service] Relay execution completed",
|
|
321
|
+
node_id=node_id,
|
|
322
|
+
service_id=service_id,
|
|
323
|
+
action=action,
|
|
324
|
+
success=success
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
return result
|
|
328
|
+
|
|
329
|
+
except Exception as e:
|
|
330
|
+
logger.error(
|
|
331
|
+
"[Android Service] Relay execution failed",
|
|
332
|
+
node_id=node_id,
|
|
333
|
+
service_id=service_id,
|
|
334
|
+
action=action,
|
|
335
|
+
error=str(e)
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
"success": False,
|
|
340
|
+
"node_id": node_id,
|
|
341
|
+
"node_type": "androidService",
|
|
342
|
+
"error": str(e),
|
|
343
|
+
"result": {
|
|
344
|
+
"service_id": service_id,
|
|
345
|
+
"action": action,
|
|
346
|
+
"connection_type": "relay",
|
|
347
|
+
"timestamp": datetime.now().isoformat()
|
|
348
|
+
},
|
|
349
|
+
"execution_time": time.time() - start_time,
|
|
350
|
+
"timestamp": datetime.now().isoformat()
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async def execute_service(
|
|
354
|
+
self,
|
|
355
|
+
node_id: str,
|
|
356
|
+
service_id: str,
|
|
357
|
+
action: str,
|
|
358
|
+
parameters: Dict[str, Any],
|
|
359
|
+
android_host: str = "localhost",
|
|
360
|
+
android_port: int = 8888
|
|
361
|
+
) -> Dict[str, Any]:
|
|
362
|
+
"""Execute an Android system service action.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
node_id: Node identifier
|
|
366
|
+
service_id: Android service ID (e.g., 'battery', 'network', 'app_launcher')
|
|
367
|
+
action: Service action to perform (e.g., 'status', 'launch', 'list')
|
|
368
|
+
parameters: Action-specific parameters
|
|
369
|
+
android_host: Android device API host
|
|
370
|
+
android_port: Android device API port
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
Execution result with service response data
|
|
374
|
+
"""
|
|
375
|
+
start_time = time.time()
|
|
376
|
+
|
|
377
|
+
try:
|
|
378
|
+
# Check if relay connection is available (remote device)
|
|
379
|
+
from services.android import get_current_relay_client
|
|
380
|
+
relay_client = get_current_relay_client()
|
|
381
|
+
|
|
382
|
+
if relay_client and relay_client.is_paired():
|
|
383
|
+
# Use relay for remote Android device
|
|
384
|
+
logger.info(
|
|
385
|
+
"[Android Service] Using relay connection",
|
|
386
|
+
node_id=node_id,
|
|
387
|
+
service_id=service_id,
|
|
388
|
+
action=action
|
|
389
|
+
)
|
|
390
|
+
return await self._execute_via_relay(
|
|
391
|
+
node_id, service_id, action, parameters, start_time
|
|
392
|
+
)
|
|
393
|
+
else:
|
|
394
|
+
# Use local HTTP connection
|
|
395
|
+
base_url = f"http://{android_host}:{android_port}/api"
|
|
396
|
+
logger.info(
|
|
397
|
+
"[Android Service] Using local HTTP connection",
|
|
398
|
+
node_id=node_id,
|
|
399
|
+
service_id=service_id,
|
|
400
|
+
action=action,
|
|
401
|
+
base_url=base_url
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
# Build request payload
|
|
405
|
+
request_payload = {
|
|
406
|
+
"action": action,
|
|
407
|
+
"parameters": parameters
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
# Make request to Android device API
|
|
411
|
+
async with httpx.AsyncClient(timeout=self.default_timeout) as client:
|
|
412
|
+
response = await client.post(
|
|
413
|
+
f"{base_url}/{service_id}",
|
|
414
|
+
json=request_payload
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
# Parse response
|
|
418
|
+
if response.status_code == 200:
|
|
419
|
+
response_data = response.json()
|
|
420
|
+
|
|
421
|
+
# Check if Android service returned success
|
|
422
|
+
service_success = response_data.get('success', True)
|
|
423
|
+
|
|
424
|
+
result = {
|
|
425
|
+
"success": service_success,
|
|
426
|
+
"node_id": node_id,
|
|
427
|
+
"node_type": "androidService",
|
|
428
|
+
"result": {
|
|
429
|
+
"service_id": service_id,
|
|
430
|
+
"action": action,
|
|
431
|
+
"data": response_data.get('data', response_data),
|
|
432
|
+
"response_time": response.elapsed.total_seconds(),
|
|
433
|
+
"android_host": android_host,
|
|
434
|
+
"android_port": android_port,
|
|
435
|
+
"timestamp": datetime.now().isoformat()
|
|
436
|
+
},
|
|
437
|
+
"execution_time": time.time() - start_time,
|
|
438
|
+
"timestamp": datetime.now().isoformat()
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if not service_success:
|
|
442
|
+
result["result"]["error"] = response_data.get('error', 'Service execution failed')
|
|
443
|
+
|
|
444
|
+
log_execution_time(logger, f"android_service_{service_id}_{action}", start_time, time.time())
|
|
445
|
+
|
|
446
|
+
logger.info(
|
|
447
|
+
"[Android Service] Execution completed",
|
|
448
|
+
node_id=node_id,
|
|
449
|
+
service_id=service_id,
|
|
450
|
+
action=action,
|
|
451
|
+
success=service_success
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
return result
|
|
455
|
+
|
|
456
|
+
else:
|
|
457
|
+
# HTTP error
|
|
458
|
+
error_msg = f"HTTP {response.status_code}: {response.text}"
|
|
459
|
+
logger.error(
|
|
460
|
+
"[Android Service] HTTP error",
|
|
461
|
+
node_id=node_id,
|
|
462
|
+
service_id=service_id,
|
|
463
|
+
status_code=response.status_code,
|
|
464
|
+
error=error_msg
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
return {
|
|
468
|
+
"success": False,
|
|
469
|
+
"node_id": node_id,
|
|
470
|
+
"node_type": "androidService",
|
|
471
|
+
"error": error_msg,
|
|
472
|
+
"result": {
|
|
473
|
+
"service_id": service_id,
|
|
474
|
+
"action": action,
|
|
475
|
+
"status_code": response.status_code,
|
|
476
|
+
"timestamp": datetime.now().isoformat()
|
|
477
|
+
},
|
|
478
|
+
"execution_time": time.time() - start_time,
|
|
479
|
+
"timestamp": datetime.now().isoformat()
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
except httpx.ConnectError as e:
|
|
483
|
+
error_msg = f"Cannot connect to Android device at {android_host}:{android_port}. Ensure ADB port forwarding is active: adb forward tcp:{android_port} tcp:{android_port}"
|
|
484
|
+
logger.error(
|
|
485
|
+
"[Android Service] Connection error",
|
|
486
|
+
node_id=node_id,
|
|
487
|
+
service_id=service_id,
|
|
488
|
+
error=str(e),
|
|
489
|
+
android_host=android_host,
|
|
490
|
+
android_port=android_port
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
"success": False,
|
|
495
|
+
"node_id": node_id,
|
|
496
|
+
"node_type": "androidService",
|
|
497
|
+
"error": error_msg,
|
|
498
|
+
"result": {
|
|
499
|
+
"service_id": service_id,
|
|
500
|
+
"action": action,
|
|
501
|
+
"connection_error": str(e),
|
|
502
|
+
"timestamp": datetime.now().isoformat()
|
|
503
|
+
},
|
|
504
|
+
"execution_time": time.time() - start_time,
|
|
505
|
+
"timestamp": datetime.now().isoformat()
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
except httpx.TimeoutException as e:
|
|
509
|
+
error_msg = f"Request timeout after {self.default_timeout}s"
|
|
510
|
+
logger.error(
|
|
511
|
+
"[Android Service] Timeout",
|
|
512
|
+
node_id=node_id,
|
|
513
|
+
service_id=service_id,
|
|
514
|
+
timeout=self.default_timeout
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
"success": False,
|
|
519
|
+
"node_id": node_id,
|
|
520
|
+
"node_type": "androidService",
|
|
521
|
+
"error": error_msg,
|
|
522
|
+
"result": {
|
|
523
|
+
"service_id": service_id,
|
|
524
|
+
"action": action,
|
|
525
|
+
"timeout": self.default_timeout,
|
|
526
|
+
"timestamp": datetime.now().isoformat()
|
|
527
|
+
},
|
|
528
|
+
"execution_time": time.time() - start_time,
|
|
529
|
+
"timestamp": datetime.now().isoformat()
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
except Exception as e:
|
|
533
|
+
logger.error(
|
|
534
|
+
"[Android Service] Unexpected error",
|
|
535
|
+
node_id=node_id,
|
|
536
|
+
service_id=service_id,
|
|
537
|
+
error=str(e),
|
|
538
|
+
exc_info=True
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
return {
|
|
542
|
+
"success": False,
|
|
543
|
+
"node_id": node_id,
|
|
544
|
+
"node_type": "androidService",
|
|
545
|
+
"error": str(e),
|
|
546
|
+
"result": {
|
|
547
|
+
"service_id": service_id,
|
|
548
|
+
"action": action,
|
|
549
|
+
"timestamp": datetime.now().isoformat()
|
|
550
|
+
},
|
|
551
|
+
"execution_time": time.time() - start_time,
|
|
552
|
+
"timestamp": datetime.now().isoformat()
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async def check_device_status(
|
|
556
|
+
self,
|
|
557
|
+
android_host: str = "localhost",
|
|
558
|
+
android_port: int = 8888
|
|
559
|
+
) -> Dict[str, Any]:
|
|
560
|
+
"""Check if Android device API is reachable.
|
|
561
|
+
|
|
562
|
+
Returns:
|
|
563
|
+
Status check result with online status
|
|
564
|
+
"""
|
|
565
|
+
try:
|
|
566
|
+
base_url = f"http://{android_host}:{android_port}/api"
|
|
567
|
+
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
568
|
+
response = await client.get(f"{base_url}/status")
|
|
569
|
+
|
|
570
|
+
return {
|
|
571
|
+
"online": response.status_code == 200,
|
|
572
|
+
"data": response.json() if response.status_code == 200 else None,
|
|
573
|
+
"android_host": android_host,
|
|
574
|
+
"android_port": android_port
|
|
575
|
+
}
|
|
576
|
+
except Exception as e:
|
|
577
|
+
logger.warning(
|
|
578
|
+
"[Android Service] Device offline",
|
|
579
|
+
android_host=android_host,
|
|
580
|
+
android_port=android_port,
|
|
581
|
+
error=str(e)
|
|
582
|
+
)
|
|
583
|
+
return {
|
|
584
|
+
"online": False,
|
|
585
|
+
"error": str(e),
|
|
586
|
+
"android_host": android_host,
|
|
587
|
+
"android_port": android_port
|
|
588
|
+
}
|