(socket: net.Socket)
| 352 | } |
| 353 | |
| 354 | private handleConnection(socket: net.Socket): void { |
| 355 | // Hello first so the proxy can verify versions before piping any |
| 356 | // application bytes. The proxy reads exactly one line, then forwards. |
| 357 | const hello: DaemonHello = { |
| 358 | codegraph: CodeGraphPackageVersion, |
| 359 | pid: process.pid, |
| 360 | socketPath: this.socketPath, |
| 361 | protocol: 1, |
| 362 | }; |
| 363 | socket.write(JSON.stringify(hello) + '\n'); |
| 364 | |
| 365 | // Read the optional client-hello (proxy → daemon) to learn the client's |
| 366 | // peer pids, then hand the socket to the session. Fail-safe: any problem — |
| 367 | // timeout, a non-hello first line, an early close — yields null pids and we |
| 368 | // fall back to the socket-close lifecycle exactly as before (#692). |
| 369 | void readClientHello(socket).then((peers) => { |
| 370 | const transport = new SocketTransport(socket); |
| 371 | const session = new MCPSession(transport, this.engine, { |
| 372 | explicitProjectPath: this.projectRoot, |
| 373 | }); |
| 374 | transport.onClose(() => this.dropClient(session)); |
| 375 | this.clients.add(session); |
| 376 | this.clientPeers.set(session, peers); |
| 377 | this.disarmIdleTimer(); |
| 378 | session.start(); |
| 379 | // Observe inbound bytes purely to feed the inactivity backstop — a second |
| 380 | // 'data' listener that reads nothing, added AFTER the transport's so the |
| 381 | // unshifted client-hello tail reaches the transport intact. |
| 382 | socket.on('data', () => { this.lastActivityAt = Date.now(); }); |
| 383 | }); |
| 384 | } |
| 385 | |
| 386 | private dropClient(session: MCPSession): void { |
| 387 | if (!this.clients.delete(session)) return; |
no test coverage detected