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:
somew1nd committed 2026-08-26 15:53:14 -07:00
1 parent a36a66ce6d
commit c2c90a6378
84 files changed
+5612 -645

No files matched your search

+93
View File
@@ -360,3 +360,96 @@ async def get_model_config_and_env():
"env_filename": ".env",
"env_vars": env_vars,
}
@router.get("/server-status")
async def get_server_runtime_status():
"""Retrieve runtime status, PID, port, and uptime of the Artemis server."""
import os
from artemis.runtime.server_lifecycle import get_server_status
try:
from apps.admin_console.core.state import state
except ImportError:
from admin_console.core.state import state
port = getattr(state, "port", 8000)
status = get_server_status(port=port)
status["current_pid"] = os.getpid()
return status
@router.post("/restart")
async def restart_server_endpoint():
"""Request a graceful restart of the Artemis server from thin clients/UI."""
import asyncio
import os
import sys
import threading
try:
from apps.admin_console.core.state import state
except ImportError:
from admin_console.core.state import state
port = getattr(state, "port", 8000)
current_pid = os.getpid()
def _restart_worker():
import time
time.sleep(0.6)
if sys.platform != "win32":
try:
os.execv(sys.executable, [sys.executable] + sys.argv)
except Exception:
import subprocess
subprocess.Popen([sys.executable] + sys.argv)
os._exit(0)
else:
import subprocess
subprocess.Popen([sys.executable] + sys.argv)
os._exit(0)
threading.Thread(target=_restart_worker, daemon=True).start()
return {
"status": "restarting",
"message": "Artemis server is restarting. Client reconnection should occur in 2-3 seconds.",
"previous_pid": current_pid,
"port": port,
}
@router.post("/shutdown")
async def shutdown_server_endpoint():
"""Request a graceful shutdown of the Artemis server."""
import os
import signal
import threading
try:
from apps.admin_console.core.state import state
except ImportError:
from admin_console.core.state import state
current_pid = os.getpid()
def _shutdown_worker():
import time
time.sleep(0.5)
try:
os.kill(current_pid, signal.SIGTERM)
except Exception:
os._exit(0)
threading.Thread(target=_shutdown_worker, daemon=True).start()
return {
"status": "shutting_down",
"message": "Artemis server is shutting down.",
"pid": current_pid,
}