Skip to content

feat: Add script to install the OpenShell CLI and a utility to resolve its binary path.#969

Open
Abmarne wants to merge 2 commits intoNVIDIA:mainfrom
Abmarne:main
Open

feat: Add script to install the OpenShell CLI and a utility to resolve its binary path.#969
Abmarne wants to merge 2 commits intoNVIDIA:mainfrom
Abmarne:main

Conversation

@Abmarne
Copy link
Copy Markdown

@Abmarne Abmarne commented Mar 26, 2026

Fixes: #967

I have identified and addressed the two core issues preventing successful NemoClaw installation and connectivity on Ubuntu 22.04 and 24.04: the cgroup v2 incompatibility on newer Ubuntu versions and the shadowing of the OpenShell binary by an NPM package of the same name.

Summary of Fixes

  1. Resolved cgroup v2 Incompatibility (Ubuntu 24.04):

    • The OpenShell host-side gateway (which embeds k3s) requires cgroupns=host on Ubuntu 24.04 due to the default shift to cgroup v2.
    • Solution: Users should run sudo ./scripts/setup-spark.sh (or manually configure /etc/docker/daemon.json) to adjust the Docker daemon configuration. This ensures the k3s cluster inside the gateway container can manage pod network namespaces correctly.
  2. Resolved OpenShell Binary Shadowing (NPM conflict):

    • The openclaw dependency pulls in an NPM package named openshell which provides a Node.js server. On some systems, this package shadows the OpenShell Rust CLI on the system PATH, causing NemoClaw to run the wrong binary and crash during step 3/7 of the onboarding.
    • Fixed bin/lib/resolve-openshell.js: Updated the binary resolution logic to explicitly verify that any openshell binary found on PATH is the Rust CLI (checked via -V and specific version string regex) and not the Node.js package.
    • Fixed scripts/install-openshell.sh: Updated the installation script to use openshell -V for version detection. It now correctly identifies shadowing NPM packages and proceeds with the CLI installation even if that name is already taken on the PATH.
    • Enhanced Onboarding Preflight: The isOpenshellInstalled() check in nemoclaw onboard now uses the robust resolution logic, ensuring it properly detects and fixes shadowing issues during the initial setup.

Detailed Actions Taken

  • Robust Resolution: Added an isRustCli() helper to bin/lib/resolve-openshell.js that performs a 2-second timeout version check to distinguish between system binaries and Node.js packages.
  • Installation Integrity: Modified scripts/install-openshell.sh to prevent it from skipping installation when a "fake" NPM openshell binary is present.
  • Clear Messaging: Improved the error messages when nemoclaw detects a shadowing binary, warning the user and attempting to reinstall the correct CLI to a local or system path.

Verification of Fix

With these changes, even if a user has the openshell NPM package installed, nemoclaw onboard will:

  1. Correcty identify that the OpenShell Rust CLI is missing or shadowed.
  2. Install the correct Rust binary to ~/.local/bin or /usr/local/bin.
  3. Prefer the Rust binary over the NPM package when starting the host gateway.

Summary by CodeRabbit

  • Bug Fixes
    • More reliable detection and validation of the installed openshell CLI so the correct Rust-based binary is identified and used.
    • Installer now times out unresponsive binaries to avoid hangs during detection.
    • Improved handling and clearer warnings when a different (shadowing) executable is found in PATH, proceeding to install or upgrade as needed.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

Added runtime validation to ensure resolved openshell executables are the Rust CLI binary. A new internal isRustCli(path) runs openshell -V under a short timeout and checks output; resolution and installation paths now require that check before accepting or comparing versions.

Changes

Cohort / File(s) Summary
Binary Resolution Validation
bin/lib/resolve-openshell.js
Added internal isRustCli(path) that executes the candidate with -V (2s timeout) and matches openshell <semver>. Updated command-v and fallback checks to require isRustCli() and executable permission before returning a candidate.
Installation Script Validation
scripts/install-openshell.sh
Switched detection to openshell -V (wrapped with timeout/gtimeout when available); validate output pattern before extracting semver. If output doesn't match Rust CLI format, warn about PATH shadowing and skip version compare, proceeding to install/upgrade.

Sequence Diagram(s)

sequenceDiagram
  participant Installer as Installer Script
  participant Resolver as resolve-openshell.js
  participant Candidate as Candidate Binary
  participant Timeout as timeout/gtimeout

  Installer->>Resolver: ask for `openshell` path
  Resolver->>Candidate: attempt `command -v` or candidate path
  alt candidate found
    Resolver->>Timeout: run `Candidate -V` (2s)
    Timeout->>Candidate: execute
    Candidate-->>Timeout: stdout/stderr (maybe hangs)
    Timeout-->>Resolver: output or timeout
    Resolver->>Resolver: match /^openshell \d+\.\d+\.\d+/
    alt match
      Resolver-->>Installer: return candidate path
    else no match / timeout / error
      Resolver-->>Installer: reject candidate (continue search)
    end
  else no candidate
    Resolver-->>Installer: no binary found
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I sniffed the PATH where shadows play,
I ran a -V to chase them away.
A timeout hop, a semver peep—
Now the true Rust binary we keep. 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding a script to install OpenShell CLI and a utility to resolve its binary path.
Linked Issues check ✅ Passed The changes directly address all objectives from issue #967: detecting npm shadowing with isRustCli(), improving install-openshell.sh to verify the correct binary, and preventing resolveOpenshell() from selecting the npm package.
Out of Scope Changes check ✅ Passed All changes are focused on resolving the OpenShell binary shadowing problem and improving installation reliability as outlined in issue #967; no out-of-scope changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/lib/resolve-openshell.js`:
- Around line 24-33: The isRustCli function currently uses execSync with a
shell-interpolated command (`${path} -V`) which allows command injection and
breaks on paths with spaces; change to use execFileSync to invoke the binary
directly: add execFileSync to the child_process import (e.g., include
execFileSync alongside execSync) and replace the execSync call in isRustCli with
execFileSync(path, ["-V"], { encoding: "utf-8", stdio: ["ignore","pipe","pipe"],
timeout: 2000 }) so the version check runs without shell parsing and handles
spaced paths safely.

In `@scripts/install-openshell.sh`:
- Line 56: The unbounded probe VERSION_OUT="$(openshell -V 2>&1 || echo "")" can
hang; replace it with a bounded timeout call: attempt to use the GNU timeout
binary first (timeout -s KILL 2s ...) and fall back to gtimeout for BSD, running
openshell -V under that 2s timeout and capturing stdout/stderr into VERSION_OUT
(preserving the existing || echo "" fallback if the timeout/command fails);
update the assignment that uses VERSION_OUT so it calls the timeout wrapper
around the openshell -V invocation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 919e053b-322e-4503-8193-78b12f435ba4

📥 Commits

Reviewing files that changed from the base of the PR and between f0f53e4 and ecb9ac0.

📒 Files selected for processing (2)
  • bin/lib/resolve-openshell.js
  • scripts/install-openshell.sh

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
bin/lib/resolve-openshell.js (1)

7-36: JSDoc for resolveOpenshell is now separated from its function.

The original JSDoc block (lines 7-18) documents resolveOpenshell, but the new isRustCli function was inserted between them, creating visual confusion. Consider reorganizing:

📝 Suggested reorganization

Move isRustCli above the JSDoc for resolveOpenshell, or place it after resolveOpenshell (hoisting will still work):

 const { execSync, execFileSync } = require("child_process");
 const fs = require("fs");

+/**
+ * Verify if the binary is the OpenShell Rust CLI (and not a shadowing NPM package).
+ * `@param` {string} path Absolute path to binary
+ * `@returns` {boolean}
+ */
+function isRustCli(path) {
+  try {
+    const output = execFileSync(path, ["-V"], {
+      encoding: "utf-8",
+      stdio: ["ignore", "pipe", "pipe"],
+      timeout: 2000,
+    }).trim();
+    return /^openshell\s+[0-9]+\.[0-9]+\.[0-9]+/i.test(output);
+  } catch {
+    return false;
+  }
+}
+
 /**
  * Resolve the openshell binary path.
  * ...
  */
-/**
- * Verify if the binary is the OpenShell Rust CLI...
- */
-function isRustCli(path) { ... }
-
 function resolveOpenshell(opts = {}) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/resolve-openshell.js` around lines 7 - 36, The JSDoc for
resolveOpenshell is separated from its function by the newly inserted isRustCli,
which causes visual confusion; fix it by relocating isRustCli so the docblock
immediately precedes resolveOpenshell (either move isRustCli above the JSDoc or
place it after the resolveOpenshell function), or alternatively move the JSDoc
to sit directly above isRustCli and add a new JSDoc for resolveOpenshell—ensure
the resolveOpenshell JSDoc and the resolveOpenshell function are adjacent and
keep the isRustCli declaration near related utility functions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bin/lib/resolve-openshell.js`:
- Around line 7-36: The JSDoc for resolveOpenshell is separated from its
function by the newly inserted isRustCli, which causes visual confusion; fix it
by relocating isRustCli so the docblock immediately precedes resolveOpenshell
(either move isRustCli above the JSDoc or place it after the resolveOpenshell
function), or alternatively move the JSDoc to sit directly above isRustCli and
add a new JSDoc for resolveOpenshell—ensure the resolveOpenshell JSDoc and the
resolveOpenshell function are adjacent and keep the isRustCli declaration near
related utility functions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9b55bf1e-eee5-4a81-99a3-8e97c5709497

📥 Commits

Reviewing files that changed from the base of the PR and between ecb9ac0 and 48da3db.

📒 Files selected for processing (2)
  • bin/lib/resolve-openshell.js
  • scripts/install-openshell.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/install-openshell.sh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

install.sh fails at step 3/7 — OpenShell CLI binary not installed, npm package shadows it

1 participant