feat: introduce MCP server, cross-platform runtime, and Showcase UI reorganization

- Showcase UI Reorganization:
  * Move web UI dashboard from root directory into apps/showcase_ui/ for cleaner project structure
  * Add 'artemis ui' CLI command (artemis/interfaces/cli/commands/ui.py) and launcher scripts for the interactive showcase console
- Standalone MCP Server & Notifiers (mcp_server/):
  * Implement dedicated MCP server package with mobile automation tools (device_state, inspect_trace, task_manager, task_runner)
  * Add multi-channel notifiers (AgentAPI, Desktop notifications, Webhooks, File output, and Composite routing)
  * Introduce background task runner and trace store for autonomous mobile execution monitoring
- Cross-Platform Runtime & Toolchain Support:
  * Add artemis/platform/ with OS-specific implementations for Linux, macOS (Darwin), and Windows
  * Add artemis/runtime/supervisor.py for resilient process and agent runtime supervision
  * Add artemis/toolchain/ with dependency resolution and SDK descriptors
  * Add cross-platform launcher scripts (start.sh, start.bat) and installer scripts (scripts/install_deps.*)
- Core Enhancements & Diagnostics:
  * Enhance coordinate normalization, OCR API fallback, and perception pipeline
  * Add credential validation (credentials_validator.py) and system diagnostics
  * Update bilingual documentation (README.md, README_CN.md) and architecture diagrams
- Test Suite Expansion:
  * Add integration tests for cloud gateway, remote ADB, and Artemis login
  * Add unit tests covering core diagnostics, platform detection, toolchain, credentials validator, optional OCR, and MCP tools
This commit is contained in:
somew1nd committed 2026-08-18 13:59:27 -07:00
1 parent 78e478f502
commit 9a5aaf99b7
249 files changed
+37226 -9339

No files matched your search

+16 -13
View File
@@ -22,7 +22,9 @@ from typing import Any, Literal
from adbutils import AdbClient, AdbDevice
from artemis.clients.ui_automator_client import UIAutomatorClient
from artemis.config.paths import get_temp_dir
from artemis.drivers.base import BaseDeviceDriver, KeyCode, ScreenData, SwipeDirection
from artemis.toolchain import find_ffmpeg, find_scrcpy
from artemis.utils.logger import get_logger
logger = get_logger(__name__)
@@ -231,7 +233,7 @@ class AndroidAdbDriver(BaseDeviceDriver):
start_y: int,
end_x: int,
end_y: int,
duration_ms: int = 400,
duration_ms: int = 800,
) -> bool:
try:
cmd = f"input swipe {start_x} {start_y} {end_x} {end_y} {duration_ms}"
@@ -245,31 +247,33 @@ class AndroidAdbDriver(BaseDeviceDriver):
async def swipe_direction(
self,
direction: SwipeDirection | Literal["up", "down", "left", "right"],
duration_ms: int = 400,
duration_ms: int = 800,
) -> bool:
dir_str = str(direction).lower()
mid_x = self._width // 2
mid_x = int(
self._width * 0.6
) # Lock to 60% width to avoid edge gestures and alphabet fast-scroll sidebar
mid_y = self._height // 2
if dir_str == "up":
# Drag bottom to top -> scrolls down
# Drag bottom to top -> scrolls down (0.7 -> 0.3 leaves ~50-60% overlap, 800ms prevents fling)
return await self.swipe(
mid_x, int(self._height * 0.75), mid_x, int(self._height * 0.25), duration_ms
mid_x, int(self._height * 0.7), mid_x, int(self._height * 0.3), duration_ms
)
elif dir_str == "down":
# Drag top to bottom -> scrolls up
return await self.swipe(
mid_x, int(self._height * 0.25), mid_x, int(self._height * 0.75), duration_ms
mid_x, int(self._height * 0.3), mid_x, int(self._height * 0.7), duration_ms
)
elif dir_str == "left":
# Drag right to left -> scrolls right
return await self.swipe(
int(self._width * 0.8), mid_y, int(self._width * 0.2), mid_y, duration_ms
int(self._width * 0.75), mid_y, int(self._width * 0.25), mid_y, duration_ms
)
elif dir_str == "right":
# Drag left to right -> scrolls left
return await self.swipe(
int(self._width * 0.2), mid_y, int(self._width * 0.8), mid_y, duration_ms
int(self._width * 0.25), mid_y, int(self._width * 0.75), mid_y, duration_ms
)
return False
@@ -407,14 +411,15 @@ class AndroidAdbDriver(BaseDeviceDriver):
async def start_video_recording(self, output_dir: Path | None = None) -> None:
"""Starts screen recording via scrcpy in background."""
out_dir = output_dir or Path("/tmp")
out_dir = output_dir or get_temp_dir("recordings")
out_dir.mkdir(parents=True, exist_ok=True)
self._recording_mkv_path = out_dir / "recording.mkv"
self._recording_output_path = out_dir / "recording.mp4"
logger.info(f"Starting scrcpy video recording to {self._recording_mkv_path}...")
try:
scrcpy_bin = find_scrcpy()
cmd = [
"scrcpy",
scrcpy_bin,
"--serial",
self.device_id,
"--no-window",
@@ -449,10 +454,8 @@ class AndroidAdbDriver(BaseDeviceDriver):
if mkv and mkv.exists() and mp4:
try:
from artemis.utils.video import get_ffmpeg_path
proc = await asyncio.create_subprocess_exec(
get_ffmpeg_path(),
find_ffmpeg(),
"-y",
"-i",
str(mkv),
+3 -3
View File
@@ -130,7 +130,7 @@ class BaseDeviceDriver(ABC):
start_y: int,
end_x: int,
end_y: int,
duration_ms: int = 400,
duration_ms: int = 800,
) -> bool:
"""Performs a drag/swipe gesture from start to end pixel coordinates."""
...
@@ -139,7 +139,7 @@ class BaseDeviceDriver(ABC):
async def swipe_direction(
self,
direction: SwipeDirection | Literal["up", "down", "left", "right"],
duration_ms: int = 400,
duration_ms: int = 800,
) -> bool:
"""Performs standard cardinal scrolling gesture across device viewport."""
...
@@ -210,7 +210,7 @@ class BaseDeviceDriver(ABC):
self,
start_norm: list[int],
end_norm: list[int],
duration_ms: int = 400,
duration_ms: int = 800,
) -> bool:
"""Swipes between normalized (0-1000 scale) screen coordinates."""
width, height = self.screen_size