diff --git a/bin/nemoclaw.js b/bin/nemoclaw.js index e45c80cc92e..f542e58fdba 100755 --- a/bin/nemoclaw.js +++ b/bin/nemoclaw.js @@ -620,16 +620,11 @@ async function onboard(args) { await runOnboard({ nonInteractive, resume }); } -async function setup() { +async function setup(args = []) { console.log(""); console.log(" ⚠ `nemoclaw setup` is deprecated. Use `nemoclaw onboard` instead."); - console.log(" Running legacy setup.sh for backwards compatibility..."); console.log(""); - await ensureApiKey(); - const { defaultSandbox } = registry.listSandboxes(); - const safeName = - defaultSandbox && /^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(defaultSandbox) ? defaultSandbox : ""; - run(`bash "${SCRIPTS}/setup.sh" ${shellQuote(safeName)}`); + await onboard(args); } async function setupSpark() { @@ -1188,7 +1183,7 @@ const [cmd, ...args] = process.argv.slice(2); await onboard(args); break; case "setup": - await setup(); + await setup(args); break; case "setup-spark": await setupSpark(); diff --git a/scripts/brev-setup.sh b/scripts/brev-setup.sh index 1bf5d9de4ef..7c098467261 100755 --- a/scripts/brev-setup.sh +++ b/scripts/brev-setup.sh @@ -2,7 +2,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # -# Brev VM bootstrap — installs prerequisites then runs setup.sh. +# Brev VM bootstrap — installs prerequisites then runs nemoclaw onboard. # # Run on a fresh Brev VM: # export NVIDIA_API_KEY=nvapi-... @@ -12,7 +12,7 @@ # 1. Installs Docker (if missing) # 2. Installs NVIDIA Container Toolkit (if GPU present) # 3. Installs openshell CLI from GitHub release (binary, no Rust build) -# 4. Runs setup.sh +# 4. Installs nemoclaw CLI and runs nemoclaw onboard set -euo pipefail @@ -177,9 +177,18 @@ elif command -v nvidia-smi >/dev/null 2>&1; then fi fi -# --- 5. Run setup.sh --- +# --- 5. Install nemoclaw CLI and run onboard --- +REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +info "Installing nemoclaw CLI..." +export npm_config_prefix="$HOME/.local" +export PATH="$HOME/.local/bin:$PATH" +(cd "$REPO_DIR/nemoclaw" && npm install && npm run build) >/dev/null 2>&1 +(cd "$REPO_DIR" && npm install --ignore-scripts && npm link) >/dev/null 2>&1 +info "nemoclaw $(nemoclaw --version) installed" + # Use sg docker to ensure docker group is active (usermod -aG doesn't # take effect in the current session without re-login) -info "Running setup.sh..." +info "Running nemoclaw onboard..." export NVIDIA_API_KEY -exec sg docker -c "bash $SCRIPT_DIR/setup.sh" +exec sg docker -c "nemoclaw onboard --non-interactive" diff --git a/scripts/setup.sh b/scripts/setup.sh deleted file mode 100755 index 4ba7f13dd41..00000000000 --- a/scripts/setup.sh +++ /dev/null @@ -1,324 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# NemoClaw setup — run this on the HOST to set up everything. -# -# Prerequisites: -# - Docker running (Colima, Docker Desktop, or native) -# - openshell CLI installed (pip install openshell @ git+https://github.com/NVIDIA/OpenShell.git) -# - NVIDIA_API_KEY set in environment (from build.nvidia.com) -# -# Usage: -# export NVIDIA_API_KEY=nvapi-... -# ./scripts/setup.sh [sandbox-name] -# -# What it does: -# 1. Starts an OpenShell gateway (or reuses existing) -# 2. Fixes CoreDNS for Colima environments -# 3. Creates nvidia-nim provider (build.nvidia.com) -# 4. Creates vllm-local provider (if vLLM is running) -# 5. Sets inference route to nvidia-nim by default -# 6. Builds and creates the NemoClaw sandbox -# 7. Prints next steps - -set -euo pipefail - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -# shellcheck source=./lib/runtime.sh -. "$SCRIPT_DIR/lib/runtime.sh" - -_ts() { date '+%H:%M:%S'; } -info() { echo -e "${GREEN}[$(_ts)]${NC} $1"; } -warn() { echo -e "${YELLOW}[$(_ts)]${NC} $1"; } -fail() { - echo -e "${RED}[$(_ts)]${NC} $1" - exit 1 -} - -upsert_provider() { - local name="$1" - local type="$2" - local credential="$3" - local config="$4" - - if openshell provider create --name "$name" --type "$type" \ - --credential "$credential" \ - --config "$config" 2>&1 | grep -q "AlreadyExists"; then - openshell provider update "$name" \ - --credential "$credential" \ - --config "$config" >/dev/null - info "Updated $name provider" - else - info "Created $name provider" - fi -} - -# Resolve DOCKER_HOST for macOS user-scoped runtimes when needed. -ORIGINAL_DOCKER_HOST="${DOCKER_HOST:-}" -if docker_host="$(detect_docker_host)"; then - export DOCKER_HOST="$docker_host" - if [ -n "$ORIGINAL_DOCKER_HOST" ]; then - warn "Using DOCKER_HOST from environment: $docker_host" - else - case "$(docker_host_runtime "$docker_host" || true)" in - colima) - warn "Using Colima Docker socket: ${docker_host#unix://}" - ;; - docker-desktop) - warn "Using Docker Desktop socket: ${docker_host#unix://}" - ;; - custom) - warn "Using Docker host: $docker_host" - ;; - esac - fi -fi - -# Check prerequisites -command -v openshell >/dev/null || fail "openshell CLI not found. Install the binary from https://github.com/NVIDIA/OpenShell/releases" -command -v docker >/dev/null || fail "docker not found" -[ -n "${NVIDIA_API_KEY:-}" ] || fail "NVIDIA_API_KEY not set. Get one from build.nvidia.com" - -CONTAINER_RUNTIME="$(infer_container_runtime_from_info "$(docker info 2>/dev/null || true)")" -if is_unsupported_macos_runtime "$(uname -s)" "$CONTAINER_RUNTIME"; then - fail "Podman on macOS is not supported yet. NemoClaw currently depends on OpenShell support for Podman on macOS. Use Colima or Docker Desktop instead." -fi -if [ "$CONTAINER_RUNTIME" != "unknown" ]; then - info "Container runtime: $CONTAINER_RUNTIME" -fi -SANDBOX_NAME="${1:-${NEMOCLAW_SANDBOX_NAME:-nemoclaw}}" -info "Using sandbox name: ${SANDBOX_NAME}" - -OPEN_SHELL_VERSION_RAW="$(openshell -V 2>/dev/null || true)" -OPEN_SHELL_VERSION_LOWER="${OPEN_SHELL_VERSION_RAW,,}" -if [[ "$OPEN_SHELL_VERSION_LOWER" =~ openshell[[:space:]]+([0-9]+\.[0-9]+\.[0-9]+) ]]; then - export IMAGE_TAG="${BASH_REMATCH[1]}" - export OPENSHELL_CLUSTER_IMAGE="ghcr.io/nvidia/openshell/cluster:${BASH_REMATCH[1]}" - info "Using pinned OpenShell gateway image: ${OPENSHELL_CLUSTER_IMAGE}" -elif [[ -n "$OPEN_SHELL_VERSION_RAW" ]]; then - warn "Could not parse openshell version from 'openshell -V': ${OPEN_SHELL_VERSION_RAW}" - warn "Skipping OpenShell gateway image pinning." -fi - -# 1. Gateway — always start fresh to avoid stale state -info "Starting OpenShell gateway..." -openshell gateway destroy -g nemoclaw >/dev/null 2>&1 || true -docker volume ls -q --filter "name=openshell-cluster-nemoclaw" | grep . && docker volume ls -q --filter "name=openshell-cluster-nemoclaw" | xargs docker volume rm || true -GATEWAY_ARGS=(--name nemoclaw) -command -v nvidia-smi >/dev/null 2>&1 && GATEWAY_ARGS+=(--gpu) -if ! openshell gateway start "${GATEWAY_ARGS[@]}" 2>&1 | grep -E "Gateway|✓|Error|error"; then - warn "Gateway start failed. Cleaning up stale state..." - openshell gateway destroy -g nemoclaw >/dev/null 2>&1 || true - docker volume ls -q --filter "name=openshell-cluster-nemoclaw" | grep . && docker volume ls -q --filter "name=openshell-cluster-nemoclaw" | xargs docker volume rm || true - fail "Stale state removed. Please rerun: nemoclaw onboard" -fi - -# Verify gateway is actually healthy (may need a moment after start) -for i in 1 2 3 4 5; do - if openshell status 2>&1 | grep -q "Connected"; then - break - fi - if [ "$i" -eq 5 ]; then - warn "Gateway health check failed. Cleaning up stale state..." - openshell gateway destroy -g nemoclaw >/dev/null 2>&1 || true - docker volume ls -q --filter "name=openshell-cluster-nemoclaw" | grep . && docker volume ls -q --filter "name=openshell-cluster-nemoclaw" | xargs docker volume rm || true - fail "Stale state removed. Please rerun: nemoclaw onboard" - fi - sleep 2 -done -info "Gateway is healthy" - -# 2. CoreDNS fix — k3s-inside-Docker has broken DNS forwarding on all platforms -if [ "$CONTAINER_RUNTIME" != "unknown" ]; then - info "Patching CoreDNS DNS forwarding..." - bash "$SCRIPT_DIR/fix-coredns.sh" nemoclaw 2>&1 || warn "CoreDNS patch failed (may not be needed)" -fi - -# 3. Providers -info "Setting up inference providers..." - -# nvidia-nim (build.nvidia.com) -# Use env-name-only form so openshell reads the value from the environment -# internally — the literal key value never appears in the process argument list. -upsert_provider \ - "nvidia-nim" \ - "openai" \ - "NVIDIA_API_KEY" \ - "OPENAI_BASE_URL=https://integrate.api.nvidia.com/v1" - -# vllm-local (if vLLM is installed or running) -if check_local_provider_health "vllm-local" || python3 -c "import vllm" 2>/dev/null; then - VLLM_LOCAL_BASE_URL="$(get_local_provider_base_url "vllm-local")" - upsert_provider \ - "vllm-local" \ - "openai" \ - "OPENAI_API_KEY=dummy" \ - "OPENAI_BASE_URL=$VLLM_LOCAL_BASE_URL" -fi - -# 4a. Ollama (macOS local inference) -if [ "$(uname -s)" = "Darwin" ]; then - if ! command -v ollama >/dev/null 2>&1; then - info "Installing Ollama..." - brew install ollama 2>/dev/null || warn "Ollama install failed (brew required). Install manually: https://ollama.com" - fi - if command -v ollama >/dev/null 2>&1; then - # Start Ollama service if not running - if ! check_local_provider_health "ollama-local"; then - info "Starting Ollama service..." - OLLAMA_HOST=0.0.0.0:11434 ollama serve >/dev/null 2>&1 & - sleep 2 - fi - OLLAMA_LOCAL_BASE_URL="$(get_local_provider_base_url "ollama-local")" - upsert_provider \ - "ollama-local" \ - "openai" \ - "OPENAI_API_KEY=ollama" \ - "OPENAI_BASE_URL=$OLLAMA_LOCAL_BASE_URL" - fi -fi - -# 4b. Inference route — default to nvidia-nim -info "Setting inference route to nvidia-nim / Nemotron 3 Super..." -openshell inference set --no-verify --provider nvidia-nim --model nvidia/nemotron-3-super-120b-a12b >/dev/null 2>&1 - -# 5. Swap check — prevent OOM during sandbox image push (Linux only) -if [ "$(uname -s)" = "Linux" ]; then - MIN_TOTAL_MB=12000 - total_ram_mb=$(awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo 2>/dev/null || echo 0) - total_swap_mb=$(awk '/SwapTotal/{printf "%d", $2/1024}' /proc/meminfo 2>/dev/null || echo 0) - total_mb=$((total_ram_mb + total_swap_mb)) - if [ "$total_mb" -lt "$MIN_TOTAL_MB" ] && [ ! -f /swapfile ]; then - # Bail if disk can't fit a 4 GB swap file - free_disk_kb=$(df / --output=avail -k 2>/dev/null | tail -1 | tr -d ' ') - if [ -n "$free_disk_kb" ] && [ "$free_disk_kb" -lt 5000000 ]; then - warn "Insufficient disk space ($((free_disk_kb / 1024)) MB free, need ~5 GB) to create swap file. Skipping." - else - warn "Low memory detected (${total_mb} MB). Sandbox creation may fail with OOM." - warn "Consider manually creating a swap file:" - warn " sudo dd if=/dev/zero of=/swapfile bs=1M count=4096 status=none && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile" - fi - elif [ "$total_mb" -ge "$MIN_TOTAL_MB" ]; then - info "Memory OK: ${total_ram_mb} MB RAM + ${total_swap_mb} MB swap" - fi -fi - -# 6. Build and create sandbox -info "Deleting old ${SANDBOX_NAME} sandbox (if any)..." -openshell sandbox delete "$SANDBOX_NAME" >/dev/null 2>&1 || true - -# Pre-build the base image if it's not available (GHCR image may not exist on -# forks or before the first base-image workflow run). This ensures the -# Dockerfile's `FROM ${BASE_IMAGE}` can resolve locally. -BASE_IMAGE="${BASE_IMAGE:-ghcr.io/nvidia/nemoclaw/sandbox-base:latest}" -if ! docker image inspect "$BASE_IMAGE" >/dev/null 2>&1 && ! docker pull "$BASE_IMAGE" 2>/dev/null; then - if [ -f "$REPO_DIR/Dockerfile.base" ]; then - info "Base image not in registry — building Dockerfile.base locally..." - docker build -f "$REPO_DIR/Dockerfile.base" -t "$BASE_IMAGE" "$REPO_DIR" 2>&1 | tail -5 - info "Local base image built" - else - warn "Dockerfile.base not found — sandbox build may fall back to full rebuild" - fi -fi - -info "Building and creating NemoClaw sandbox (this takes a few minutes on first run)..." - -# Stage a clean build context (openshell doesn't honor .dockerignore) -BUILD_CTX="$(mktemp -d)" -cp "$REPO_DIR/Dockerfile" "$BUILD_CTX/" -cp -r "$REPO_DIR/nemoclaw" "$BUILD_CTX/nemoclaw" -cp -r "$REPO_DIR/nemoclaw-blueprint" "$BUILD_CTX/nemoclaw-blueprint" -cp -r "$REPO_DIR/scripts" "$BUILD_CTX/scripts" -rm -rf "$BUILD_CTX/nemoclaw/node_modules" -bash "$BUILD_CTX/scripts/clean-staged-tree.sh" "$BUILD_CTX/nemoclaw-blueprint" 2>/dev/null || true - -# Capture full output to a temp file so we can filter for display but still -# detect failures. The raw log is kept on failure for debugging. -CREATE_LOG=$(mktemp /tmp/nemoclaw-create-XXXXXX.log) -SANDBOX_BUILD_START=$(date +%s) - -# Background progress reporter: tails the log for Docker build steps and -# prints a heartbeat every 30s so CI (and humans) can see what's happening. -( - while true; do - sleep 30 - if [ ! -f "$CREATE_LOG" ]; then break; fi - ELAPSED=$(($(date +%s) - SANDBOX_BUILD_START)) - LAST_STEP=$(grep -oE "^Step [0-9]+/[0-9]+" "$CREATE_LOG" 2>/dev/null | tail -1 || true) - LAST_LINE=$(tail -1 "$CREATE_LOG" 2>/dev/null | head -c 120 || true) - # Filter out lines that might contain secrets - if echo "$LAST_LINE" | grep -qi "API_KEY\|TOKEN\|SECRET\|CREDENTIAL"; then - LAST_LINE="[filtered]" - fi - echo -e "${GREEN}[$(_ts)]${NC} ⏳ Sandbox build ${ELAPSED}s elapsed${LAST_STEP:+ — $LAST_STEP}${LAST_LINE:+ — $LAST_LINE}" - done -) & -PROGRESS_PID=$! - -set +e -# NVIDIA_API_KEY is NOT passed into the sandbox. Inference is proxied through -# the OpenShell gateway which injects the stored credential server-side. -openshell sandbox create --from "$BUILD_CTX/Dockerfile" --name "$SANDBOX_NAME" \ - --provider nvidia-nim \ - --no-tty -- true \ - >"$CREATE_LOG" 2>&1 -CREATE_RC=$? -set -e - -# Stop progress reporter -kill "$PROGRESS_PID" 2>/dev/null || true -wait "$PROGRESS_PID" 2>/dev/null || true - -SANDBOX_BUILD_ELAPSED=$(($(date +%s) - SANDBOX_BUILD_START)) -info "Sandbox build finished in ${SANDBOX_BUILD_ELAPSED}s (exit code: $CREATE_RC)" - -rm -rf "$BUILD_CTX" - -# Show progress lines (filter apt noise and env var dumps that contain NVIDIA_API_KEY) -grep -E "^ (Step |Building |Built |Pushing |\[progress\]|Successfully |Created sandbox|Image )|✓" "$CREATE_LOG" || true - -if [ "$CREATE_RC" != "0" ]; then - echo "" - warn "Last 20 lines of build output:" - tail -20 "$CREATE_LOG" | grep -v "NVIDIA_API_KEY" - echo "" - fail "Sandbox creation failed (exit $CREATE_RC). Full log: $CREATE_LOG" -fi -rm -f "$CREATE_LOG" - -# Verify sandbox is Ready (not just that a record exists) -# Strip ANSI color codes before checking phase -SANDBOX_LINE=$(openshell sandbox list 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | awk -v name="$SANDBOX_NAME" '$1 == name { print; exit }') -if ! echo "$SANDBOX_LINE" | grep -q "Ready"; then - SANDBOX_PHASE=$(echo "$SANDBOX_LINE" | awk '{print $NF}') - echo "" - warn "Sandbox phase: ${SANDBOX_PHASE:-unknown}" - # Check for common failure modes - SB_DETAIL=$(openshell sandbox get "$SANDBOX_NAME" 2>&1 || true) - if echo "$SB_DETAIL" | grep -qi "ImagePull\|ErrImagePull\|image.*not found"; then - warn "Image pull failure detected. The sandbox image was built inside the" - warn "gateway but k3s can't find it. This is a known openshell issue." - warn "Workaround: run 'openshell gateway destroy && openshell gateway start'" - warn "and re-run this script." - fi - fail "Sandbox created but not Ready (phase: ${SANDBOX_PHASE:-unknown}). Check 'openshell sandbox get ${SANDBOX_NAME}'." -fi - -# 6. DNS proxy — run a forwarder in the sandbox pod so the isolated -# sandbox namespace can resolve hostnames (fixes #626). -info "Setting up sandbox DNS proxy..." -bash "$SCRIPT_DIR/setup-dns-proxy.sh" nemoclaw "$SANDBOX_NAME" 2>&1 || warn "DNS proxy setup failed (sandbox DNS may not work)" - -# 7. Done -echo "" -info "Setup complete!" -echo "" -echo " openclaw agent --agent main --local -m 'how many rs are there in strawberry?' --session-id s1" -echo "" diff --git a/scripts/walkthrough.sh b/scripts/walkthrough.sh index fe176220d16..3a02ec6381b 100755 --- a/scripts/walkthrough.sh +++ b/scripts/walkthrough.sh @@ -13,7 +13,7 @@ # the TUI prompts the operator to approve or deny the request. # # Prerequisites: -# - NemoClaw setup complete (./scripts/setup.sh) +# - NemoClaw setup complete (nemoclaw onboard) # - NVIDIA_API_KEY in environment # # Suggested prompts that trigger the approval flow: diff --git a/test/cli.test.js b/test/cli.test.js index 241e1d3bd81..4641ff7ad0d 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -126,6 +126,20 @@ describe("CLI dispatch", () => { expect(r.out.includes("Unknown onboard option(s): --non-interactiv")).toBeTruthy(); }); + it("setup forwards unknown options into onboard parsing", () => { + const r = run("setup --non-interactiv"); + expect(r.code).toBe(1); + expect(r.out.includes("deprecated")).toBeTruthy(); + expect(r.out.includes("Unknown onboard option(s): --non-interactiv")).toBeTruthy(); + }); + + it("setup forwards --resume into onboard parsing", () => { + const r = run("setup --resume"); + expect(r.code).toBe(1); + expect(r.out.includes("deprecated")).toBeTruthy(); + expect(r.out.includes("No resumable onboarding session was found")).toBeTruthy(); + }); + it("debug --help exits 0 and shows usage", () => { const r = run("debug --help"); expect(r.code).toBe(0); diff --git a/test/e2e/brev-e2e.test.js b/test/e2e/brev-e2e.test.js index cd298ddd758..858d30ce9cf 100644 --- a/test/e2e/brev-e2e.test.js +++ b/test/e2e/brev-e2e.test.js @@ -169,50 +169,47 @@ describe.runIf(hasRequiredVars)("Brev E2E", () => { }); console.log(`[${elapsed()}] Bootstrap complete`); - // Install nemoclaw CLI — brev-setup.sh creates the sandbox but doesn't - // install the host-side CLI that the test scripts need for `nemoclaw status`. - // The `bin` field is in the root package.json (not nemoclaw/), so we need to: - // 1. Build the TypeScript plugin (in nemoclaw/) - // 2. npm link from the repo root (where bin.nemoclaw is defined) - // Use npm_config_prefix so npm link writes to ~/.local/bin (no sudo needed), - // which is already on PATH in runRemoteTest. - console.log(`[${elapsed()}] Installing nemoclaw CLI...`); + // Verify the CLI installed by brev-setup.sh is visible to the non-login + // SSH sessions used by runRemoteTest. + console.log(`[${elapsed()}] Verifying nemoclaw CLI...`); ssh( [ `export npm_config_prefix=$HOME/.local`, `export PATH=$HOME/.local/bin:$PATH`, - `cd ${remoteDir}/nemoclaw && npm install && npm run build`, - `cd ${remoteDir} && npm install --ignore-scripts && npm link`, `which nemoclaw && nemoclaw --version`, ].join(" && "), { timeout: 120_000 }, ); - console.log(`[${elapsed()}] nemoclaw CLI installed`); + console.log(`[${elapsed()}] nemoclaw CLI verified`); - // Register the sandbox in nemoclaw's local registry. - // setup.sh creates the sandbox via openshell directly but doesn't write - // ~/.nemoclaw/sandboxes.json, which `nemoclaw status` needs. - console.log(`[${elapsed()}] Registering sandbox in nemoclaw registry...`); - ssh( - `mkdir -p ~/.nemoclaw && cat > ~/.nemoclaw/sandboxes.json << 'REGISTRY' -{ - "sandboxes": { - "e2e-test": { - "name": "e2e-test", - "createdAt": "${new Date().toISOString()}", - "model": null, - "nimContainer": null, - "provider": "nvidia-nim", - "gpuEnabled": false, - "policies": [] - } - }, - "defaultSandbox": "e2e-test" -} -REGISTRY`, - { timeout: 10_000 }, + // Assert the onboard bootstrap persisted the sandbox registry entry. + console.log(`[${elapsed()}] Verifying sandbox registry...`); + const registry = JSON.parse(ssh(`cat ~/.nemoclaw/sandboxes.json`, { timeout: 10_000 })); + expect(registry.defaultSandbox).toBe("e2e-test"); + expect(registry.sandboxes).toHaveProperty("e2e-test"); + const sandbox = registry.sandboxes["e2e-test"]; + expect(sandbox).toMatchObject({ + name: "e2e-test", + gpuEnabled: false, + policies: [], + }); + const normalizedSandbox = { + ...sandbox, + createdAt: "", + model: "", + nimContainer: "", + }; + expect(normalizedSandbox).toEqual( + expect.objectContaining({ + name: "e2e-test", + createdAt: "", + model: "", + nimContainer: "", + gpuEnabled: false, + policies: [], + }), ); - console.log(`[${elapsed()}] Sandbox registered`); + console.log(`[${elapsed()}] Sandbox registry verified`); console.log(`[${elapsed()}] beforeAll complete — total bootstrap time: ${elapsed()}`); }, 2_700_000); // 45 min diff --git a/test/gateway-cleanup.test.js b/test/gateway-cleanup.test.js index 0972e8c6aae..25da35ded11 100644 --- a/test/gateway-cleanup.test.js +++ b/test/gateway-cleanup.test.js @@ -39,9 +39,4 @@ describe("gateway cleanup: Docker volumes removed on failure (#17)", () => { expect(content.includes("docker volume") && content.includes("openshell-cluster")).toBe(true); expect(content.includes("remove_related_docker_volumes")).toBe(true); }); - - it("setup.sh: includes Docker volume cleanup on failure", () => { - const content = fs.readFileSync(path.join(ROOT, "scripts/setup.sh"), "utf-8"); - expect(content.includes("docker volume") && content.includes("openshell-cluster")).toBe(true); - }); }); diff --git a/test/runner.test.js b/test/runner.test.js index 7ef65911078..9b4e2d0f7d0 100644 --- a/test/runner.test.js +++ b/test/runner.test.js @@ -261,38 +261,6 @@ describe("regression guards", () => { expect(src.includes("delete process.env.NVIDIA_API_KEY")).toBeTruthy(); }); - it("setup.sh uses env-name-only form for nvidia-nim credential", () => { - const fs = require("fs"); - const src = fs.readFileSync( - path.join(import.meta.dirname, "..", "scripts", "setup.sh"), - "utf-8", - ); - // Should use "NVIDIA_API_KEY" (name only), not "NVIDIA_API_KEY=$NVIDIA_API_KEY" (value) - const lines = src.split("\n"); - for (const line of lines) { - if (line.includes("upsert_provider") || line.includes("--credential")) continue; - if (line.trim().startsWith("#")) continue; - // Check credential argument lines passed to upsert_provider - if (line.includes('"NVIDIA_API_KEY=')) { - // Allow "NVIDIA_API_KEY" alone but not "NVIDIA_API_KEY=$..." - expect(line.includes("NVIDIA_API_KEY=$")).toBe(false); - } - } - }); - - it("setup.sh does not pass NVIDIA_API_KEY in sandbox create env args", () => { - const fs = require("fs"); - const src = fs.readFileSync( - path.join(import.meta.dirname, "..", "scripts", "setup.sh"), - "utf-8", - ); - // Find sandbox create command — should not have env NVIDIA_API_KEY - const createLines = src.split("\n").filter((l) => l.includes("sandbox create")); - for (const line of createLines) { - expect(line.includes("NVIDIA_API_KEY")).toBe(false); - } - }); - it("setupSpark does not pass NVIDIA_API_KEY to sudo", () => { const fs = require("fs"); const src = fs.readFileSync( diff --git a/test/setup-sandbox-name.test.js b/test/setup-sandbox-name.test.js deleted file mode 100644 index be6899d8a83..00000000000 --- a/test/setup-sandbox-name.test.js +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -// -// Verify that setup.sh uses a parameterized sandbox name instead of -// hardcoding "nemoclaw". Gateway name must stay hardcoded. -// -// See: https://github.com/NVIDIA/NemoClaw/issues/197 - -import { describe, it, expect } from "vitest"; -import fs from "node:fs"; -import path from "node:path"; -import { execSync } from "node:child_process"; - -const ROOT = path.resolve(import.meta.dirname, ".."); - -describe("setup.sh sandbox name parameterization (#197)", () => { - const content = fs.readFileSync(path.join(ROOT, "scripts/setup.sh"), "utf-8"); - - it("accepts sandbox name as $1 with env var fallback and default", () => { - // $1 takes priority, then NEMOCLAW_SANDBOX_NAME env var, then "nemoclaw" - expect( - content.includes('SANDBOX_NAME="${1:-${NEMOCLAW_SANDBOX_NAME:-nemoclaw}}"'), - ).toBeTruthy(); - }); - - it("sandbox create uses $SANDBOX_NAME, not hardcoded", () => { - const createLine = content.match(/openshell sandbox create.*--name\s+(\S+)/); - expect(createLine).toBeTruthy(); - expect( - createLine[1].includes("$SANDBOX_NAME") || createLine[1].includes('"$SANDBOX_NAME"'), - ).toBeTruthy(); - }); - - it("sandbox delete uses $SANDBOX_NAME, not hardcoded", () => { - const deleteLine = content.match(/openshell sandbox delete\s+(\S+)/); - expect(deleteLine).toBeTruthy(); - expect( - deleteLine[1].includes("$SANDBOX_NAME") || deleteLine[1].includes('"$SANDBOX_NAME"'), - ).toBeTruthy(); - }); - - it("sandbox get uses $SANDBOX_NAME, not hardcoded", () => { - const getLine = content.match(/openshell sandbox get\s+(\S+)/); - expect(getLine).toBeTruthy(); - expect( - getLine[1].includes("$SANDBOX_NAME") || getLine[1].includes('"$SANDBOX_NAME"'), - ).toBeTruthy(); - }); - - it("gateway name stays hardcoded to nemoclaw", () => { - expect(content.includes("gateway destroy -g nemoclaw")).toBeTruthy(); - expect(content.includes("--name nemoclaw")).toBeTruthy(); - }); - - it("$1 arg actually sets SANDBOX_NAME in bash", () => { - const result = execSync( - 'bash -c \'SANDBOX_NAME="${1:-${NEMOCLAW_SANDBOX_NAME:-nemoclaw}}"; echo "$SANDBOX_NAME"\' -- my-test-box', - { encoding: "utf-8" }, - ).trim(); - expect(result).toBe("my-test-box"); - }); - - it("NEMOCLAW_SANDBOX_NAME env var is used when no $1 arg", () => { - const result = execSync( - 'bash -c \'SANDBOX_NAME="${1:-${NEMOCLAW_SANDBOX_NAME:-nemoclaw}}"; echo "$SANDBOX_NAME"\'', - { encoding: "utf-8", env: { ...process.env, NEMOCLAW_SANDBOX_NAME: "e2e-test" } }, - ).trim(); - expect(result).toBe("e2e-test"); - }); - - it("no arg and no env var defaults to nemoclaw in bash", () => { - const result = execSync( - 'bash -c \'SANDBOX_NAME="${1:-${NEMOCLAW_SANDBOX_NAME:-nemoclaw}}"; echo "$SANDBOX_NAME"\'', - { encoding: "utf-8", env: { PATH: process.env.PATH } }, - ).trim(); - expect(result).toBe("nemoclaw"); - }); -});