Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
61 changes: 37 additions & 24 deletions bin/nemoclaw.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ const onboardSession = require("./lib/onboard-session");
const { parseLiveSandboxNames } = require("./lib/runtime-recovery");
const { NOTICE_ACCEPT_ENV, NOTICE_ACCEPT_FLAG } = require("./lib/usage-notice");
const { executeDeploy } = require("../dist/lib/deploy");
const {
runDeprecatedOnboardAliasCommand,
runOnboardCommand,
} = require("../dist/lib/onboard-command");

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

Expand Down Expand Up @@ -779,36 +783,45 @@ function exitWithSpawnResult(result) {

async function onboard(args) {
const { onboard: runOnboard } = require("./lib/onboard");
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] [${NOTICE_ACCEPT_FLAG}]`,
);
process.exit(1);
}
const nonInteractive = args.includes("--non-interactive");
const resume = args.includes("--resume");
const acceptThirdPartySoftware =
args.includes(NOTICE_ACCEPT_FLAG) || String(process.env[NOTICE_ACCEPT_ENV] || "") === "1";
await runOnboard({ nonInteractive, resume, acceptThirdPartySoftware });
await runOnboardCommand({
args,
noticeAcceptFlag: NOTICE_ACCEPT_FLAG,
noticeAcceptEnv: NOTICE_ACCEPT_ENV,
env: process.env,
runOnboard,
error: console.error,
exit: (code) => process.exit(code),
});
}

async function setup(args = []) {
console.log("");
console.log(" ⚠ `nemoclaw setup` is deprecated. Use `nemoclaw onboard` instead.");
console.log("");
await onboard(args);
const { onboard: runOnboard } = require("./lib/onboard");
await runDeprecatedOnboardAliasCommand({
kind: "setup",
args,
noticeAcceptFlag: NOTICE_ACCEPT_FLAG,
noticeAcceptEnv: NOTICE_ACCEPT_ENV,
env: process.env,
runOnboard,
log: console.log,
error: console.error,
exit: (code) => process.exit(code),
});
}

async function setupSpark(args = []) {
console.log("");
console.log(" ⚠ `nemoclaw setup-spark` is deprecated.");
console.log(" Current OpenShell releases handle the old DGX Spark cgroup issue themselves.");
console.log(" Use `nemoclaw onboard` instead.");
console.log("");
await onboard(args);
const { onboard: runOnboard } = require("./lib/onboard");
await runDeprecatedOnboardAliasCommand({
kind: "setup-spark",
args,
noticeAcceptFlag: NOTICE_ACCEPT_FLAG,
noticeAcceptEnv: NOTICE_ACCEPT_ENV,
env: process.env,
runOnboard,
log: console.log,
error: console.error,
exit: (code) => process.exit(code),
});
}

async function deploy(instanceName) {
Expand Down
88 changes: 88 additions & 0 deletions src/lib/onboard-command.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it, vi } from "vitest";

import {
parseOnboardArgs,
runDeprecatedOnboardAliasCommand,
runOnboardCommand,
} from "../../dist/lib/onboard-command";

describe("onboard command", () => {
it("parses onboard flags", () => {
expect(
parseOnboardArgs(
["--non-interactive", "--resume", "--yes-i-accept-third-party-software"],
"--yes-i-accept-third-party-software",
"NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE",
{ env: {}, error: () => {}, exit: ((code: number) => { throw new Error(String(code)); }) as never },
),
).toEqual({
nonInteractive: true,
resume: true,
acceptThirdPartySoftware: true,
});
});

it("accepts the env-based third-party notice acknowledgement", () => {
expect(
parseOnboardArgs(
[],
"--yes-i-accept-third-party-software",
"NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE",
{
env: { NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" },
error: () => {},
exit: ((code: number) => {
throw new Error(String(code));
}) as never,
},
),
).toEqual({
nonInteractive: false,
resume: false,
acceptThirdPartySoftware: true,
});
});

it("runs onboard with parsed options", async () => {
const runOnboard = vi.fn(async () => {});
await runOnboardCommand({
args: ["--resume"],
noticeAcceptFlag: "--yes-i-accept-third-party-software",
noticeAcceptEnv: "NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE",
env: {},
runOnboard,
error: () => {},
exit: ((code: number) => {
throw new Error(String(code));
}) as never,
});
expect(runOnboard).toHaveBeenCalledWith({
nonInteractive: false,
resume: true,
acceptThirdPartySoftware: false,
});
});

it("prints the setup-spark deprecation text before delegating", async () => {
const lines: string[] = [];
const runOnboard = vi.fn(async () => {});
await runDeprecatedOnboardAliasCommand({
kind: "setup-spark",
args: [],
noticeAcceptFlag: "--yes-i-accept-third-party-software",
noticeAcceptEnv: "NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE",
env: {},
runOnboard,
log: (message = "") => lines.push(message),
error: () => {},
exit: ((code: number) => {
throw new Error(String(code));
}) as never,
});
expect(lines.join("\n")).toContain("setup-spark` is deprecated");
expect(lines.join("\n")).toContain("Use `nemoclaw onboard` instead");
});
});
72 changes: 72 additions & 0 deletions src/lib/onboard-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

export interface OnboardCommandOptions {
nonInteractive: boolean;
resume: boolean;
acceptThirdPartySoftware: boolean;
}

export interface RunOnboardCommandDeps {
args: string[];
noticeAcceptFlag: string;
noticeAcceptEnv: string;
env: NodeJS.ProcessEnv;
runOnboard: (options: OnboardCommandOptions) => Promise<void>;
log?: (message?: string) => void;
error?: (message?: string) => void;
exit?: (code: number) => never;
}

const ONBOARD_BASE_ARGS = ["--non-interactive", "--resume"];

export function parseOnboardArgs(
args: string[],
noticeAcceptFlag: string,
noticeAcceptEnv: string,
deps: Pick<RunOnboardCommandDeps, "env" | "error" | "exit">,
): OnboardCommandOptions {
const error = deps.error ?? console.error;
const exit = deps.exit ?? ((code: number) => process.exit(code));
const allowedArgs = new Set([...ONBOARD_BASE_ARGS, noticeAcceptFlag]);
const unknownArgs = args.filter((arg) => !allowedArgs.has(arg));
if (unknownArgs.length > 0) {
error(` Unknown onboard option(s): ${unknownArgs.join(", ")}`);
error(
` Usage: nemoclaw onboard [--non-interactive] [--resume] [${noticeAcceptFlag}]`,
);
exit(1);
}

return {
nonInteractive: args.includes("--non-interactive"),
resume: args.includes("--resume"),
acceptThirdPartySoftware:
args.includes(noticeAcceptFlag) || String(deps.env[noticeAcceptEnv] || "") === "1",
};
}

export async function runOnboardCommand(deps: RunOnboardCommandDeps): Promise<void> {
const options = parseOnboardArgs(deps.args, deps.noticeAcceptFlag, deps.noticeAcceptEnv, deps);
await deps.runOnboard(options);
}

export interface RunAliasCommandDeps extends RunOnboardCommandDeps {
kind: "setup" | "setup-spark";
}

export async function runDeprecatedOnboardAliasCommand(
deps: RunAliasCommandDeps,
): Promise<void> {
const log = deps.log ?? console.log;
log("");
if (deps.kind === "setup") {
log(" ⚠ `nemoclaw setup` is deprecated. Use `nemoclaw onboard` instead.");
} else {
log(" ⚠ `nemoclaw setup-spark` is deprecated.");
log(" Current OpenShell releases handle the old DGX Spark cgroup issue themselves.");
log(" Use `nemoclaw onboard` instead.");
}
log("");
await runOnboardCommand(deps);
}
4 changes: 2 additions & 2 deletions test/runner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,8 @@ describe("regression guards", () => {
path.join(import.meta.dirname, "..", "bin", "nemoclaw.js"),
"utf-8",
);
expect(src).toContain("`nemoclaw setup-spark` is deprecated.");
expect(src).toContain("await onboard(args);");
expect(src).toContain("runDeprecatedOnboardAliasCommand");
expect(src).toContain('kind: "setup-spark"');
expect(src).not.toContain('sudo bash "${SCRIPTS}/setup-spark.sh"');
});

Expand Down
Loading