diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index cc853fc10e0..4f4edb49d8a 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -1775,6 +1775,9 @@ async function preflight() { console.log(" Cleaning up previous NemoClaw session..."); runOpenshell(["forward", "stop", "18789"], { ignoreError: true }); runOpenshell(["gateway", "destroy", "-g", GATEWAY_NAME], { ignoreError: true }); + // Sandboxes under the destroyed gateway no longer exist in OpenShell — + // clear the local registry so `nemoclaw list` stays consistent. (#532) + registry.clearAll(); console.log(" ✓ Previous session cleaned up"); } @@ -1901,6 +1904,8 @@ async function startGatewayWithOptions(_gpu, { exitOnFailure = true } = {}) { if (hasStaleGateway(gwInfo)) { runOpenshell(["gateway", "destroy", "-g", GATEWAY_NAME], { ignoreError: true }); + // Sandboxes under the destroyed gateway no longer exist — clear registry. (#532) + registry.clearAll(); } const gwArgs = ["--name", GATEWAY_NAME]; diff --git a/bin/lib/registry.js b/bin/lib/registry.js index 885ea8c2ee6..de2cd0d9a77 100644 --- a/bin/lib/registry.js +++ b/bin/lib/registry.js @@ -120,6 +120,7 @@ function withLock(fn) { } } +/** Load the sandbox registry from disk, returning an empty state if absent or corrupt. */ function load() { try { if (fs.existsSync(REGISTRY_FILE)) { @@ -150,11 +151,13 @@ function save(data) { } } +/** Return the sandbox entry for the given name, or null if not found. */ function getSandbox(name) { const data = load(); return data.sandboxes[name] || null; } +/** Return the name of the default sandbox, falling back to the first registered one. */ function getDefault() { const data = load(); if (data.defaultSandbox && data.sandboxes[data.defaultSandbox]) { @@ -165,6 +168,7 @@ function getDefault() { return names.length > 0 ? names[0] : null; } +/** Register a new sandbox in the registry, setting it as default if none exists. */ function registerSandbox(entry) { return withLock(() => { const data = load(); @@ -184,6 +188,7 @@ function registerSandbox(entry) { }); } +/** Merge updates into an existing sandbox entry. Returns false if the sandbox does not exist. */ function updateSandbox(name, updates) { return withLock(() => { const data = load(); @@ -197,6 +202,7 @@ function updateSandbox(name, updates) { }); } +/** Remove a sandbox by name and reassign the default if necessary. */ function removeSandbox(name) { return withLock(() => { const data = load(); @@ -211,6 +217,7 @@ function removeSandbox(name) { }); } +/** List all registered sandboxes and the current default. */ function listSandboxes() { const data = load(); return { @@ -219,6 +226,7 @@ function listSandboxes() { }; } +/** Set the named sandbox as the default. Returns false if the sandbox does not exist. */ function setDefault(name) { return withLock(() => { const data = load(); @@ -229,7 +237,15 @@ function setDefault(name) { }); } +/** Reset the registry to an empty state, removing all sandboxes and the default selection. */ +function clearAll() { + withLock(() => { + save({ sandboxes: {}, defaultSandbox: null }); + }); +} + module.exports = { + clearAll, load, save, getSandbox, diff --git a/test/registry.test.js b/test/registry.test.js index 80fd0ddedc5..738677ae5b8 100644 --- a/test/registry.test.js +++ b/test/registry.test.js @@ -289,4 +289,29 @@ describe("advisory file locking", () => { const { sandboxes } = registry.listSandboxes(); expect(sandboxes.length).toBe(20); }); + + it("clearAll removes all sandboxes and resets default", () => { + registry.registerSandbox({ name: "alpha" }); + registry.registerSandbox({ name: "beta" }); + registry.setDefault("beta"); + registry.clearAll(); + const { sandboxes, defaultSandbox } = registry.listSandboxes(); + assert.equal(sandboxes.length, 0); + assert.equal(defaultSandbox, null); + }); + + it("clearAll persists empty state to disk", () => { + registry.registerSandbox({ name: "persist-me" }); + registry.clearAll(); + const data = JSON.parse(fs.readFileSync(regFile, "utf-8")); + assert.deepEqual(data.sandboxes, {}); + assert.equal(data.defaultSandbox, null); + }); + + it("clearAll is safe to call on empty registry", () => { + registry.clearAll(); + const { sandboxes, defaultSandbox } = registry.listSandboxes(); + assert.equal(sandboxes.length, 0); + assert.equal(defaultSandbox, null); + }); });