• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

apowers313 / aiforge / 20962792399

13 Jan 2026 03:39PM UTC coverage: 84.707%. First build
20962792399

push

github

apowers313
feat: initial commit

787 of 905 branches covered (86.96%)

Branch coverage included in aggregate %.

4248 of 5039 new or added lines in 70 files covered. (84.3%)

4248 of 5039 relevant lines covered (84.3%)

13.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.19
/src/server/websocket/WebSocketServer.ts
1
/**
2
 * WebSocketServer - WebSocket server for terminal I/O
3
 */
4
import { WebSocketServer as WsServer, type WebSocket } from 'ws';
1✔
5
import type { Server as HttpServer } from 'http';
6
import { HeartbeatManager, type HeartbeatWebSocketServer } from './heartbeat.js';
1✔
7
import { TerminalHandler, type TerminalMessage } from './handlers/terminal.js';
1✔
8
import type { PtyManager } from '@server/services/pty/PtyManager.js';
9
import { logger } from '@server/utils/logger.js';
1✔
10

11
/**
12
 * Extended WebSocket with isAlive tracking
13
 */
14
export interface ExtendedWebSocket extends WebSocket {
15
  isAlive: boolean;
16
}
17

18
/**
19
 * Options for creating a WebSocket server
20
 */
21
export interface WebSocketServerOptions {
22
  server: HttpServer;
23
  ptyManager: PtyManager;
24
  path?: string;
25
  heartbeatInterval?: number;
26
}
27

28
/**
29
 * Create and configure a WebSocket server for terminal communication
30
 */
31
export function createWebSocketServer(options: WebSocketServerOptions): WsServer {
1✔
32
  const {
10✔
33
    server,
10✔
34
    ptyManager,
10✔
35
    path = '/ws/terminal',
10✔
36
    heartbeatInterval = 30000,
10✔
37
  } = options;
10✔
38

39
  // Create WebSocket server
40
  const wss = new WsServer({ server, path });
10✔
41

42
  // Create terminal handler
43
  const terminalHandler = new TerminalHandler(ptyManager);
10✔
44

45
  // Create and start heartbeat manager
46
  const heartbeatManager = new HeartbeatManager(
10✔
47
    wss as unknown as HeartbeatWebSocketServer,
10✔
48
    { interval: heartbeatInterval },
10✔
49
  );
10✔
50
  heartbeatManager.start();
10✔
51

52
  // Handle new connections
53
  wss.on('connection', (ws: ExtendedWebSocket) => {
10✔
54
    logger.debug('WebSocket client connected');
6✔
55

56
    ws.isAlive = true;
6✔
57

58
    // Handle pong responses
59
    ws.on('pong', () => {
6✔
60
      ws.isAlive = true;
1✔
61
    });
6✔
62

63
    // Handle incoming messages
64
    ws.on('message', (data: Buffer) => {
6✔
65
      try {
2✔
66
        const message = JSON.parse(data.toString('utf8')) as TerminalMessage;
2✔
67
        terminalHandler.handleMessage(ws, message);
2✔
68
      } catch (err) {
2✔
69
        logger.error({ err }, 'Failed to parse WebSocket message');
1✔
70
        ws.send(JSON.stringify({
1✔
71
          type: 'error',
1✔
72
          code: 'INVALID_MESSAGE',
1✔
73
          message: 'Invalid message format',
1✔
74
        }));
1✔
75
      }
1✔
76
    });
6✔
77

78
    // Handle close
79
    ws.on('close', () => {
6✔
80
      logger.debug('WebSocket client disconnected');
1✔
81
      terminalHandler.handleDisconnect(ws);
1✔
82
    });
6✔
83

84
    // Handle errors
85
    ws.on('error', (err) => {
6✔
86
      logger.error({ err }, 'WebSocket error');
1✔
87
    });
6✔
88
  });
10✔
89

90
  // Handle server close
91
  wss.on('close', () => {
10✔
92
    heartbeatManager.stop();
1✔
93
  });
10✔
94

95
  logger.info({ path }, 'WebSocket server started');
10✔
96

97
  return wss;
10✔
98
}
10✔
99

100
/**
101
 * WebSocket server wrapper for easier integration
102
 */
103
export class TerminalWebSocketServer {
1✔
104
  private readonly _wss: WsServer;
4✔
105
  private readonly _heartbeatManager: HeartbeatManager;
4✔
106
  private readonly _terminalHandler: TerminalHandler;
4✔
107

108
  constructor(options: WebSocketServerOptions) {
4✔
109
    const {
4✔
110
      server,
4✔
111
      ptyManager,
4✔
112
      path = '/ws/terminal',
4✔
113
      heartbeatInterval = 30000,
4✔
114
    } = options;
4✔
115

116
    // Create WebSocket server
117
    this._wss = new WsServer({ server, path });
4✔
118

119
    // Create terminal handler
120
    this._terminalHandler = new TerminalHandler(ptyManager);
4✔
121

122
    // Create heartbeat manager
123
    this._heartbeatManager = new HeartbeatManager(
4✔
124
      this._wss as unknown as HeartbeatWebSocketServer,
4✔
125
      { interval: heartbeatInterval },
4✔
126
    );
4✔
127

128
    this._setupHandlers();
4✔
129
  }
4✔
130

131
  private _setupHandlers(): void {
4✔
132
    // Handle new connections
133
    this._wss.on('connection', (ws: ExtendedWebSocket) => {
4✔
134
      logger.debug('WebSocket client connected');
1✔
135

136
      ws.isAlive = true;
1✔
137

138
      // Handle pong responses
139
      ws.on('pong', () => {
1✔
NEW
140
        ws.isAlive = true;
×
141
      });
1✔
142

143
      // Handle incoming messages
144
      ws.on('message', (data: Buffer) => {
1✔
NEW
145
        try {
×
NEW
146
          const message = JSON.parse(data.toString('utf8')) as TerminalMessage;
×
NEW
147
          this._terminalHandler.handleMessage(ws, message);
×
NEW
148
        } catch (err) {
×
NEW
149
          logger.error({ err }, 'Failed to parse WebSocket message');
×
NEW
150
          ws.send(JSON.stringify({
×
NEW
151
            type: 'error',
×
NEW
152
            code: 'INVALID_MESSAGE',
×
NEW
153
            message: 'Invalid message format',
×
NEW
154
          }));
×
NEW
155
        }
×
156
      });
1✔
157

158
      // Handle close
159
      ws.on('close', () => {
1✔
NEW
160
        logger.debug('WebSocket client disconnected');
×
NEW
161
        this._terminalHandler.handleDisconnect(ws);
×
162
      });
1✔
163

164
      // Handle errors
165
      ws.on('error', (err) => {
1✔
NEW
166
        logger.error({ err }, 'WebSocket error');
×
167
      });
1✔
168
    });
4✔
169
  }
4✔
170

171
  /**
172
   * Start the heartbeat
173
   */
174
  start(): void {
4✔
175
    this._heartbeatManager.start();
1✔
176
    logger.info('WebSocket server heartbeat started');
1✔
177
  }
1✔
178

179
  /**
180
   * Stop the heartbeat and close all connections
181
   */
182
  stop(): void {
4✔
183
    this._heartbeatManager.stop();
1✔
184
    this._wss.close();
1✔
185
    logger.info('WebSocket server stopped');
1✔
186
  }
1✔
187

188
  /**
189
   * Get the underlying WebSocket server
190
   */
191
  get wss(): WsServer {
4✔
192
    return this._wss;
2✔
193
  }
2✔
194
}
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc