android-midscene-automation 0.2.3 → 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 +11 -1
- package/README.md +63 -1
- 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 +10 -8
- package/server/artemis.ts +34 -0
- package/server/http-api.ts +13 -1
- package/src/App.vue +72 -23
- 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 +369 -186
- package/src/types.ts +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
"""Platform Abstraction Layer (PAL) single entry point and global singleton."""
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
from artemis.platform.base import (
|
|
20
|
+
IPlatform,
|
|
21
|
+
IPlatformPaths,
|
|
22
|
+
IPlatformProcess,
|
|
23
|
+
OSType,
|
|
24
|
+
)
|
|
25
|
+
from artemis.platform.darwin import DarwinPlatform
|
|
26
|
+
from artemis.platform.linux import LinuxPlatform
|
|
27
|
+
from artemis.platform.windows import WindowsPlatform
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _create_platform() -> IPlatform:
|
|
31
|
+
"""Factory creating the appropriate platform instance for current runtime environment."""
|
|
32
|
+
if sys.platform == "win32":
|
|
33
|
+
p = WindowsPlatform()
|
|
34
|
+
elif sys.platform == "darwin":
|
|
35
|
+
p = DarwinPlatform()
|
|
36
|
+
else:
|
|
37
|
+
p = LinuxPlatform()
|
|
38
|
+
|
|
39
|
+
# Automatically initialize UTF-8 environment
|
|
40
|
+
p.process.setup_utf8_io()
|
|
41
|
+
return p
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Global singleton PAL instance
|
|
45
|
+
platform: IPlatform = _create_platform()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_platform() -> IPlatform:
|
|
49
|
+
"""Get the active platform abstraction instance."""
|
|
50
|
+
global platform
|
|
51
|
+
return platform
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
"platform",
|
|
56
|
+
"get_platform",
|
|
57
|
+
"OSType",
|
|
58
|
+
"IPlatform",
|
|
59
|
+
"IPlatformPaths",
|
|
60
|
+
"IPlatformProcess",
|
|
61
|
+
]
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
"""Platform Abstraction Layer (PAL) - Base Protocols and Types."""
|
|
16
|
+
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Protocol
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OSType(str, Enum):
|
|
23
|
+
"""Operating System Type."""
|
|
24
|
+
|
|
25
|
+
LINUX = "linux"
|
|
26
|
+
MACOS = "darwin"
|
|
27
|
+
WINDOWS = "windows"
|
|
28
|
+
UNKNOWN = "unknown"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class IPlatformPaths(Protocol):
|
|
32
|
+
"""Standardized directory and path resolver for the target operating system."""
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def config_dir(self) -> Path:
|
|
36
|
+
"""Directory for persistent user/app configuration files."""
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def data_dir(self) -> Path:
|
|
41
|
+
"""Directory for persistent data (traces, SQLite DBs, replays)."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def cache_dir(self) -> Path:
|
|
46
|
+
"""Directory for non-essential cached data."""
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def logs_dir(self) -> Path:
|
|
51
|
+
"""Directory for persistent runtime logs."""
|
|
52
|
+
...
|
|
53
|
+
|
|
54
|
+
def temp_dir(self, subfolder: str | None = None) -> Path:
|
|
55
|
+
"""Unified temporary files directory for screenshots, recordings, and scratchpads."""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
def resolve_app_dir(self) -> Path:
|
|
59
|
+
"""Resolved central application root directory for backwards compatibility."""
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class IPlatformProcess(Protocol):
|
|
64
|
+
"""Platform-specific process management, signals, and stream handling."""
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def path_separator(self) -> str:
|
|
68
|
+
"""Separator character used for PATH and PYTHONPATH (':' on POSIX, ';' on Windows)."""
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
def terminate_process_tree(self, pid: int, timeout_seconds: float = 3.0) -> bool:
|
|
72
|
+
"""Safely and recursively terminate a process and all its child processes."""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
def setup_utf8_io(self) -> None:
|
|
76
|
+
"""Ensure standard I/O and process streams operate in UTF-8 mode."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class IPlatform(Protocol):
|
|
81
|
+
"""Unified Platform Abstraction Layer interface."""
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def os_type(self) -> OSType:
|
|
85
|
+
"""Operating system type identifier."""
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def paths(self) -> IPlatformPaths:
|
|
90
|
+
"""Platform paths resolver."""
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def process(self) -> IPlatformProcess:
|
|
95
|
+
"""Platform process and execution manager."""
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
def get_package_manager_name(self) -> str | None:
|
|
99
|
+
"""Name of the detected preferred package manager (e.g. apt, brew, winget)."""
|
|
100
|
+
...
|
|
101
|
+
|
|
102
|
+
def get_install_command(self, tool_name: str) -> str:
|
|
103
|
+
"""Generate platform-specific installation command for a missing tool."""
|
|
104
|
+
...
|
|
@@ -0,0 +1,184 @@
|
|
|
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
|
+
"""macOS Platform Implementation (Apple Filesystem Hierarchy & Homebrew Integration)."""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
import shutil
|
|
20
|
+
import signal
|
|
21
|
+
import tempfile
|
|
22
|
+
import time
|
|
23
|
+
|
|
24
|
+
from artemis.platform.base import IPlatform, IPlatformPaths, IPlatformProcess, OSType
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DarwinPlatformPaths(IPlatformPaths):
|
|
28
|
+
"""macOS paths adhering to Apple standard Library/Application Support structure."""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self._home = Path.home()
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def config_dir(self) -> Path:
|
|
35
|
+
p = self._home / "Library" / "Application Support" / "artemis"
|
|
36
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
return p
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def data_dir(self) -> Path:
|
|
41
|
+
env_dir = os.getenv("ARTEMIS_APP_DIR") or os.getenv("ANTIGRAVITY_APP_DIR")
|
|
42
|
+
if env_dir:
|
|
43
|
+
p = Path(env_dir)
|
|
44
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
return p
|
|
46
|
+
|
|
47
|
+
# Check legacy ~/.artemis or ~/.gemini/jetski directory if it exists
|
|
48
|
+
legacy_artemis = self._home / ".artemis"
|
|
49
|
+
if legacy_artemis.exists():
|
|
50
|
+
return legacy_artemis
|
|
51
|
+
|
|
52
|
+
legacy_jetski = self._home / ".gemini" / "jetski"
|
|
53
|
+
if legacy_jetski.exists():
|
|
54
|
+
return legacy_jetski
|
|
55
|
+
|
|
56
|
+
p = self._home / "Library" / "Application Support" / "artemis"
|
|
57
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
return p
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def cache_dir(self) -> Path:
|
|
62
|
+
p = self._home / "Library" / "Caches" / "artemis"
|
|
63
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
return p
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def logs_dir(self) -> Path:
|
|
68
|
+
p = self._home / "Library" / "Logs" / "artemis"
|
|
69
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
return p
|
|
71
|
+
|
|
72
|
+
def temp_dir(self, subfolder: str | None = None) -> Path:
|
|
73
|
+
base = Path(tempfile.gettempdir()) / "artemis"
|
|
74
|
+
target = base / subfolder if subfolder else base
|
|
75
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
return target
|
|
77
|
+
|
|
78
|
+
def resolve_app_dir(self) -> Path:
|
|
79
|
+
return self.data_dir
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class DarwinPlatformProcess(IPlatformProcess):
|
|
83
|
+
"""macOS process management."""
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def path_separator(self) -> str:
|
|
87
|
+
return ":"
|
|
88
|
+
|
|
89
|
+
def terminate_process_tree(self, pid: int, timeout_seconds: float = 3.0) -> bool:
|
|
90
|
+
try:
|
|
91
|
+
import psutil
|
|
92
|
+
|
|
93
|
+
parent = psutil.Process(pid)
|
|
94
|
+
children = parent.children(recursive=True)
|
|
95
|
+
for child in children:
|
|
96
|
+
try:
|
|
97
|
+
child.send_signal(signal.SIGTERM)
|
|
98
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
parent.send_signal(signal.SIGTERM)
|
|
102
|
+
|
|
103
|
+
# Avoid calling wait_procs on direct children of the current process.
|
|
104
|
+
# In POSIX, psutil.Process.wait() on a child of os.getpid() invokes
|
|
105
|
+
# os.waitpid(), which reaps the zombie and prevents asyncio / uvloop
|
|
106
|
+
# event loops from detecting that the child exited, causing proc.wait()
|
|
107
|
+
# to hang indefinitely.
|
|
108
|
+
current_pid = os.getpid()
|
|
109
|
+
external_procs = [
|
|
110
|
+
p
|
|
111
|
+
for p in children + [parent]
|
|
112
|
+
if p.pid != current_pid and getattr(p, "ppid", lambda: None)() != current_pid
|
|
113
|
+
]
|
|
114
|
+
direct_children = [
|
|
115
|
+
p for p in children + [parent] if getattr(p, "ppid", lambda: None)() == current_pid
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
if external_procs:
|
|
119
|
+
gone, alive = psutil.wait_procs(external_procs, timeout=timeout_seconds)
|
|
120
|
+
for p in alive:
|
|
121
|
+
try:
|
|
122
|
+
p.kill()
|
|
123
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
deadline = time.time() + timeout_seconds
|
|
127
|
+
for p in direct_children:
|
|
128
|
+
while time.time() < deadline:
|
|
129
|
+
try:
|
|
130
|
+
if not p.is_running() or p.status() == psutil.STATUS_ZOMBIE:
|
|
131
|
+
break
|
|
132
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
133
|
+
break
|
|
134
|
+
time.sleep(0.05)
|
|
135
|
+
else:
|
|
136
|
+
try:
|
|
137
|
+
p.kill()
|
|
138
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
return True
|
|
142
|
+
except Exception:
|
|
143
|
+
try:
|
|
144
|
+
os.kill(pid, signal.SIGKILL)
|
|
145
|
+
return True
|
|
146
|
+
except OSError:
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
def setup_utf8_io(self) -> None:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class DarwinPlatform(IPlatform):
|
|
154
|
+
"""macOS Platform Adapter."""
|
|
155
|
+
|
|
156
|
+
def __init__(self):
|
|
157
|
+
self._paths = DarwinPlatformPaths()
|
|
158
|
+
self._process = DarwinPlatformProcess()
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def os_type(self) -> OSType:
|
|
162
|
+
return OSType.MACOS
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def paths(self) -> IPlatformPaths:
|
|
166
|
+
return self._paths
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def process(self) -> IPlatformProcess:
|
|
170
|
+
return self._process
|
|
171
|
+
|
|
172
|
+
def get_package_manager_name(self) -> str | None:
|
|
173
|
+
if shutil.which("brew"):
|
|
174
|
+
return "brew"
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
def get_install_command(self, tool_name: str) -> str:
|
|
178
|
+
pkg_map = {
|
|
179
|
+
"adb": "android-platform-tools",
|
|
180
|
+
"ffmpeg": "ffmpeg",
|
|
181
|
+
"scrcpy": "scrcpy",
|
|
182
|
+
}
|
|
183
|
+
pkg = pkg_map.get(tool_name, tool_name)
|
|
184
|
+
return f"brew install {pkg}"
|
|
@@ -0,0 +1,207 @@
|
|
|
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
|
+
"""Linux Platform Implementation (XDG Base Directory Specification & POSIX Process Model)."""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
import shutil
|
|
20
|
+
import signal
|
|
21
|
+
import tempfile
|
|
22
|
+
import time
|
|
23
|
+
|
|
24
|
+
from artemis.platform.base import IPlatform, IPlatformPaths, IPlatformProcess, OSType
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LinuxPlatformPaths(IPlatformPaths):
|
|
28
|
+
"""Linux paths adhering to the XDG Base Directory specification with legacy fallbacks."""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self._home = Path.home()
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def config_dir(self) -> Path:
|
|
35
|
+
xdg_config = os.getenv("XDG_CONFIG_HOME")
|
|
36
|
+
if xdg_config:
|
|
37
|
+
p = Path(xdg_config) / "artemis"
|
|
38
|
+
else:
|
|
39
|
+
p = self._home / ".config" / "artemis"
|
|
40
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
return p
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def data_dir(self) -> Path:
|
|
45
|
+
# Check custom app dir override first
|
|
46
|
+
env_dir = os.getenv("ARTEMIS_APP_DIR") or os.getenv("ANTIGRAVITY_APP_DIR")
|
|
47
|
+
if env_dir:
|
|
48
|
+
p = Path(env_dir)
|
|
49
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
return p
|
|
51
|
+
|
|
52
|
+
xdg_data = os.getenv("XDG_DATA_HOME")
|
|
53
|
+
if xdg_data:
|
|
54
|
+
p = Path(xdg_data) / "artemis"
|
|
55
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
56
|
+
return p
|
|
57
|
+
|
|
58
|
+
# Check legacy ~/.gemini/jetski or ~/.artemis directory if it exists
|
|
59
|
+
legacy_jetski = self._home / ".gemini" / "jetski"
|
|
60
|
+
if legacy_jetski.exists():
|
|
61
|
+
return legacy_jetski
|
|
62
|
+
|
|
63
|
+
legacy_artemis = self._home / ".artemis"
|
|
64
|
+
if legacy_artemis.exists():
|
|
65
|
+
return legacy_artemis
|
|
66
|
+
|
|
67
|
+
p = self._home / ".local" / "share" / "artemis"
|
|
68
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
return p
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def cache_dir(self) -> Path:
|
|
73
|
+
xdg_cache = os.getenv("XDG_CACHE_HOME")
|
|
74
|
+
if xdg_cache:
|
|
75
|
+
p = Path(xdg_cache) / "artemis"
|
|
76
|
+
else:
|
|
77
|
+
p = self._home / ".cache" / "artemis"
|
|
78
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
return p
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def logs_dir(self) -> Path:
|
|
83
|
+
p = self.data_dir / "logs"
|
|
84
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
return p
|
|
86
|
+
|
|
87
|
+
def temp_dir(self, subfolder: str | None = None) -> Path:
|
|
88
|
+
base = Path(tempfile.gettempdir()) / "artemis"
|
|
89
|
+
target = base / subfolder if subfolder else base
|
|
90
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
return target
|
|
92
|
+
|
|
93
|
+
def resolve_app_dir(self) -> Path:
|
|
94
|
+
return self.data_dir
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class LinuxPlatformProcess(IPlatformProcess):
|
|
98
|
+
"""Linux process management using POSIX signals and process groups."""
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def path_separator(self) -> str:
|
|
102
|
+
return ":"
|
|
103
|
+
|
|
104
|
+
def terminate_process_tree(self, pid: int, timeout_seconds: float = 3.0) -> bool:
|
|
105
|
+
try:
|
|
106
|
+
import psutil
|
|
107
|
+
|
|
108
|
+
parent = psutil.Process(pid)
|
|
109
|
+
children = parent.children(recursive=True)
|
|
110
|
+
for child in children:
|
|
111
|
+
try:
|
|
112
|
+
child.send_signal(signal.SIGTERM)
|
|
113
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
parent.send_signal(signal.SIGTERM)
|
|
117
|
+
|
|
118
|
+
# Avoid calling wait_procs on direct children of the current process.
|
|
119
|
+
# In POSIX, psutil.Process.wait() on a child of os.getpid() invokes
|
|
120
|
+
# os.waitpid(), which reaps the zombie and prevents asyncio / uvloop
|
|
121
|
+
# event loops from detecting that the child exited, causing proc.wait()
|
|
122
|
+
# to hang indefinitely.
|
|
123
|
+
current_pid = os.getpid()
|
|
124
|
+
external_procs = [
|
|
125
|
+
p
|
|
126
|
+
for p in children + [parent]
|
|
127
|
+
if p.pid != current_pid and getattr(p, "ppid", lambda: None)() != current_pid
|
|
128
|
+
]
|
|
129
|
+
direct_children = [
|
|
130
|
+
p for p in children + [parent] if getattr(p, "ppid", lambda: None)() == current_pid
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
if external_procs:
|
|
134
|
+
gone, alive = psutil.wait_procs(external_procs, timeout=timeout_seconds)
|
|
135
|
+
for p in alive:
|
|
136
|
+
try:
|
|
137
|
+
p.kill()
|
|
138
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
deadline = time.time() + timeout_seconds
|
|
142
|
+
for p in direct_children:
|
|
143
|
+
while time.time() < deadline:
|
|
144
|
+
try:
|
|
145
|
+
if not p.is_running() or p.status() == psutil.STATUS_ZOMBIE:
|
|
146
|
+
break
|
|
147
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
148
|
+
break
|
|
149
|
+
time.sleep(0.05)
|
|
150
|
+
else:
|
|
151
|
+
try:
|
|
152
|
+
p.kill()
|
|
153
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
return True
|
|
157
|
+
except Exception:
|
|
158
|
+
try:
|
|
159
|
+
os.kill(pid, signal.SIGKILL)
|
|
160
|
+
return True
|
|
161
|
+
except OSError:
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
def setup_utf8_io(self) -> None:
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class LinuxPlatform(IPlatform):
|
|
169
|
+
"""Linux Platform Adapter."""
|
|
170
|
+
|
|
171
|
+
def __init__(self):
|
|
172
|
+
self._paths = LinuxPlatformPaths()
|
|
173
|
+
self._process = LinuxPlatformProcess()
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def os_type(self) -> OSType:
|
|
177
|
+
return OSType.LINUX
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def paths(self) -> IPlatformPaths:
|
|
181
|
+
return self._paths
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def process(self) -> IPlatformProcess:
|
|
185
|
+
return self._process
|
|
186
|
+
|
|
187
|
+
def get_package_manager_name(self) -> str | None:
|
|
188
|
+
for pm in ("apt-get", "dnf", "pacman", "zypper"):
|
|
189
|
+
if shutil.which(pm):
|
|
190
|
+
return pm
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
def get_install_command(self, tool_name: str) -> str:
|
|
194
|
+
pm = self.get_package_manager_name()
|
|
195
|
+
pkg_map = {
|
|
196
|
+
"adb": {"apt-get": "adb", "dnf": "android-tools", "pacman": "android-tools"},
|
|
197
|
+
"ffmpeg": {"apt-get": "ffmpeg", "dnf": "ffmpeg", "pacman": "ffmpeg"},
|
|
198
|
+
"scrcpy": {"apt-get": "scrcpy", "dnf": "scrcpy", "pacman": "scrcpy"},
|
|
199
|
+
}
|
|
200
|
+
pkg = pkg_map.get(tool_name, {}).get(pm, tool_name)
|
|
201
|
+
if pm == "apt-get":
|
|
202
|
+
return f"sudo apt-get install -y {pkg}"
|
|
203
|
+
elif pm == "dnf":
|
|
204
|
+
return f"sudo dnf install -y {pkg}"
|
|
205
|
+
elif pm == "pacman":
|
|
206
|
+
return f"sudo pacman -S --noconfirm {pkg}"
|
|
207
|
+
return f"Install {tool_name} using your system package manager."
|