diff --git a/bin/hermes-web-ui.mjs b/bin/hermes-web-ui.mjs index 7099867..12a52d9 100755 --- a/bin/hermes-web-ui.mjs +++ b/bin/hermes-web-ui.mjs @@ -419,7 +419,14 @@ function startDaemon(port) { } function stopDaemon() { - const pid = getPid() + const pidFromFile = readPidFile() + if (pidFromFile && !isRunning(pidFromFile)) { + removePid() + console.log(` ✓ hermes-web-ui was not running (cleaned stale PID: ${pidFromFile})`) + return + } + + const pid = pidFromFile ?? recoverPidFromPort() if (!pid) { console.log(' ✗ hermes-web-ui is not running') process.exit(1) @@ -442,7 +449,11 @@ function stopDaemon() { } catch {} // Force kill if still alive if (isRunning(pid)) { - process.kill(pid, 'SIGKILL') + try { + process.kill(pid, 'SIGKILL') + } catch (err) { + if (err?.code !== 'ESRCH') throw err + } } removePid() console.log(` ✓ hermes-web-ui stopped (PID: ${pid})`) @@ -701,4 +712,5 @@ export { getListeningPids, parseUnixNetstatListeningPids, resetDefaultLogin, + stopDaemon, } diff --git a/tests/server/cli-port-detection.test.ts b/tests/server/cli-port-detection.test.ts index 907d4aa..f6c9a50 100644 --- a/tests/server/cli-port-detection.test.ts +++ b/tests/server/cli-port-detection.test.ts @@ -125,6 +125,22 @@ describe('CLI port detection', () => { } }) + it('cleans a stale server PID file during stop', async () => { + const home = mkdtempSync(join(tmpdir(), 'hermes-web-ui-cli-stale-pid-')) + process.env.HERMES_WEB_UI_HOME = home + const pidFile = join(home, 'server.pid') + writeFileSync(pidFile, '999999999\n') + + try { + const { stopDaemon } = await loadCli() + stopDaemon() + + expect(existsSync(pidFile)).toBe(false) + } finally { + rmSync(home, { recursive: true, force: true }) + } + }) + it('resets an existing admin user to the default password', async () => { const home = mkdtempSync(join(tmpdir(), 'hermes-web-ui-cli-default-login-')) process.env.HERMES_WEB_UI_HOME = home