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:
1 parent
78e478f502
commit
9a5aaf99b7
249 files changed
+37226
-9339
No files matched your search
@@ -583,35 +583,88 @@ def draw_action_overlay_on_image(
|
||||
|
||||
# 2. Click Sequence
|
||||
elif action in ("click_sequence", "tap_sequence"):
|
||||
sequence = action_args.get("sequence") or action_args.get("targets") or []
|
||||
sequence = (
|
||||
action_args.get("sequence")
|
||||
or action_args.get("targets")
|
||||
or action_args.get("coordinates")
|
||||
or []
|
||||
)
|
||||
if isinstance(sequence, str):
|
||||
sequence_str = sequence.strip()
|
||||
try:
|
||||
import json
|
||||
|
||||
sequence = json.loads(sequence_str)
|
||||
except Exception:
|
||||
try:
|
||||
import ast
|
||||
|
||||
sequence = ast.literal_eval(sequence_str)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if isinstance(sequence, list):
|
||||
for idx, coord in enumerate(sequence, 1):
|
||||
pts = []
|
||||
for coord in sequence:
|
||||
pt = _resolve_coordinates(coord, width, height)
|
||||
if pt:
|
||||
x, y = pt
|
||||
draw.ellipse(
|
||||
[x - dot_radius, y - dot_radius, x + dot_radius, y + dot_radius],
|
||||
fill="red",
|
||||
outline="white",
|
||||
width=line_width,
|
||||
pts.append(pt)
|
||||
|
||||
# Draw connecting lines/arrows between consecutive sequence points
|
||||
for i in range(len(pts) - 1):
|
||||
p1, p2 = pts[i], pts[i + 1]
|
||||
draw.line([p1, p2], fill="white", width=line_width + 3)
|
||||
draw.line([p1, p2], fill="red", width=line_width)
|
||||
|
||||
# Arrow head
|
||||
dx = p2[0] - p1[0]
|
||||
dy = p2[1] - p1[1]
|
||||
angle = math.atan2(dy, dx)
|
||||
arrow_len = max(16, int(base_dim * 0.025))
|
||||
arrow_angle = math.pi / 6
|
||||
x1 = p2[0] - arrow_len * math.cos(angle - arrow_angle)
|
||||
y1 = p2[1] - arrow_len * math.sin(angle - arrow_angle)
|
||||
x2 = p2[0] - arrow_len * math.cos(angle + arrow_angle)
|
||||
y2 = p2[1] - arrow_len * math.sin(angle + arrow_angle)
|
||||
draw.polygon([p2, (x1, y1), (x2, y2)], fill="red", outline="white")
|
||||
|
||||
# Draw numbered circular touch points
|
||||
for idx, (x, y) in enumerate(pts, 1):
|
||||
# Outer aura
|
||||
draw.ellipse(
|
||||
[
|
||||
x - dot_radius * 1.3,
|
||||
y - dot_radius * 1.3,
|
||||
x + dot_radius * 1.3,
|
||||
y + dot_radius * 1.3,
|
||||
],
|
||||
outline="white",
|
||||
width=line_width + 1,
|
||||
)
|
||||
# Colored circle
|
||||
draw.ellipse(
|
||||
[x - dot_radius, y - dot_radius, x + dot_radius, y + dot_radius],
|
||||
fill="red",
|
||||
outline="white",
|
||||
width=line_width,
|
||||
)
|
||||
label = str(idx)
|
||||
try:
|
||||
font = ImageFont.load_default(size=int(dot_radius * 1.2))
|
||||
draw.text(
|
||||
(x - dot_radius // 3, y - dot_radius // 2),
|
||||
label,
|
||||
fill="white",
|
||||
font=font,
|
||||
stroke_width=2,
|
||||
stroke_fill="black",
|
||||
)
|
||||
except Exception:
|
||||
draw.text(
|
||||
(x - 4, y - 6),
|
||||
label,
|
||||
fill="white",
|
||||
)
|
||||
label = str(idx)
|
||||
try:
|
||||
font = ImageFont.load_default(size=int(dot_radius * 1.2))
|
||||
draw.text(
|
||||
(x - dot_radius // 3, y - dot_radius // 2),
|
||||
label,
|
||||
fill="white",
|
||||
font=font,
|
||||
stroke_width=2,
|
||||
stroke_fill="black",
|
||||
)
|
||||
except Exception:
|
||||
draw.text(
|
||||
(x - 4, y - 6),
|
||||
label,
|
||||
fill="white",
|
||||
)
|
||||
|
||||
# 3. Swipe / Drag / Scroll
|
||||
elif action in ("swipe", "drag", "scroll"):
|
||||
|
||||
Reference in new issue
Block a user