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
4 changes: 2 additions & 2 deletions Makefile-quickstart
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ nemoclaw-tui: .check-saw-name ## Launch NemoClaw TUI (e.g. make nemoclaw-tui SAN
fi; \
if [ -z "$$SANDBOX" ]; then echo "Error: No sandboxes found on gateway '$(OPENSHELL_SAW_NAME)' workspace '$(WORKSPACE)'."; exit 1; fi; \
echo "Connecting to sandbox '$$SANDBOX' workspace '$(WORKSPACE)' (nemoclaw)..."; \
ssh -o "ProxyCommand=openshell --gateway-insecure ssh-proxy --gateway-name $(OPENSHELL_SAW_NAME) --name $$SANDBOX " \
ssh -o "ProxyCommand=openshell --gateway-insecure ssh-proxy --gateway-name $(OPENSHELL_SAW_NAME) --name $$SANDBOX --workspace $(WORKSPACE) " \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o LogLevel=ERROR \
Expand Down Expand Up @@ -447,7 +447,7 @@ nemoclaw-gui: .check-saw-name ## Open NemoClaw web UI (e.g. make nemoclaw-gui SA
fi; \
if [ -z "$$SANDBOX" ]; then echo "Error: No sandboxes found."; exit 1; fi; \
SANDBOX_NAME=$$SANDBOX GATEWAY_NAME=$(OPENSHELL_SAW_NAME) \
GUI_PORT=$(GUI_PORT) $(SCRIPTS_DIR)/openshell-saw-gui.sh
WORKSPACE=$(WORKSPACE) GUI_PORT=$(GUI_PORT) $(SCRIPTS_DIR)/openshell-saw-gui.sh

openclaw-gui: .check-saw-name ## Open OpenClaw web UI (e.g. make openclaw-gui SANDBOX_NAME=notebook GUI_PORT=28789)
@if [ -n "$(SANDBOX_NAME)" ]; then \
Expand Down
53 changes: 40 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,15 +264,29 @@ openshell gateway add https://$(oc get route openshell-saw-gateway -n openshell-
# Log in as alice / alice in the browser

# 15. Verify sandboxes
# sandbox list without --workspace only shows workspace "default"
openshell --gateway-insecure sandbox list
openshell --gateway-insecure sandbox list --workspace cuda-dev

# 16. Launch TUI (pick one)
OPENSHELL_SAW_NAME=openshell-saw SANDBOX_NAME=cuda-sandbox make nemoclaw-tui # NemoClaw
OPENSHELL_SAW_NAME=openshell-saw SANDBOX_NAME=notebook make openclaw-tui # OpenClaw
OPENSHELL_SAW_NAME=openshell-saw \
SANDBOX_NAME=cuda-sandbox \
WORKSPACE=cuda-dev \
make nemoclaw-tui # NemoClaw
OPENSHELL_SAW_NAME=openshell-saw \
SANDBOX_NAME=notebook \
make openclaw-tui # OpenClaw

# 17. Launch GUI (pick one)
OPENSHELL_SAW_NAME=openshell-saw SANDBOX_NAME=cuda-sandbox GUI_PORT=18789 make nemoclaw-gui
OPENSHELL_SAW_NAME=openshell-saw SANDBOX_NAME=notebook GUI_PORT=18790 make openclaw-gui
OPENSHELL_SAW_NAME=openshell-saw \
SANDBOX_NAME=cuda-sandbox \
WORKSPACE=cuda-dev \
GUI_PORT=18789 \
make nemoclaw-gui # NemoClaw
OPENSHELL_SAW_NAME=openshell-saw \
SANDBOX_NAME=notebook \
GUI_PORT=18790 \
make openclaw-gui # OpenClaw
```

> **Note:** The gateway VM uses a self-signed TLS certificate. Pass `--gateway-insecure` to `openshell` commands, or set `export OPENSHELL_GATEWAY_INSECURE=true`.
Expand All @@ -298,16 +312,29 @@ You can set `OPENSHELL_SAW_NAME` once via `export` and all `openshell-saw-*` tar
### Validating the deployment

```bash
# List sandboxes
# List sandboxes (default workspace, then cuda-dev)
openshell --gateway-insecure sandbox list

# NemoClaw sandbox (TUI and GUI)
OPENSHELL_SAW_NAME=openshell-saw SANDBOX_NAME=cuda-sandbox make nemoclaw-tui
OPENSHELL_SAW_NAME=openshell-saw SANDBOX_NAME=cuda-sandbox GUI_PORT=18789 make nemoclaw-gui

# OpenClaw sandbox (TUI and GUI)
OPENSHELL_SAW_NAME=openshell-saw SANDBOX_NAME=notebook make openclaw-tui
OPENSHELL_SAW_NAME=openshell-saw SANDBOX_NAME=notebook GUI_PORT=18790 make openclaw-gui
openshell --gateway-insecure sandbox list --workspace cuda-dev

# NemoClaw sandbox (TUI and GUI) — workspace cuda-dev
OPENSHELL_SAW_NAME=openshell-saw \
SANDBOX_NAME=cuda-sandbox \
WORKSPACE=cuda-dev \
make nemoclaw-tui
OPENSHELL_SAW_NAME=openshell-saw \
SANDBOX_NAME=cuda-sandbox \
WORKSPACE=cuda-dev \
GUI_PORT=18789 \
make nemoclaw-gui

# OpenClaw sandbox (TUI and GUI) — workspace default
OPENSHELL_SAW_NAME=openshell-saw \
SANDBOX_NAME=notebook \
make openclaw-tui
OPENSHELL_SAW_NAME=openshell-saw \
SANDBOX_NAME=notebook \
GUI_PORT=18790 \
make openclaw-gui

# Legacy aliases (default to nemoclaw)
make openshell-saw-tui
Expand Down
34 changes: 32 additions & 2 deletions charts/saw-bom/scripts/apply_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,26 @@ def create_sandbox_generic(self, sandbox, workspace_name="default"):
"sudo docker logs $CNAME 2>&1 | tail -30"
], check=False)

def chown_sandbox_home(self, sandbox_name):
"""Chown /sandbox to the supervisor's sandbox uid.

The image bakes UID 65532. The supervisor rewrites passwd to
whatever uid is free (1000, 998, …) and does not chown existing
files. openshell sandbox exec cannot chown (not root); docker
exec -u 0 can. After passwd rewrite, name 'sandbox' is the
runtime uid, so this works on any cluster.
"""
log(f"Chowning /sandbox to sandbox user in '{sandbox_name}'")
self.sh.run([
"bash", "-c",
"CNAME=$(sudo docker ps -a "
f"--filter 'name=openshell.*{sandbox_name}' "
"--format '{{.Names}}' | head -1) && "
"[ -n \"$CNAME\" ] && "
"sudo docker exec -u 0 \"$CNAME\" "
"chown -R sandbox:sandbox /sandbox",
], check=False)

def install_nemoclaw_cli(self, cli_image):
if not cli_image:
return
Expand Down Expand Up @@ -532,6 +552,9 @@ def start_openclaw_gateway(self, sandbox_name, dashboard_route,
log(f" waiting for sandbox ready... (attempt {i+1})")
time.sleep(5)

# Supervisor has rewritten passwd by Ready; match /sandbox to that uid.
self.chown_sandbox_home(sandbox_name)

token = secrets_mod.token_hex(16)
exec_cmd = ["openshell", "sandbox", "exec", "-n",
sandbox_name] + ws_args + ["--no-tty", "--"]
Expand Down Expand Up @@ -751,9 +774,16 @@ def main():
cred = resolve_credential(prov)
deployer.create_provider(prov, cred, ws.name)
if not inference_set and prov.model:
log(f" Setting inference route: "
f"provider={prov.name} model={prov.model}")
log(f" Setting inference routes: "
f"provider={prov.name} model={prov.model}"
f" workspace={ws.name}")
sh.run(["openshell", "inference", "set",
"--provider", prov.name,
"--model", prov.model,
"--workspace", ws.name,
"--no-verify"], check=False)
sh.run(["openshell", "inference", "set",
"--system",
"--provider", prov.name,
"--model", prov.model,
"--no-verify"], check=False)
Expand Down
9 changes: 5 additions & 4 deletions scripts/openshell-saw-gui.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set -euo pipefail

GATEWAY_NAME="${GATEWAY_NAME:?GATEWAY_NAME is required}"
SANDBOX_NAME="${SANDBOX_NAME:-${OPENSHELL_SAW_NAME:-${GATEWAY_NAME}}}"
WORKSPACE="${WORKSPACE:-default}"
GUI_PORT="${GUI_PORT:-18789}"
SSH_USER="${SSH_USER:-sandbox}"

Expand All @@ -23,13 +24,13 @@ fi

# Fetch dashboard token via openshell sandbox exec
echo "Fetching dashboard token..."
TOKEN=$(openshell --gateway-insecure sandbox exec -n "${SANDBOX_NAME}" --no-tty -- \
TOKEN=$(openshell --gateway-insecure sandbox exec -n "${SANDBOX_NAME}" --workspace "${WORKSPACE}" --no-tty -- \
cat /sandbox/.openclaw/openclaw.json 2>/dev/null \
| grep -v 'TLS certificate verification is disabled' \
| python3 -c "import sys,json; c=json.load(sys.stdin); print((c.get('gateway',{}).get('auth',{}).get('token','')))" 2>/dev/null | grep -oE '^[a-f0-9]+$' || true)

if [[ -z "${TOKEN}" ]]; then
TOKEN=$(openshell --gateway-insecure sandbox exec -n "${SANDBOX_NAME}" --no-tty -- \
TOKEN=$(openshell --gateway-insecure sandbox exec -n "${SANDBOX_NAME}" --workspace "${WORKSPACE}" --no-tty -- \
cat /tmp/auth-token 2>/dev/null | grep -oE '[a-f0-9]{32,}' || true)
fi

Expand All @@ -47,9 +48,9 @@ echo "Press Ctrl-C to stop."
echo ""

# Port-forward via openshell ssh-proxy — uses local OIDC token
ssh -o "ProxyCommand=openshell --gateway-insecure ssh-proxy --gateway-name ${GATEWAY_NAME} --name ${SANDBOX_NAME}" \
ssh -o "ProxyCommand=openshell --gateway-insecure ssh-proxy --gateway-name ${GATEWAY_NAME} --name ${SANDBOX_NAME} --workspace ${WORKSPACE}" \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o LogLevel=ERROR \
-L "${GUI_PORT}:127.0.0.1:18789" \
-N "${SSH_USER}@openshell-${SANDBOX_NAME}.default"
-N "${SSH_USER}@openshell-${SANDBOX_NAME}.${WORKSPACE}"