android-midscene-automation 0.2.4 → 1.1.0
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/CHANGELOG.md +7 -0
- package/README.md +35 -0
- package/artemis-main/.dockerignore +15 -0
- package/artemis-main/.env.example +34 -0
- package/artemis-main/CONTRIBUTING.md +35 -0
- package/artemis-main/Dockerfile +68 -0
- package/artemis-main/LICENSE +202 -0
- package/artemis-main/Makefile +115 -0
- package/artemis-main/README.md +322 -0
- package/artemis-main/README_CN.md +320 -0
- package/artemis-main/apps/__init__.py +15 -0
- package/artemis-main/apps/admin_console/__init__.py +19 -0
- package/artemis-main/apps/admin_console/core/__init__.py +13 -0
- package/artemis-main/apps/admin_console/core/config.py +43 -0
- package/artemis-main/apps/admin_console/core/security.py +178 -0
- package/artemis-main/apps/admin_console/core/state.py +243 -0
- package/artemis-main/apps/admin_console/database/__init__.py +13 -0
- package/artemis-main/apps/admin_console/database/connection.py +60 -0
- package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
- package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
- package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
- package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
- package/artemis-main/apps/admin_console/index.html +3787 -0
- package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
- package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
- package/artemis-main/apps/admin_console/routers/media.py +198 -0
- package/artemis-main/apps/admin_console/routers/replay.py +91 -0
- package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
- package/artemis-main/apps/admin_console/routers/steps.py +109 -0
- package/artemis-main/apps/admin_console/routers/stream.py +56 -0
- package/artemis-main/apps/admin_console/routers/system.py +761 -0
- package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
- package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
- package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
- package/artemis-main/apps/admin_console/server.py +508 -0
- package/artemis-main/apps/admin_console/services/__init__.py +13 -0
- package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
- package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
- package/artemis-main/apps/admin_console/services/media_service.py +623 -0
- package/artemis-main/apps/admin_console/services/model_service.py +148 -0
- package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
- package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
- package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
- package/artemis-main/apps/showcase_ui/README.md +31 -0
- package/artemis-main/apps/showcase_ui/angular.json +132 -0
- package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
- package/artemis-main/apps/showcase_ui/package.json +39 -0
- package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
- package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
- package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
- package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
- package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
- package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
- package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
- package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
- package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
- package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
- package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
- package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
- package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
- package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
- package/artemis-main/apps/showcase_ui/src/index.html +37 -0
- package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
- package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
- package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
- package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
- package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
- package/artemis-main/artemis/__init__.py +44 -0
- package/artemis-main/artemis/__main__.py +18 -0
- package/artemis-main/artemis/_legacy_imports.py +94 -0
- package/artemis-main/artemis/_version.py +30 -0
- package/artemis-main/artemis/agents/__init__.py +17 -0
- package/artemis-main/artemis/agents/checker/checker.json +9 -0
- package/artemis-main/artemis/agents/checker/checker.py +859 -0
- package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
- package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
- package/artemis-main/artemis/agents/explorer/constants.py +52 -0
- package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
- package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
- package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
- package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
- package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
- package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
- package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
- package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
- package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
- package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
- package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
- package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
- package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
- package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
- package/artemis-main/artemis/agents/flash/runner.py +1167 -0
- package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
- package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
- package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
- package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
- package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
- package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
- package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
- package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
- package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
- package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
- package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
- package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
- package/artemis-main/artemis/agents/operator/operator.json +3 -0
- package/artemis-main/artemis/agents/operator/operator.py +1394 -0
- package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
- package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
- package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
- package/artemis-main/artemis/agents/planner/planner.json +32 -0
- package/artemis-main/artemis/agents/planner/planner.py +436 -0
- package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
- package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
- package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
- package/artemis-main/artemis/agents/validator/categories.py +33 -0
- package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
- package/artemis-main/artemis/agents/validator/incidents.py +120 -0
- package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
- package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
- package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
- package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
- package/artemis-main/artemis/agents/validator/validator.py +188 -0
- package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
- package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
- package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
- package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
- package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
- package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
- package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
- package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
- package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
- package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
- package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
- package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
- package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
- package/artemis-main/artemis/clients/accessibility_client.py +438 -0
- package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
- package/artemis-main/artemis/config/__init__.py +309 -0
- package/artemis-main/artemis/config/agent.py +1098 -0
- package/artemis-main/artemis/config/constants.py +151 -0
- package/artemis-main/artemis/config/core/__init__.py +24 -0
- package/artemis-main/artemis/config/core/engine.py +118 -0
- package/artemis-main/artemis/config/core/interpolator.py +78 -0
- package/artemis-main/artemis/config/llm.py +267 -0
- package/artemis-main/artemis/config/paths.py +244 -0
- package/artemis-main/artemis/config/runtime.py +197 -0
- package/artemis-main/artemis/config/settings.py +315 -0
- package/artemis-main/artemis/constants.py +47 -0
- package/artemis-main/artemis/context.py +289 -0
- package/artemis-main/artemis/controllers/__init__.py +41 -0
- package/artemis-main/artemis/controllers/controller_factory.py +41 -0
- package/artemis-main/artemis/controllers/device_controller.py +29 -0
- package/artemis-main/artemis/controllers/types.py +37 -0
- package/artemis-main/artemis/controllers/unified_controller.py +649 -0
- package/artemis-main/artemis/core/__init__.py +21 -0
- package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
- package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
- package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
- package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
- package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
- package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
- package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
- package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
- package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
- package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
- package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
- package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
- package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
- package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
- package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
- package/artemis-main/artemis/core/tool_declaration.py +43 -0
- package/artemis-main/artemis/core/tool_failure.py +56 -0
- package/artemis-main/artemis/data_engine/context_vars.py +24 -0
- package/artemis-main/artemis/data_engine/engine.py +1615 -0
- package/artemis-main/artemis/data_engine/history_reader.py +142 -0
- package/artemis-main/artemis/data_engine/models.py +131 -0
- package/artemis-main/artemis/data_engine/storage.py +1245 -0
- package/artemis-main/artemis/data_engine/trace.py +727 -0
- package/artemis-main/artemis/drivers/__init__.py +48 -0
- package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
- package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
- package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
- package/artemis-main/artemis/drivers/android/recorder.py +57 -0
- package/artemis-main/artemis/drivers/base.py +290 -0
- package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
- package/artemis-main/artemis/drivers/factory.py +80 -0
- package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
- package/artemis-main/artemis/drivers/types.py +67 -0
- package/artemis-main/artemis/graph/checkpoints.py +1243 -0
- package/artemis-main/artemis/graph/graph.py +1055 -0
- package/artemis-main/artemis/graph/perception.py +234 -0
- package/artemis-main/artemis/graph/state.py +151 -0
- package/artemis-main/artemis/graph/visibility.py +238 -0
- package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
- package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
- package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
- package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
- package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
- package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
- package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
- package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
- package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
- package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
- package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
- package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
- package/artemis-main/artemis/interfaces/cli/main.py +109 -0
- package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
- package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
- package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
- package/artemis-main/artemis/llm/__init__.py +25 -0
- package/artemis-main/artemis/llm/fake_model.py +179 -0
- package/artemis-main/artemis/llm/google/__init__.py +57 -0
- package/artemis-main/artemis/llm/google/provider.py +185 -0
- package/artemis-main/artemis/llm/google/usage.py +148 -0
- package/artemis-main/artemis/llm/parser.py +32 -0
- package/artemis-main/artemis/llm/reliability.py +267 -0
- package/artemis-main/artemis/llm/router.py +389 -0
- package/artemis-main/artemis/llm/schemas.py +36 -0
- package/artemis-main/artemis/llm/structured.py +196 -0
- package/artemis-main/artemis/main.py +58 -0
- package/artemis-main/artemis/mcp/__init__.py +20 -0
- package/artemis-main/artemis/mcp/action_executor.py +760 -0
- package/artemis-main/artemis/mcp/action_manifest.py +320 -0
- package/artemis-main/artemis/mcp/action_names.py +172 -0
- package/artemis-main/artemis/mcp/action_server.py +202 -0
- package/artemis-main/artemis/mcp/action_session.py +245 -0
- package/artemis-main/artemis/mcp/action_specs.py +868 -0
- package/artemis-main/artemis/mcp/action_types.py +86 -0
- package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
- package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
- package/artemis-main/artemis/mcp/actuators/base.py +113 -0
- package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
- package/artemis-main/artemis/mcp/adb_server.py +490 -0
- package/artemis-main/artemis/mcp/observation.py +133 -0
- package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
- package/artemis-main/artemis/memory/__init__.py +90 -0
- package/artemis-main/artemis/memory/chunking.py +1843 -0
- package/artemis-main/artemis/memory/context_policy.py +226 -0
- package/artemis-main/artemis/memory/step_capsule.md +73 -0
- package/artemis-main/artemis/memory/step_memory.py +281 -0
- package/artemis-main/artemis/memory/transcript.py +798 -0
- package/artemis-main/artemis/platform/__init__.py +61 -0
- package/artemis-main/artemis/platform/base.py +104 -0
- package/artemis-main/artemis/platform/darwin.py +184 -0
- package/artemis-main/artemis/platform/linux.py +207 -0
- package/artemis-main/artemis/platform/windows.py +193 -0
- package/artemis-main/artemis/resources/__init__.py +46 -0
- package/artemis-main/artemis/runtime/__init__.py +108 -0
- package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
- package/artemis-main/artemis/runtime/awake_lease.py +283 -0
- package/artemis-main/artemis/runtime/awake_service.py +343 -0
- package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
- package/artemis-main/artemis/runtime/daemon_client.py +422 -0
- package/artemis-main/artemis/runtime/device_lock.py +1013 -0
- package/artemis-main/artemis/runtime/device_pool.py +478 -0
- package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
- package/artemis-main/artemis/runtime/process_probe.py +98 -0
- package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
- package/artemis-main/artemis/runtime/supervisor.py +121 -0
- package/artemis-main/artemis/runtime/trace_store.py +375 -0
- package/artemis-main/artemis/sdk/__init__.py +26 -0
- package/artemis-main/artemis/sdk/agent.py +485 -0
- package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
- package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
- package/artemis-main/artemis/sdk/constants.py +15 -0
- package/artemis-main/artemis/sdk/run_outcome.py +58 -0
- package/artemis-main/artemis/sdk/types/__init__.py +61 -0
- package/artemis-main/artemis/sdk/types/agent.py +89 -0
- package/artemis-main/artemis/sdk/utils.py +28 -0
- package/artemis-main/artemis/services/llm.py +1040 -0
- package/artemis-main/artemis/services/token_meter.py +267 -0
- package/artemis-main/artemis/telemetry/__init__.py +35 -0
- package/artemis-main/artemis/telemetry/models.py +64 -0
- package/artemis-main/artemis/telemetry/replay.py +34 -0
- package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
- package/artemis-main/artemis/telemetry/tracer.py +85 -0
- package/artemis-main/artemis/toolchain/__init__.py +52 -0
- package/artemis-main/artemis/toolchain/descriptors.py +92 -0
- package/artemis-main/artemis/toolchain/resolver.py +185 -0
- package/artemis-main/artemis/tools/base.py +232 -0
- package/artemis-main/artemis/tools/command_tool.py +1240 -0
- package/artemis-main/artemis/tools/committee_prompts.json +5 -0
- package/artemis-main/artemis/tools/committee_tool.py +422 -0
- package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
- package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
- package/artemis-main/artemis/tools/explorer_tool.py +581 -0
- package/artemis-main/artemis/tools/history/__init__.py +398 -0
- package/artemis-main/artemis/tools/history/replay.py +120 -0
- package/artemis-main/artemis/tools/history/screenshot.py +139 -0
- package/artemis-main/artemis/tools/history/search.py +563 -0
- package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
- package/artemis-main/artemis/tools/index.py +47 -0
- package/artemis-main/artemis/tools/log_tool.py +108 -0
- package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
- package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
- package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
- package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
- package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
- package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
- package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
- package/artemis-main/artemis/tools/scratchpad.py +819 -0
- package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
- package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
- package/artemis-main/artemis/tools/video_tool.py +261 -0
- package/artemis-main/artemis/tools/wait_tool.py +97 -0
- package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
- package/artemis-main/artemis/utils/coordinates.py +606 -0
- package/artemis-main/artemis/utils/credentials_validator.py +201 -0
- package/artemis-main/artemis/utils/cv_canvas.py +189 -0
- package/artemis-main/artemis/utils/cython_compat.py +45 -0
- package/artemis-main/artemis/utils/element_hit_test.py +80 -0
- package/artemis-main/artemis/utils/image_diff.py +335 -0
- package/artemis-main/artemis/utils/image_hash.py +80 -0
- package/artemis-main/artemis/utils/notes.py +470 -0
- package/artemis-main/artemis/utils/ocr_api.py +105 -0
- package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
- package/artemis-main/artemis/utils/plan_grammar.py +513 -0
- package/artemis-main/artemis/utils/python_executor.py +213 -0
- package/artemis-main/artemis/utils/startup_progress.py +60 -0
- package/artemis-main/artemis/utils/task_tree.py +1174 -0
- package/artemis-main/artemis/utils/text.py +37 -0
- package/artemis-main/artemis/utils/ui_filter.py +651 -0
- package/artemis-main/artemis/utils/video.py +685 -0
- package/artemis-main/artemis/utils/visualization.py +802 -0
- package/artemis-main/config/artemis.example.jsonc +12 -0
- package/artemis-main/mcp_server/README.md +111 -0
- package/artemis-main/mcp_server/__init__.py +21 -0
- package/artemis-main/mcp_server/__main__.py +20 -0
- package/artemis-main/mcp_server/background/__init__.py +15 -0
- package/artemis-main/mcp_server/background/task_runner.py +453 -0
- package/artemis-main/mcp_server/base.py +49 -0
- package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
- package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
- package/artemis-main/mcp_server/notifiers/base.py +45 -0
- package/artemis-main/mcp_server/notifiers/composite.py +87 -0
- package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
- package/artemis-main/mcp_server/notifiers/file.py +69 -0
- package/artemis-main/mcp_server/notifiers/script.py +90 -0
- package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
- package/artemis-main/mcp_server/rules.md +68 -0
- package/artemis-main/mcp_server/server.py +106 -0
- package/artemis-main/mcp_server/tools/__init__.py +29 -0
- package/artemis-main/mcp_server/tools/device_state.py +107 -0
- package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
- package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
- package/artemis-main/mcp_server/tools/task_manager.py +627 -0
- package/artemis-main/mcp_server/tools/task_runner.py +548 -0
- package/artemis-main/mcp_server/utils/__init__.py +15 -0
- package/artemis-main/mcp_server/utils/device_utils.py +180 -0
- package/artemis-main/mcp_server/utils/env_utils.py +65 -0
- package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
- package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
- package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
- package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
- package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
- package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
- package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
- package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
- package/artemis-main/packages/artemis-client/README.md +112 -0
- package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
- package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
- package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
- package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
- package/artemis-main/pyproject.toml +259 -0
- package/artemis-main/pyright-core.json +17 -0
- package/artemis-main/scripts/check_dependency_sources.py +68 -0
- package/artemis-main/scripts/install_deps.ps1 +520 -0
- package/artemis-main/scripts/install_deps.sh +533 -0
- package/artemis-main/scripts/quality_ratchet.py +97 -0
- package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
- package/artemis-main/scripts/run_edge_case_part.py +327 -0
- package/artemis-main/scripts/run_ui.py +242 -0
- package/artemis-main/scripts/start.ps1 +500 -0
- package/artemis-main/scripts/test_all_edge_cases.py +269 -0
- package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
- package/artemis-main/setup.py +164 -0
- package/artemis-main/start.bat +35 -0
- package/artemis-main/start.sh +423 -0
- package/artemis-main/third_party/mobile_use/LICENSE +190 -0
- package/artemis-main/third_party/mobile_use/METADATA +27 -0
- package/artemis-main/third_party/mobile_use/NOTICE +12 -0
- package/artemis-main/third_party/mobile_use/__init__.py +19 -0
- package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
- package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
- package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
- package/artemis-main/third_party/mobile_use/config/output.py +184 -0
- package/artemis-main/third_party/mobile_use/context.py +119 -0
- package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
- package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
- package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
- package/artemis-main/third_party/mobile_use/graph.py +54 -0
- package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
- package/artemis-main/third_party/mobile_use/main.py +130 -0
- package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
- package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
- package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
- package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
- package/artemis-main/third_party/mobile_use/testing.py +59 -0
- package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
- package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
- package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
- package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
- package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
- package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
- package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
- package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
- package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
- package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
- package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
- package/artemis-main/uv.lock +4908 -0
- package/bin/android-midscene-automation.js +1 -5
- package/bin/artemis-config.mjs +15 -0
- package/bin/artemis-embedded.py +56 -0
- package/bin/start-artemis.mjs +30 -0
- package/bin/start-dev.mjs +112 -0
- package/bin/test_artemis_embedded.py +44 -0
- package/package.json +4 -2
- package/server/artemis.ts +34 -0
- package/server/http-api.ts +12 -0
- package/src/App.vue +10 -5
- package/src/api.ts +5 -0
- package/src/appium-recorder/AppiumPage.vue +1 -1
- package/src/appium-recorder/components/ComponentInspector.vue +1 -1
- package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
- package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
- package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
- package/src/appium-recorder/flow-appearance.ts +4 -4
- package/src/components/AppSidebar.vue +8 -7
- package/src/components/MidsceneMaintenanceNotice.vue +17 -0
- package/src/components/analytics/AnalysisCharts.vue +4 -4
- package/src/components/analytics/ReportAnalysis.vue +1 -1
- package/src/components/generator/TestCaseFileUpload.vue +1 -1
- package/src/components/help/HelpCenter.vue +56 -37
- package/src/pages/AiGeneratorPage.vue +1 -1
- package/src/pages/ArtemisPage.vue +72 -0
- package/src/pages/ConfigPage.vue +18 -19
- package/src/style.css +340 -121
- package/src/types.ts +1 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Device discovery, ADB resolution, and emulator management for MCP workflows."""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import shutil
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
import time
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def resolve_adb_path() -> str:
|
|
25
|
+
"""Resolves the absolute path to the adb binary across platforms."""
|
|
26
|
+
# 1. Check if adb is in PATH
|
|
27
|
+
which_adb = shutil.which("adb")
|
|
28
|
+
if which_adb:
|
|
29
|
+
return which_adb
|
|
30
|
+
|
|
31
|
+
# 2. Check ANDROID_HOME / ANDROID_SDK_ROOT
|
|
32
|
+
for env_var in ("ANDROID_HOME", "ANDROID_SDK_ROOT"):
|
|
33
|
+
sdk_root = os.getenv(env_var)
|
|
34
|
+
if sdk_root:
|
|
35
|
+
candidate = os.path.join(
|
|
36
|
+
sdk_root, "platform-tools", "adb.exe" if sys.platform == "win32" else "adb"
|
|
37
|
+
)
|
|
38
|
+
if os.path.exists(candidate):
|
|
39
|
+
return candidate
|
|
40
|
+
|
|
41
|
+
# 3. Check standard OS-specific SDK locations
|
|
42
|
+
candidates = []
|
|
43
|
+
if sys.platform == "win32":
|
|
44
|
+
local_appdata = os.getenv("LOCALAPPDATA", "")
|
|
45
|
+
if local_appdata:
|
|
46
|
+
candidates.append(
|
|
47
|
+
os.path.join(local_appdata, "Android", "Sdk", "platform-tools", "adb.exe")
|
|
48
|
+
)
|
|
49
|
+
elif sys.platform == "darwin":
|
|
50
|
+
candidates.extend(
|
|
51
|
+
[
|
|
52
|
+
os.path.expanduser("~/Library/Android/sdk/platform-tools/adb"),
|
|
53
|
+
"/opt/homebrew/bin/adb",
|
|
54
|
+
"/usr/local/bin/adb",
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
else: # Linux
|
|
58
|
+
candidates.extend(
|
|
59
|
+
[
|
|
60
|
+
"/usr/bin/adb",
|
|
61
|
+
os.path.expanduser("~/Android/Sdk/platform-tools/adb"),
|
|
62
|
+
"/usr/local/bin/adb",
|
|
63
|
+
]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
for cand in candidates:
|
|
67
|
+
if os.path.exists(cand):
|
|
68
|
+
return cand
|
|
69
|
+
|
|
70
|
+
return "adb"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def resolve_emulator_path() -> str | None:
|
|
74
|
+
"""Resolves the path to the Android emulator executable."""
|
|
75
|
+
which_emulator = shutil.which("emulator")
|
|
76
|
+
if which_emulator:
|
|
77
|
+
return which_emulator
|
|
78
|
+
|
|
79
|
+
candidates = [
|
|
80
|
+
os.path.expanduser("~/Android/Sdk/emulator/emulator"),
|
|
81
|
+
os.path.expanduser("~/Library/Android/sdk/emulator/emulator"),
|
|
82
|
+
]
|
|
83
|
+
if sys.platform == "win32":
|
|
84
|
+
local_appdata = os.getenv("LOCALAPPDATA", "")
|
|
85
|
+
if local_appdata:
|
|
86
|
+
candidates.append(
|
|
87
|
+
os.path.join(local_appdata, "Android", "Sdk", "emulator", "emulator.exe")
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
for cand in candidates:
|
|
91
|
+
if os.path.exists(cand):
|
|
92
|
+
return cand
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_connected_devices(adb_path: str | None = None) -> list[str]:
|
|
97
|
+
"""Returns a list of connected and authorized Android device serials."""
|
|
98
|
+
adb = adb_path or resolve_adb_path()
|
|
99
|
+
try:
|
|
100
|
+
res = subprocess.run(
|
|
101
|
+
[adb, "devices"],
|
|
102
|
+
stdin=subprocess.DEVNULL,
|
|
103
|
+
capture_output=True,
|
|
104
|
+
text=True,
|
|
105
|
+
timeout=5,
|
|
106
|
+
)
|
|
107
|
+
devices: list[str] = []
|
|
108
|
+
for line in res.stdout.splitlines():
|
|
109
|
+
line_str = line.strip()
|
|
110
|
+
if line_str and not line_str.startswith("List of devices"):
|
|
111
|
+
parts = line_str.split()
|
|
112
|
+
if len(parts) >= 2 and parts[1] == "device":
|
|
113
|
+
devices.append(parts[0])
|
|
114
|
+
return devices
|
|
115
|
+
except Exception:
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def is_emulator_running(adb_path: str | None = None) -> bool:
|
|
120
|
+
"""Checks if an Android emulator (e.g. emulator-5554) is running and accessible."""
|
|
121
|
+
adb = adb_path or resolve_adb_path()
|
|
122
|
+
devices = get_connected_devices(adb)
|
|
123
|
+
return any(d.startswith("emulator-") for d in devices)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def ensure_emulator(
|
|
127
|
+
avd_name: str = "AndroidWorldAvd",
|
|
128
|
+
adb_path: str | None = None,
|
|
129
|
+
emulator_path: str | None = None,
|
|
130
|
+
timeout_seconds: int = 90,
|
|
131
|
+
) -> bool:
|
|
132
|
+
"""Ensures an emulator is running. If not, boots the specified AVD in the background."""
|
|
133
|
+
adb = adb_path or resolve_adb_path()
|
|
134
|
+
if is_emulator_running(adb):
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
emu_exe = emulator_path or resolve_emulator_path()
|
|
138
|
+
if not emu_exe or not os.path.exists(emu_exe):
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
command = [emu_exe, "-avd", avd_name, "-no-snapshot", "-grpc", "8554"]
|
|
143
|
+
if sys.platform == "win32":
|
|
144
|
+
subprocess.Popen(
|
|
145
|
+
command,
|
|
146
|
+
stdin=subprocess.DEVNULL,
|
|
147
|
+
stdout=subprocess.DEVNULL,
|
|
148
|
+
stderr=subprocess.DEVNULL,
|
|
149
|
+
creationflags=(subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS),
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
subprocess.Popen(
|
|
153
|
+
command,
|
|
154
|
+
stdin=subprocess.DEVNULL,
|
|
155
|
+
stdout=subprocess.DEVNULL,
|
|
156
|
+
stderr=subprocess.DEVNULL,
|
|
157
|
+
start_new_session=True,
|
|
158
|
+
)
|
|
159
|
+
except Exception:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
start_time = time.time()
|
|
163
|
+
while time.time() - start_time < timeout_seconds:
|
|
164
|
+
try:
|
|
165
|
+
res = subprocess.run(
|
|
166
|
+
[adb, "-s", "emulator-5554", "shell", "getprop", "sys.boot_completed"],
|
|
167
|
+
stdin=subprocess.DEVNULL,
|
|
168
|
+
capture_output=True,
|
|
169
|
+
text=True,
|
|
170
|
+
timeout=3,
|
|
171
|
+
)
|
|
172
|
+
if "1" in res.stdout:
|
|
173
|
+
time.sleep(2) # Stabilization
|
|
174
|
+
return True
|
|
175
|
+
except (subprocess.SubprocessError, OSError):
|
|
176
|
+
# adb not ready yet while the emulator boots: keep polling.
|
|
177
|
+
pass
|
|
178
|
+
time.sleep(2)
|
|
179
|
+
|
|
180
|
+
return False
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Environment and runtime discovery utilities for MCP."""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_project_root() -> str:
|
|
24
|
+
"""Returns the absolute path to the project root repository."""
|
|
25
|
+
# Deliberate lazy import: this helper must keep working when the artemis
|
|
26
|
+
# package (or its config bootstrap) is unavailable or broken, falling back
|
|
27
|
+
# to filesystem-relative resolution below.
|
|
28
|
+
try:
|
|
29
|
+
from artemis.config.paths import ROOT_DIR
|
|
30
|
+
|
|
31
|
+
return str(ROOT_DIR)
|
|
32
|
+
except Exception:
|
|
33
|
+
# mcp_server/utils/env_utils.py -> root is 3 levels up
|
|
34
|
+
return os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def resolve_python_executable(project_root: str | None = None) -> str:
|
|
38
|
+
"""Resolves the preferred Python executable (.venv or current sys.executable)."""
|
|
39
|
+
root = project_root or get_project_root()
|
|
40
|
+
|
|
41
|
+
if sys.platform == "win32":
|
|
42
|
+
venv_python = os.path.join(root, ".venv", "Scripts", "python.exe")
|
|
43
|
+
else:
|
|
44
|
+
venv_python = os.path.join(root, ".venv", "bin", "python")
|
|
45
|
+
|
|
46
|
+
if os.path.exists(venv_python):
|
|
47
|
+
return venv_python
|
|
48
|
+
|
|
49
|
+
return sys.executable
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_detached_process_kwargs() -> dict[str, Any]:
|
|
53
|
+
"""Returns kwargs for subprocess.Popen to run a fully detached background process cross-platform."""
|
|
54
|
+
kwargs: dict[str, Any] = {
|
|
55
|
+
"stdin": subprocess.DEVNULL,
|
|
56
|
+
"stdout": subprocess.DEVNULL,
|
|
57
|
+
"stderr": subprocess.DEVNULL,
|
|
58
|
+
}
|
|
59
|
+
if sys.platform == "win32":
|
|
60
|
+
kwargs["creationflags"] = (
|
|
61
|
+
subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS # type: ignore[attr-defined]
|
|
62
|
+
)
|
|
63
|
+
else:
|
|
64
|
+
kwargs["start_new_session"] = True
|
|
65
|
+
return kwargs
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
plugins {
|
|
2
|
+
id("com.android.application")
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
android {
|
|
6
|
+
namespace = "com.artemis.helper"
|
|
7
|
+
compileSdk = 35
|
|
8
|
+
|
|
9
|
+
defaultConfig {
|
|
10
|
+
applicationId = "com.artemis.helper"
|
|
11
|
+
minSdk = 24
|
|
12
|
+
targetSdk = 35
|
|
13
|
+
versionCode = 6
|
|
14
|
+
versionName = "1.2.0"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
buildTypes {
|
|
18
|
+
release {
|
|
19
|
+
isMinifyEnabled = false
|
|
20
|
+
proguardFiles(
|
|
21
|
+
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
22
|
+
"proguard-rules.pro"
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
compileOptions {
|
|
27
|
+
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
28
|
+
targetCompatibility = JavaVersion.VERSION_1_8
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
dependencies {
|
|
33
|
+
// Pure standard Android SDK APIs (android.accessibilityservice, android.view.accessibility, org.json)
|
|
34
|
+
// Zero external dependencies to maximize stability, guarantee compatibility, and keep APK under 30KB.
|
|
35
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
package="com.artemis.helper">
|
|
4
|
+
|
|
5
|
+
<uses-sdk
|
|
6
|
+
android:minSdkVersion="24"
|
|
7
|
+
android:targetSdkVersion="35" />
|
|
8
|
+
|
|
9
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
10
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
11
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
|
12
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
13
|
+
|
|
14
|
+
<application
|
|
15
|
+
android:allowBackup="false"
|
|
16
|
+
android:label="@string/app_name"
|
|
17
|
+
android:supportsRtl="true">
|
|
18
|
+
|
|
19
|
+
<service
|
|
20
|
+
android:name=".ArtemisAccessibilityService"
|
|
21
|
+
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
|
|
22
|
+
android:exported="true"
|
|
23
|
+
android:foregroundServiceType="specialUse">
|
|
24
|
+
<intent-filter>
|
|
25
|
+
<action android:name="android.accessibilityservice.AccessibilityService" />
|
|
26
|
+
</intent-filter>
|
|
27
|
+
<meta-data
|
|
28
|
+
android:name="android.accessibilityservice"
|
|
29
|
+
android:resource="@xml/accessibility_service_config" />
|
|
30
|
+
<property
|
|
31
|
+
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
|
32
|
+
android:value="Artemis mobile automation test bridge" />
|
|
33
|
+
</service>
|
|
34
|
+
|
|
35
|
+
<!-- Session token drop: only a sender holding WRITE_SECURE_SETTINGS (the adb
|
|
36
|
+
shell user, the system) can deliver this broadcast, so no ordinary app on
|
|
37
|
+
the device can set a token of its own and then talk to the loopback server. -->
|
|
38
|
+
<receiver
|
|
39
|
+
android:name=".TokenReceiver"
|
|
40
|
+
android:exported="true"
|
|
41
|
+
android:permission="android.permission.WRITE_SECURE_SETTINGS">
|
|
42
|
+
<intent-filter>
|
|
43
|
+
<action android:name="com.artemis.helper.SET_TOKEN" />
|
|
44
|
+
</intent-filter>
|
|
45
|
+
</receiver>
|
|
46
|
+
|
|
47
|
+
</application>
|
|
48
|
+
|
|
49
|
+
</manifest>
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
package com.artemis.helper;
|
|
2
|
+
|
|
3
|
+
import org.json.JSONArray;
|
|
4
|
+
import org.json.JSONObject;
|
|
5
|
+
|
|
6
|
+
import java.util.ArrayList;
|
|
7
|
+
import java.util.List;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* In-memory snapshot of an Accessibility UI element node.
|
|
11
|
+
* Decoupled from live Android AccessibilityNodeInfo objects to prevent
|
|
12
|
+
* Binder proxy memory leaks, avoid stale node crashes, and ensure
|
|
13
|
+
* instant thread-safe serialization to both XML and JSON.
|
|
14
|
+
*
|
|
15
|
+
* {@code bounds} are the node's <em>visible</em> bounds: the raw
|
|
16
|
+
* {@code getBoundsInScreen} rectangle intersected with the display, the
|
|
17
|
+
* node's window and every scrollable ancestor, exactly like UIAutomator's
|
|
18
|
+
* {@code getVisibleBoundsInScreen}. They are therefore never negative and
|
|
19
|
+
* never extend past the screen.
|
|
20
|
+
*/
|
|
21
|
+
public final class A11yNode {
|
|
22
|
+
|
|
23
|
+
public int index = 0;
|
|
24
|
+
public String text = "";
|
|
25
|
+
public String resourceId = "";
|
|
26
|
+
public String className = "";
|
|
27
|
+
public String packageName = "";
|
|
28
|
+
public String contentDesc = "";
|
|
29
|
+
|
|
30
|
+
public boolean checkable = false;
|
|
31
|
+
public boolean checked = false;
|
|
32
|
+
public boolean clickable = false;
|
|
33
|
+
public boolean enabled = true;
|
|
34
|
+
public boolean focusable = false;
|
|
35
|
+
public boolean focused = false;
|
|
36
|
+
public boolean scrollable = false;
|
|
37
|
+
public boolean longClickable = false;
|
|
38
|
+
public boolean password = false;
|
|
39
|
+
public boolean selected = false;
|
|
40
|
+
public boolean visibleToUser = true;
|
|
41
|
+
|
|
42
|
+
// Window metadata for multi-window awareness
|
|
43
|
+
public int windowId = -1;
|
|
44
|
+
public String windowType = "";
|
|
45
|
+
public int windowLayer = 0;
|
|
46
|
+
public boolean windowActive = false;
|
|
47
|
+
public boolean windowFocused = false;
|
|
48
|
+
|
|
49
|
+
// Advanced semantics for Agent perception & verification
|
|
50
|
+
public boolean editable = false;
|
|
51
|
+
public boolean isHeading = false;
|
|
52
|
+
public boolean screenReaderFocusable = false;
|
|
53
|
+
public String stateDescription = "";
|
|
54
|
+
public String errorText = "";
|
|
55
|
+
public String paneTitle = "";
|
|
56
|
+
public String tooltip = "";
|
|
57
|
+
|
|
58
|
+
public int left = 0;
|
|
59
|
+
public int top = 0;
|
|
60
|
+
public int right = 0;
|
|
61
|
+
public int bottom = 0;
|
|
62
|
+
|
|
63
|
+
public int drawingOrder = 0;
|
|
64
|
+
public String hint = "";
|
|
65
|
+
|
|
66
|
+
public final List<A11yNode> children = new ArrayList<>(4);
|
|
67
|
+
|
|
68
|
+
public A11yNode() {}
|
|
69
|
+
|
|
70
|
+
public int getWidth() {
|
|
71
|
+
return right - left;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public int getHeight() {
|
|
75
|
+
return bottom - top;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public String getBoundsString() {
|
|
79
|
+
return "[" + left + "," + top + "][" + right + "," + bottom + "]";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Serializes this node and all of its descendants into standard UIAutomator XML.
|
|
84
|
+
*/
|
|
85
|
+
public void writeXml(StringBuilder sb) {
|
|
86
|
+
sb.append("<node");
|
|
87
|
+
XmlUtils.appendIntAttribute(sb, "index", index);
|
|
88
|
+
XmlUtils.appendAttribute(sb, "text", text);
|
|
89
|
+
XmlUtils.appendAttribute(sb, "resource-id", resourceId);
|
|
90
|
+
XmlUtils.appendAttribute(sb, "class", className);
|
|
91
|
+
XmlUtils.appendAttribute(sb, "package", packageName);
|
|
92
|
+
XmlUtils.appendAttribute(sb, "content-desc", contentDesc);
|
|
93
|
+
XmlUtils.appendBooleanAttribute(sb, "checkable", checkable);
|
|
94
|
+
XmlUtils.appendBooleanAttribute(sb, "checked", checked);
|
|
95
|
+
XmlUtils.appendBooleanAttribute(sb, "clickable", clickable);
|
|
96
|
+
XmlUtils.appendBooleanAttribute(sb, "enabled", enabled);
|
|
97
|
+
XmlUtils.appendBooleanAttribute(sb, "focusable", focusable);
|
|
98
|
+
XmlUtils.appendBooleanAttribute(sb, "focused", focused);
|
|
99
|
+
XmlUtils.appendBooleanAttribute(sb, "scrollable", scrollable);
|
|
100
|
+
XmlUtils.appendBooleanAttribute(sb, "long-clickable", longClickable);
|
|
101
|
+
XmlUtils.appendBooleanAttribute(sb, "password", password);
|
|
102
|
+
XmlUtils.appendBooleanAttribute(sb, "selected", selected);
|
|
103
|
+
XmlUtils.appendBooleanAttribute(sb, "visible-to-user", visibleToUser);
|
|
104
|
+
XmlUtils.appendAttribute(sb, "bounds", getBoundsString());
|
|
105
|
+
XmlUtils.appendIntAttribute(sb, "drawing-order", drawingOrder);
|
|
106
|
+
if (!hint.isEmpty()) {
|
|
107
|
+
XmlUtils.appendAttribute(sb, "hint", hint);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Window metadata (emitted on window root nodes)
|
|
111
|
+
if (windowId >= 0) {
|
|
112
|
+
XmlUtils.appendIntAttribute(sb, "window-id", windowId);
|
|
113
|
+
if (!windowType.isEmpty()) {
|
|
114
|
+
XmlUtils.appendAttribute(sb, "window-type", windowType);
|
|
115
|
+
}
|
|
116
|
+
XmlUtils.appendIntAttribute(sb, "window-layer", windowLayer);
|
|
117
|
+
if (windowActive) {
|
|
118
|
+
XmlUtils.appendBooleanAttribute(sb, "window-active", true);
|
|
119
|
+
}
|
|
120
|
+
if (windowFocused) {
|
|
121
|
+
XmlUtils.appendBooleanAttribute(sb, "window-focused", true);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Extended semantic attributes
|
|
126
|
+
if (editable) {
|
|
127
|
+
XmlUtils.appendBooleanAttribute(sb, "editable", true);
|
|
128
|
+
}
|
|
129
|
+
if (isHeading) {
|
|
130
|
+
XmlUtils.appendBooleanAttribute(sb, "heading", true);
|
|
131
|
+
}
|
|
132
|
+
if (screenReaderFocusable) {
|
|
133
|
+
XmlUtils.appendBooleanAttribute(sb, "screen-reader-focusable", true);
|
|
134
|
+
}
|
|
135
|
+
if (!stateDescription.isEmpty()) {
|
|
136
|
+
XmlUtils.appendAttribute(sb, "state-description", stateDescription);
|
|
137
|
+
}
|
|
138
|
+
if (!errorText.isEmpty()) {
|
|
139
|
+
XmlUtils.appendAttribute(sb, "error", errorText);
|
|
140
|
+
}
|
|
141
|
+
if (!paneTitle.isEmpty()) {
|
|
142
|
+
XmlUtils.appendAttribute(sb, "pane-title", paneTitle);
|
|
143
|
+
}
|
|
144
|
+
if (!tooltip.isEmpty()) {
|
|
145
|
+
XmlUtils.appendAttribute(sb, "tooltip", tooltip);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (children.isEmpty()) {
|
|
149
|
+
sb.append(" />");
|
|
150
|
+
} else {
|
|
151
|
+
sb.append('>');
|
|
152
|
+
for (int i = 0; i < children.size(); i++) {
|
|
153
|
+
children.get(i).writeXml(sb);
|
|
154
|
+
}
|
|
155
|
+
sb.append("</node>");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Serializes this node into a hierarchical JSON tree object.
|
|
161
|
+
*/
|
|
162
|
+
public JSONObject toTreeJson() {
|
|
163
|
+
JSONObject obj = toBaseJson();
|
|
164
|
+
try {
|
|
165
|
+
if (!children.isEmpty()) {
|
|
166
|
+
JSONArray childrenArr = new JSONArray();
|
|
167
|
+
for (int i = 0; i < children.size(); i++) {
|
|
168
|
+
childrenArr.put(children.get(i).toTreeJson());
|
|
169
|
+
}
|
|
170
|
+
obj.put("children", childrenArr);
|
|
171
|
+
}
|
|
172
|
+
} catch (Throwable ignored) {}
|
|
173
|
+
return obj;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Serializes this node into an element JSON object for the flat list.
|
|
178
|
+
* Does NOT include child references.
|
|
179
|
+
*/
|
|
180
|
+
public JSONObject toFlatElementJson() {
|
|
181
|
+
return toBaseJson();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private JSONObject toBaseJson() {
|
|
185
|
+
JSONObject obj = new JSONObject();
|
|
186
|
+
try {
|
|
187
|
+
obj.put("class", className);
|
|
188
|
+
obj.put("package", packageName);
|
|
189
|
+
obj.put("resource-id", resourceId);
|
|
190
|
+
obj.put("text", text);
|
|
191
|
+
obj.put("content-desc", contentDesc);
|
|
192
|
+
obj.put("bounds", getBoundsString());
|
|
193
|
+
|
|
194
|
+
JSONObject bounds = new JSONObject();
|
|
195
|
+
bounds.put("left", left);
|
|
196
|
+
bounds.put("top", top);
|
|
197
|
+
bounds.put("right", right);
|
|
198
|
+
bounds.put("bottom", bottom);
|
|
199
|
+
obj.put("parsed_bounds", bounds);
|
|
200
|
+
|
|
201
|
+
obj.put("clickable", clickable);
|
|
202
|
+
obj.put("scrollable", scrollable);
|
|
203
|
+
obj.put("checkable", checkable);
|
|
204
|
+
obj.put("checked", checked);
|
|
205
|
+
obj.put("enabled", enabled);
|
|
206
|
+
obj.put("focusable", focusable);
|
|
207
|
+
obj.put("focused", focused);
|
|
208
|
+
obj.put("selected", selected);
|
|
209
|
+
obj.put("password", password);
|
|
210
|
+
obj.put("long-clickable", longClickable);
|
|
211
|
+
obj.put("visible-to-user", visibleToUser);
|
|
212
|
+
obj.put("drawing-order", drawingOrder);
|
|
213
|
+
if (!hint.isEmpty()) {
|
|
214
|
+
obj.put("hint", hint);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Window metadata
|
|
218
|
+
if (windowId >= 0) {
|
|
219
|
+
obj.put("window_id", windowId);
|
|
220
|
+
obj.put("window_type", windowType);
|
|
221
|
+
obj.put("window_layer", windowLayer);
|
|
222
|
+
if (windowActive) obj.put("window_active", true);
|
|
223
|
+
if (windowFocused) obj.put("window_focused", true);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Extended semantics
|
|
227
|
+
if (editable) {
|
|
228
|
+
obj.put("editable", true);
|
|
229
|
+
}
|
|
230
|
+
if (isHeading) {
|
|
231
|
+
obj.put("is_heading", true);
|
|
232
|
+
}
|
|
233
|
+
if (screenReaderFocusable) {
|
|
234
|
+
obj.put("screen_reader_focusable", true);
|
|
235
|
+
}
|
|
236
|
+
if (!stateDescription.isEmpty()) {
|
|
237
|
+
obj.put("state_description", stateDescription);
|
|
238
|
+
}
|
|
239
|
+
if (!errorText.isEmpty()) {
|
|
240
|
+
obj.put("error", errorText);
|
|
241
|
+
}
|
|
242
|
+
if (!paneTitle.isEmpty()) {
|
|
243
|
+
obj.put("pane_title", paneTitle);
|
|
244
|
+
}
|
|
245
|
+
if (!tooltip.isEmpty()) {
|
|
246
|
+
obj.put("tooltip", tooltip);
|
|
247
|
+
}
|
|
248
|
+
} catch (Throwable ignored) {}
|
|
249
|
+
return obj;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Determines whether this node contains useful information or interactivity
|
|
254
|
+
* and should be included in the flat elements array for agent perception.
|
|
255
|
+
*/
|
|
256
|
+
public boolean isInformativeOrInteractive() {
|
|
257
|
+
boolean hasContent = !text.isEmpty() || !contentDesc.isEmpty() || !resourceId.isEmpty()
|
|
258
|
+
|| !stateDescription.isEmpty() || !errorText.isEmpty() || !paneTitle.isEmpty();
|
|
259
|
+
boolean isInteractive = clickable || scrollable || checkable || focusable || longClickable || editable;
|
|
260
|
+
return hasContent || isInteractive || isHeading;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Recursively collects all informative or interactive nodes into a flat list.
|
|
265
|
+
*/
|
|
266
|
+
public void collectFlatElements(List<JSONObject> flatList) {
|
|
267
|
+
if (isInformativeOrInteractive()) {
|
|
268
|
+
flatList.add(toFlatElementJson());
|
|
269
|
+
}
|
|
270
|
+
for (int i = 0; i < children.size(); i++) {
|
|
271
|
+
children.get(i).collectFlatElements(flatList);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|