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

@@ -3,7 +3,7 @@
*/
import { useEffect, useRef, useState, useCallback } from 'react'
import type { WSMessage, AgentStatus } from '../lib/types'
import type { WSMessage, AgentStatus, DevServerStatus } from '../lib/types'
interface WebSocketState {
progress: {
@@ -15,6 +15,9 @@ interface WebSocketState {
agentStatus: AgentStatus
logs: Array<{ line: string; timestamp: string }>
isConnected: boolean
devServerStatus: DevServerStatus
devServerUrl: string | null
devLogs: Array<{ line: string; timestamp: string }>
}
const MAX_LOGS = 100 // Keep last 100 log lines
@@ -25,6 +28,9 @@ export function useProjectWebSocket(projectName: string | null) {
agentStatus: 'stopped',
logs: [],
isConnected: false,
devServerStatus: 'stopped',
devServerUrl: null,
devLogs: [],
})
const wsRef = useRef<WebSocket | null>(null)
@@ -86,6 +92,24 @@ export function useProjectWebSocket(projectName: string | null) {
// Feature updates will trigger a refetch via React Query
break
case 'dev_log':
setState(prev => ({
...prev,
devLogs: [
...prev.devLogs.slice(-MAX_LOGS + 1),
{ line: message.line, timestamp: message.timestamp },
],
}))
break
case 'dev_server_status':
setState(prev => ({
...prev,
devServerStatus: message.status,
devServerUrl: message.url,
}))
break
case 'pong':
// Heartbeat response
break
@@ -131,6 +155,9 @@ export function useProjectWebSocket(projectName: string | null) {
agentStatus: 'stopped',
logs: [],
isConnected: false,
devServerStatus: 'stopped',
devServerUrl: null,
devLogs: [],
})
if (!projectName) {
@@ -164,8 +191,14 @@ export function useProjectWebSocket(projectName: string | null) {
setState(prev => ({ ...prev, logs: [] }))
}, [])
// Clear dev logs function
const clearDevLogs = useCallback(() => {
setState(prev => ({ ...prev, devLogs: [] }))
}, [])
return {
...state,
clearLogs,
clearDevLogs,
}
}