Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
3 changes: 3 additions & 0 deletions .github/workflows/nightly-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
env:
NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }}
NEMOCLAW_NON_INTERACTIVE: "1"
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
NEMOCLAW_SANDBOX_NAME: "e2e-nightly"
NEMOCLAW_RECREATE_SANDBOX: "1"
GITHUB_TOKEN: ${{ github.token }}
Expand Down Expand Up @@ -80,6 +81,7 @@ jobs:
# Non-interactive install (expect-driven Phase 3 optional). Runner has no expect; Phase 5e TUI skips if expect is absent.
RUN_E2E_CLOUD_EXPERIMENTAL_INTERACTIVE_INSTALL: "0"
NEMOCLAW_NON_INTERACTIVE: "1"
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
NEMOCLAW_RECREATE_SANDBOX: "1"
NEMOCLAW_POLICY_MODE: "custom"
NEMOCLAW_POLICY_PRESETS: "npm,pypi"
Expand Down Expand Up @@ -171,6 +173,7 @@ jobs:
timeout-minutes: 60
env:
NEMOCLAW_NON_INTERACTIVE: "1"
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
NEMOCLAW_SANDBOX_NAME: "e2e-gpu-ollama"
NEMOCLAW_RECREATE_SANDBOX: "1"
NEMOCLAW_PROVIDER: "ollama"
Expand Down
9 changes: 9 additions & 0 deletions bin/lib/onboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const registry = require("./registry");
const nim = require("./nim");
const onboardSession = require("./onboard-session");
const policies = require("./policies");
const { ensureUsageNoticeConsent } = require("./usage-notice");
const { checkPortAvailable, ensureSwap, getMemoryInfo } = require("./preflight");

// Typed modules (compiled from src/lib/*.ts → dist/lib/*.js)
Expand Down Expand Up @@ -3450,6 +3451,14 @@ async function onboard(opts = {}) {
NON_INTERACTIVE = opts.nonInteractive || process.env.NEMOCLAW_NON_INTERACTIVE === "1";
delete process.env.OPENSHELL_GATEWAY;
const resume = opts.resume === true;
const noticeAccepted = await ensureUsageNoticeConsent({
nonInteractive: isNonInteractive(),
acceptedByFlag: opts.acceptThirdPartySoftware === true,
writeLine: console.error,
});
if (!noticeAccepted) {
process.exit(1);
}
const lockResult = onboardSession.acquireOnboardLock(
`nemoclaw onboard${resume ? " --resume" : ""}${isNonInteractive() ? " --non-interactive" : ""}`,
);
Expand Down
15 changes: 15 additions & 0 deletions bin/lib/usage-notice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// Thin re-export shim — the implementation lives in src/lib/usage-notice.ts,
// compiled to dist/lib/usage-notice.js.
const usageNotice = require("../../dist/lib/usage-notice");

if (require.main === module) {
usageNotice.cli().catch((error) => {
console.error(error?.message || String(error));
process.exit(1);
});
}

module.exports = usageNotice;
32 changes: 32 additions & 0 deletions bin/lib/usage-notice.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"version": "2026-04-01b",
"title": "Third-Party Software Notice - NemoClaw Installer",
"referenceUrl": "https://docs.openclaw.ai/gateway/security",
"body": [
"NemoClaw is licensed under Apache 2.0 and automatically",
"retrieves, accesses or interacts with third-party software",
"and materials, including by deploying OpenClaw in an",
"OpenShell sandbox. Those retrieved materials are not",
"distributed with this software and are governed solely",
"by separate terms, conditions and licenses.",
"",
"You are solely responsible for finding, reviewing and",
"complying with all applicable terms, conditions, and",
"licenses, and for verifying the security, integrity and",
"suitability of any retrieved materials for your specific",
"use case.",
"",
"This software is provided \"AS IS\", without warranty of",
"any kind. The author makes no representations or",
"warranties regarding any third-party software, and",
"assumes no liability for any losses, damages, liabilities",
"or legal consequences from your use or inability to use",
"this software or any retrieved materials. Use this",
"software and the retrieved materials at your own risk.",
"",
"OpenClaw security guidance",
"https://docs.openclaw.ai/gateway/security"
],
"links": [],
"interactivePrompt": "Type 'yes' to accept the NemoClaw license and and third-party software notice and continue [no]: "
}
12 changes: 9 additions & 3 deletions bin/nemoclaw.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const { parseGatewayInference } = require("./lib/inference-config");
const { getVersion } = require("./lib/version");
const onboardSession = require("./lib/onboard-session");
const { parseLiveSandboxNames } = require("./lib/runtime-recovery");
const { NOTICE_ACCEPT_ENV, NOTICE_ACCEPT_FLAG } = require("./lib/usage-notice");

// ── Global commands ──────────────────────────────────────────────

Expand Down Expand Up @@ -622,16 +623,20 @@ function exitWithSpawnResult(result) {

async function onboard(args) {
const { onboard: runOnboard } = require("./lib/onboard");
const allowedArgs = new Set(["--non-interactive", "--resume"]);
const allowedArgs = new Set(["--non-interactive", "--resume", NOTICE_ACCEPT_FLAG]);
const unknownArgs = args.filter((arg) => !allowedArgs.has(arg));
if (unknownArgs.length > 0) {
console.error(` Unknown onboard option(s): ${unknownArgs.join(", ")}`);
console.error(" Usage: nemoclaw onboard [--non-interactive] [--resume]");
console.error(
` Usage: nemoclaw onboard [--non-interactive] [--resume] [${NOTICE_ACCEPT_FLAG}]`,
);
process.exit(1);
}
const nonInteractive = args.includes("--non-interactive");
const resume = args.includes("--resume");
await runOnboard({ nonInteractive, resume });
const acceptThirdPartySoftware =
args.includes(NOTICE_ACCEPT_FLAG) || String(process.env[NOTICE_ACCEPT_ENV] || "") === "1";
await runOnboard({ nonInteractive, resume, acceptThirdPartySoftware });
}

async function setup(args = []) {
Expand Down Expand Up @@ -1154,6 +1159,7 @@ function help() {

${G}Getting Started:${R}
${B}nemoclaw onboard${R} Configure inference endpoint and credentials
${D}(non-interactive: ${NOTICE_ACCEPT_FLAG} or ${NOTICE_ACCEPT_ENV}=1)${R}
nemoclaw setup-spark Set up on DGX Spark ${D}(fixes cgroup v2 + Docker)${R}

${G}Sandbox Management:${R}
Expand Down
12 changes: 12 additions & 0 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ Supported non-experimental choices include NVIDIA Endpoints, OpenAI, Anthropic,
Credentials are stored in `~/.nemoclaw/credentials.json`.
The legacy `nemoclaw setup` command is deprecated; use `nemoclaw onboard` instead.

For non-interactive onboarding, you must explicitly accept the third-party software notice:

```console
$ nemoclaw onboard --non-interactive --yes-i-accept-third-party-software
```

or:

```console
$ NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 nemoclaw onboard --non-interactive
```

The wizard prompts for a sandbox name.
Names must follow RFC 1123 subdomain rules: lowercase alphanumeric characters and hyphens only, and must start and end with an alphanumeric character.
Uppercase letters are automatically lowercased.
Expand Down
33 changes: 32 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,12 @@ usage() {
printf " curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash -s -- [options]\n\n"
printf " ${C_DIM}Options:${C_RESET}\n"
printf " --non-interactive Skip prompts (uses env vars / defaults)\n"
printf " --yes-i-accept-third-party-software Accept the third-party software notice in non-interactive mode\n"
printf " --version, -v Print installer version and exit\n"
printf " --help, -h Show this help message and exit\n\n"
printf " ${C_DIM}Environment:${C_RESET}\n"
printf " NVIDIA_API_KEY API key (skips credential prompt)\n"
printf " NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 Same as --yes-i-accept-third-party-software\n"
printf " NEMOCLAW_NON_INTERACTIVE=1 Same as --non-interactive\n"
printf " NEMOCLAW_SANDBOX_NAME Sandbox name to create/use\n"
printf " NEMOCLAW_RECREATE_SANDBOX=1 Recreate an existing sandbox\n"
Expand All @@ -237,6 +239,27 @@ usage() {
printf "\n"
}

show_usage_notice() {
local -a notice_cmd=(node "${SCRIPT_DIR}/bin/lib/usage-notice.js")
if [ "${NON_INTERACTIVE:-}" = "1" ]; then
notice_cmd+=(--non-interactive)
if [ "${ACCEPT_THIRD_PARTY_SOFTWARE:-}" = "1" ]; then
notice_cmd+=(--yes-i-accept-third-party-software)
fi
"${notice_cmd[@]}"
elif [ -t 0 ]; then
"${notice_cmd[@]}"
elif exec 3</dev/tty; then
info "Installer stdin is piped; attaching the usage notice to /dev/tty…"
local status=0
"${notice_cmd[@]}" <&3 || status=$?
exec 3<&-
return "$status"
else
error "Interactive third-party software acceptance requires a TTY. Re-run in a terminal or set NEMOCLAW_NON_INTERACTIVE=1 with --yes-i-accept-third-party-software."
fi
}

# spin "label" cmd [args...]
# Runs a command in the background, showing a braille spinner until it exits.
# Stdout/stderr are captured; dumped only on failure.
Expand Down Expand Up @@ -806,6 +829,7 @@ verify_nemoclaw() {
# 5. Onboard
# ---------------------------------------------------------------------------
run_onboard() {
show_usage_notice
info "Running nemoclaw onboard…"
local -a onboard_cmd=(onboard)
if command_exists node && [[ -f "${HOME}/.nemoclaw/onboard-session.json" ]]; then
Expand All @@ -827,6 +851,9 @@ run_onboard() {
fi
if [ "${NON_INTERACTIVE:-}" = "1" ]; then
onboard_cmd+=(--non-interactive)
if [ "${ACCEPT_THIRD_PARTY_SOFTWARE:-}" = "1" ]; then
onboard_cmd+=(--yes-i-accept-third-party-software)
fi
nemoclaw "${onboard_cmd[@]}"
elif [ -t 0 ]; then
nemoclaw "${onboard_cmd[@]}"
Expand All @@ -837,7 +864,7 @@ run_onboard() {
exec 3<&-
return "$status"
else
error "Interactive onboarding requires a TTY. Re-run in a terminal or set NEMOCLAW_NON_INTERACTIVE=1."
error "Interactive onboarding requires a TTY. Re-run in a terminal or set NEMOCLAW_NON_INTERACTIVE=1 with --yes-i-accept-third-party-software."
fi
}

Expand Down Expand Up @@ -880,9 +907,11 @@ post_install_message() {
main() {
# Parse flags
NON_INTERACTIVE=""
ACCEPT_THIRD_PARTY_SOFTWARE=""
for arg in "$@"; do
case "$arg" in
--non-interactive) NON_INTERACTIVE=1 ;;
--yes-i-accept-third-party-software) ACCEPT_THIRD_PARTY_SOFTWARE=1 ;;
--version | -v)
printf "nemoclaw-installer v%s\n" "$NEMOCLAW_VERSION"
exit 0
Expand All @@ -899,7 +928,9 @@ main() {
done
# Also honor env var
NON_INTERACTIVE="${NON_INTERACTIVE:-${NEMOCLAW_NON_INTERACTIVE:-}}"
ACCEPT_THIRD_PARTY_SOFTWARE="${ACCEPT_THIRD_PARTY_SOFTWARE:-${NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE:-}}"
export NEMOCLAW_NON_INTERACTIVE="${NON_INTERACTIVE}"
export NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE="${ACCEPT_THIRD_PARTY_SOFTWARE}"

_INSTALL_START=$SECONDS
print_banner
Expand Down
Loading
Loading