()
| 108 | private socketPath: string | null = null |
| 109 | |
| 110 | async start(): Promise<void> { |
| 111 | if (this.running) { |
| 112 | return |
| 113 | } |
| 114 | |
| 115 | this.socketPath = getSecureSocketPath() |
| 116 | |
| 117 | if (platform() !== 'win32') { |
| 118 | const socketDir = getSocketDir() |
| 119 | |
| 120 | // Migrate legacy socket: if socket dir path exists as a file/socket, remove it |
| 121 | try { |
| 122 | const dirStats = await stat(socketDir) |
| 123 | if (!dirStats.isDirectory()) { |
| 124 | await unlink(socketDir) |
| 125 | } |
| 126 | } catch { |
| 127 | // Doesn't exist, that's fine |
| 128 | } |
| 129 | |
| 130 | // Create socket directory with secure permissions |
| 131 | await mkdir(socketDir, { recursive: true, mode: 0o700 }) |
| 132 | |
| 133 | // Fix perms if directory already existed |
| 134 | await chmod(socketDir, 0o700).catch(() => { |
| 135 | // Ignore |
| 136 | }) |
| 137 | |
| 138 | // Clean up stale sockets |
| 139 | try { |
| 140 | const files = await readdir(socketDir) |
| 141 | for (const file of files) { |
| 142 | if (!file.endsWith('.sock')) { |
| 143 | continue |
| 144 | } |
| 145 | const pid = parseInt(file.replace('.sock', ''), 10) |
| 146 | if (isNaN(pid)) { |
| 147 | continue |
| 148 | } |
| 149 | try { |
| 150 | process.kill(pid, 0) |
| 151 | // Process is alive, leave it |
| 152 | } catch { |
| 153 | // Process is dead, remove stale socket |
| 154 | await unlink(join(socketDir, file)).catch(() => { |
| 155 | // Ignore |
| 156 | }) |
| 157 | log(`Removed stale socket for PID ${pid}`) |
| 158 | } |
| 159 | } |
| 160 | } catch { |
| 161 | // Ignore errors scanning directory |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | log(`Creating socket listener: ${this.socketPath}`) |
| 166 | |
| 167 | this.server = createServer(socket => this.handleMcpClient(socket)) |
no test coverage detected