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
2 changes: 1 addition & 1 deletion .agents/skills/nemoclaw-deploy-remote/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ $ nemoclaw deploy <instance-name>
SSH to the instance and run the OpenShell TUI to monitor activity and approve network requests:

```console
$ ssh <instance-name> 'cd /home/ubuntu/nemoclaw && set -a && . .env && set +a && openshell term'
$ ssh <instance-name> 'cd ~/nemoclaw && set -a && . .env && set +a && openshell term'
```

## Step 5: Verify Inference
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/nemoclaw-manage-policy/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion docs/deployment/deploy-to-remote-gpu.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ $ nemoclaw deploy <instance-name>
SSH to the instance and run the OpenShell TUI to monitor activity and approve network requests:

```console
$ ssh <instance-name> 'cd /home/ubuntu/nemoclaw && set -a && . .env && set +a && openshell term'
$ ssh <instance-name> 'cd ~/nemoclaw && set -a && . .env && set +a && openshell term'
```

## Verify Inference
Expand Down
2 changes: 1 addition & 1 deletion docs/network-policy/approve-network-requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
100 changes: 100 additions & 0 deletions test/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down