Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bin/lib/onboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand Down Expand 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];
Expand Down
16 changes: 16 additions & 0 deletions bin/lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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]) {
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -211,6 +217,7 @@ function removeSandbox(name) {
});
}

/** List all registered sandboxes and the current default. */
function listSandboxes() {
const data = load();
return {
Expand All @@ -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();
Expand All @@ -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,
Expand Down
25 changes: 25 additions & 0 deletions test/registry.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});