feat: add interactive terminal and dev server management

Add new features for interactive terminal sessions and dev server control:

Terminal Component:
- New Terminal.tsx component using xterm.js for full terminal emulation
- WebSocket-based PTY communication with bidirectional I/O
- Cross-platform support (Windows via winpty, Unix via built-in pty)
- Auto-reconnection with exponential backoff
- Fix duplicate WebSocket connection bug by checking CONNECTING state
- Add manual close flag to prevent auto-reconnect race conditions
- Add project tracking to avoid duplicate connects on initial activation

Dev Server Management:
- New DevServerControl.tsx for starting/stopping dev servers
- DevServerManager service for subprocess management
- WebSocket streaming of dev server output
- Project configuration service for reading package.json scripts

Backend Infrastructure:
- Terminal router with WebSocket endpoint for PTY I/O
- DevServer router for server lifecycle management
- Terminal session manager with callback-based output streaming
- Enhanced WebSocket schemas for terminal and dev server messages

UI Integration:
- New Terminal and Dev Server tabs in the main application
- Updated DebugLogViewer with improved UI and functionality
- Extended useWebSocket hook for terminal message handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-12 10:35:36 +02:00
parent b1473cdfb9
commit c1985eb285
22 changed files with 3360 additions and 66 deletions

View File

@@ -2,7 +2,7 @@
WebSocket Handlers
==================
Real-time updates for project progress and agent output.
Real-time updates for project progress, agent output, and dev server output.
"""
import asyncio
@@ -15,6 +15,7 @@ from typing import Set
from fastapi import WebSocket, WebSocketDisconnect
from .services.dev_server_manager import get_devserver_manager
from .services.process_manager import get_manager
# Lazy imports
@@ -195,16 +196,52 @@ async def project_websocket(websocket: WebSocket, project_name: str):
agent_manager.add_output_callback(on_output)
agent_manager.add_status_callback(on_status_change)
# Get dev server manager and register callbacks
devserver_manager = get_devserver_manager(project_name, project_dir)
async def on_dev_output(line: str):
"""Handle dev server output - broadcast to this WebSocket."""
try:
await websocket.send_json({
"type": "dev_log",
"line": line,
"timestamp": datetime.now().isoformat(),
})
except Exception:
pass # Connection may be closed
async def on_dev_status_change(status: str):
"""Handle dev server status change - broadcast to this WebSocket."""
try:
await websocket.send_json({
"type": "dev_server_status",
"status": status,
"url": devserver_manager.detected_url,
})
except Exception:
pass # Connection may be closed
# Register dev server callbacks
devserver_manager.add_output_callback(on_dev_output)
devserver_manager.add_status_callback(on_dev_status_change)
# Start progress polling task
poll_task = asyncio.create_task(poll_progress(websocket, project_name, project_dir))
try:
# Send initial status
# Send initial agent status
await websocket.send_json({
"type": "agent_status",
"status": agent_manager.status,
})
# Send initial dev server status
await websocket.send_json({
"type": "dev_server_status",
"status": devserver_manager.status,
"url": devserver_manager.detected_url,
})
# Send initial progress
count_passing_tests = _get_count_passing_tests()
passing, in_progress, total = count_passing_tests(project_dir)
@@ -244,9 +281,13 @@ async def project_websocket(websocket: WebSocket, project_name: str):
except asyncio.CancelledError:
pass
# Unregister callbacks
# Unregister agent callbacks
agent_manager.remove_output_callback(on_output)
agent_manager.remove_status_callback(on_status_change)
# Unregister dev server callbacks
devserver_manager.remove_output_callback(on_dev_output)
devserver_manager.remove_status_callback(on_dev_status_change)
# Disconnect from manager
await manager.disconnect(websocket, project_name)