(root: string)
| 153 | * falling back to the registry. |
| 154 | */ |
| 155 | export async function stopDaemonAt(root: string): Promise<StopResult> { |
| 156 | let pid: number | null = null; |
| 157 | try { |
| 158 | const info = decodeLockInfo(fs.readFileSync(getDaemonPidPath(root), 'utf8')); |
| 159 | pid = info?.pid ?? null; |
| 160 | } catch { |
| 161 | /* no lockfile */ |
| 162 | } |
| 163 | if (pid == null) { |
| 164 | const rec = listDaemons({ prune: false }).find( |
| 165 | (r) => path.resolve(r.root) === path.resolve(root) |
| 166 | ); |
| 167 | pid = rec?.pid ?? null; |
| 168 | } |
| 169 | |
| 170 | if (pid == null) { |
| 171 | cleanupDaemonArtifacts(root); |
| 172 | return { root, pid: null, outcome: 'no-daemon' }; |
| 173 | } |
| 174 | if (!isProcessAlive(pid)) { |
| 175 | cleanupDaemonArtifacts(root); |
| 176 | return { root, pid, outcome: 'not-running' }; |
| 177 | } |
| 178 | |
| 179 | // POSIX: SIGTERM runs the daemon's graceful shutdown. Windows: TerminateProcess |
| 180 | // (no graceful path), so we always sweep artifacts ourselves below. |
| 181 | try { process.kill(pid, 'SIGTERM'); } catch { /* raced to exit */ } |
| 182 | let outcome: StopResult['outcome'] = 'term'; |
| 183 | if (!(await waitForDeath(pid, 3000))) { |
| 184 | try { process.kill(pid, 'SIGKILL'); } catch { /* raced to exit */ } |
| 185 | await waitForDeath(pid, 2000); |
| 186 | outcome = 'kill'; |
| 187 | } |
| 188 | cleanupDaemonArtifacts(root); |
| 189 | return { root, pid, outcome }; |
| 190 | } |
| 191 | |
| 192 | /** Stop every registered, live daemon. */ |
| 193 | export async function stopAllDaemons(): Promise<StopResult[]> { |
no test coverage detected