Skip to content

feat(onboard): add user-local Ollama install fallback for non-interactive Linux (#4114)#4135

Merged
cv merged 3 commits into
mainfrom
feat/ollama-user-local-install-4114
May 23, 2026
Merged

feat(onboard): add user-local Ollama install fallback for non-interactive Linux (#4114)#4135
cv merged 3 commits into
mainfrom
feat/ollama-user-local-install-4114

Conversation

@laitingsheng
Copy link
Copy Markdown
Contributor

@laitingsheng laitingsheng commented May 23, 2026

Summary

NEMOCLAW_PROVIDER=install-ollama calls https://ollama.com/install.sh, which is sudo-bound with no opt-out env var. A headless NEMOCLAW_NON_INTERACTIVE=1 run without passwordless sudo aborts at the password prompt mid-install.

Add a sudo-free user-local install mode that replicates the binary-extraction portion of install.sh (L159-187) into ${HOME}/.local. Auto-detect mirrors scripts/install-openshell.sh L385-429: root or passwordless sudo → system; non-interactive without sudo → user-local; interactive → system so sudo can prompt. NEMOCLAW_OLLAMA_INSTALL_MODE=user/system pins explicitly.

Related Issue

Resolves #4114

Changes

  • src/lib/onboard/install-ollama-linux.ts — new module holding the mode-decision logic plus the system and user-local install paths.
  • src/lib/onboard.ts — Linux install branch collapses to a single helper call (net +7/-47).
  • docs/inference/use-local-inference.mdx — new "Linux Install Modes" subsection.
  • docs/reference/commands.mdx — env-var allowlist entry for NEMOCLAW_OLLAMA_INSTALL_MODE.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • make docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Tinson Lai tinsonl@nvidia.com

Summary by CodeRabbit

  • Documentation

    • Added a "Linux Install Modes" section explaining system vs user-local Ollama installs, PATH/relaunch notes, service/CUDA/driver differences, zstd installation guidance for apt vs non-apt setups, and the NEMOCLAW_OLLAMA_INSTALL_MODE override.
  • New Features

    • Installer now supports explicit system or user-local install modes and a non-interactive/user-local fallback when root/sudo is unavailable.
  • Tests

    • Expanded tests for mode selection, archive/arch handling, zstd behavior, JetPack variants, and non-interactive fallback scenarios.

Review Change Stack

…tive Linux

The `install-ollama` provider previously called `https://ollama.com/install.sh`
unconditionally. The official installer is sudo-bound, so a headless
`NEMOCLAW_NON_INTERACTIVE=1` run without passwordless sudo crashed at the
sudo password prompt mid-install.

Replicate the binary-extraction portion of `install.sh` (lines 159-187) into
`src/lib/onboard/install-ollama-linux.ts`, targeting `${HOME}/.local` and
running without sudo, systemd, or the `ollama` system user. Use the same
auto-detect decision tree as `scripts/install-openshell.sh` (root or
passwordless sudo → system; non-interactive headless → user-local;
interactive → system so sudo can prompt). Add `NEMOCLAW_OLLAMA_INSTALL_MODE`
for explicit override.

Resolves #4114

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 23, 2026

📝 Walkthrough

Walkthrough

Adds a new Linux installer module that selects system vs user-local Ollama installs, integrates it into onboarding (replacing inline shell logic), updates docs for install modes and zstd guidance, and adds comprehensive tests for mode decision and both install paths.

Changes

Linux Ollama Install Modes

Layer / File(s) Summary
Documentation: Install Modes and Environment Variable
docs/inference/use-local-inference.mdx, docs/reference/commands.mdx
Documents automatic system vs user-local selection, NEMOCLAW_OLLAMA_INSTALL_MODE override, behavioral differences (systemd/CUDA/ post-reboot relaunch), and expanded zstd guidance for Debian/Ubuntu vs user-local installs.
Installer core: types, wiring, mode resolution
src/lib/onboard/install-ollama-linux.ts
Adds types, DI/test seams, and mode resolution (env/root/sudo/TTY) plus helper probes used across install paths.
Arch & JetPack detection
src/lib/onboard/install-ollama-linux.ts
Maps Node arch to Ollama tarball arch and detects JetPack variants from /etc/nv_tegra_release to select add-on tarballs.
User-local install implementation
src/lib/onboard/install-ollama-linux.ts
Probes for .tar.zst vs .tgz, handles zstd presence or manual-install hints, extracts into ~/.local, starts daemon with nohup/OLLAMA_HOST bound to 127.0.0.1, logs PATH hints, and returns structured result.
System install implementation & systemd override
src/lib/onboard/install-ollama-linux.ts
Runs official install.sh, bootstraps/expects zstd for extraction (apt via sudo when needed), applies managed systemd loopback override, and handles fallback/manual start and failure returns.
Entry point: installOllamaOnLinux
src/lib/onboard/install-ollama-linux.ts
Resolves mode and dispatches to the matching install path, returning InstallOllamaLinuxResult.
Onboarding integration: replace inline installer with new module
src/lib/onboard.ts
Replaces prior inline Linux install flow with installOllamaOnLinux({ isNonInteractive }), updates systemd helper import, and removes old zstd prerequisite helper.
Installer unit tests
src/lib/onboard/install-ollama-linux.test.ts
Vitest coverage for arch mapping, decideInstallOllamaLinuxMode (env precedence/root/sudo/TTY), user-local extraction/start, .tgz fallback, zstd hints, JetPack selection, daemon failures, and PATH warnings.
Onboard-selection tests
test/onboard-selection.test.ts
Pins several subprocess tests to NEMOCLAW_OLLAMA_INSTALL_MODE=system for deterministic no-TTY behavior and adds #4114 test for non-interactive user-local fallback when passwordless sudo is unavailable.

Sequence Diagram(s)

sequenceDiagram
  participant Onboard as onboard.ts
  participant Decide as decideInstallOllamaLinuxMode
  participant System as installOllamaSystem
  participant UserLocal as installOllamaUserLocal
  participant Daemon as OllamaDaemon
  Onboard->>Decide: resolve mode (env / root / sudo / TTY)
  Decide-->>Onboard: "user-local"
  Onboard->>UserLocal: download & extract to ~/.local
  UserLocal->>Daemon: start serve (127.0.0.1) via nohup
  UserLocal->>UserLocal: waitForHttp readiness
  UserLocal-->>Onboard: InstallOllamaLinuxResult{ok,true}
  Onboard->>Decide: resolve mode (env / root / sudo / TTY)
  Decide-->>Onboard: "system"
  Onboard->>System: run official install.sh (may use sudo)
  System->>System: apply systemd loopback override
  System->>Daemon: systemd start or manual fallback
  System->>System: waitForHttp readiness
  System-->>Onboard: InstallOllamaLinuxResult{ok,true}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#4122: Modifies Ollama onboarding tests and systemd loopback override handling; overlaps test harness and install-mode behavior.
  • NVIDIA/NemoClaw#4108: Touches systemd loopback override paths and CUDA driver configuration related to Ollama system installs.

Suggested labels

Provider: Ollama, Local Models, documentation

Suggested reviewers

  • ericksoa
  • cv

Poem

🐇 I nibble tarballs down in ~/.local light,
When sudo is sleeping in the headless night.
Two paths I learned—system or local delight,
I patch the flow and start the daemon right.
Hop, patch, and ship—a tiny rabbit byte!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.63% 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 PR title accurately summarizes the main change: adding a user-local Ollama install fallback for non-interactive Linux environments, directly addressing issue #4114.
Linked Issues check ✅ Passed The code changes comprehensively implement all requirements from issue #4114: user-local install fallback for aarch64/amd64 Linux, automatic selection when system installer unavailable, and NEMOCLAW_OLLAMA_INSTALL_MODE environment variable override.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the user-local Ollama install fallback feature; documentation updates, code refactoring in onboard.ts, and test additions all support the primary objective.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ollama-user-local-install-4114

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 23, 2026

E2E Advisor Recommendation

Required E2E: gpu-repo-local-ollama-openclaw, gpu-e2e
Optional E2E: gpu-double-onboard-e2e, onboard-negative-paths-e2e, docs-validation-e2e

Dispatch hint: gpu-e2e

Auto-dispatched E2E: gpu-e2e via nightly-e2e.yaml at 840614d8c69d3432ad0627ec8d5b17e2089c1f1cnightly run

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • gpu-repo-local-ollama-openclaw (high): Most targeted scenario coverage for local Ollama onboarding on a GPU runner: runs nemoclaw onboard with NEMOCLAW_PROVIDER=ollama, then validates local Ollama inference and the Ollama proxy path through the sandbox.
  • gpu-e2e (high): Legacy dispatchable E2E for the real local Ollama user flow: installer/onboard with NEMOCLAW_PROVIDER=ollama, Ollama loopback startup, auth proxy, model pull, sandbox creation, and inference through the sandbox. This should catch integration regressions from replacing the inline Linux install/start logic.

Optional E2E

  • gpu-double-onboard-e2e (high): Useful adjacent confidence for Ollama re-onboarding and persisted auth-proxy token consistency after changes to how Linux Ollama is installed and started.
  • onboard-negative-paths-e2e (medium): Adjacent coverage for onboarding failure handling. The PR adds new invalid/failing Linux Ollama install branches, though the existing E2E may not cover the exact user-local zstd/architecture cases.
  • docs-validation-e2e (low): Docs changed the local inference guide and command reference environment-variable table; docs validation can catch command/reference drift and broken docs examples.

New E2E recommendations

  • linux-ollama-user-local-install (high): Existing GPU/local Ollama E2Es may run on hosts with root/passwordless sudo or preinstalled Ollama, so they do not reliably exercise the new non-interactive, no-passwordless-sudo ${HOME}/.local install path.
    • Suggested test: Add a fresh Linux E2E that forces NEMOCLAW_OLLAMA_INSTALL_MODE=user or runs as a non-root user without passwordless sudo, verifies Ollama is extracted to ${HOME}/.local/bin, starts on 127.0.0.1, prints the PATH hint when needed, then completes local Ollama onboarding through the authenticated proxy and sandbox inference.
  • linux-ollama-user-local-negative-paths (medium): The new user-local mode has distinct hard-fail behavior for missing zstd and unsupported architectures that is currently covered by unit tests but not by an end-to-end onboarding transcript.
    • Suggested test: Add an E2E negative-path case for user-local Ollama install with zstd unavailable that asserts the command exits before saving an inference route and prints per-distro install hints.

Dispatch hint

  • Workflow: nightly-e2e.yaml
  • jobs input: gpu-e2e

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 23, 2026

E2E Scenario Advisor Recommendation

Required scenario E2E: None
Optional scenario E2E: None

Workflow run

Full scenario advisor summary

E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required scenario E2E

  • None. No scenario workflow, scenario metadata, scenario runtime, or validation-suite files changed.

Optional scenario E2E

  • None.

Relevant changed files

  • None.

@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ⚠️ No requested jobs ran

Run: 26341940380
Target ref: 6e479e5c1b9b3f87304d7fc2ecd428293f1f6718
Workflow ref: main
Requested jobs: gpu-e2e
Summary: 0 passed, 0 failed, 1 skipped

Job Result
gpu-e2e ⏭️ skipped

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 23, 2026

PR Review Advisor

Findings: 1 needs attention, 1 worth checking, 0 nice ideas
Since last review: 1 prior item resolved, 2 still apply, 0 new items found

Review findings

🛠️ Needs attention

  • New installer helper exceeds the monolith growth threshold (src/lib/onboard/install-ollama-linux.ts:1): The previous architecture finding still applies. This PR introduces a new 410-line onboarding installer module in a high-risk host-glue area. The deterministic monolith check marks this as a blocker because the current monolith grew by more than 20 lines without extraction or offsetting consolidation.
    • Recommendation: Split the module into smaller units, for example mode decision, artifact selection/probing, user-local extraction, system install, daemon launch, and dependency-hint helpers, or offset the growth with equivalent extraction from existing onboarding code before merge.
    • Evidence: monolithDeltas reports src/lib/onboard/install-ollama-linux.ts with baseLines 0, headLines 410, delta 410, severity blocker. Drift evidence shows the file is new, while src/lib/onboard.ts remains active but net-negative by 40 lines.

🔎 Worth checking

  • User-local path installs executable tarballs without version or integrity verification (src/lib/onboard/install-ollama-linux.ts:249): The previous installer-trust finding still applies. The new user-local installer downloads and extracts Ollama release tarballs directly from ollama.com and immediately launches the resulting binary. HTTPS is used, but there is no pinned version, checksum, signature, or digest verification for the executable artifact, so the installed binary can drift independently of the NemoClaw release and cannot be audited from this PR.
    • Recommendation: Prefer a pinned release artifact with a checked SHA256/signature, or document and implement an explicit trust boundary equivalent to the existing official installer path. Add a negative test for checksum/signature failure if verification is added.
    • Evidence: downloadAndExtractUserLocal builds https://ollama.com/download/${tarballName}.tar.zst or .tgz, extracts it into ${HOME}/.local, and startUserLocalOllamaDaemon launches ${HOME}/.local/bin/ollama. The changed tests cover tarball selection and fallback but do not validate artifact integrity.

🌱 Nice ideas

  • None.
Since last review details

Current findings:

  • New installer helper exceeds the monolith growth threshold (src/lib/onboard/install-ollama-linux.ts:1): The previous architecture finding still applies. This PR introduces a new 410-line onboarding installer module in a high-risk host-glue area. The deterministic monolith check marks this as a blocker because the current monolith grew by more than 20 lines without extraction or offsetting consolidation.
    • Recommendation: Split the module into smaller units, for example mode decision, artifact selection/probing, user-local extraction, system install, daemon launch, and dependency-hint helpers, or offset the growth with equivalent extraction from existing onboarding code before merge.
    • Evidence: monolithDeltas reports src/lib/onboard/install-ollama-linux.ts with baseLines 0, headLines 410, delta 410, severity blocker. Drift evidence shows the file is new, while src/lib/onboard.ts remains active but net-negative by 40 lines.
  • User-local path installs executable tarballs without version or integrity verification (src/lib/onboard/install-ollama-linux.ts:249): The previous installer-trust finding still applies. The new user-local installer downloads and extracts Ollama release tarballs directly from ollama.com and immediately launches the resulting binary. HTTPS is used, but there is no pinned version, checksum, signature, or digest verification for the executable artifact, so the installed binary can drift independently of the NemoClaw release and cannot be audited from this PR.
    • Recommendation: Prefer a pinned release artifact with a checked SHA256/signature, or document and implement an explicit trust boundary equivalent to the existing official installer path. Add a negative test for checksum/signature failure if verification is added.
    • Evidence: downloadAndExtractUserLocal builds https://ollama.com/download/${tarballName}.tar.zst or .tgz, extracts it into ${HOME}/.local, and startUserLocalOllamaDaemon launches ${HOME}/.local/bin/ollama. The changed tests cover tarball selection and fallback but do not validate artifact integrity.

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

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

🧹 Nitpick comments (2)
docs/inference/use-local-inference.mdx (2)

47-47: ⚡ Quick win

Split this into one sentence per source line.

Line 47 currently packs multiple sentences onto one line. Please break each sentence onto its own line.

As per coding guidelines, "One sentence per line in source (makes diffs readable). Flag paragraphs where multiple sentences appear on the same line."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/inference/use-local-inference.mdx` at line 47, The paragraph beginning
"The user-local install replicates only the binary extraction step of the
official installer." currently contains multiple sentences on one source line;
split it so each sentence is on its own line (e.g., break after "...official
installer.", after "It downloads the release tarball, extracts it to
`${HOME}/.local`, and launches `${HOME}/.local/bin/ollama serve` once.", after
"It does not configure a systemd service, does not create the `ollama` system
user, and does not install CUDA drivers,", and after "so the daemon must be
relaunched manually after a reboot."), ensuring one sentence per line for the
paragraph that starts with that phrase.

39-39: ⚡ Quick win

Use active voice for the mode-selection sentence.

Line 39 is passive ("The choice is made automatically"). Please rewrite it in active voice.

As per coding guidelines, "Active voice required. Flag passive constructions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/inference/use-local-inference.mdx` at line 39, Replace the passive
sentence "The choice is made automatically" with an active-voice sentence; e.g.,
change it to "The system selects the mode automatically" (or "We automatically
select the mode") so the document uses active voice for the mode-selection
statement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/onboard/install-ollama-linux.ts`:
- Around line 250-259: The shell commands in install-ollama-linux.ts interpolate
installDir and binPath directly into strings passed to runShellImpl (see the
lines building commands with zstUrl/tgzUrl and the tar/curl pipelines), which
breaks if the home path contains single quotes; update those call sites to
safely quote or escape shell arguments (installDir and binPath) before building
the command string—either use a shell-quoting helper (e.g., a shellEscape
function) around installDir/binPath or stop interpolating and call
runShellImpl/child_process with an argv array so arguments are passed safely;
ensure all occurrences that reference installDir/binPath (including the commands
using zstUrl, tgzUrl and the tar/curl pipelines) are updated.

In `@test/onboard-selection.test.ts`:
- Around line 5800-5813: The spawnSync call that creates `result` can hang if a
sudo prompt appears; update the options object passed to
`spawnSync(process.execPath, [scriptPath], {...})` to include a `timeout` (e.g.,
5000–10000 ms) so the test fails deterministically instead of hanging, by adding
`timeout: <ms>` to the options; keep the rest of the env and encoding settings
the same and adjust any assertions if needed to account for a timeout-terminated
process.

---

Nitpick comments:
In `@docs/inference/use-local-inference.mdx`:
- Line 47: The paragraph beginning "The user-local install replicates only the
binary extraction step of the official installer." currently contains multiple
sentences on one source line; split it so each sentence is on its own line
(e.g., break after "...official installer.", after "It downloads the release
tarball, extracts it to `${HOME}/.local`, and launches
`${HOME}/.local/bin/ollama serve` once.", after "It does not configure a systemd
service, does not create the `ollama` system user, and does not install CUDA
drivers,", and after "so the daemon must be relaunched manually after a
reboot."), ensuring one sentence per line for the paragraph that starts with
that phrase.
- Line 39: Replace the passive sentence "The choice is made automatically" with
an active-voice sentence; e.g., change it to "The system selects the mode
automatically" (or "We automatically select the mode") so the document uses
active voice for the mode-selection statement.
🪄 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: Enterprise

Run ID: 4e3ae3a9-9cba-4067-806c-6471ca34fa59

📥 Commits

Reviewing files that changed from the base of the PR and between 7d8e67d and 6e479e5.

📒 Files selected for processing (6)
  • docs/inference/use-local-inference.mdx
  • docs/reference/commands.mdx
  • src/lib/onboard.ts
  • src/lib/onboard/install-ollama-linux.test.ts
  • src/lib/onboard/install-ollama-linux.ts
  • test/onboard-selection.test.ts

Comment thread src/lib/onboard/install-ollama-linux.ts Outdated
Comment on lines +5800 to +5813
const result = spawnSync(process.execPath, [scriptPath], {
cwd: repoRoot,
encoding: "utf-8",
env: {
...process.env,
HOME: tmpDir,
PATH: `${fakeBin}:${process.env.PATH || ""}`,
NEMOCLAW_NON_INTERACTIVE: "1",
NEMOCLAW_PROVIDER: "ollama",
NEMOCLAW_YES: "1",
// No NEMOCLAW_OLLAMA_INSTALL_MODE — auto-detect routes through
// user-local because the stubbed `sudo -n true` returns exit 1.
},
});
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 | 🟡 Minor | ⚡ Quick win

Add a spawnSync timeout to this fallback regression test.

This case is specifically guarding against a non-interactive sudo hang. Without timeout on spawnSync, a regression back to a password prompt can wedge the worker instead of failing deterministically.

Suggested fix
     const result = spawnSync(process.execPath, [scriptPath], {
       cwd: repoRoot,
       encoding: "utf-8",
+      timeout: PROVIDER_SELECTION_TEST_TIMEOUT_MS,
       env: {
         ...process.env,
         HOME: tmpDir,
         PATH: `${fakeBin}:${process.env.PATH || ""}`,
         NEMOCLAW_NON_INTERACTIVE: "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
const result = spawnSync(process.execPath, [scriptPath], {
cwd: repoRoot,
encoding: "utf-8",
env: {
...process.env,
HOME: tmpDir,
PATH: `${fakeBin}:${process.env.PATH || ""}`,
NEMOCLAW_NON_INTERACTIVE: "1",
NEMOCLAW_PROVIDER: "ollama",
NEMOCLAW_YES: "1",
// No NEMOCLAW_OLLAMA_INSTALL_MODE — auto-detect routes through
// user-local because the stubbed `sudo -n true` returns exit 1.
},
});
const result = spawnSync(process.execPath, [scriptPath], {
cwd: repoRoot,
encoding: "utf-8",
timeout: PROVIDER_SELECTION_TEST_TIMEOUT_MS,
env: {
...process.env,
HOME: tmpDir,
PATH: `${fakeBin}:${process.env.PATH || ""}`,
NEMOCLAW_NON_INTERACTIVE: "1",
NEMOCLAW_PROVIDER: "ollama",
NEMOCLAW_YES: "1",
// No NEMOCLAW_OLLAMA_INSTALL_MODE — auto-detect routes through
// user-local because the stubbed `sudo -n true` returns exit 1.
},
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/onboard-selection.test.ts` around lines 5800 - 5813, The spawnSync call
that creates `result` can hang if a sudo prompt appears; update the options
object passed to `spawnSync(process.execPath, [scriptPath], {...})` to include a
`timeout` (e.g., 5000–10000 ms) so the test fails deterministically instead of
hanging, by adding `timeout: <ms>` to the options; keep the rest of the env and
encoding settings the same and adjust any assertions if needed to account for a
timeout-terminated process.

@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ⚠️ No requested jobs ran

Run: 26342353106
Target ref: 42ec7f5a435ae78ed7ee80dbe1351bd115771dcd
Workflow ref: main
Requested jobs: gpu-e2e
Summary: 0 passed, 0 failed, 1 skipped

Job Result
gpu-e2e ⏭️ skipped

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@github-actions
Copy link
Copy Markdown
Contributor

Selective E2E Results — ⚠️ No requested jobs ran

Run: 26342891533
Target ref: 840614d8c69d3432ad0627ec8d5b17e2089c1f1c
Workflow ref: main
Requested jobs: gpu-e2e
Summary: 0 passed, 0 failed, 1 skipped

Job Result
gpu-e2e ⏭️ skipped

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/onboard-selection.test.ts (1)

5842-5874: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert that the fallback stays fully non-interactive.

This test records promptCalls, but never checks it. If onboarding accidentally starts prompting again, the stubbed credentials.prompt will feed empty strings and this case can still pass. Add an explicit promptCalls === 0 assertion here.

Suggested fix
     const payload = JSON.parse(result.stdout.trim());
 
+    assert.equal(payload.promptCalls, 0);
     assert.equal(payload.result.provider, "ollama-local");
     assert.ok(
       !payload.runCommands.some((cmd: string) => cmd.includes("ollama.com/install.sh")),
       "User-local install must NOT run the official curl|sh installer",
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/onboard-selection.test.ts` around lines 5842 - 5874, The test parses
payload but never asserts the recorded promptCalls; add an explicit assertion
that promptCalls is zero (e.g., using assert.equal or assert.strictEqual) to
ensure onboarding remained fully non-interactive. Place this check after payload
is parsed (after const payload = JSON.parse(...)) and before other
payload.runCommands assertions, referencing the existing promptCalls variable so
the test fails if any credential prompt was invoked.
♻️ Duplicate comments (1)
test/onboard-selection.test.ts (1)

5825-5838: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a timeout to the fallback subprocess.

This regression test is meant to catch non-interactive install failures, but the outer spawnSync can still hang indefinitely if the child script regresses into a blocking path. Make it fail deterministically instead.

Suggested fix
     const result = spawnSync(process.execPath, [scriptPath], {
       cwd: repoRoot,
       encoding: "utf-8",
       env: {
         ...process.env,
         HOME: tmpDir,
         PATH: `${fakeBin}:${process.env.PATH || ""}`,
         NEMOCLAW_NON_INTERACTIVE: "1",
         NEMOCLAW_PROVIDER: "ollama",
         NEMOCLAW_YES: "1",
         // No NEMOCLAW_OLLAMA_INSTALL_MODE — auto-detect routes through
         // user-local because the stubbed `sudo -n true` returns exit 1.
       },
+      timeout: PROVIDER_SELECTION_TEST_TIMEOUT_MS,
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/onboard-selection.test.ts` around lines 5825 - 5838, The spawnSync call
that runs the test harness (the variable result created by
spawnSync(process.execPath, [scriptPath], { ... })) can hang indefinitely; add a
deterministic timeout to the options passed into spawnSync (e.g., timeout in
milliseconds) so the child is killed and the test fails fast; update the same
options object (where HOME, PATH, NEMOCLAW_* env are set) to include timeout and
optionally killSignal/encoding/maxBuffer to ensure the subprocess is terminated
and output captured reliably.
🧹 Nitpick comments (3)
docs/inference/use-local-inference.mdx (2)

158-158: ⚡ Quick win

Rewrite this sentence in active voice.

Line 158 uses passive construction (“are still passed through”). Use active voice with a clear actor.
As per coding guidelines, "Active voice required. Flag passive constructions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/inference/use-local-inference.mdx` at line 158, Replace the passive
clause "are still passed through" with an active actor: rewrite the sentence to
say that the bootstrap registry passes unknown or custom tags through to the
Ollama runner, and mention that the Ollama runner validates the choice (e.g.,
"The bootstrap registry passes unknown or custom tags through to the Ollama
runner, which validates the choice itself.").

50-50: ⚡ Quick win

Use active voice for the reboot behavior.

Line 50 switches to passive voice (“must be relaunched manually”). Rewrite this as a direct action to keep the doc voice consistent.
As per coding guidelines, "Active voice required. Flag passive constructions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/inference/use-local-inference.mdx` at line 50, Rewrite the passive
sentence "must be relaunched manually after a reboot" into active voice; for
example, change the clause in the sentence that reads "so the daemon must be
relaunched manually after a reboot" to an active construction such as "so you
must relaunch the daemon manually after a reboot" (update the line that contains
"does not configure a systemd service, does not create the `ollama` system user,
and does not install CUDA drivers, so the daemon must be relaunched manually
after a reboot" accordingly).
src/lib/onboard/install-ollama-linux.ts (1)

384-393: 💤 Low value

Consider using the full binary path in the manual fallback launch.

The manual fallback at line 386 uses ollama serve without an explicit path, relying on /usr/local/bin being in PATH. For consistency with the returned binPath and robustness in edge-case shell environments, consider using the full path.

Suggested fix
   if (overrideState === "not-applicable" && !findReachableOllamaHostImpl()) {
     log("  Starting Ollama...");
-    runShellImpl(`OLLAMA_HOST=127.0.0.1:${OLLAMA_PORT} ollama serve > /dev/null 2>&1 &`, {
+    runShellImpl(`OLLAMA_HOST=127.0.0.1:${OLLAMA_PORT} /usr/local/bin/ollama serve > /dev/null 2>&1 &`, {
       ignoreError: true,
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/install-ollama-linux.ts` around lines 384 - 393, The fallback
launch uses a bare "ollama serve" which may fail if /usr/local/bin isn't in
PATH; update the runShellImpl invocation in the overrideState ===
"not-applicable" branch (the block that checks findReachableOllamaHostImpl() and
uses OLLAMA_PORT) to call the explicit binary path (matching the returned
binPath "/usr/local/bin/ollama") when invoking the daemon, leaving the rest of
the command, ignoreError option, and subsequent waitForHttpImpl and errorLog
logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@test/onboard-selection.test.ts`:
- Around line 5842-5874: The test parses payload but never asserts the recorded
promptCalls; add an explicit assertion that promptCalls is zero (e.g., using
assert.equal or assert.strictEqual) to ensure onboarding remained fully
non-interactive. Place this check after payload is parsed (after const payload =
JSON.parse(...)) and before other payload.runCommands assertions, referencing
the existing promptCalls variable so the test fails if any credential prompt was
invoked.

---

Duplicate comments:
In `@test/onboard-selection.test.ts`:
- Around line 5825-5838: The spawnSync call that runs the test harness (the
variable result created by spawnSync(process.execPath, [scriptPath], { ... }))
can hang indefinitely; add a deterministic timeout to the options passed into
spawnSync (e.g., timeout in milliseconds) so the child is killed and the test
fails fast; update the same options object (where HOME, PATH, NEMOCLAW_* env are
set) to include timeout and optionally killSignal/encoding/maxBuffer to ensure
the subprocess is terminated and output captured reliably.

---

Nitpick comments:
In `@docs/inference/use-local-inference.mdx`:
- Line 158: Replace the passive clause "are still passed through" with an active
actor: rewrite the sentence to say that the bootstrap registry passes unknown or
custom tags through to the Ollama runner, and mention that the Ollama runner
validates the choice (e.g., "The bootstrap registry passes unknown or custom
tags through to the Ollama runner, which validates the choice itself.").
- Line 50: Rewrite the passive sentence "must be relaunched manually after a
reboot" into active voice; for example, change the clause in the sentence that
reads "so the daemon must be relaunched manually after a reboot" to an active
construction such as "so you must relaunch the daemon manually after a reboot"
(update the line that contains "does not configure a systemd service, does not
create the `ollama` system user, and does not install CUDA drivers, so the
daemon must be relaunched manually after a reboot" accordingly).

In `@src/lib/onboard/install-ollama-linux.ts`:
- Around line 384-393: The fallback launch uses a bare "ollama serve" which may
fail if /usr/local/bin isn't in PATH; update the runShellImpl invocation in the
overrideState === "not-applicable" branch (the block that checks
findReachableOllamaHostImpl() and uses OLLAMA_PORT) to call the explicit binary
path (matching the returned binPath "/usr/local/bin/ollama") when invoking the
daemon, leaving the rest of the command, ignoreError option, and subsequent
waitForHttpImpl and errorLog logic unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 247b1fa4-968a-4580-af77-78aae5002af5

📥 Commits

Reviewing files that changed from the base of the PR and between 6e479e5 and 840614d.

📒 Files selected for processing (5)
  • docs/inference/use-local-inference.mdx
  • src/lib/onboard.ts
  • src/lib/onboard/install-ollama-linux.test.ts
  • src/lib/onboard/install-ollama-linux.ts
  • test/onboard-selection.test.ts

@cv cv merged commit 527171c into main May 23, 2026
31 checks passed
@cv cv added the v0.0.51 Release target label May 23, 2026
@miyoungc miyoungc mentioned this pull request May 26, 2026
12 tasks
miyoungc added a commit that referenced this pull request May 26, 2026
## Summary
Refresh NemoClaw documentation and generated user skills for the v0.0.50
and v0.0.51 release-prep window.
Remove obsolete legacy docs version metadata now that Fern docs no
longer use `docs/project.json`, `docs/versions1.json`, or the legacy
Sphinx config.

## Source summary
- #1757 -> `docs/manage-sandboxes/messaging-channels.mdx`,
`docs/reference/commands.mdx`, `docs/about/release-notes.mdx`: Document
Slack channel allowlisting with `SLACK_ALLOWED_CHANNELS`.
- #4134 -> `docs/manage-sandboxes/messaging-channels.mdx`,
`docs/reference/commands.mdx`, `docs/about/release-notes.mdx`: Document
Cloudflare named tunnel support through `CLOUDFLARE_TUNNEL_TOKEN`.
- #4186 and #4135 -> `docs/inference/use-local-inference.mdx`,
`docs/reference/commands.mdx`, `docs/about/release-notes.mdx`: Document
Ollama upgrade and user-local install behavior.
- #4185 -> `docs/network-policy/integration-policy-examples.mdx`,
`docs/about/release-notes.mdx`: Clarify Jira policy validation probes.
- Release cleanup ->
`.claude/skills/nemoclaw-contributor-update-docs/SKILL.md`,
`docs/CONTRIBUTING.md`, `.github/PULL_REQUEST_TEMPLATE.md`,
`scripts/bump-version.ts`: Stop using legacy docs version JSON files and
align docs verification on `npm run docs`.

## Changes
- Add v0.0.50 and v0.0.51 release notes.
- Regenerate NemoClaw user skills from the current Fern docs.
- Remove obsolete `docs/conf.py`, `docs/project.json`, and
`docs/versions1.json`.
- Update docs workflow guidance and PR templates to use `npm run docs`
instead of `make docs`.
- Remove release-version JSON handling from `scripts/bump-version.ts`.

## Type of Change
- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [x] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Verification
- [ ] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [ ] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [x] Docs updated for user-facing behavior changes
- [ ] `npm run docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Additional verification:
- `python3 scripts/docs-to-skills.py docs/ .agents/skills/ --prefix
nemoclaw-user --doc-platform fern-mdx`
- `npm run build:cli`
- `npx tsc --noEmit --allowSyntheticDefaultImports --module NodeNext
--moduleResolution NodeNext --target ES2022 --types node
scripts/bump-version.ts`
- `ReadLints` on touched docs, skills, template, and script files
- Searched for stale `versions1.json`, `project.json`, and `make docs`
references

Known gaps:
- `npm run docs` was not rerun after cleanup because the earlier Fern
CLI fetch failed with npm registry `403 Forbidden` in this environment.
- A broad `npm run typecheck -- --noEmit` hit an unrelated existing
`scripts/dev-tier-selector.js` type error.

---
Signed-off-by: Miyoung Cho <miyoungc@nvidia.com>

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added resource profiling with CPU/RAM configuration controls for
sandboxes
* Enhanced local Ollama inference with automatic GPU memory-aware model
fallback
* Added `nemoclaw resources` command to display host hardware inventory
* Enabled Cloudflare named tunnel support via environment configuration

* **Documentation**
* Improved setup guides for local inference, sandbox hardening, and
policy validation
* Enhanced troubleshooting for messaging delivery and host service
routing
  * Added release notes for v0.0.50 and v0.0.51

* **Chores**
* Updated build documentation commands from `make docs` to `npm run
docs`

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NemoClaw/pull/4262?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix v0.0.51 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Ollama][Install] Add user-local install fallback when sudo/system installer is unavailable

3 participants