Features: - Web Dashboard: FastAPI-based dashboard with Vue.js frontend - Multi-device support (ADB, HDC, iOS) - Real-time WebSocket updates for task progress - Device management with status tracking - Task queue with execution controls (start/stop/re-execute) - Detailed task information display (thinking, actions, completion messages) - Screenshot viewing per device - LAN deployment support with configurable CORS - Callback Hooks: Interrupt and modify task execution - step_callback: Called after each step with StepResult - before_action_callback: Called before executing action - Support for task interruption and dynamic task switching - Example scripts demonstrating callback usage - Configuration: Environment-based configuration - .env file support for all settings - .env.example template with documentation - Model API configuration (base URL, model name, API key) - Dashboard configuration (host, port, CORS, device type) - Phone agent configuration (delays, max steps, language) Technical improvements: - Fixed forward reference issue with StepResult - Added package exports for callback types and configs - Enhanced dependencies with FastAPI, WebSocket support - Thread-safe task execution with device locking - Async WebSocket broadcasting from sync thread pool Co-Authored-By: Claude <noreply@anthropic.com>
176 lines
4.5 KiB
Python
176 lines
4.5 KiB
Python
"""
|
|
AutoGLM Dashboard - FastAPI Main Application.
|
|
|
|
This is the main entry point for the web dashboard.
|
|
Run with: uvicorn dashboard.main:app --host 0.0.0.0 --port 8080 --reload
|
|
"""
|
|
|
|
import os
|
|
from contextlib import asynccontextmanager
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from dotenv import load_dotenv
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import FileResponse, JSONResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
from dashboard.api import devices_router, tasks_router, websocket_router
|
|
from dashboard.config import config
|
|
from dashboard.dependencies import (
|
|
get_device_manager,
|
|
get_task_executor,
|
|
get_ws_manager,
|
|
)
|
|
from dashboard.services.device_manager import DeviceManager
|
|
from dashboard.services.task_executor import TaskExecutor
|
|
from dashboard.services.websocket_manager import WebSocketManager
|
|
|
|
# Load .env file
|
|
load_dotenv()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""Application lifespan manager.
|
|
|
|
Handles startup and shutdown events.
|
|
"""
|
|
# Startup
|
|
print("=" * 50)
|
|
print("AutoGLM Dashboard Starting...")
|
|
print("=" * 50)
|
|
|
|
# Initialize services
|
|
device_manager = get_device_manager()
|
|
task_executor = get_task_executor()
|
|
ws_manager = get_ws_manager()
|
|
|
|
# Link services
|
|
task_executor.set_ws_manager(ws_manager)
|
|
|
|
# Scan for devices on startup
|
|
print("Scanning for devices...")
|
|
try:
|
|
devices = await device_manager.refresh_devices()
|
|
print(f"Found {len(devices)} device(s)")
|
|
for device in devices:
|
|
status = "connected" if device.is_connected else "disconnected"
|
|
print(f" - {device.device_id} ({status})")
|
|
except Exception as e:
|
|
print(f"Error scanning devices: {e}")
|
|
|
|
print("=" * 50)
|
|
print(f"Dashboard running on http://{config.HOST}:{config.PORT}")
|
|
print(f"API docs: http://{config.HOST}:{config.PORT}/docs")
|
|
print("=" * 50)
|
|
|
|
yield
|
|
|
|
# Shutdown
|
|
print("Shutting down dashboard...")
|
|
|
|
|
|
# Create FastAPI app
|
|
app = FastAPI(
|
|
title="AutoGLM Dashboard",
|
|
description="Web-based multi-device control interface for AutoGLM",
|
|
version="0.1.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
# Configure CORS for LAN access
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=config.CORS_ORIGINS,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
# Exception handlers
|
|
@app.exception_handler(Exception)
|
|
async def global_exception_handler(request: Request, exc: Exception):
|
|
"""Global exception handler."""
|
|
return JSONResponse(
|
|
status_code=500,
|
|
content={"error": str(exc), "type": type(exc).__name__},
|
|
)
|
|
|
|
|
|
# Include routers
|
|
app.include_router(devices_router, prefix="/api")
|
|
app.include_router(tasks_router, prefix="/api")
|
|
app.include_router(websocket_router)
|
|
|
|
|
|
# Health check
|
|
@app.get("/health")
|
|
async def health_check():
|
|
"""Health check endpoint."""
|
|
device_manager = get_device_manager()
|
|
task_executor = get_task_executor()
|
|
ws_manager = get_ws_manager()
|
|
|
|
devices = device_manager.list_all_devices()
|
|
connected_devices = sum(1 for d in devices if d.is_connected)
|
|
|
|
return {
|
|
"status": "healthy",
|
|
"timestamp": datetime.now().isoformat(),
|
|
"devices": {
|
|
"total": len(devices),
|
|
"connected": connected_devices,
|
|
},
|
|
"tasks": {
|
|
"active": task_executor.get_active_task_count(),
|
|
},
|
|
"websocket": {
|
|
"connections": ws_manager.get_connection_count(),
|
|
},
|
|
}
|
|
|
|
|
|
# Root endpoint - serve dashboard or API info
|
|
@app.get("/")
|
|
async def root():
|
|
"""Root endpoint - returns API info or serves dashboard."""
|
|
# Check if static files exist
|
|
static_path = Path(__file__).parent / "static" / "index.html"
|
|
|
|
if static_path.exists():
|
|
return FileResponse(static_path)
|
|
|
|
# Return API info if dashboard not built
|
|
return {
|
|
"name": "AutoGLM Dashboard API",
|
|
"version": "0.1.0",
|
|
"docs": "/docs",
|
|
"endpoints": {
|
|
"devices": "/api/devices",
|
|
"tasks": "/api/tasks",
|
|
"websocket": "/ws",
|
|
"health": "/health",
|
|
},
|
|
}
|
|
|
|
|
|
# Mount static files for dashboard (if exists)
|
|
static_path = Path(__file__).parent / "static"
|
|
if static_path.exists():
|
|
app.mount("/static", StaticFiles(directory=str(static_path)), name="static")
|
|
|
|
|
|
# Run script entry point
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
uvicorn.run(
|
|
"dashboard.main:app",
|
|
host=config.HOST,
|
|
port=config.PORT,
|
|
reload=config.DEBUG,
|
|
)
|