Skip to content
Closed
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
42 changes: 42 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,47 @@ version_major() {
printf '%s\n' "${1#v}" | cut -d. -f1
}

# ── Port availability preflight ──────────────────────────────────────
# NemoClaw requires two ports to be free before setup starts:
# NEMOCLAW_GATEWAY_PORT (default 8080) – OpenShell gateway
# NEMOCLAW_DASHBOARD_PORT (default 18789) – OpenClaw dashboard
# Override via env var to avoid killing existing services.
NEMOCLAW_GATEWAY_PORT="${NEMOCLAW_GATEWAY_PORT:-8080}"
NEMOCLAW_DASHBOARD_PORT="${NEMOCLAW_DASHBOARD_PORT:-18789}"

_port_in_use() {
local p="$1"
if command -v ss &>/dev/null; then
ss -tlnH 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"
return $?
elif command -v netstat &>/dev/null; then
netstat -tlnH 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"
return $?
fi
return 1
}
Comment on lines +70 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify netstat flag support and output format differences

echo "=== Checking ss availability and -H flag ==="
if command -v ss &>/dev/null; then
  echo "ss is available"
  ss -tlnH 2>&1 | head -3
else
  echo "ss not found"
fi

echo ""
echo "=== Checking netstat -H flag (expected to fail on most systems) ==="
if command -v netstat &>/dev/null; then
  echo "netstat is available"
  netstat -tlnH 2>&1 | head -3
  echo ""
  echo "=== netstat without -H for comparison ==="
  netstat -tln 2>&1 | head -5
else
  echo "netstat not found"
fi

Repository: NVIDIA/NemoClaw

Length of output: 203


🏁 Script executed:

# Find and read the install.sh file
find . -name "install.sh" -type f | head -5

Repository: NVIDIA/NemoClaw

Length of output: 92


🏁 Script executed:

# Search for netstat documentation or usage patterns in the codebase
rg "netstat" --type sh -B 2 -A 2

Repository: NVIDIA/NemoClaw

Length of output: 332


🌐 Web query:

netstat -H flag compatibility unix systems

💡 Result:

netstat -H is not a portable/standard netstat option across Unix-like systems (Linux net-tools netstat doesn’t document -H, and BSD netstat uses -h for other purposes, not -H). (mankier.com)

What is widely compatible is H as a routing-table output flag (not a command-line option):

  • On Solaris, when you run netstat -rn, the routing table “Flags” column can include H meaning “route is to a host and not a network”. (docs.oracle.com)
  • On FreeBSD, the routing table flags include H (RTF_HOST) meaning “Host entry (net otherwise)”. (man.freebsd.org)

So if you’re trying to detect “host routes” portably, do something like:

  • netstat -rn and then interpret the Flags column; don’t rely on a -H option. (docs.oracle.com)

Citations:


🏁 Script executed:

# Read the install.sh file to confirm context and line numbers
head -100 install.sh | tail -40

Repository: NVIDIA/NemoClaw

Length of output: 1622


🏁 Script executed:

# Check for any IPv6 related patterns or tests in the codebase
rg -i "ipv6|\[::\]|::1" --type sh -B 2 -A 2

Repository: NVIDIA/NemoClaw

Length of output: 305


netstat does not support the -H flag on most Unix-like systems, causing silent failures on BSD, macOS, and traditional Linux installations.

The -H (no header) flag is specific to ss from iproute2. Traditional netstat on BSD, macOS, and systems using net-tools does not recognize -H and will error or ignore it. When netstat -tlnH fails, the fallback returns 1 (port free), which silently misses port conflicts during deployment.

Additionally, the grep pattern :${p}$ may not match all IPv6 listener formats consistently.

🐛 Proposed fix
 _port_in_use() {
   local p="$1"
   if command -v ss &>/dev/null; then
     ss -tlnH 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"
     return $?
   elif command -v netstat &>/dev/null; then
-    netstat -tlnH 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"
+    # netstat output varies by OS; skip header with tail, match port at end
+    netstat -tln 2>/dev/null | tail -n +3 | awk '{print $4}' | grep -qE "[:.]${p}$"
     return $?
   fi
   return 1
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_port_in_use() {
local p="$1"
if command -v ss &>/dev/null; then
ss -tlnH 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"
return $?
elif command -v netstat &>/dev/null; then
netstat -tlnH 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"
return $?
fi
return 1
}
_port_in_use() {
local p="$1"
if command -v ss &>/dev/null; then
ss -tlnH 2>/dev/null | awk '{print $4}' | grep -q ":${p}$"
return $?
elif command -v netstat &>/dev/null; then
# netstat lacks -H flag; skip single header line
netstat -tln 2>/dev/null | tail -n +2 | awk '{print $4}' | grep -qE ":${p}$"
return $?
fi
return 1
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@install.sh` around lines 70 - 80, The _port_in_use function uses netstat with
the invalid -H flag and a brittle grep pattern; change the netstat branch to
call netstat -tln (no -H), skip header rows if present, and extract the port
robustly (e.g., use awk to split the local-address column on ":" and take the
last field or use a regex to capture the trailing port) so IPv4, IPv6 (::) and
formats with brackets are handled; keep the ss branch as-is (ss -tlnH), replace
the grep ":${p}$" check with a comparison against the extracted port (or grep -E
"(:|\\])${p}$" if you prefer) so _port_in_use reliably returns 0 when the port
is in use across ss and netstat.


check_required_ports() {
local failed=0
for spec in "${NEMOCLAW_GATEWAY_PORT}:gateway" "${NEMOCLAW_DASHBOARD_PORT}:dashboard"; do
local port="${spec%%:*}" label="${spec##*:}"
if _port_in_use "$port"; then
echo "[ERROR] Port $port ($label) is already in use." >&2
echo "[ERROR] Find the process : ss -tlnp | grep :$port" >&2
echo "[ERROR] Or override : export NEMOCLAW_${label^^}_PORT=<free>" >&2
failed=1
fi
done
if [ "$failed" -eq 1 ]; then
echo "" >&2
echo "[ERROR] Free the ports above (or set override env vars), then re-run." >&2
exit 1
fi
echo "[INFO] Ports ${NEMOCLAW_GATEWAY_PORT} (gateway) and ${NEMOCLAW_DASHBOARD_PORT} (dashboard) are free."
}
# ─────────────────────────────────────────────────────────────────────


ensure_supported_runtime() {
command_exists node || error "${RUNTIME_REQUIREMENT_MSG} Node.js was not found on PATH."
command_exists npm || error "${RUNTIME_REQUIREMENT_MSG} npm was not found on PATH."
Expand Down Expand Up @@ -98,6 +139,7 @@ install_nodejs() {
|| { rm -f "$nvm_tmp"; error "Failed to download nvm installer"; }
local actual_hash
if command_exists sha256sum; then
check_required_ports
actual_hash="$(sha256sum "$nvm_tmp" | awk '{print $1}')"
Comment on lines 141 to 143

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: check_required_ports is incorrectly placed inside conditional hash-check logic.

The port check is nested inside the if command_exists sha256sum branch within install_nodejs(). This means:

  1. Skipped if Node.js exists: install_nodejs() returns early at line 129 if node is found, so ports are never checked for existing Node.js installations.
  2. Skipped if sha256sum unavailable: The check only runs in the sha256sum branch, not the shasum fallback (lines 144-145) or the "no tool" fallback (lines 147-148).
  3. Wrong scope: Port availability is a preflight concern independent of Node.js installation.

Move check_required_ports to the beginning of main() before any installation steps.

🐛 Proposed fix

Remove the misplaced call from line 142:

   if command_exists sha256sum; then
-check_required_ports
     actual_hash="$(sha256sum "$nvm_tmp" | awk '{print $1}')"

Add the port check at the start of main():

 main() {
   info "=== NemoClaw Installer ==="
+
+  check_required_ports

   install_nodejs
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@install.sh` around lines 141 - 143, The call to check_required_ports is
incorrectly nested inside install_nodejs’s sha256sum branch; move the check out
of install_nodejs and invoke check_required_ports at the start of main() before
any installation logic so port checks run regardless of Node.js presence or
which checksum tool (sha256sum/shasum) is available; remove the misplaced
check_required_ports invocation inside the command_exists sha256sum conditional
(and ensure no duplicate calls remain).

elif command_exists shasum; then
actual_hash="$(shasum -a 256 "$nvm_tmp" | awk '{print $1}')"
Expand Down