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

@@ -0,0 +1,112 @@
import {
getStepPreImageUrl,
getStepPostImageUrl
} from './action-formatter.util';
describe('action-formatter.util screenshot chaining', () => {
it('should chain pre/post images correctly in FailureAnalyzer multi-step recovery', () => {
const stepData = {
step_id: 'step-06',
pre_image_name: '01a0e8d8cddbeb6a352e3bef1afbee0f0476a05b0fc62abb78e59c7b6aa16079',
post_image_name: '484361b1ab4b340415e01042e3496f0edc0549c837c897ece242835d12d7d71d',
action_taken: {
action: 'tap',
coordinates: [540, 720],
timestamp: 1787077538.0
},
last_execution_result: {
status: 'success',
repair_status: 'fixed',
execution: [
{
attempts: ['Pre-execution validation failed']
}
]
},
generic_tools: [
{
trace_id: 'tool-save-note',
name: 'save_note',
type: 'tool',
timestamp: 1787077537.0,
payload: { args: { title: 'Note 1' } }
},
{
trace_id: 'agent-failure-analyzer',
name: 'failure_analyzer',
agent_name: 'failure_analyzer',
type: 'agent',
timestamp: 1787077540.0,
payload: {
args: {
pre_screenshot_name: '01a0e8d8cddbeb6a352e3bef1afbee0f0476a05b0fc62abb78e59c7b6aa16079',
post_screenshot_name: 'fbdf17fa7a970d2c37aa52ae4336119e76a7d13ea457811cf8aaab19b3f53843'
}
}
},
{
trace_id: 'action-click-1',
name: 'click',
type: 'action',
agent_name: 'failure_analyzer',
timestamp: 1787077607.0,
payload: {
args: { target: '[500, 300]' },
result: {
outcome: 'Clicked successfully',
post_image_name: '18bd2ac49e64976f1e74e828ddae76e54da1587044b1d3fb01317dd202ff7c67'
}
}
},
{
trace_id: 'action-click-seq-2',
name: 'click_sequence',
type: 'action',
agent_name: 'failure_analyzer',
timestamp: 1787077631.0,
payload: {
args: { sequence: '[[500, 300], [876, 360]]' },
result: {
outcome: 'Sequence clicked successfully',
post_image_name: '484361b1ab4b340415e01042e3496f0edc0549c837c897ece242835d12d7d71d'
}
}
}
]
};
const action1 = stepData.generic_tools[2]; // click
const action2 = stepData.generic_tools[3]; // click_sequence
// Action 1: Pre should be the failed state screenshot (fbdf17fa...), Post should be Action 1's post (18bd2ac4...)
const act1Pre = getStepPreImageUrl(stepData, action1);
const act1Post = getStepPostImageUrl(stepData, action1);
expect(act1Pre).toBe('/images/fbdf17fa7a970d2c37aa52ae4336119e76a7d13ea457811cf8aaab19b3f53843');
expect(act1Post).toBe('/images/18bd2ac49e64976f1e74e828ddae76e54da1587044b1d3fb01317dd202ff7c67');
// Action 2: "下一次的决策前是上一次的动作后" -> Pre MUST be Action 1's post (18bd2ac4...), Post should be Action 2's post (484361b1...)
const act2Pre = getStepPreImageUrl(stepData, action2);
const act2Post = getStepPostImageUrl(stepData, action2);
expect(act2Pre).toBe('/images/18bd2ac49e64976f1e74e828ddae76e54da1587044b1d3fb01317dd202ff7c67');
expect(act2Post).toBe('/images/484361b1ab4b340415e01042e3496f0edc0549c837c897ece242835d12d7d71d');
});
it('should handle standard single-action step correctly', () => {
const stepData = {
step_id: 'step-01',
pre_image_name: 'pre123',
post_image_name: 'post456',
action_taken: {
action: 'tap',
coordinates: [100, 200],
timestamp: 1000
}
};
const preUrl = getStepPreImageUrl(stepData, stepData.action_taken);
const postUrl = getStepPostImageUrl(stepData, stepData.action_taken);
expect(preUrl).toBe('/images/pre123');
expect(postUrl).toBe('/images/post456');
});
});