android-midscene-automation 0.2.4 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +159 -29
- 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,193 @@
|
|
|
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
|
+
"""Windows Platform Implementation (KnownFolders/AppData, Win32 Process Tree & UTF-8 Injection)."""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
import shutil
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
import tempfile
|
|
23
|
+
|
|
24
|
+
from artemis.platform.base import IPlatform, IPlatformPaths, IPlatformProcess, OSType
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class WindowsPlatformPaths(IPlatformPaths):
|
|
28
|
+
"""Windows paths adhering to KnownFolders (%APPDATA% / %LOCALAPPDATA%)."""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self._home = Path.home()
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def config_dir(self) -> Path:
|
|
35
|
+
appdata = os.getenv("APPDATA")
|
|
36
|
+
if appdata:
|
|
37
|
+
p = Path(appdata) / "Artemis"
|
|
38
|
+
else:
|
|
39
|
+
p = self._home / "AppData" / "Roaming" / "Artemis"
|
|
40
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
return p
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def data_dir(self) -> Path:
|
|
45
|
+
env_dir = os.getenv("ARTEMIS_APP_DIR") or os.getenv("ANTIGRAVITY_APP_DIR")
|
|
46
|
+
if env_dir:
|
|
47
|
+
p = Path(env_dir)
|
|
48
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
return p
|
|
50
|
+
|
|
51
|
+
local_appdata = os.getenv("LOCALAPPDATA")
|
|
52
|
+
if local_appdata:
|
|
53
|
+
p = Path(local_appdata) / "Artemis"
|
|
54
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
55
|
+
return p
|
|
56
|
+
|
|
57
|
+
legacy_jetski = self._home / ".gemini" / "jetski"
|
|
58
|
+
if legacy_jetski.exists():
|
|
59
|
+
return legacy_jetski
|
|
60
|
+
|
|
61
|
+
legacy_artemis = self._home / ".artemis"
|
|
62
|
+
if legacy_artemis.exists():
|
|
63
|
+
return legacy_artemis
|
|
64
|
+
|
|
65
|
+
p = self._home / "AppData" / "Local" / "Artemis"
|
|
66
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
67
|
+
return p
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def cache_dir(self) -> Path:
|
|
71
|
+
p = self.data_dir / "cache"
|
|
72
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
return p
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def logs_dir(self) -> Path:
|
|
77
|
+
p = self.data_dir / "logs"
|
|
78
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
return p
|
|
80
|
+
|
|
81
|
+
def temp_dir(self, subfolder: str | None = None) -> Path:
|
|
82
|
+
base = Path(tempfile.gettempdir()) / "artemis"
|
|
83
|
+
target = base / subfolder if subfolder else base
|
|
84
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
return target
|
|
86
|
+
|
|
87
|
+
def resolve_app_dir(self) -> Path:
|
|
88
|
+
return self.data_dir
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class WindowsPlatformProcess(IPlatformProcess):
|
|
92
|
+
"""Windows process management with tree termination and UTF-8 console streams."""
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def path_separator(self) -> str:
|
|
96
|
+
return ";"
|
|
97
|
+
|
|
98
|
+
def terminate_process_tree(self, pid: int, timeout_seconds: float = 3.0) -> bool:
|
|
99
|
+
# 1. Try taskkill /F /T /PID on Windows (fastest and cleanest for process trees)
|
|
100
|
+
try:
|
|
101
|
+
res = subprocess.run(
|
|
102
|
+
["taskkill", "/F", "/T", "/PID", str(pid)],
|
|
103
|
+
stdout=subprocess.DEVNULL,
|
|
104
|
+
stderr=subprocess.DEVNULL,
|
|
105
|
+
check=False,
|
|
106
|
+
timeout=timeout_seconds,
|
|
107
|
+
)
|
|
108
|
+
if res.returncode == 0:
|
|
109
|
+
return True
|
|
110
|
+
except (OSError, subprocess.SubprocessError):
|
|
111
|
+
# taskkill missing, not runnable, or timed out; try psutil below.
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
# 2. Fallback to psutil
|
|
115
|
+
try:
|
|
116
|
+
import psutil
|
|
117
|
+
|
|
118
|
+
parent = psutil.Process(pid)
|
|
119
|
+
children = parent.children(recursive=True)
|
|
120
|
+
for child in children:
|
|
121
|
+
try:
|
|
122
|
+
child.kill()
|
|
123
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
124
|
+
pass
|
|
125
|
+
parent.kill()
|
|
126
|
+
return True
|
|
127
|
+
except Exception:
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
def setup_utf8_io(self) -> None:
|
|
131
|
+
"""Configures Windows stdout/stderr to UTF-8 mode to prevent encoding crashes."""
|
|
132
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
133
|
+
try:
|
|
134
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
135
|
+
except (OSError, ValueError):
|
|
136
|
+
# Stream is detached/closed or does not support reconfigure.
|
|
137
|
+
pass
|
|
138
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
139
|
+
try:
|
|
140
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
141
|
+
except (OSError, ValueError):
|
|
142
|
+
# Stream is detached/closed or does not support reconfigure.
|
|
143
|
+
pass
|
|
144
|
+
os.environ["PYTHONUTF8"] = "1"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class WindowsPlatform(IPlatform):
|
|
148
|
+
"""Windows Platform Adapter."""
|
|
149
|
+
|
|
150
|
+
def __init__(self):
|
|
151
|
+
self._paths = WindowsPlatformPaths()
|
|
152
|
+
self._process = WindowsPlatformProcess()
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def os_type(self) -> OSType:
|
|
156
|
+
return OSType.WINDOWS
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def paths(self) -> IPlatformPaths:
|
|
160
|
+
return self._paths
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def process(self) -> IPlatformProcess:
|
|
164
|
+
return self._process
|
|
165
|
+
|
|
166
|
+
def get_package_manager_name(self) -> str | None:
|
|
167
|
+
for pm in ("winget", "choco", "scoop"):
|
|
168
|
+
if shutil.which(pm):
|
|
169
|
+
return pm
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
def get_install_command(self, tool_name: str) -> str:
|
|
173
|
+
pm = self.get_package_manager_name() or "winget"
|
|
174
|
+
pkg_map = {
|
|
175
|
+
"adb": {
|
|
176
|
+
"winget": "winget install Google.PlatformTools",
|
|
177
|
+
"choco": "choco install adb",
|
|
178
|
+
"scoop": "scoop install adb",
|
|
179
|
+
},
|
|
180
|
+
"ffmpeg": {
|
|
181
|
+
"winget": "winget install Gyan.FFmpeg",
|
|
182
|
+
"choco": "choco install ffmpeg",
|
|
183
|
+
"scoop": "scoop install ffmpeg",
|
|
184
|
+
},
|
|
185
|
+
"scrcpy": {
|
|
186
|
+
"winget": "winget install Genymobile.scrcpy",
|
|
187
|
+
"choco": "choco install scrcpy",
|
|
188
|
+
"scoop": "scoop install scrcpy",
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
if tool_name in pkg_map and pm in pkg_map[tool_name]:
|
|
192
|
+
return pkg_map[tool_name][pm]
|
|
193
|
+
return f"winget install {tool_name} (or download official installer)"
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
"""Access to immutable defaults and web assets shipped with Artemis.
|
|
16
|
+
|
|
17
|
+
Python wheels are installed as ordinary directories by pip, so callers that
|
|
18
|
+
need to hand a path to another library (for example FastAPI's ``FileResponse``)
|
|
19
|
+
can safely use the paths returned here. Source checkouts continue to prefer
|
|
20
|
+
their editable ``config`` and ``apps/showcase_ui/dist`` trees; these resources
|
|
21
|
+
are the installation fallback.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from importlib.resources import files
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _resource_path(*parts: str) -> Path:
|
|
29
|
+
"""Return a filesystem path inside the installed resource package."""
|
|
30
|
+
resource = files(__name__).joinpath(*parts)
|
|
31
|
+
return Path(str(resource))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_bundled_config_path(filename: str) -> Path | None:
|
|
35
|
+
"""Return a bundled configuration template when it exists."""
|
|
36
|
+
candidate = _resource_path("config", filename)
|
|
37
|
+
return candidate if candidate.is_file() else None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_bundled_showcase_dist() -> Path | None:
|
|
41
|
+
"""Return the bundled Angular browser build when it is complete."""
|
|
42
|
+
candidate = _resource_path("showcase_ui")
|
|
43
|
+
return candidate if (candidate / "index.html").is_file() else None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
__all__ = ["get_bundled_config_path", "get_bundled_showcase_dist"]
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
"""Artemis cross-platform runtime and process supervisor subsystem."""
|
|
16
|
+
|
|
17
|
+
from artemis.runtime.device_lock import (
|
|
18
|
+
ConcurrencyMode,
|
|
19
|
+
DeviceBusyError,
|
|
20
|
+
DeviceExecutionLock,
|
|
21
|
+
)
|
|
22
|
+
from artemis.runtime.adb_endpoint import (
|
|
23
|
+
ADB_ENDPOINT_ID_ENV,
|
|
24
|
+
AdbEndpoint,
|
|
25
|
+
AdbSession,
|
|
26
|
+
AdbTarget,
|
|
27
|
+
InvalidAdbEndpoint,
|
|
28
|
+
current_adb_endpoint,
|
|
29
|
+
)
|
|
30
|
+
from artemis.runtime.device_pool import DevicePool, DeviceStatus, device_pool
|
|
31
|
+
from artemis.runtime.process_probe import pid_is_alive
|
|
32
|
+
from artemis.runtime.daemon_client import (
|
|
33
|
+
ensure_daemon_running,
|
|
34
|
+
get_daemon_session,
|
|
35
|
+
get_daemon_status,
|
|
36
|
+
is_artemis_daemon,
|
|
37
|
+
is_daemon_running,
|
|
38
|
+
stop_task_on_daemon,
|
|
39
|
+
submit_batch_to_daemon,
|
|
40
|
+
submit_task_to_daemon,
|
|
41
|
+
wait_for_daemon_task,
|
|
42
|
+
)
|
|
43
|
+
from artemis.runtime.awake_service import (
|
|
44
|
+
ensure_device_awake,
|
|
45
|
+
sanitize_device_state,
|
|
46
|
+
screen_awake_service,
|
|
47
|
+
shutdown_awake_service,
|
|
48
|
+
start_awake_service,
|
|
49
|
+
)
|
|
50
|
+
from artemis.runtime.server_lifecycle import (
|
|
51
|
+
clear_server_info,
|
|
52
|
+
find_server_pids,
|
|
53
|
+
get_server_status,
|
|
54
|
+
is_port_in_use,
|
|
55
|
+
read_server_info,
|
|
56
|
+
stop_server,
|
|
57
|
+
write_server_info,
|
|
58
|
+
)
|
|
59
|
+
from artemis.runtime.supervisor import ProcessSupervisor, process_supervisor
|
|
60
|
+
from artemis.runtime.cancel_requests import (
|
|
61
|
+
clear_cancel_request,
|
|
62
|
+
is_cancel_requested,
|
|
63
|
+
request_cancel,
|
|
64
|
+
watch_for_cancel_request,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
__all__ = [
|
|
68
|
+
"ADB_ENDPOINT_ID_ENV",
|
|
69
|
+
"AdbEndpoint",
|
|
70
|
+
"AdbSession",
|
|
71
|
+
"AdbTarget",
|
|
72
|
+
"ConcurrencyMode",
|
|
73
|
+
"DeviceBusyError",
|
|
74
|
+
"DeviceExecutionLock",
|
|
75
|
+
"DevicePool",
|
|
76
|
+
"DeviceStatus",
|
|
77
|
+
"InvalidAdbEndpoint",
|
|
78
|
+
"clear_cancel_request",
|
|
79
|
+
"clear_server_info",
|
|
80
|
+
"current_adb_endpoint",
|
|
81
|
+
"device_pool",
|
|
82
|
+
"ensure_daemon_running",
|
|
83
|
+
"ensure_device_awake",
|
|
84
|
+
"find_server_pids",
|
|
85
|
+
"get_daemon_session",
|
|
86
|
+
"get_daemon_status",
|
|
87
|
+
"get_server_status",
|
|
88
|
+
"is_artemis_daemon",
|
|
89
|
+
"is_daemon_running",
|
|
90
|
+
"is_cancel_requested",
|
|
91
|
+
"is_port_in_use",
|
|
92
|
+
"pid_is_alive",
|
|
93
|
+
"ProcessSupervisor",
|
|
94
|
+
"process_supervisor",
|
|
95
|
+
"read_server_info",
|
|
96
|
+
"request_cancel",
|
|
97
|
+
"sanitize_device_state",
|
|
98
|
+
"screen_awake_service",
|
|
99
|
+
"shutdown_awake_service",
|
|
100
|
+
"start_awake_service",
|
|
101
|
+
"stop_server",
|
|
102
|
+
"stop_task_on_daemon",
|
|
103
|
+
"submit_batch_to_daemon",
|
|
104
|
+
"submit_task_to_daemon",
|
|
105
|
+
"wait_for_daemon_task",
|
|
106
|
+
"watch_for_cancel_request",
|
|
107
|
+
"write_server_info",
|
|
108
|
+
]
|
|
@@ -0,0 +1,173 @@
|
|
|
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
|
+
"""Immutable ADB endpoint and task target primitives.
|
|
16
|
+
|
|
17
|
+
The selected endpoint is a user preference. An :class:`AdbTarget` is an
|
|
18
|
+
execution snapshot. Keeping those concepts separate prevents a queued or
|
|
19
|
+
running task from silently moving to another ADB server when the preference
|
|
20
|
+
changes in the Admin Console.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from dataclasses import asdict, dataclass
|
|
26
|
+
import os
|
|
27
|
+
import re
|
|
28
|
+
import subprocess
|
|
29
|
+
from typing import Any, Mapping, MutableMapping, Sequence
|
|
30
|
+
|
|
31
|
+
from artemis.config import settings
|
|
32
|
+
from artemis.config.constants import DEFAULT_ADB_HOST, DEFAULT_ADB_PORT
|
|
33
|
+
from artemis.toolchain import toolchain
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
ADB_ENDPOINT_ID_ENV = "ARTEMIS_ADB_ENDPOINT_ID"
|
|
37
|
+
_LOCAL_HOSTS = frozenset({"127.0.0.1", "localhost", "::1"})
|
|
38
|
+
_SAFE_HOST_PATTERN = re.compile(r"^[A-Za-z0-9._:\-\[\]]+$")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class InvalidAdbEndpoint(ValueError):
|
|
42
|
+
"""Raised when an ADB server endpoint is malformed."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
46
|
+
class AdbEndpoint:
|
|
47
|
+
"""Network address of one ADB server."""
|
|
48
|
+
|
|
49
|
+
host: str
|
|
50
|
+
port: int
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def create(cls, host: str, port: int) -> AdbEndpoint:
|
|
54
|
+
clean_host = str(host).strip()
|
|
55
|
+
if clean_host.startswith("[") and clean_host.endswith("]"):
|
|
56
|
+
clean_host = clean_host[1:-1]
|
|
57
|
+
if not clean_host:
|
|
58
|
+
raise InvalidAdbEndpoint("ADB server host cannot be empty.")
|
|
59
|
+
if len(clean_host) > 253 or not _SAFE_HOST_PATTERN.fullmatch(clean_host):
|
|
60
|
+
raise InvalidAdbEndpoint(
|
|
61
|
+
"Enter a valid IP address or host name without a URL scheme or path."
|
|
62
|
+
)
|
|
63
|
+
if not 1 <= int(port) <= 65535:
|
|
64
|
+
raise InvalidAdbEndpoint("ADB server port must be between 1 and 65535.")
|
|
65
|
+
return cls(host=clean_host, port=int(port))
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def from_mapping(cls, value: Mapping[str, Any]) -> AdbEndpoint:
|
|
69
|
+
return cls.create(str(value.get("host", "")), int(value.get("port", 0)))
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def local(cls) -> AdbEndpoint:
|
|
73
|
+
return cls(DEFAULT_ADB_HOST, DEFAULT_ADB_PORT)
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def socket(self) -> str:
|
|
77
|
+
host = self.host
|
|
78
|
+
if ":" in host and not host.startswith("["):
|
|
79
|
+
host = f"[{host}]"
|
|
80
|
+
return f"tcp:{host}:{self.port}"
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def identity(self) -> str:
|
|
84
|
+
"""Stable identity used for task snapshots and device-lock scoping."""
|
|
85
|
+
return self.socket.lower()
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def is_local_default(self) -> bool:
|
|
89
|
+
return self.host.lower() in _LOCAL_HOSTS and self.port == DEFAULT_ADB_PORT
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def mode(self) -> str:
|
|
93
|
+
return "local" if self.is_local_default else "remote"
|
|
94
|
+
|
|
95
|
+
def to_dict(self) -> dict[str, Any]:
|
|
96
|
+
return {
|
|
97
|
+
**asdict(self),
|
|
98
|
+
"socket": self.socket,
|
|
99
|
+
"identity": self.identity,
|
|
100
|
+
"mode": self.mode,
|
|
101
|
+
"is_local_default": self.is_local_default,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
def apply_to_environment(
|
|
105
|
+
self,
|
|
106
|
+
environment: MutableMapping[str, str] | None = None,
|
|
107
|
+
) -> MutableMapping[str, str]:
|
|
108
|
+
target = environment if environment is not None else os.environ
|
|
109
|
+
target["ADB_HOST"] = self.host
|
|
110
|
+
target["ADB_PORT"] = str(self.port)
|
|
111
|
+
target["ADB_SERVER_SOCKET"] = self.socket
|
|
112
|
+
target[ADB_ENDPOINT_ID_ENV] = self.identity
|
|
113
|
+
return target
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass(frozen=True, slots=True)
|
|
117
|
+
class AdbTarget:
|
|
118
|
+
"""A device serial bound to the ADB endpoint that discovered it."""
|
|
119
|
+
|
|
120
|
+
endpoint: AdbEndpoint
|
|
121
|
+
serial: str | None = None
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def lock_scope(self) -> str:
|
|
125
|
+
return self.endpoint.identity
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def lock_key(self) -> str:
|
|
129
|
+
return f"{self.lock_scope}/{self.serial or 'pending'}"
|
|
130
|
+
|
|
131
|
+
def to_dict(self) -> dict[str, Any]:
|
|
132
|
+
return {"endpoint": self.endpoint.to_dict(), "serial": self.serial}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class AdbSession:
|
|
136
|
+
"""Execute ADB commands against one explicit, immutable endpoint."""
|
|
137
|
+
|
|
138
|
+
def __init__(self, endpoint: AdbEndpoint, adb_path: str | None = None) -> None:
|
|
139
|
+
self.endpoint = endpoint
|
|
140
|
+
self.adb_path = adb_path or toolchain.resolve("adb")
|
|
141
|
+
|
|
142
|
+
def command(self, arguments: Sequence[str]) -> list[str]:
|
|
143
|
+
if not self.adb_path:
|
|
144
|
+
raise FileNotFoundError("Android Platform Tools (adb) could not be found.")
|
|
145
|
+
return [
|
|
146
|
+
self.adb_path,
|
|
147
|
+
"-H",
|
|
148
|
+
self.endpoint.host,
|
|
149
|
+
"-P",
|
|
150
|
+
str(self.endpoint.port),
|
|
151
|
+
*arguments,
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
def environment(self, base: Mapping[str, str] | None = None) -> dict[str, str]:
|
|
155
|
+
environment = dict(base if base is not None else os.environ)
|
|
156
|
+
self.endpoint.apply_to_environment(environment)
|
|
157
|
+
return environment
|
|
158
|
+
|
|
159
|
+
def run(self, arguments: Sequence[str], **kwargs: Any) -> subprocess.CompletedProcess:
|
|
160
|
+
kwargs.setdefault("env", self.environment())
|
|
161
|
+
return subprocess.run(self.command(arguments), **kwargs)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def current_adb_endpoint() -> AdbEndpoint:
|
|
165
|
+
"""Return the process preference as an immutable endpoint snapshot."""
|
|
166
|
+
host = settings.ADB_HOST or os.environ.get("ADB_HOST") or DEFAULT_ADB_HOST
|
|
167
|
+
port = settings.ADB_PORT or os.environ.get("ADB_PORT") or DEFAULT_ADB_PORT
|
|
168
|
+
return AdbEndpoint.create(str(host), int(port))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def adb_command(arguments: Sequence[str]) -> list[str]:
|
|
172
|
+
"""Resolve the ADB binary and add the configured server's host and port."""
|
|
173
|
+
return AdbSession(current_adb_endpoint()).command(arguments)
|