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,184 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Structured output schemas, event recording, and output file management."""
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Annotated, Any
|
|
25
|
+
import warnings
|
|
26
|
+
|
|
27
|
+
from pydantic import BaseModel, Field, model_validator
|
|
28
|
+
|
|
29
|
+
from artemis.config.constants import (
|
|
30
|
+
ENV_EVENTS_OUTPUT_PATH,
|
|
31
|
+
ENV_RESULTS_OUTPUT_PATH,
|
|
32
|
+
)
|
|
33
|
+
from artemis.utils.cython_compat import CyFunctionDetector
|
|
34
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
35
|
+
|
|
36
|
+
logger = get_logger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class OutputConfig(BaseModel):
|
|
40
|
+
"""Configuration for structured output formatting and schemas."""
|
|
41
|
+
|
|
42
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
43
|
+
structured_output: Annotated[
|
|
44
|
+
type[BaseModel] | dict | None,
|
|
45
|
+
Field(
|
|
46
|
+
default=None,
|
|
47
|
+
description=(
|
|
48
|
+
"Optional structured schema (as a BaseModel or dict) to shape the output. "
|
|
49
|
+
"If provided, it takes precedence over 'output_description'."
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
]
|
|
53
|
+
output_description: Annotated[
|
|
54
|
+
str | None,
|
|
55
|
+
Field(
|
|
56
|
+
default=None,
|
|
57
|
+
description=(
|
|
58
|
+
"Optional natural language description of the expected output format. "
|
|
59
|
+
"Used only if 'structured_output' is not provided. "
|
|
60
|
+
"Example: 'Output a JSON with 3 keys: color, price, websiteUrl'."
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
]
|
|
64
|
+
enable_outputter: Annotated[
|
|
65
|
+
bool | None,
|
|
66
|
+
Field(
|
|
67
|
+
default=None,
|
|
68
|
+
description="Explicitly enable or disable Outputter post-execution synthesis.",
|
|
69
|
+
),
|
|
70
|
+
] = None
|
|
71
|
+
force_synthesis: Annotated[
|
|
72
|
+
bool,
|
|
73
|
+
Field(
|
|
74
|
+
default=False,
|
|
75
|
+
description="Whether to run Outputter synthesis even without explicit output_description or schema.",
|
|
76
|
+
),
|
|
77
|
+
] = False
|
|
78
|
+
|
|
79
|
+
def __str__(self) -> str:
|
|
80
|
+
s_builder = ""
|
|
81
|
+
if self.structured_output:
|
|
82
|
+
s_builder += f"Structured Output: {self.structured_output}\n"
|
|
83
|
+
if self.output_description:
|
|
84
|
+
s_builder += f"Output Description: {self.output_description}\n"
|
|
85
|
+
if self.output_description and self.structured_output:
|
|
86
|
+
s_builder += (
|
|
87
|
+
"Both 'structured_output' and 'output_description' are provided. "
|
|
88
|
+
"'structured_output' will take precedence.\n"
|
|
89
|
+
)
|
|
90
|
+
return s_builder
|
|
91
|
+
|
|
92
|
+
@model_validator(mode="after")
|
|
93
|
+
def warn_if_both_outputs_provided(self) -> "OutputConfig":
|
|
94
|
+
if self.structured_output and self.output_description:
|
|
95
|
+
warnings.warn(
|
|
96
|
+
"Both 'structured_output' and 'output_description' are provided. "
|
|
97
|
+
"'structured_output' will take precedence.",
|
|
98
|
+
stacklevel=2,
|
|
99
|
+
)
|
|
100
|
+
return self
|
|
101
|
+
|
|
102
|
+
def needs_structured_format(self, default_enabled: bool = True) -> bool:
|
|
103
|
+
"""Returns True if Outputter should run to synthesize/structure the output."""
|
|
104
|
+
if self.enable_outputter is False:
|
|
105
|
+
return False
|
|
106
|
+
if self.enable_outputter is True or self.force_synthesis:
|
|
107
|
+
return True
|
|
108
|
+
if not default_enabled:
|
|
109
|
+
return False
|
|
110
|
+
return bool(self.structured_output or self.output_description)
|
|
111
|
+
|
|
112
|
+
def structured_output_schema(self) -> dict | type[BaseModel] | None:
|
|
113
|
+
"""The structured output as a schema accepted by ``with_structured_output``."""
|
|
114
|
+
so = self.structured_output
|
|
115
|
+
if isinstance(so, dict):
|
|
116
|
+
return so
|
|
117
|
+
elif isinstance(so, BaseModel):
|
|
118
|
+
return type(so)
|
|
119
|
+
elif isinstance(so, type) and issubclass(so, BaseModel):
|
|
120
|
+
return so
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
def bind_structured_output(self, llm: Any, llm_fallback: Any) -> tuple[Any, Any] | None:
|
|
124
|
+
"""Bind the structured output schema to the main and fallback models.
|
|
125
|
+
|
|
126
|
+
Returns None when no structured output is configured.
|
|
127
|
+
"""
|
|
128
|
+
schema = self.structured_output_schema()
|
|
129
|
+
if schema is None:
|
|
130
|
+
return None
|
|
131
|
+
return llm.with_structured_output(schema), llm_fallback.with_structured_output(schema)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def prepare_output_files() -> tuple[str | None, str | None]:
|
|
135
|
+
"""Validate and prepare events and results output files from environment variables."""
|
|
136
|
+
events_output_path = os.getenv(ENV_EVENTS_OUTPUT_PATH)
|
|
137
|
+
results_output_path = os.getenv(ENV_RESULTS_OUTPUT_PATH)
|
|
138
|
+
|
|
139
|
+
def validate_and_prepare_file(file_path: str) -> str | None:
|
|
140
|
+
if not file_path:
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
path_obj = Path(file_path)
|
|
144
|
+
|
|
145
|
+
if path_obj.exists() and path_obj.is_dir():
|
|
146
|
+
logger.error(f"Error: Path '{file_path}' points to an existing directory, not a file.")
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
if not path_obj.suffix or file_path.endswith(("/", "\\")):
|
|
150
|
+
logger.error(f"Error: Path '{file_path}' appears to be a directory path, not a file.")
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
path_obj.parent.mkdir(parents=True, exist_ok=True)
|
|
155
|
+
path_obj.touch(exist_ok=True)
|
|
156
|
+
return file_path
|
|
157
|
+
except OSError as e:
|
|
158
|
+
logger.error(f"Error creating file '{file_path}': {e}")
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
validated_events_path = (
|
|
162
|
+
validate_and_prepare_file(events_output_path) if events_output_path else None
|
|
163
|
+
)
|
|
164
|
+
validated_results_path = (
|
|
165
|
+
validate_and_prepare_file(results_output_path) if results_output_path else None
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return validated_events_path, validated_results_path
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def record_events(output_path: Path | None, events: list[str] | BaseModel | Any) -> None:
|
|
172
|
+
"""Serialize and write execution events to disk."""
|
|
173
|
+
if not output_path:
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
if isinstance(events, str):
|
|
177
|
+
events_content = events
|
|
178
|
+
elif isinstance(events, BaseModel):
|
|
179
|
+
events_content = events.model_dump_json(indent=2)
|
|
180
|
+
else:
|
|
181
|
+
events_content = json.dumps(events, indent=2)
|
|
182
|
+
|
|
183
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
184
|
+
f.write(events_content)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Context variables for global state management.
|
|
20
|
+
|
|
21
|
+
Uses ContextVar to avoid prop drilling and maintain clean function signatures.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Any, Literal
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
from enum import StrEnum
|
|
29
|
+
except ImportError:
|
|
30
|
+
from enum import Enum
|
|
31
|
+
|
|
32
|
+
class StrEnum(str, Enum):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
from pydantic import BaseModel, ConfigDict
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AppLaunchResult(BaseModel):
|
|
40
|
+
"""Result of initial app launch attempt."""
|
|
41
|
+
|
|
42
|
+
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
|
43
|
+
|
|
44
|
+
locked_app_package: str
|
|
45
|
+
locked_app_initial_launch_success: bool | None
|
|
46
|
+
locked_app_initial_launch_error: str | None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class DevicePlatform(StrEnum):
|
|
50
|
+
"""Mobile device platform enumeration."""
|
|
51
|
+
|
|
52
|
+
ANDROID = "android"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class DeviceContext(BaseModel):
|
|
56
|
+
model_config = ConfigDict(
|
|
57
|
+
arbitrary_types_allowed=True,
|
|
58
|
+
extra="allow",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
host_platform: Literal["WINDOWS", "LINUX", "DARWIN", "MACOS"] | str = "DARWIN"
|
|
62
|
+
mobile_platform: DevicePlatform = DevicePlatform.ANDROID
|
|
63
|
+
device_id: str = "default-device"
|
|
64
|
+
|
|
65
|
+
device_width: int = 1080
|
|
66
|
+
device_height: int = 2400
|
|
67
|
+
|
|
68
|
+
def to_str(self):
|
|
69
|
+
return (
|
|
70
|
+
f"Host platform: {self.host_platform}\n"
|
|
71
|
+
f"Mobile platform: {self.mobile_platform.value}\n"
|
|
72
|
+
f"Device ID: {self.device_id}\n"
|
|
73
|
+
f"Device width: {self.device_width}\n"
|
|
74
|
+
f"Device height: {self.device_height}\n"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ExecutionSetupBase(BaseModel):
|
|
79
|
+
"""Execution setup for a task."""
|
|
80
|
+
|
|
81
|
+
model_config = ConfigDict(
|
|
82
|
+
arbitrary_types_allowed=True,
|
|
83
|
+
extra="allow",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
traces_path: Path | None = None
|
|
87
|
+
trace_name: str | None = None
|
|
88
|
+
enable_remote_tracing: bool = False
|
|
89
|
+
app_lock_status: AppLaunchResult | None = None
|
|
90
|
+
|
|
91
|
+
def get_locked_app_package(self) -> str | None:
|
|
92
|
+
"""Get the locked app package name if app locking is enabled.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
The locked app package name, or None if app locking is not enabled.
|
|
96
|
+
"""
|
|
97
|
+
if self.app_lock_status:
|
|
98
|
+
return self.app_lock_status.locked_app_package
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class DeviceClientAccessors:
|
|
103
|
+
"""Checked accessors for the device clients held by a context model.
|
|
104
|
+
|
|
105
|
+
The inheriting model declares the ``adb_client`` and ``ui_adb_client`` fields.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
adb_client: Any
|
|
109
|
+
ui_adb_client: Any
|
|
110
|
+
|
|
111
|
+
def get_adb_client(self) -> Any:
|
|
112
|
+
if self.adb_client is None:
|
|
113
|
+
raise ValueError("No ADB client in context.")
|
|
114
|
+
return self.adb_client
|
|
115
|
+
|
|
116
|
+
def get_ui_adb_client(self) -> Any:
|
|
117
|
+
if self.ui_adb_client is None:
|
|
118
|
+
raise ValueError("No UIAutomator client in context.")
|
|
119
|
+
return self.ui_adb_client
|
|
@@ -0,0 +1,13 @@
|
|
|
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.
|
package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
from shutil import which
|
|
21
|
+
import time
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from artemis.context import ArtemisContext, DevicePlatform
|
|
25
|
+
from third_party.mobile_use.utils.logger import ArtemisLogger, get_logger
|
|
26
|
+
from third_party.mobile_use.utils.shell_utils import run_shell_command_on_host
|
|
27
|
+
|
|
28
|
+
logger = get_logger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_adb_device(ctx: ArtemisContext) -> Any:
|
|
32
|
+
"""Retrieve the ADB device instance (AdbDevice locally or RemoteAdbDevice in Cloud mode)."""
|
|
33
|
+
if ctx.adb_client is None:
|
|
34
|
+
if os.environ.get("ARTEMIS_CLOUD_MODE") == "1":
|
|
35
|
+
from cloud_service.virtualization import RemoteAdbClient
|
|
36
|
+
|
|
37
|
+
ctx.adb_client = RemoteAdbClient()
|
|
38
|
+
return ctx.adb_client.device(serial=ctx.device.device_id)
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
if ctx.device.mobile_platform != DevicePlatform.ANDROID:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
adb = ctx.get_adb_client()
|
|
45
|
+
return adb.device(serial=ctx.device.device_id)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_first_device(
|
|
49
|
+
logger: ArtemisLogger | None = None,
|
|
50
|
+
) -> tuple[str | None, DevicePlatform | None, None]:
|
|
51
|
+
"""Gets the first available device, prioritizing idle and unassigned devices."""
|
|
52
|
+
try:
|
|
53
|
+
from artemis.runtime import device_pool
|
|
54
|
+
|
|
55
|
+
chosen = device_pool.select_device()
|
|
56
|
+
if chosen:
|
|
57
|
+
return chosen, DevicePlatform.ANDROID, None
|
|
58
|
+
except Exception as exc:
|
|
59
|
+
if logger:
|
|
60
|
+
logger.debug(f"Device pool selection fallback: {exc}")
|
|
61
|
+
|
|
62
|
+
if which("adb"):
|
|
63
|
+
try:
|
|
64
|
+
android_output = run_shell_command_on_host("adb devices")
|
|
65
|
+
lines = android_output.strip().split("\n")
|
|
66
|
+
for line in lines:
|
|
67
|
+
if "device" in line and not line.startswith("List of devices"):
|
|
68
|
+
return line.split()[0], DevicePlatform.ANDROID, None
|
|
69
|
+
except RuntimeError as e:
|
|
70
|
+
if logger:
|
|
71
|
+
logger.error(f"ADB command failed: {e}")
|
|
72
|
+
|
|
73
|
+
return None, None, None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_device_date(ctx: ArtemisContext) -> str:
|
|
77
|
+
device = get_adb_device(ctx)
|
|
78
|
+
if not device:
|
|
79
|
+
return time.strftime("%Y-%m-%d %H:%M:%S")
|
|
80
|
+
try:
|
|
81
|
+
return str(device.shell("date")).strip()
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.debug(f"Failed to get device date: {e}")
|
|
84
|
+
return time.strftime("%Y-%m-%d %H:%M:%S")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def list_packages(ctx: ArtemisContext) -> str:
|
|
88
|
+
"""List installed packages dynamically from the physical/remote device."""
|
|
89
|
+
device = get_adb_device(ctx)
|
|
90
|
+
if not device:
|
|
91
|
+
return ""
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
cmd = ["pm", "list", "packages", "-f"]
|
|
95
|
+
raw_output = str(device.shell(" ".join(cmd)))
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.error(f"Failed to query package list from device: {e}")
|
|
98
|
+
return ""
|
|
99
|
+
|
|
100
|
+
lines = raw_output.strip().split("\n")
|
|
101
|
+
packages = []
|
|
102
|
+
for line in lines:
|
|
103
|
+
if "=" in line:
|
|
104
|
+
package_name = line.split("=")[-1].strip()
|
|
105
|
+
if package_name:
|
|
106
|
+
packages.append(package_name)
|
|
107
|
+
elif line.startswith("package:"):
|
|
108
|
+
package_name = line.split("package:")[-1].strip()
|
|
109
|
+
if package_name:
|
|
110
|
+
packages.append(package_name)
|
|
111
|
+
|
|
112
|
+
return "\n".join(sorted(packages))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def list_packages_async(ctx: ArtemisContext) -> str:
|
|
116
|
+
return list_packages(ctx)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_current_foreground_package(ctx: ArtemisContext) -> str | None:
|
|
120
|
+
"""Get the package name of the currently focused/foreground app dynamically."""
|
|
121
|
+
device = get_adb_device(ctx)
|
|
122
|
+
if not device:
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
app_info = device.current_app()
|
|
127
|
+
if app_info and app_info.package:
|
|
128
|
+
return app_info.package
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logger.debug(f"device.current_app failed: {e}. Falling back to dumpsys.")
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
output = str(device.shell("dumpsys window | grep mCurrentFocus"))
|
|
134
|
+
if "mCurrentFocus=" in output:
|
|
135
|
+
segment = output.split("mCurrentFocus=")[-1]
|
|
136
|
+
if "/" in segment:
|
|
137
|
+
tokens = segment.split()
|
|
138
|
+
for token in tokens:
|
|
139
|
+
if "." in token and not token.startswith("Window"):
|
|
140
|
+
package = token.split("/")[0]
|
|
141
|
+
package = package.rstrip("}")
|
|
142
|
+
if package and "." in package:
|
|
143
|
+
return package
|
|
144
|
+
except Exception as e:
|
|
145
|
+
logger.debug(f"Fallback dumpsys parsing failed: {e}")
|
|
146
|
+
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
async def get_current_foreground_package_async(ctx: ArtemisContext) -> str | None:
|
|
151
|
+
return get_current_foreground_package(ctx)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Geometry, element selector and tap result models shared by device drivers."""
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TapOutput(BaseModel):
|
|
25
|
+
"""Output from tap operations."""
|
|
26
|
+
|
|
27
|
+
error: str | None = Field(default=None, description="Error message if tap failed")
|
|
28
|
+
success: bool = Field(default=True, description="Whether the tap was successfully dispatched")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ElementQuery(BaseModel):
|
|
32
|
+
"""Selects a UI element by resource id or text; ``index`` picks among the matches."""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(frozen=True)
|
|
35
|
+
resource_id: str | None = None
|
|
36
|
+
text: str | None = None
|
|
37
|
+
index: int = 0
|
|
38
|
+
|
|
39
|
+
def describe(self) -> str:
|
|
40
|
+
return f"resource_id={self.resource_id}, text={self.text}"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CoordinatesSelectorRequest(BaseModel):
|
|
44
|
+
"""Absolute pixel coordinate pair."""
|
|
45
|
+
|
|
46
|
+
model_config = ConfigDict(extra="forbid")
|
|
47
|
+
x: int
|
|
48
|
+
y: int
|
|
49
|
+
|
|
50
|
+
def to_str(self) -> str:
|
|
51
|
+
return f"{self.x}, {self.y}"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PercentagesSelectorRequest(BaseModel):
|
|
55
|
+
"""Normalized percentage coordinate pair (0-100%)."""
|
|
56
|
+
|
|
57
|
+
model_config = ConfigDict(extra="forbid")
|
|
58
|
+
x_percent: int = Field(ge=0, le=100, description="X percentage (0-100)")
|
|
59
|
+
y_percent: int = Field(ge=0, le=100, description="Y percentage (0-100)")
|
|
60
|
+
|
|
61
|
+
def to_str(self) -> str:
|
|
62
|
+
return f"{self.x_percent}%, {self.y_percent}%"
|
|
63
|
+
|
|
64
|
+
def to_coords(self, width: int, height: int) -> CoordinatesSelectorRequest:
|
|
65
|
+
"""Convert percentages to absolute pixel coordinates."""
|
|
66
|
+
x = min(max(int(width * self.x_percent / 100), 0), max(0, width - 1))
|
|
67
|
+
y = min(max(int(height * self.y_percent / 100), 0), max(0, height - 1))
|
|
68
|
+
return CoordinatesSelectorRequest(x=x, y=y)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Bounds(BaseModel):
|
|
72
|
+
"""Bounding box defined by top-left (x1, y1) and bottom-right (x2, y2)."""
|
|
73
|
+
|
|
74
|
+
x1: int
|
|
75
|
+
y1: int
|
|
76
|
+
x2: int
|
|
77
|
+
y2: int
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def width(self) -> int:
|
|
81
|
+
return max(0, self.x2 - self.x1)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def height(self) -> int:
|
|
85
|
+
return max(0, self.y2 - self.y1)
|
|
86
|
+
|
|
87
|
+
def get_center(self) -> CoordinatesSelectorRequest:
|
|
88
|
+
"""Get the center point of the bounds."""
|
|
89
|
+
return CoordinatesSelectorRequest(
|
|
90
|
+
x=(self.x1 + self.x2) // 2,
|
|
91
|
+
y=(self.y1 + self.y2) // 2,
|
|
92
|
+
)
|