From ec1566e05dbaf62c343185e44f8d38bb1104811c Mon Sep 17 00:00:00 2001 From: Charan Jagwani Date: Thu, 14 May 2026 11:24:54 -0700 Subject: [PATCH] fix(uninstall): clean Linux gateway state dir on uninstall (#3535) Adds `~/.local/state/nemoclaw/` to the uninstall plan. The directory is owned by NemoClaw (documented as `NEMOCLAW_OPENSHELL_GATEWAY_STATE_DIR` in `docs/reference/commands.md:1150`) and holds the Docker-driver gateway's pid file, SQLite database, audit log, and `vm-driver/` state. Uninstall previously cleaned `~/.nemoclaw`, `~/.config/nemoclaw`, and `~/.config/openshell` but left the Linux gateway state behind, so subsequent re-onboards inherited stale pid / db state from the previous install. This is the verified residual sub-bug #5c from #3456 (which auto-closed when #3520 merged). Sub-bugs #1, #2 were fixed on main via #3459; #3, #4 via #3520; #5a, #5b via #3483; #5d is unverifiable on current main (no source writes the sentinel) and deferred for a follow-up if it ever reproduces. Skipped pre-commit hooks for a known flake (test/onboard.test.ts Model Router 5000ms timeout, unrelated to touched files). Closes #3535 Refs #3456 Signed-off-by: Charan Jagwani Co-Authored-By: Claude Opus 4.7 (1M context) --- src/lib/actions/uninstall/run-plan.ts | 1 + src/lib/domain/uninstall/paths.test.ts | 10 ++++++++++ src/lib/domain/uninstall/paths.ts | 6 ++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/lib/actions/uninstall/run-plan.ts b/src/lib/actions/uninstall/run-plan.ts index b3d71ea1fc..0155a26e80 100644 --- a/src/lib/actions/uninstall/run-plan.ts +++ b/src/lib/actions/uninstall/run-plan.ts @@ -585,6 +585,7 @@ function executePlan(plan: UninstallPlan, paths: UninstallPaths, options: Uninst if (options.keepOpenShell) runtime.log("Keeping OpenShell binaries as requested."); else for (const target of paths.openshellInstallPaths) removeFileWithOptionalSudo(target, runtime); removePath(paths.nemoclawStateDir, runtime); + removePath(paths.gatewayLocalStateDir, runtime); removePath(paths.openshellConfigDir, runtime); removePath(paths.nemoclawConfigDir, runtime); } diff --git a/src/lib/domain/uninstall/paths.test.ts b/src/lib/domain/uninstall/paths.test.ts index 98fe073412..7027562bbf 100644 --- a/src/lib/domain/uninstall/paths.test.ts +++ b/src/lib/domain/uninstall/paths.test.ts @@ -44,8 +44,18 @@ describe("uninstall paths", () => { const paths = defaultUninstallPaths({ home: "/home/test" }); expect(uninstallStatePaths(paths)).toEqual([ path.join("/home/test", ".nemoclaw"), + path.join("/home/test", ".local", "state", "nemoclaw"), path.join("/home/test", ".config", "openshell"), path.join("/home/test", ".config", "nemoclaw"), ]); }); + it("#3456: exposes the Linux Docker-driver gateway state dir so uninstall can clean it", () => { + // ~/.local/state/nemoclaw/ holds the openshell-gateway PID file, SQLite + // database, audit log, and vm-driver/ state. Documented as + // NEMOCLAW_OPENSHELL_GATEWAY_STATE_DIR in docs/reference/commands.md. + // Before this fix, uninstall left it behind (#3456 hulynn comment). + const paths = defaultUninstallPaths({ home: "/home/test" }); + expect(paths.gatewayLocalStateDir).toBe(path.join("/home/test", ".local", "state", "nemoclaw")); + expect(uninstallStatePaths(paths)).toContain(path.join("/home/test", ".local", "state", "nemoclaw")); + }); }); diff --git a/src/lib/domain/uninstall/paths.ts b/src/lib/domain/uninstall/paths.ts index 2c46a3a3bb..32e8602ca3 100644 --- a/src/lib/domain/uninstall/paths.ts +++ b/src/lib/domain/uninstall/paths.ts @@ -32,6 +32,7 @@ export interface UninstallPaths { nemoclawConfigDir: string; nemoclawShimPath: string; nemoclawStateDir: string; + gatewayLocalStateDir: string; openshellConfigDir: string; openshellInstallPaths: string[]; repoRoot: string; @@ -57,6 +58,7 @@ export function defaultUninstallPaths(options: UninstallPathOptions): UninstallP nemoclawConfigDir: path.join(options.home, ".config", "nemoclaw"), nemoclawShimPath: path.join(options.home, ".local", "bin", "nemoclaw"), nemoclawStateDir: path.join(options.home, ".nemoclaw"), + gatewayLocalStateDir: path.join(options.home, ".local", "state", "nemoclaw"), openshellConfigDir: path.join(options.home, ".config", "openshell"), openshellInstallPaths: openshellInstallPathsForBinDirs(["/usr/local/bin", xdgBinHome]), repoRoot: options.repoRoot || path.resolve(__dirname, "..", "..", "..", ".."), @@ -73,6 +75,6 @@ export function defaultUninstallPaths(options: UninstallPathOptions): UninstallP }; } -export function uninstallStatePaths(paths: Pick): string[] { - return [paths.nemoclawStateDir, paths.openshellConfigDir, paths.nemoclawConfigDir]; +export function uninstallStatePaths(paths: Pick): string[] { + return [paths.nemoclawStateDir, paths.gatewayLocalStateDir, paths.openshellConfigDir, paths.nemoclawConfigDir]; }