| 57 | const DEFAULT_MAX_RECONNECT_ATTEMPTS = 3 |
| 58 | |
| 59 | export class SSHSessionManagerImpl implements SSHSessionManager { |
| 60 | private proc: Subprocess |
| 61 | private options: SSHSessionManagerOptions |
| 62 | private connected = false |
| 63 | private disconnected = false |
| 64 | private readLoopAbort: AbortController | null = null |
| 65 | private reconnectAttempt = 0 |
| 66 | private readonly maxReconnectAttempts: number |
| 67 | private userInitiatedDisconnect = false |
| 68 | private reconnecting = false |
| 69 | |
| 70 | constructor(proc: Subprocess, options: SSHSessionManagerOptions) { |
| 71 | this.proc = proc |
| 72 | this.options = options |
| 73 | this.maxReconnectAttempts = |
| 74 | options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS |
| 75 | } |
| 76 | |
| 77 | connect(): void { |
| 78 | if (this.connected) return |
| 79 | |
| 80 | this.readLoopAbort = new AbortController() |
| 81 | this.startReadLoop() |
| 82 | this.monitorExit() |
| 83 | |
| 84 | this.connected = true |
| 85 | this.options.onConnected() |
| 86 | } |
| 87 | |
| 88 | private async startReadLoop(): Promise<void> { |
| 89 | const stdout = this.proc.stdout |
| 90 | if (!stdout) { |
| 91 | this.options.onError(new Error('SSH process stdout is not available')) |
| 92 | return |
| 93 | } |
| 94 | |
| 95 | const reader = (stdout as ReadableStream<Uint8Array>).getReader() |
| 96 | const decoder = new TextDecoder() |
| 97 | let lineBuffer = '' |
| 98 | |
| 99 | try { |
| 100 | while (!this.disconnected) { |
| 101 | const { done, value } = await reader.read() |
| 102 | if (done) break |
| 103 | |
| 104 | lineBuffer += decoder.decode(value, { stream: true }) |
| 105 | const lines = lineBuffer.split('\n') |
| 106 | lineBuffer = lines.pop() ?? '' |
| 107 | |
| 108 | for (const line of lines) { |
| 109 | const trimmed = line.trim() |
| 110 | if (!trimmed) continue |
| 111 | this.processLine(trimmed) |
| 112 | } |
| 113 | } |
| 114 | } catch (err) { |
| 115 | if (!this.disconnected) { |
| 116 | this.options.onError( |
nothing calls this directly
no outgoing calls
no test coverage detected