From 5352c8d7fc1300440817e134a9ae709bdbb46409 Mon Sep 17 00:00:00 2001 From: Kagura Chen Date: Wed, 18 Mar 2026 11:40:08 +0800 Subject: [PATCH 1/3] fix(setup): use dynamic sandbox name instead of hardcoded 'nemoclaw' Replace all hardcoded 'nemoclaw' sandbox name references in setup.sh with a $SANDBOX_NAME variable that: 1. Accepts the name as the first CLI argument ($1) 2. Falls back to reading from ~/.nemoclaw/sandboxes.json (the onboard wizard's registry) to respect the name chosen during onboarding 3. Defaults to 'nemoclaw' if neither is available This fixes the split-brain issue where the onboard wizard stores a custom sandbox name in the registry but setup.sh always creates a sandbox named 'nemoclaw', causing all subsequent commands (connect, policy-add, Telegram bridge) to fail with 'sandbox not found'. Fixes #197 --- scripts/setup.sh | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 77d24a77ab6..67572f0481c 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -11,7 +11,10 @@ # # Usage: # export NVIDIA_API_KEY=nvapi-... -# ./scripts/setup.sh +# ./scripts/setup.sh [sandbox-name] +# +# The sandbox name can also be set via the onboard wizard's +# ~/.nemoclaw/sandboxes.json registry. Defaults to "nemoclaw". # # What it does: # 1. Starts an OpenShell gateway (or reuses existing) @@ -71,10 +74,24 @@ command -v docker > /dev/null || fail "docker not found" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +# Sandbox name — can be passed as $1, read from onboard config, or defaults to "nemoclaw". +# This ensures setup.sh respects the name chosen during `nemoclaw onboard`. +if [ -n "${1:-}" ]; then + SANDBOX_NAME="$1" +elif [ -f "$HOME/.nemoclaw/sandboxes.json" ]; then + # Read the first sandbox name from the onboard registry + _sb_name=$(node -e "const s=require('$HOME/.nemoclaw/sandboxes.json');console.log(Object.keys(s)[0]||'nemoclaw')" 2>/dev/null || echo "nemoclaw") + SANDBOX_NAME="${_sb_name}" + unset _sb_name +else + SANDBOX_NAME="nemoclaw" +fi +info "Using sandbox name: ${SANDBOX_NAME}" + # 1. Gateway — always start fresh to avoid stale state info "Starting OpenShell gateway..." -openshell gateway destroy -g nemoclaw > /dev/null 2>&1 || true -GATEWAY_ARGS=(--name nemoclaw) +openshell gateway destroy -g "$SANDBOX_NAME" > /dev/null 2>&1 || true +GATEWAY_ARGS=(--name "$SANDBOX_NAME") command -v nvidia-smi > /dev/null 2>&1 && GATEWAY_ARGS+=(--gpu) openshell gateway start "${GATEWAY_ARGS[@]}" 2>&1 | grep -E "Gateway|✓|Error|error" || true @@ -139,8 +156,8 @@ 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. Build and create sandbox -info "Deleting old nemoclaw sandbox (if any)..." -openshell sandbox delete nemoclaw > /dev/null 2>&1 || true +info "Deleting old ${SANDBOX_NAME} sandbox (if any)..." +openshell sandbox delete "$SANDBOX_NAME" > /dev/null 2>&1 || true info "Building and creating NemoClaw sandbox (this takes a few minutes on first run)..." @@ -162,7 +179,7 @@ fi # detect failures. The raw log is kept on failure for debugging. CREATE_LOG=$(mktemp /tmp/nemoclaw-create-XXXXXX.log) set +e -openshell sandbox create --from "$BUILD_CTX/Dockerfile" --name nemoclaw \ +openshell sandbox create --from "$BUILD_CTX/Dockerfile" --name "$SANDBOX_NAME" \ --provider nvidia-nim \ -- env NVIDIA_API_KEY="$NVIDIA_API_KEY" > "$CREATE_LOG" 2>&1 CREATE_RC=$? @@ -183,20 +200,20 @@ 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' | grep "nemoclaw") +SANDBOX_LINE=$(openshell sandbox list 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | grep "$SANDBOX_NAME") 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 nemoclaw 2>&1 || true) + 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 nemoclaw'." + fail "Sandbox created but not Ready (phase: ${SANDBOX_PHASE:-unknown}). Check 'openshell sandbox get ${SANDBOX_NAME}'." fi # 6. Done From 646874039c154e8a4baa63ea3725953a7ff0eb71 Mon Sep 17 00:00:00 2001 From: Kagura Chen Date: Wed, 18 Mar 2026 13:23:02 +0800 Subject: [PATCH 2/3] fix: harden sandbox name resolution and readiness check Address review feedback: - Add explicit Node.js availability check before reading sandboxes.json - Warn (don't silently fallback) when registry lookup fails - Use awk exact-match instead of grep substring to prevent sandbox name collisions and set -euo pipefail abort on no match - Add explicit error when sandbox not found in list --- scripts/setup.sh | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 67572f0481c..1ca8db58401 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -79,10 +79,19 @@ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" if [ -n "${1:-}" ]; then SANDBOX_NAME="$1" elif [ -f "$HOME/.nemoclaw/sandboxes.json" ]; then - # Read the first sandbox name from the onboard registry - _sb_name=$(node -e "const s=require('$HOME/.nemoclaw/sandboxes.json');console.log(Object.keys(s)[0]||'nemoclaw')" 2>/dev/null || echo "nemoclaw") - SANDBOX_NAME="${_sb_name}" - unset _sb_name + if command -v node > /dev/null 2>&1; then + _sb_name="$(node -e 'const p=process.argv[1];const s=require(p);process.stdout.write(Object.keys(s)[0]||"")' "$HOME/.nemoclaw/sandboxes.json" 2>/dev/null || true)" + if [ -n "${_sb_name}" ]; then + SANDBOX_NAME="${_sb_name}" + else + warn "Could not read sandbox name from ~/.nemoclaw/sandboxes.json; defaulting to \"nemoclaw\"" + SANDBOX_NAME="nemoclaw" + fi + unset _sb_name + else + warn "Node.js not found; cannot read ~/.nemoclaw/sandboxes.json. Defaulting to \"nemoclaw\"" + SANDBOX_NAME="nemoclaw" + fi else SANDBOX_NAME="nemoclaw" fi @@ -200,7 +209,16 @@ 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' | grep "$SANDBOX_NAME") +# Use awk for exact name match to avoid substring collisions, with || true to prevent +# set -euo pipefail from aborting on no match +SANDBOX_LINE=$( + openshell sandbox list 2>&1 \ + | sed 's/\x1b\[[0-9;]*m//g' \ + | awk -v name="$SANDBOX_NAME" '$1 == name { print; exit }' || true +) +if [ -z "$SANDBOX_LINE" ]; then + fail "Sandbox '${SANDBOX_NAME}' not found in 'openshell sandbox list'." +fi if ! echo "$SANDBOX_LINE" | grep -q "Ready"; then SANDBOX_PHASE=$(echo "$SANDBOX_LINE" | awk '{print $NF}') echo "" From 7ba672185132667f13b3023ac3dfe76285f4f697 Mon Sep 17 00:00:00 2001 From: Kagura Chen Date: Wed, 18 Mar 2026 14:02:01 +0800 Subject: [PATCH 3/3] fix: differentiate sandbox list command failures from not-found Address CodeRabbit review: the previous '|| true' on the openshell sandbox list pipeline collapsed command errors (CLI/gateway/auth issues) into the same empty-result path as 'sandbox not found'. Now we capture the command exit code separately and fail with the actual error message when the list command itself fails. --- scripts/setup.sh | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 1ca8db58401..8518a7628f7 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -209,13 +209,19 @@ rm -f "$CREATE_LOG" # Verify sandbox is Ready (not just that a record exists) # Strip ANSI color codes before checking phase -# Use awk for exact name match to avoid substring collisions, with || true to prevent -# set -euo pipefail from aborting on no match -SANDBOX_LINE=$( - openshell sandbox list 2>&1 \ - | sed 's/\x1b\[[0-9;]*m//g' \ - | awk -v name="$SANDBOX_NAME" '$1 == name { print; exit }' || true -) +# Separate command failures from "sandbox not found" — don't let || true hide errors +set +e +SANDBOX_LIST_RAW="$( + openshell sandbox list 2>&1 | sed 's/\x1b\[[0-9;]*m//g' +)" +SANDBOX_LIST_RC=$? +set -e +if [ "$SANDBOX_LIST_RC" -ne 0 ]; then + fail "Failed to query sandboxes via 'openshell sandbox list': ${SANDBOX_LIST_RAW}" +fi + +# Use awk for exact name match to avoid substring collisions +SANDBOX_LINE="$(printf '%s\n' "$SANDBOX_LIST_RAW" | awk -v name="$SANDBOX_NAME" '$1 == name { print; exit }')" if [ -z "$SANDBOX_LINE" ]; then fail "Sandbox '${SANDBOX_NAME}' not found in 'openshell sandbox list'." fi