Integrate terminal emulator in server.ts (Phase 1)

This commit is contained in:
Jared Miller 2026-01-31 09:45:38 -05:00
parent c9908c87c3
commit 0366e459f5
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C

View file

@ -19,6 +19,7 @@ import {
updateLastSeen, updateLastSeen,
updateSessionStats, updateSessionStats,
} from "./db"; } from "./db";
import { createTerminal, disposeTerminal, type TerminalSession } from "./terminal";
import type { import type {
AnswerResponse, AnswerResponse,
ClientMessage, ClientMessage,
@ -33,6 +34,7 @@ const sessionWebSockets = new Map<number, ServerWebSocket<SessionData>>();
const sessionStates = new Map<number, SessionState>(); const sessionStates = new Map<number, SessionState>();
// Buffer for incomplete ANSI sequences per session to avoid leaking partial control codes // Buffer for incomplete ANSI sequences per session to avoid leaking partial control codes
const ansiCarryovers = new Map<number, string>(); const ansiCarryovers = new Map<number, string>();
const sessionTerminals = new Map<number, TerminalSession>();
interface SessionData { interface SessionData {
deviceId: number; deviceId: number;
@ -437,6 +439,10 @@ const server = Bun.serve<SessionData>({
// Initialize in-memory session state // Initialize in-memory session state
sessionStates.set(session.id, createDefaultSessionState()); sessionStates.set(session.id, createDefaultSessionState());
// Create terminal emulator with default size (will be resized later)
const termSession = createTerminal(80, 24);
sessionTerminals.set(session.id, termSession);
console.debug( console.debug(
`Session ${session.id} started for device ${device.id}`, `Session ${session.id} started for device ${device.id}`,
); );
@ -506,6 +512,12 @@ const server = Bun.serve<SessionData>({
appendOutput(sessionId, body); // Store raw ANSI without trailing incomplete fragment appendOutput(sessionId, body); // Store raw ANSI without trailing incomplete fragment
// Write to terminal emulator
const termSession = sessionTerminals.get(sessionId);
if (termSession) {
termSession.terminal.write(msg.data);
}
broadcastSSE({ broadcastSSE({
type: "output", type: "output",
session_id: sessionId, session_id: sessionId,
@ -615,6 +627,13 @@ const server = Bun.serve<SessionData>({
console.debug( console.debug(
`Session ${ws.data.sessionId} resized to ${msg.cols}x${msg.rows}`, `Session ${ws.data.sessionId} resized to ${msg.cols}x${msg.rows}`,
); );
// Resize terminal emulator
const termSession = sessionTerminals.get(ws.data.sessionId);
if (termSession) {
termSession.terminal.resize(msg.cols, msg.rows);
}
return; return;
} }
@ -654,6 +673,13 @@ const server = Bun.serve<SessionData>({
sessionWebSockets.delete(ws.data.sessionId); sessionWebSockets.delete(ws.data.sessionId);
ansiCarryovers.delete(ws.data.sessionId); ansiCarryovers.delete(ws.data.sessionId);
// Dispose terminal emulator
const termSession = sessionTerminals.get(ws.data.sessionId);
if (termSession) {
disposeTerminal(termSession);
sessionTerminals.delete(ws.data.sessionId);
}
// Persist final state before cleanup // Persist final state before cleanup
const state = sessionStates.get(ws.data.sessionId); const state = sessionStates.get(ws.data.sessionId);
if (state?.dirty) { if (state?.dirty) {