From 94efcd8476ff1d3e789cc579932a817f36a9a4a1 Mon Sep 17 00:00:00 2001 From: peteryuqin Date: Thu, 2 Apr 2026 14:58:08 -0400 Subject: [PATCH] fix(deploy): use the remote user's home for Brev sync Signed-off-by: Intern Dev Made-with: Cursor --- .../skills/nemoclaw-deploy-remote/SKILL.md | 2 +- .../skills/nemoclaw-manage-policy/SKILL.md | 2 +- docs/deployment/deploy-to-remote-gpu.md | 2 +- .../approve-network-requests.md | 2 +- test/cli.test.js | 100 ++++++++++++++++++ 5 files changed, 104 insertions(+), 4 deletions(-) diff --git a/.agents/skills/nemoclaw-deploy-remote/SKILL.md b/.agents/skills/nemoclaw-deploy-remote/SKILL.md index 95aaeed01b3..7123cba77d6 100644 --- a/.agents/skills/nemoclaw-deploy-remote/SKILL.md +++ b/.agents/skills/nemoclaw-deploy-remote/SKILL.md @@ -68,7 +68,7 @@ $ nemoclaw deploy SSH to the instance and run the OpenShell TUI to monitor activity and approve network requests: ```console -$ ssh 'cd /home/ubuntu/nemoclaw && set -a && . .env && set +a && openshell term' +$ ssh 'cd ~/nemoclaw && set -a && . .env && set +a && openshell term' ``` ## Step 5: Verify Inference diff --git a/.agents/skills/nemoclaw-manage-policy/SKILL.md b/.agents/skills/nemoclaw-manage-policy/SKILL.md index bac54ae583e..2b0f2ac528c 100644 --- a/.agents/skills/nemoclaw-manage-policy/SKILL.md +++ b/.agents/skills/nemoclaw-manage-policy/SKILL.md @@ -27,7 +27,7 @@ $ openshell term For a remote sandbox, pass the instance name: ```console -$ ssh my-gpu-box 'cd /home/ubuntu/nemoclaw && . .env && openshell term' +$ ssh my-gpu-box 'cd ~/nemoclaw && . .env && openshell term' ``` The TUI displays the sandbox state, active inference provider, and a live feed of network activity. diff --git a/docs/deployment/deploy-to-remote-gpu.md b/docs/deployment/deploy-to-remote-gpu.md index fd7fa8379a7..aa8c569fd8e 100644 --- a/docs/deployment/deploy-to-remote-gpu.md +++ b/docs/deployment/deploy-to-remote-gpu.md @@ -83,7 +83,7 @@ $ nemoclaw deploy SSH to the instance and run the OpenShell TUI to monitor activity and approve network requests: ```console -$ ssh 'cd /home/ubuntu/nemoclaw && set -a && . .env && set +a && openshell term' +$ ssh 'cd ~/nemoclaw && set -a && . .env && set +a && openshell term' ``` ## Verify Inference diff --git a/docs/network-policy/approve-network-requests.md b/docs/network-policy/approve-network-requests.md index 57ed29908f9..e765377984c 100644 --- a/docs/network-policy/approve-network-requests.md +++ b/docs/network-policy/approve-network-requests.md @@ -41,7 +41,7 @@ $ openshell term For a remote sandbox, pass the instance name: ```console -$ ssh my-gpu-box 'cd /home/ubuntu/nemoclaw && . .env && openshell term' +$ ssh my-gpu-box 'cd ~/nemoclaw && . .env && openshell term' ``` The TUI displays the sandbox state, active inference provider, and a live feed of network activity. diff --git a/test/cli.test.js b/test/cli.test.js index 736fb58f1c6..20ee76b9e00 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -116,6 +116,106 @@ describe("CLI dispatch", () => { expect(r.out).toContain("NemoClaw Services"); }); + it("deploy uses the remote user's home directory instead of /home/ubuntu", () => { + const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-cli-deploy-home-")); + const localBin = path.join(home, "bin"); + const sshLog = path.join(home, "ssh.log"); + const rsyncLog = path.join(home, "rsync.log"); + const scpLog = path.join(home, "scp.log"); + fs.mkdirSync(localBin, { recursive: true }); + + const remoteHome = "/home/custombrevuser"; + + fs.writeFileSync( + path.join(localBin, "brev"), + [ + "#!/usr/bin/env bash", + 'if [ "$1" = "ls" ] && [ "$2" = "--json" ]; then', + ' echo \'[{"name":"shade-box","status":"RUNNING","build_status":"COMPLETED","shell_status":"READY"}]\'', + " exit 0", + "fi", + 'if [ "$1" = "ls" ]; then', + " echo shade-box", + " exit 0", + "fi", + "exit 0", + ].join("\n"), + { mode: 0o755 }, + ); + fs.writeFileSync( + path.join(localBin, "ssh-keyscan"), + [ + "#!/usr/bin/env bash", + "echo fake-ssh-host-key", + "exit 0", + ].join("\n"), + { mode: 0o755 }, + ); + fs.writeFileSync( + path.join(localBin, "ssh"), + [ + "#!/usr/bin/env bash", + `log_file=${JSON.stringify(sshLog)}`, + 'printf \'%s\\n\' "$*" >> "$log_file"', + 'if [ "$1" = "-G" ]; then', + " echo hostname shade-box.brev.test", + " exit 0", + "fi", + "prev=", + "last=", + 'for arg in "$@"; do prev=$last; last=$arg; done', + // Compare to literal argv "$HOME" (remote probe); do not expand shell $HOME on RHS. + "if [ \"$prev\" = \"echo\" ] && [ \"$last\" = '$HOME' ]; then", + ` echo '${remoteHome}'`, + " exit 0", + "fi", + "exit 0", + ].join("\n"), + { mode: 0o755 }, + ); + fs.writeFileSync( + path.join(localBin, "rsync"), + [ + "#!/usr/bin/env bash", + `log_file=${JSON.stringify(rsyncLog)}`, + 'printf \'%s\\n\' "$*" >> "$log_file"', + "exit 0", + ].join("\n"), + { mode: 0o755 }, + ); + fs.writeFileSync( + path.join(localBin, "scp"), + [ + "#!/usr/bin/env bash", + `log_file=${JSON.stringify(scpLog)}`, + 'printf \'%s\\n\' "$*" >> "$log_file"', + "exit 0", + ].join("\n"), + { mode: 0o755 }, + ); + + const r = runWithEnv("deploy shade-box", { + HOME: home, + PATH: `${localBin}:${process.env.PATH || ""}`, + NVIDIA_API_KEY: "nvapi-test-key-1234567890", + NEMOCLAW_DEPLOY_NO_CONNECT: "1", + }); + + expect(r.code).toBe(0); + const sshOut = fs.readFileSync(sshLog, "utf8"); + expect(sshOut).toContain("mkdir -p"); + expect(sshOut).toContain(`${remoteHome}/nemoclaw`); + expect(sshOut).not.toContain("/home/ubuntu/nemoclaw"); + + const rsyncOut = fs.readFileSync(rsyncLog, "utf8"); + expect(rsyncOut).toContain(`shade-box:${remoteHome}/nemoclaw/`); + expect(rsyncOut).not.toContain("/home/ubuntu/nemoclaw"); + + const scpOut = fs.readFileSync(scpLog, "utf8"); + expect(scpOut).toContain(`shade-box:${remoteHome}/nemoclaw/.env`); + expect(scpOut).not.toContain("/home/ubuntu/nemoclaw"); + }); + it("unknown onboard option exits 1", () => { const r = run("onboard --non-interactiv"); expect(r.code).toBe(1);