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
12 changes: 6 additions & 6 deletions brev/welcome-ui/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
REPO_ROOT = os.environ.get("REPO_ROOT", os.path.join(ROOT, "..", ".."))
SANDBOX_DIR = os.path.join(REPO_ROOT, "sandboxes", "nemoclaw")
NEMOCLAW_IMAGE = "ghcr.io/nvidia/nemoclaw-community/sandboxes/nemoclaw:local"
POLICY_FILE = os.path.join(SANDBOX_DIR, "policy.yaml")
# POLICY_FILE = os.path.join(SANDBOX_DIR, "policy.yaml")

LOG_FILE = "/tmp/nemoclaw-sandbox-create.log"
BREV_ENV_ID = os.environ.get("BREV_ENV_ID", "")
Expand Down Expand Up @@ -114,7 +114,7 @@ def _cleanup_existing_sandbox():
pass


def _run_sandbox_create(brev_ui_url: str):
def _run_sandbox_create():
"""Background thread: runs nemoclaw sandbox create and monitors until ready."""
global _sandbox_state

Expand All @@ -125,6 +125,8 @@ def _run_sandbox_create(brev_ui_url: str):

_cleanup_existing_sandbox()

chat_ui_url = _build_openclaw_url(token=None)

env = os.environ.copy()
# Use `env` to inject vars into the sandbox command. Avoids the
# nemoclaw -e flag which has a quoting bug that causes SSH to
Expand All @@ -135,11 +137,10 @@ def _run_sandbox_create(brev_ui_url: str):
"nemoclaw", "sandbox", "create",
"--name", "nemoclaw",
"--from", NEMOCLAW_IMAGE,
"--policy", POLICY_FILE,
"--forward", "18789",
"--",
"env",
f"BREV_UI_URL={brev_ui_url}",
f"CHAT_UI_URL={chat_ui_url}",
"nemoclaw-start",
]

Expand Down Expand Up @@ -264,11 +265,10 @@ def _handle_install_openclaw(self):
"error": "Sandbox is already running",
})

brev_ui_url = f"http://{self.headers.get('Host', 'localhost:8080')}"
_maybe_detect_brev_id(self.headers.get("Host", ""))

thread = threading.Thread(
target=_run_sandbox_create,
args=(brev_ui_url,),
daemon=True,
)
thread.start()
Expand Down
12 changes: 11 additions & 1 deletion sandboxes/nemoclaw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,21 @@ docker build -t nemoclaw sandboxes/nemoclaw/
```bash
nemoclaw sandbox create --from sandboxes/nemoclaw \
--forward 18789 \
-- nemoclaw-start
-- env CHAT_UI_URL=http://127.0.0.1:18789 \
nemoclaw-start
```

The `--from <path>` flag builds the image and imports it into the cluster automatically.

`CHAT_UI_URL` is the URL where the chat UI will be accessed. The origin is
added to `allowedOrigins` so the browser can authenticate without the slow
device-pairing fallback. Examples:

| Environment | `CHAT_UI_URL` |
|---|---|
| Local | `http://127.0.0.1:18789` |
| Brev | `https://187890-<id>.brevlab.com` |

`nemoclaw-start` then:

1. Substitutes `__NVIDIA_*_API_KEY__` placeholders in the bundled JS with runtime environment variables (if provided)
Expand Down
54 changes: 35 additions & 19 deletions sandboxes/nemoclaw/nemoclaw-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@
# placeholder API keys. At startup this script substitutes the real keys from
# environment variables into the bundled JS, then launches the gateway.
#
# Required env vars (for NVIDIA model endpoints):
# Required env vars:
# CHAT_UI_URL — URL where the chat UI will be accessed
# (e.g. http://127.0.0.1:18789 for local,
# https://187890-<id>.brevlab.com for Brev)
#
# Optional env vars (for NVIDIA model endpoints):
# NVIDIA_INFERENCE_API_KEY — key for inference-api.nvidia.com
# NVIDIA_INTEGRATE_API_KEY — key for integrate.api.nvidia.com
#
# Usage (env vars inlined via env command to avoid nemoclaw -e quoting bug):
# nemoclaw sandbox create --name nemoclaw --from sandboxes/nemoclaw/ \
# --forward 18789 \
# -- env NVIDIA_INFERENCE_API_KEY=<key> \
# -- env CHAT_UI_URL=http://127.0.0.1:18789 \
# NVIDIA_INFERENCE_API_KEY=<key> \
# NVIDIA_INTEGRATE_API_KEY=<key> \
# nemoclaw-start
set -euo pipefail
Expand Down Expand Up @@ -75,25 +81,27 @@ export NVIDIA_API_KEY=" "

GATEWAY_PORT=18789

# Derive the Brev environment ID so we can build the correct gateway origin.
# BREV_UI_URL (if set) points at the *welcome UI* port, not the gateway port,
# so we must always compute the gateway origin separately.
if [ -z "${BREV_ENV_ID:-}" ] && [ -n "${BREV_UI_URL:-}" ]; then
BREV_ENV_ID=$(echo "$BREV_UI_URL" | sed -n 's|.*//[0-9]*-\([^.]*\)\.brevlab\.com.*|\1|p')
fi

if [ -n "${BREV_ENV_ID:-}" ]; then
export OPENCLAW_ORIGIN="https://${GATEWAY_PORT}0-${BREV_ENV_ID}.brevlab.com"
else
export OPENCLAW_ORIGIN="http://127.0.0.1:${GATEWAY_PORT}"
if [ -z "${CHAT_UI_URL:-}" ]; then
echo "Error: CHAT_UI_URL environment variable is required." >&2
echo "Set it to the URL where the chat UI will be accessed, e.g.:" >&2
echo " Local: CHAT_UI_URL=http://127.0.0.1:18789" >&2
echo " Brev: CHAT_UI_URL=https://187890-<brev-id>.brevlab.com" >&2
exit 1
fi

python3 -c "
import json, os
from urllib.parse import urlparse
cfg = json.load(open(os.environ['HOME'] + '/.openclaw/openclaw.json'))
local = 'http://127.0.0.1:${GATEWAY_PORT}'
parsed = urlparse(os.environ['CHAT_UI_URL'])
chat_origin = f'{parsed.scheme}://{parsed.netloc}'
origins = [local]
if chat_origin != local:
origins.append(chat_origin)
cfg['gateway']['controlUi'] = {
'allowInsecureAuth': True,
'allowedOrigins': [os.environ['OPENCLAW_ORIGIN']]
'allowedOrigins': origins,
}
json.dump(cfg, open(os.environ['HOME'] + '/.openclaw/openclaw.json', 'w'), indent=2)
"
Expand All @@ -115,12 +123,20 @@ nohup openclaw gateway > /tmp/gateway.log 2>&1 &
CONFIG_FILE="${HOME}/.openclaw/openclaw.json"
token=$(grep -o '"token"\s*:\s*"[^"]*"' "${CONFIG_FILE}" 2>/dev/null | head -1 | cut -d'"' -f4 || true)

echo ""
echo "OpenClaw gateway starting in background."
echo " Logs: /tmp/gateway.log"
CHAT_UI_BASE="${CHAT_UI_URL%/}"
if [ -n "${token}" ]; then
echo " UI: http://127.0.0.1:18789/?token=${token}"
LOCAL_URL="http://127.0.0.1:18789/?token=${token}"
CHAT_URL="${CHAT_UI_BASE}/?token=${token}"
else
echo " UI: http://127.0.0.1:18789/"
LOCAL_URL="http://127.0.0.1:18789/"
CHAT_URL="${CHAT_UI_BASE}/"
fi

echo ""
echo "OpenClaw gateway starting in background."
echo " Logs: /tmp/gateway.log"
echo " UI: ${CHAT_URL}"
if [ "${CHAT_UI_BASE}" != "http://127.0.0.1:18789" ]; then
echo " Local: ${LOCAL_URL}"
fi
echo ""