Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
20 changes: 19 additions & 1 deletion .github/workflows/nightly-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ on:
description: >-
Comma-separated job names to run (empty = all).
Valid: cloud-e2e, cloud-onboard-e2e, cloud-inference-e2e,
skill-agent-e2e, docs-validation-e2e, messaging-providers-e2e,
skill-agent-e2e, openclaw-skill-cli-e2e, docs-validation-e2e, messaging-providers-e2e,
openclaw-slack-pairing-e2e,
openclaw-tui-chat-correlation-e2e,
issue-3600-gpu-proof-optional-e2e,
Expand Down Expand Up @@ -232,6 +232,21 @@ jobs:
env_json: '{"NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE":"1","NEMOCLAW_NON_INTERACTIVE":"1","NEMOCLAW_RECREATE_SANDBOX":"1","NEMOCLAW_SANDBOX_NAME":"e2e-skill-agent"}'
nvidia_api_key: true
secrets: *nightly-e2e-default-secrets
openclaw-skill-cli-e2e:
if: >-
github.repository == 'NVIDIA/NemoClaw' && (github.event_name != 'workflow_dispatch' ||
inputs.jobs == '' ||
contains(format(',{0},', inputs.jobs), ',openclaw-skill-cli-e2e,'))
uses: ./.github/workflows/e2e-script.yaml
with:
ref: ${{ inputs.target_ref || github.ref }}
script: test/e2e/test-openclaw-skill-cli-e2e.sh
timeout_minutes: 25
artifact_name: "install-log-openclaw-skill-cli"
artifact_path: "/tmp/nemoclaw-e2e-openclaw-skill-cli-install.log"
env_json: '{"NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE":"1","NEMOCLAW_NON_INTERACTIVE":"1","NEMOCLAW_RECREATE_SANDBOX":"1","NEMOCLAW_SANDBOX_NAME":"e2e-openclaw-skill-cli"}'
nvidia_api_key: true
secrets: *nightly-e2e-default-secrets
docs-validation-e2e:
if: >-
github.repository == 'NVIDIA/NemoClaw' &&
Expand Down Expand Up @@ -1899,6 +1914,7 @@ jobs:
cloud-onboard-e2e,
cloud-inference-e2e,
skill-agent-e2e,
openclaw-skill-cli-e2e,
docs-validation-e2e,
messaging-providers-e2e,
openclaw-slack-pairing-e2e,
Expand Down Expand Up @@ -2007,6 +2023,7 @@ jobs:
cloud-onboard-e2e,
cloud-inference-e2e,
skill-agent-e2e,
openclaw-skill-cli-e2e,
docs-validation-e2e,
messaging-providers-e2e,
openclaw-slack-pairing-e2e,
Expand Down Expand Up @@ -2172,6 +2189,7 @@ jobs:
cloud-onboard-e2e,
cloud-inference-e2e,
skill-agent-e2e,
openclaw-skill-cli-e2e,
docs-validation-e2e,
messaging-providers-e2e,
openclaw-slack-pairing-e2e,
Expand Down
2 changes: 1 addition & 1 deletion scripts/nemoclaw-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1994,7 +1994,7 @@ export https_proxy="$_PROXY_URL"
export no_proxy="$_NO_PROXY_VAL"
PROXYEOF
local _openclaw_env_name _openclaw_env_value _escaped_openclaw_env_value
for _openclaw_env_name in OPENCLAW_HOME OPENCLAW_STATE_DIR OPENCLAW_CONFIG_PATH OPENCLAW_OAUTH_DIR; do
for _openclaw_env_name in OPENCLAW_HOME OPENCLAW_STATE_DIR OPENCLAW_CONFIG_PATH OPENCLAW_OAUTH_DIR OPENCLAW_WORKSPACE_DIR; do
_openclaw_env_value="${!_openclaw_env_name:-}"
[ -n "$_openclaw_env_value" ] || continue
_escaped_openclaw_env_value="$(printf '%s' "$_openclaw_env_value" | sed "s/'/'\\\\''/g")"
Expand Down
7 changes: 2 additions & 5 deletions src/lib/onboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3563,11 +3563,8 @@ async function createSandbox(
// 18789 and the gateway listens on the wrong port. (#2267, #1925)
const effectiveDashboardPort = getDashboardForwardPort(chatUiUrl);
envArgs.push(formatEnvAssignment("NEMOCLAW_DASHBOARD_PORT", effectiveDashboardPort));
onboardHermesDashboard.appendHermesDashboardEnvArgs(
envArgs,
hermesDashboardState,
formatEnvAssignment,
);
require("./onboard/openclaw-runtime-env").appendOpenClawRuntimeEnvArgs(envArgs, agent);
onboardHermesDashboard.appendHermesDashboardEnvArgs(envArgs, hermesDashboardState, formatEnvAssignment);
require("./onboard/host-proxy-env").appendHostProxyEnvArgs(envArgs);
// Propagate NEMOCLAW_PROXY_HOST / NEMOCLAW_PROXY_PORT to the runtime
// sandbox container. patchStagedDockerfile() already substitutes them
Expand Down
56 changes: 56 additions & 0 deletions src/lib/onboard/openclaw-runtime-env.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it } from "vitest";
import { appendOpenClawRuntimeEnvArgs } from "./openclaw-runtime-env";

describe("appendOpenClawRuntimeEnvArgs", () => {
it("pins HOME, STATE_DIR, and WORKSPACE_DIR for the default OpenClaw config dir", () => {
const envArgs: string[] = [];
appendOpenClawRuntimeEnvArgs(envArgs, null);
expect(envArgs).toEqual([
"OPENCLAW_HOME=/sandbox",
"OPENCLAW_STATE_DIR=/sandbox/.openclaw",
"OPENCLAW_WORKSPACE_DIR=/sandbox/.openclaw/workspace",
]);
});

it("derives the env values from agent.configPaths.dir when supplied for an OpenClaw agent", () => {
const envArgs: string[] = [];
appendOpenClawRuntimeEnvArgs(envArgs, {
name: "openclaw",
configPaths: { dir: "/srv/agent-root/.openclaw" },
});
expect(envArgs).toEqual([
"OPENCLAW_HOME=/srv/agent-root",
"OPENCLAW_STATE_DIR=/srv/agent-root/.openclaw",
"OPENCLAW_WORKSPACE_DIR=/srv/agent-root/.openclaw/workspace",
]);
});

it("falls back to the default dir when the OpenClaw agent omits configPaths", () => {
const envArgs: string[] = [];
appendOpenClawRuntimeEnvArgs(envArgs, { name: "openclaw", configPaths: undefined });
expect(envArgs).toEqual([
"OPENCLAW_HOME=/sandbox",
"OPENCLAW_STATE_DIR=/sandbox/.openclaw",
"OPENCLAW_WORKSPACE_DIR=/sandbox/.openclaw/workspace",
]);
});

it("appends to an existing envArgs array without dropping prior entries", () => {
const envArgs = ["CHAT_UI_URL=http://127.0.0.1:18789"];
appendOpenClawRuntimeEnvArgs(envArgs, null);
expect(envArgs[0]).toBe("CHAT_UI_URL=http://127.0.0.1:18789");
expect(envArgs).toHaveLength(4);
});

it("skips injection for non-OpenClaw agents so OPENCLAW_* state cannot leak across agent runtimes", () => {
const envArgs: string[] = [];
appendOpenClawRuntimeEnvArgs(envArgs, {
name: "hermes",
configPaths: { dir: "/sandbox/.hermes" },
});
expect(envArgs).toEqual([]);
});
});
25 changes: 25 additions & 0 deletions src/lib/onboard/openclaw-runtime-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import path from "node:path";
import { formatEnvAssignment } from "../core/url-utils";

const DEFAULT_OPENCLAW_CONFIG_DIR = "/sandbox/.openclaw";

type AgentLike = {
readonly name?: string;
readonly configPaths?: { readonly dir?: string };
} | null;

function isOpenClawAgent(agent: AgentLike): boolean {
return !agent || agent.name === "openclaw";
}

export function appendOpenClawRuntimeEnvArgs(envArgs: string[], agent: AgentLike): void {
if (!isOpenClawAgent(agent)) return;
const configDir = agent?.configPaths?.dir || DEFAULT_OPENCLAW_CONFIG_DIR;
const homeDir = path.posix.dirname(configDir);
envArgs.push(formatEnvAssignment("OPENCLAW_HOME", homeDir));
envArgs.push(formatEnvAssignment("OPENCLAW_STATE_DIR", configDir));
envArgs.push(formatEnvAssignment("OPENCLAW_WORKSPACE_DIR", `${configDir}/workspace`));
}
Loading
Loading