Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
19c04c3
feat(onboard): preserve Jetson GPU device groups
cjagwani Aug 12, 2026
b0ec392
Merge branch 'main' into codex/jetson-gpu-followup-7610
prekshivyas Aug 13, 2026
de82709
Merge remote-tracking branch 'origin/main' into pr-8910
prekshivyas Aug 13, 2026
0985303
docs(jetson): define native managed replacement
prekshivyas Aug 13, 2026
fff5a76
Merge remote-tracking branch 'origin/main' into pr-8910
prekshivyas Aug 13, 2026
3a87992
merge: reconcile Jetson group preservation with main
prekshivyas Aug 17, 2026
15ee273
Merge remote-tracking branch 'origin/main' into codex/jetson-gpu-foll…
prekshivyas Aug 17, 2026
91a3a9e
test(onboard): keep Jetson cases linear
prekshivyas Aug 17, 2026
15330cd
merge: refresh Jetson follow-up with main
prekshivyas Aug 17, 2026
8dfde23
merge: refresh Jetson follow-up with main
cv Aug 23, 2026
94e6e89
Merge branch 'main' into codex/jetson-gpu-followup-7610
cv Aug 24, 2026
84bfafe
Merge branch 'main' into codex/jetson-gpu-followup-7610
cv Aug 24, 2026
317fa99
Merge branch 'main' into codex/jetson-gpu-followup-7610
cv Aug 24, 2026
821529a
Merge branch 'main' into codex/jetson-gpu-followup-7610
cv Aug 24, 2026
9e7cb05
Merge branch 'main' into codex/jetson-gpu-followup-7610
cv Aug 24, 2026
aa0a5a2
Merge branch 'main' into codex/jetson-gpu-followup-7610
cv Aug 24, 2026
558e8eb
Merge branch 'main' into codex/jetson-gpu-followup-7610
cv Aug 24, 2026
c0c92f5
Merge branch 'main' into codex/jetson-gpu-followup-7610
cv Aug 24, 2026
fe2f324
merge(onboard): sync Jetson GPU follow-up with main
prekshivyas Aug 29, 2026
6462718
Merge remote-tracking branch 'origin/main' into codex/jetson-gpu-foll…
prekshivyas Aug 29, 2026
c23e69e
fix(onboard): install Jetson helper atomically
prekshivyas Aug 29, 2026
2d63cc8
Merge remote-tracking branch 'origin/main' into codex/jetson-gpu-foll…
prekshivyas Aug 29, 2026
4214192
fix(onboard): constrain Jetson group handoff
prekshivyas Aug 29, 2026
7f4f93c
Merge remote-tracking branch 'origin/main' into codex/jetson-gpu-foll…
prekshivyas Aug 29, 2026
5ade992
fix(onboard): make Jetson bootstrap retry-safe
prekshivyas Aug 29, 2026
c3b8e34
docs(e2e): name Jetson bootstrap CI owner
prekshivyas Aug 29, 2026
08935f2
merge: resolve conflicts with main
github-actions[bot] Sep 2, 2026
da9ecdf
merge: resolve conflicts with main
github-actions[bot] Sep 3, 2026
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
5 changes: 3 additions & 2 deletions .github/workflows/platform-vitest-main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ jobs:
"@
Invoke-WslScript -Distro $env:WSL_DISTRO -User $env:WSL_TEST_USER -Script $script

- name: Run full Vitest suite in WSL
- name: Run Vitest suite in WSL
shell: powershell
run: |
. "$env:TRUSTED_WSL_HELPER"
Expand All @@ -338,7 +338,8 @@ jobs:
cd $workdir
export NEMOCLAW_EXEC_TIMEOUT=60000
export NEMOCLAW_TEST_TIMEOUT=60000
npx vitest run --testTimeout 60000 --shard='${{ matrix.shard }}/4'
npx vitest run --testTimeout 60000 --shard='${{ matrix.shard }}/4' \
--exclude test/runtime/sandbox/jetson-device-group-bootstrap.test.ts
"@
Invoke-WslScript -Distro $env:WSL_DISTRO -User $env:WSL_TEST_USER -Script $script

Expand Down
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@
COPY scripts/lib/entrypoint-env-wrapper.sh /usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh
COPY scripts/lib/gateway-supervisor.sh /usr/local/lib/nemoclaw/gateway-supervisor.sh
COPY scripts/lib/sandbox-rlimits.sh /usr/local/lib/nemoclaw/sandbox-rlimits.sh
COPY --chown=root:root --chmod=0500 scripts/jetson-device-group-bootstrap.sh /usr/local/lib/nemoclaw/jetson-device-group-bootstrap.sh
COPY scripts/lib/openclaw_device_approval_policy.py /usr/local/lib/nemoclaw/openclaw_device_approval_policy.py
COPY scripts/lib/clean_runtime_shell_env_shim.py /usr/local/lib/nemoclaw/clean_runtime_shell_env_shim.py
COPY scripts/lib/normalize_mutable_config_perms.py /usr/local/lib/nemoclaw/normalize_mutable_config_perms.py
Expand Down Expand Up @@ -1582,10 +1583,10 @@
# CHAT_UI_URL is a non-loopback address (Brev Launchable, remote deployments)
# since terminal-based pairing is impossible in those contexts.
# Default: "0" (device auth enabled for local deployments — secure by default).
ARG NEMOCLAW_DISABLE_DEVICE_AUTH=0

Check warning on line 1586 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1586 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
# Internal audit provenance for the opt-out above. Standard onboarding rewrites
# this to managed-onboard; direct image builders retain operator provenance.
ARG NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE=operator

Check warning on line 1589 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1589 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
# Compatibility build arg for older custom Dockerfiles and rebuild tooling.
# NemoClaw-managed images intentionally do not consume it; gateway auth tokens
# are generated at container startup and are never baked into image layers.
Expand Down Expand Up @@ -1619,7 +1620,7 @@
# NEMOCLAW_MESSAGING_PLAN_B64 intentionally remains ARG-only: Docker exposes it
# to build RUN processes without retaining the full plan in the final image env.
# Direct ARG interpolation into inline source is a code injection vector (C-2).
ENV NEMOCLAW_MODEL=${NEMOCLAW_MODEL} \

Check warning on line 1623 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1623 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1623 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1623 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
NEMOCLAW_INFERENCE_PROVIDER_ID=${NEMOCLAW_INFERENCE_PROVIDER_ID} \
NEMOCLAW_UPSTREAM_PROVIDER=${NEMOCLAW_UPSTREAM_PROVIDER} \
NEMOCLAW_PRIMARY_MODEL_REF=${NEMOCLAW_PRIMARY_MODEL_REF} \
Expand Down Expand Up @@ -2357,6 +2358,8 @@
&& check_metadata /usr/local/bin/nemoclaw-managed-bootstrap 'root:root:755' \
&& test ! -L /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh \
&& check_metadata /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh 'root:root:444' \
&& test ! -L /usr/local/lib/nemoclaw/jetson-device-group-bootstrap.sh \
&& check_metadata /usr/local/lib/nemoclaw/jetson-device-group-bootstrap.sh 'root:root:500' \
&& check_metadata /usr/local/bin/nemoclaw-gateway-control 'root:root:700' \
&& check_metadata /usr/local/lib/nemoclaw/preloads/sandbox-safety-net.js 'root:root:644'

Expand Down
82 changes: 82 additions & 0 deletions scripts/jetson-device-group-bootstrap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Compatibility bridge for #7610. Remove when the minimum supported OpenShell
# release natively preserves Jetson device groups across the sandbox-user handoff.

set -euo pipefail

fail() {
printf 'Jetson device-group bootstrap: %s\n' "$*" >&2
exit 1
}

[ "$(/usr/bin/id -u)" -eq 0 ] || fail "must run as root"
[ "${1:-}" = "--device-group-gids" ] || fail "device group argument is missing"
group_gids="${2:-}"
[ "${3:-}" = "--" ] || fail "supervisor delimiter is missing"
shift 3
[ "${1:-}" = "/opt/openshell/bin/openshell-sandbox" ] \
|| fail "OpenShell supervisor entrypoint is invalid"
/usr/bin/id sandbox >/dev/null 2>&1 || fail "sandbox user is missing"
if [ ! -f /etc/group ] || [ -L /etc/group ]; then
fail "container group database is invalid"
fi

IFS=',' read -r -a gids <<<"$group_gids"
if [ "${#gids[@]}" -eq 0 ] || [ "${#gids[@]}" -gt 16 ]; then
fail "device group count is invalid"
fi

declare -A seen=()
for gid in "${gids[@]}"; do
[[ "$gid" =~ ^[1-9][0-9]{0,9}$ ]] || fail "device group ID is invalid"
[ "$gid" -le 2147483647 ] || fail "device group ID is out of range"
[ -z "${seen[$gid]:-}" ] || fail "device group ID is duplicated"
seen[$gid]=1
done

declare -a group_names=()
declare -a create_groups=()
for gid in "${gids[@]}"; do
group_record="$(/usr/bin/getent group "$gid" || true)"
if [ -z "$group_record" ]; then
group_name="nemoclaw_gpu_$gid"
[ -z "$(/usr/bin/getent group "$group_name" || true)" ] \
|| fail "generated device group name already exists"
create_group=1
else
if [[ "$group_record" == *$'\n'* ]]; then
fail "device group record is invalid"
fi
IFS=':' read -r group_name _ resolved_gid _ extra <<<"$group_record"
if [ -z "$group_name" ] || [ "$resolved_gid" != "$gid" ] || [ -n "$extra" ]; then
fail "device group record is invalid"
fi
case "$group_name" in
video | render | "nemoclaw_gpu_$gid") ;;
*) fail "existing device group is not approved" ;;
esac
create_group=0
fi
group_names+=("$group_name")
create_groups+=("$create_group")
done

for index in "${!gids[@]}"; do
gid="${gids[$index]}"
group_name="${group_names[$index]}"
if [ "${create_groups[$index]}" -eq 1 ]; then
/usr/sbin/groupadd --gid "$gid" "$group_name"
fi
/usr/sbin/usermod --append --groups "$group_name" sandbox
done

sandbox_groups=" $(/usr/bin/id -G sandbox) "
for gid in "${gids[@]}"; do
[[ "$sandbox_groups" == *" $gid "* ]] \
|| fail "sandbox membership verification failed"
done

exec "$@"
100 changes: 99 additions & 1 deletion src/lib/onboard/docker-gpu-jetson-groups.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,56 @@ import fs from "node:fs";

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

import { detectTegraDeviceGroupGids } from "./docker-gpu-jetson-groups";
import {
detectTegraDeviceGroupGids,
detectTegraGpuDevicePaths,
JETSON_DEVICE_GROUP_BOOTSTRAP,
normalizeTegraGpuDevicePaths,
resolveJetsonDeviceGroupBootstrap,
} from "./docker-gpu-jetson-groups";

describe("detectTegraGpuDevicePaths", () => {
it("returns existing non-symlink character devices when nvmap anchors detection (#7610)", () => {
const paths = ["/dev/nvmap", "/dev/nvhost-gpu", "/dev/nvgpu/igpu0/link"];

expect(
detectTegraGpuDevicePaths({
listDevicePaths: () => paths,
statDevicePath: (devicePath) =>
devicePath === "/dev/nvmap"
? { isCharacterDevice: true, isSymbolicLink: false }
: devicePath === "/dev/nvgpu/igpu0/link"
? { isCharacterDevice: true, isSymbolicLink: true }
: null,
}),
).toEqual(["/dev/nvmap"]);
});

it.each([
null,
{ isCharacterDevice: true, isSymbolicLink: true },
{ isCharacterDevice: false, isSymbolicLink: false },
])("returns no paths for invalid nvmap access case %# (#7610)", (nvmapAccess) => {
expect(
detectTegraGpuDevicePaths({
listDevicePaths: () => ["/dev/nvmap", "/dev/dri/renderD128"],
statDevicePath: (devicePath) =>
devicePath === "/dev/nvmap"
? nvmapAccess
: { isCharacterDevice: true, isSymbolicLink: false },
}),
).toEqual([]);
});

it("rejects device-policy paths outside the bounded Tegra set (#7610)", () => {
expect(() => normalizeTegraGpuDevicePaths(["/dev/nvmap", "/dev/input/event0"])).toThrow(
"Jetson GPU device paths are invalid or excessive",
);
expect(
normalizeTegraGpuDevicePaths(["/dev/dri/renderD128", "/dev/nvmap", "/dev/nvmap"]),
).toEqual(["/dev/dri/renderD128", "/dev/nvmap"]);
});
});

describe("detectTegraDeviceGroupGids", () => {
afterEach(() => {
Expand Down Expand Up @@ -65,6 +114,15 @@ describe("detectTegraDeviceGroupGids", () => {
}
});

it("requires nvmap before accepting an unrelated render group (#7610)", () => {
expect(
detectTegraDeviceGroupGids({
listDevicePaths: () => ["/dev/dri/renderD128"],
statDeviceAccess: () => ({ gid: 104, mode: 0o660 }),
}),
).toEqual([]);
});

it("rejects regular files and symlinks before reading their group", () => {
const lstat = vi
.spyOn(fs, "lstatSync")
Expand Down Expand Up @@ -131,3 +189,43 @@ describe("detectTegraDeviceGroupGids", () => {
}
});
});

describe("resolveJetsonDeviceGroupBootstrap", () => {
const supervisorArgv = ["/opt/openshell/bin/openshell-sandbox", "--workdir", "/sandbox"];

it("wraps only the exact OpenClaw supervisor with normalized group IDs (#7610)", () => {
expect(
resolveJetsonDeviceGroupBootstrap({
agent: "openclaw",
deviceGroupGids: ["44", "110"],
supervisorArgv,
}),
).toEqual({
deviceGroupGids: ["44", "110"],
supervisorArgv: [
JETSON_DEVICE_GROUP_BOOTSTRAP,
"--device-group-gids",
"44,110",
"--",
...supervisorArgv,
],
});
expect(
resolveJetsonDeviceGroupBootstrap({
agent: "hermes",
deviceGroupGids: ["44"],
supervisorArgv,
}),
).toBeNull();
});

it("rejects a different OpenClaw launch authority (#7610)", () => {
expect(() =>
resolveJetsonDeviceGroupBootstrap({
agent: "openclaw",
deviceGroupGids: ["44"],
supervisorArgv: ["/tmp/openshell-sandbox"],
}),
).toThrow("requires the OpenShell supervisor entrypoint");
});
});
118 changes: 117 additions & 1 deletion src/lib/onboard/docker-gpu-jetson-groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,28 @@ const TEGRA_GPU_DEVICE_NODES = [
"/dev/nvgpu/igpu0/as",
"/dev/nvgpu/igpu0/prof",
] as const;
const TEGRA_GPU_DEVICE_NODE_SET = new Set<string>(TEGRA_GPU_DEVICE_NODES);
const NVMAP_DEVICE = "/dev/nvmap";
const READ_WRITE_PERMISSION_BITS = 0o6;
const MAX_DOCKER_SUPPLEMENTARY_GID = 2_147_483_647;
const MAX_JETSON_DEVICE_GROUPS = 16;
const MAX_JETSON_GPU_DEVICE_PATHS = 32;

export const NEMOCLAW_MANAGED_AGENT_LABEL = "io.nvidia.nemoclaw.agent";
export const OPENSHELL_SANDBOX_ENTRYPOINT = "/opt/openshell/bin/openshell-sandbox";
export const JETSON_DEVICE_GROUP_BOOTSTRAP =
"/usr/local/lib/nemoclaw/jetson-device-group-bootstrap.sh";

type DeviceGroupAccess = {
gid: number;
mode: number;
};

type DevicePathAccess = {
isCharacterDevice: boolean;
isSymbolicLink: boolean;
};

/**
* Find real DRI render character devices without following symlinks or
* scanning other DRI device families.
Expand Down Expand Up @@ -51,6 +65,55 @@ function listTegraGpuDevicePaths(): string[] {
return [...TEGRA_GPU_DEVICE_NODES, ...discoverTegraRenderDevicePaths()];
}

export function normalizeTegraGpuDevicePaths(devicePaths: readonly string[]): string[] {
const normalized = [...new Set(devicePaths)];
if (
normalized.length > MAX_JETSON_GPU_DEVICE_PATHS ||
normalized.some(
(devicePath) =>
devicePath.length > 64 ||
(!TEGRA_GPU_DEVICE_NODE_SET.has(devicePath) &&
!/^\/dev\/dri\/renderD\d+$/u.test(devicePath)),
)
) {
throw new Error("Jetson GPU device paths are invalid or excessive.");
}
return normalized.includes(NVMAP_DEVICE) ? normalized : [];
}

/**
* Return the fixed Jetson GPU device paths that are safe to add to an
* OpenShell filesystem policy. `/dev/nvmap` anchors detection so a host with
* only an unrelated DRI render device does not receive Jetson grants.
*/
export function detectTegraGpuDevicePaths(
deps: {
statDevicePath?: (path: string) => DevicePathAccess | null;
listDevicePaths?: () => string[];
} = {},
): string[] {
const devicePaths = deps.listDevicePaths?.() ?? listTegraGpuDevicePaths();
const statDevicePath =
deps.statDevicePath ??
((devicePath: string): DevicePathAccess | null => {
try {
const stat = fs.lstatSync(devicePath);
return {
isCharacterDevice: stat.isCharacterDevice(),
isSymbolicLink: stat.isSymbolicLink(),
};
} catch {
return null;
}
});

const detectedPaths = devicePaths.filter((devicePath) => {
const access = statDevicePath(devicePath);
return access?.isCharacterDevice === true && access.isSymbolicLink === false;
});
return normalizeTegraGpuDevicePaths(detectedPaths);
}

/**
* Source-of-truth boundary for Jetson/Tegra supplementary device groups:
*
Expand Down Expand Up @@ -86,9 +149,14 @@ export function detectTegraDeviceGroupGids(
return null;
}
});
const deviceAccess = new Map(
devicePaths.map((devicePath) => [devicePath, statAccess(devicePath)] as const),
);
if (deviceAccess.get(NVMAP_DEVICE) == null) return [];

const gids = new Set<string>();
for (const node of devicePaths) {
const access = statAccess(node);
const access = deviceAccess.get(node) ?? null;
const gid = access?.gid ?? null;
const groupAccessBits = access === null ? 0 : (access.mode >> 3) & READ_WRITE_PERMISSION_BITS;
const otherAccessBits = access === null ? 0 : access.mode & READ_WRITE_PERMISSION_BITS;
Expand All @@ -105,3 +173,51 @@ export function detectTegraDeviceGroupGids(
}
return [...gids].sort((left, right) => Number(left) - Number(right));
}

export function normalizeJetsonDeviceGroupGids(groupGids: readonly string[]): string[] {
const normalized = groupGids.map((gid) => String(gid).trim());
if (
normalized.length > MAX_JETSON_DEVICE_GROUPS ||
new Set(normalized).size !== normalized.length ||
normalized.some((gid) => {
if (!/^[1-9][0-9]*$/u.test(gid)) return true;
const parsed = Number(gid);
return !Number.isSafeInteger(parsed) || parsed > MAX_DOCKER_SUPPLEMENTARY_GID;
})
) {
throw new Error("Docker clone received invalid or excessive supplementary group IDs.");
}
return normalized;
}

export type JetsonDeviceGroupBootstrapPlan = Readonly<{
deviceGroupGids: readonly string[];
supervisorArgv: readonly string[];
}>;

/**
* Render the only approved root bootstrap handoff for an OpenClaw managed
* image. The exact agent identity and supervisor entrypoint keep this launch
* authority closed to other images and commands.
*/
export function resolveJetsonDeviceGroupBootstrap(input: {
agent: string | null | undefined;
deviceGroupGids: readonly string[];
supervisorArgv: readonly string[];
}): JetsonDeviceGroupBootstrapPlan | null {
const deviceGroupGids = Object.freeze(normalizeJetsonDeviceGroupGids(input.deviceGroupGids));
if (input.agent !== "openclaw" || deviceGroupGids.length === 0) return null;
if (input.supervisorArgv[0] !== OPENSHELL_SANDBOX_ENTRYPOINT) {
throw new Error("Jetson device-group bootstrap requires the OpenShell supervisor entrypoint.");
}
return Object.freeze({
deviceGroupGids,
supervisorArgv: Object.freeze([
JETSON_DEVICE_GROUP_BOOTSTRAP,
"--device-group-gids",
deviceGroupGids.join(","),
"--",
...input.supervisorArgv,
]),
});
}
Loading
Loading