Skip to content
Closed
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
34 changes: 31 additions & 3 deletions nemoclaw-blueprint/orchestrator/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import argparse
import json
import os
import re
import shutil
import subprocess
import sys
Expand Down Expand Up @@ -72,6 +73,19 @@ def openshell_available() -> bool:
return shutil.which("openshell") is not None


def normalize_sandbox_name(name: object) -> str:
"""Normalize sandbox name to lowercase and validate it."""
if not isinstance(name, str):
raise ValueError(f"Invalid sandbox name: expected string, got {type(name).__name__!r}.")
normalized = name.lower()
# Only lowercase letters, numbers, and hyphens allowed
if not re.match(r'^[a-z0-9-]+$', normalized):
raise ValueError(f"Invalid sandbox name: '{name}'. Only lowercase letters, numbers, and hyphens are allowed.")
if len(normalized) > 64:
raise ValueError(f"Sandbox name too long: '{name}'. Must be 64 characters or less.")
return normalized


# ---------------------------------------------------------------------------
# Actions
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -109,12 +123,18 @@ def action_plan(
if endpoint_url:
inference_cfg = {**inference_cfg, "endpoint": endpoint_url}

# Normalize sandbox name to lowercase
try:
sandbox_name = normalize_sandbox_name(sandbox_cfg.get("name", "openclaw"))
except ValueError as exc:
sys.exit(f"Error: {exc}")

Comment thread
coderabbitai[bot] marked this conversation as resolved.
plan: dict[str, Any] = {
"run_id": rid,
"profile": profile,
"sandbox": {
"image": sandbox_cfg.get("image", "openclaw"),
"name": sandbox_cfg.get("name", "openclaw"),
"name": sandbox_name,
"forward_ports": sandbox_cfg.get("forward_ports", [18789]),
},
"inference": {
Expand Down Expand Up @@ -160,7 +180,11 @@ def action_apply(

sandbox_cfg: dict[str, Any] = blueprint.get("components", {}).get("sandbox", {})

sandbox_name: str = sandbox_cfg.get("name", "openclaw")
# Normalize sandbox name to lowercase
try:
sandbox_name: str = normalize_sandbox_name(sandbox_cfg.get("name", "openclaw"))
except ValueError as exc:
sys.exit(f"Error: {exc}")
sandbox_image: str = sandbox_cfg.get("image", "openclaw")
forward_ports: list[int] = sandbox_cfg.get("forward_ports", [18789])

Expand Down Expand Up @@ -282,7 +306,11 @@ def action_rollback(rid: str) -> None:
plan_file = state_dir / "plan.json"
if plan_file.exists():
plan = json.loads(plan_file.read_text())
sandbox_name = plan.get("sandbox_name", "openclaw")
# Normalize sandbox name from plan (in case it was saved before normalization)
try:
sandbox_name = normalize_sandbox_name(plan.get("sandbox_name", "openclaw"))
except ValueError as exc:
sys.exit(f"Error: {exc}")

progress(30, f"Stopping sandbox {sandbox_name}")
run_cmd(
Expand Down
5 changes: 5 additions & 0 deletions nemoclaw/dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ export interface NemoClawConfig {
sandboxName: string;
inferenceProvider: string;
}
/**
* Normalizes a sandbox name to lowercase and validates it.
* Returns the normalized name or the default if invalid.
*/
export declare function normalizeSandboxName(name: string | unknown, defaultName: string): string;
export declare function getPluginConfig(api: OpenClawPluginApi): NemoClawConfig;
export default function register(api: OpenClawPluginApi): void;
//# sourceMappingURL=index.d.ts.map
2 changes: 1 addition & 1 deletion nemoclaw/dist/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 21 additions & 3 deletions nemoclaw/dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion nemoclaw/dist/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading