diff --git a/src/lib/sandbox-state.ts b/src/lib/sandbox-state.ts index 344c3321b05..5b7b655ccc9 100644 --- a/src/lib/sandbox-state.ts +++ b/src/lib/sandbox-state.ts @@ -342,6 +342,21 @@ export function restoreSandboxState( return { success: false, restoredDirs, failedDirs: [...localDirs] }; } + // Remove existing state dirs before extracting so stale files from + // later snapshots don't persist after restoring an earlier one. + const rmCmd = localDirs + .map((d) => `rm -rf "${writableDir}/${d}"`) + .join(" && "); + _log(`Cleaning target dirs before restore: ${rmCmd}`); + const rmResult = spawnSync( + "ssh", + [...sshArgs(configFile, sandboxName), rmCmd], + { stdio: ["ignore", "pipe", "pipe"], timeout: 30000 }, + ); + if (rmResult.status !== 0) { + _log(`WARNING: pre-restore cleanup failed (exit ${rmResult.status}): ${(rmResult.stderr?.toString() || "").substring(0, 200)}`); + } + const extractCmd = `tar -xf - -C ${writableDir}`; const sshResult = spawnSync( "ssh", diff --git a/src/nemoclaw.ts b/src/nemoclaw.ts index 0a18a2a9d88..8e741233502 100644 --- a/src/nemoclaw.ts +++ b/src/nemoclaw.ts @@ -1806,6 +1806,16 @@ function sandboxSnapshot(sandboxName, subArgs) { break; } case "restore": { + const isLive = captureOpenshell(["sandbox", "list"], { ignoreError: true }); + if (isLive.status !== 0) { + console.error(" Failed to query live sandbox state from OpenShell."); + process.exit(1); + } + const liveNames = parseLiveSandboxNames(isLive.output || ""); + if (!liveNames.has(sandboxName)) { + console.error(` Sandbox '${sandboxName}' is not running. Cannot restore snapshot.`); + process.exit(1); + } const timestamp = subArgs[1] || null; let backupPath; if (timestamp) {