feat: add multi-device concurrency, server lifecycle CLI, and unified execution stream
- Implement DevicePool and DeviceExecutionLock concurrency modes (per-device FIFO vs global serialization) - Extend ArtemisClient SDK and CLI with explicit device targeting and concurrency strategy configuration - Add server lifecycle management (artemis restart, stop, status) with cross-platform PID discovery - Update MCP server tools to support multi-device dispatch, trace tagging, and per-device execution - Enhance Showcase UI with step-by-step visual replay, playback speed controls, and device serial badges - Add comprehensive unit test coverage and edge-case verification scripts
This commit is contained in:
1 parent
a36a66ce6d
commit
c2c90a6378
84 files changed
+5612
-645
No files matched your search
@@ -253,9 +253,11 @@ export class AgentStreamComponent implements AfterViewInit {
|
||||
return list.sort((a, b) => {
|
||||
const statusA = this.getTaskStatus(a);
|
||||
const statusB = this.getTaskStatus(b);
|
||||
if (statusA === 'running' || statusA === 'paused') return -1;
|
||||
if (statusB === 'running' || statusB === 'paused') return 1;
|
||||
return a.start_time - b.start_time;
|
||||
const isRunA = statusA === 'running' || statusA === 'paused';
|
||||
const isRunB = statusB === 'running' || statusB === 'paused';
|
||||
if (isRunA && !isRunB) return -1;
|
||||
if (!isRunA && isRunB) return 1;
|
||||
return (a.start_time || 0) - (b.start_time || 0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -598,18 +600,43 @@ export class AgentStreamComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
public getTaskStatus(session: Session): 'running' | 'paused' | 'completed' | 'pending' | 'failed' | 'cancelled' {
|
||||
if (session.session_id === this.agentService.runningSessionId() && (this.agentService.agentStatus() === 'running' || this.agentService.agentStatus() === 'paused')) {
|
||||
return this.agentService.agentStatus() as 'running' | 'paused';
|
||||
}
|
||||
if (session.status) {
|
||||
const s = session.status.toLowerCase();
|
||||
if (s === 'running' || s === 'paused' || s === 'completed' || s === 'pending' || s === 'failed' || s === 'cancelled') {
|
||||
if (s === 'completed' || s === 'success' || s === 'failed' || s === 'cancelled') {
|
||||
return (s === 'success' ? 'completed' : s) as any;
|
||||
}
|
||||
if (s === 'running' || s === 'paused' || s === 'pending') {
|
||||
return s as any;
|
||||
}
|
||||
}
|
||||
if (session.session_id === this.agentService.runningSessionId() && (this.agentService.agentStatus() === 'running' || this.agentService.agentStatus() === 'paused')) {
|
||||
return this.agentService.agentStatus() as 'running' | 'paused';
|
||||
}
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the device serial number for the session
|
||||
*/
|
||||
public getDeviceSerial(session: Session): string | null {
|
||||
const serial = session.device_serial || session.device_id;
|
||||
if (serial && serial !== 'pending' && serial !== 'null' && serial !== 'undefined') {
|
||||
return serial;
|
||||
}
|
||||
if (session.device_info) {
|
||||
try {
|
||||
const info = typeof session.device_info === 'string' ? JSON.parse(session.device_info) : session.device_info;
|
||||
const s = info?.device_id || info?.device_serial;
|
||||
if (s && s !== 'pending' && s !== 'null' && s !== 'undefined') {
|
||||
return s;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public selectTask(sessionId: string, event?: Event): void {
|
||||
if (event) event.stopPropagation();
|
||||
this.agentService.selectSession(sessionId, true);
|
||||
|
||||
Reference in new issue
Block a user