diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index cb3b804eaa6..f8c85cbfd7e 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -150,8 +150,17 @@ async function startGateway(gpu) { async function createSandbox(gpu) { step(3, 7, "Creating sandbox"); - const nameAnswer = await prompt(" Sandbox name [my-assistant]: "); - const sandboxName = nameAnswer || "my-assistant"; + const nameAnswer = await prompt(" Sandbox name (lowercase, numbers, hyphens) [my-assistant]: "); + const sandboxName = (nameAnswer || "my-assistant").trim().toLowerCase(); + + // Validate: RFC 1123 subdomain — lowercase alphanumeric and hyphens, + // must start and end with alphanumeric (required by Kubernetes/OpenShell) + if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(sandboxName)) { + console.error(` Invalid sandbox name: '${sandboxName}'`); + console.error(" Names must be lowercase, contain only letters, numbers, and hyphens,"); + console.error(" and must start and end with a letter or number."); + process.exit(1); + } // Check if sandbox already exists in registry const existing = registry.getSandbox(sandboxName); @@ -162,7 +171,7 @@ async function createSandbox(gpu) { return sandboxName; } // Destroy old sandbox - run(`openshell sandbox delete ${sandboxName} 2>/dev/null || true`, { ignoreError: true }); + run(`openshell sandbox delete "${sandboxName}" 2>/dev/null || true`, { ignoreError: true }); registry.removeSandbox(sandboxName); } @@ -181,7 +190,7 @@ async function createSandbox(gpu) { const basePolicyPath = path.join(ROOT, "nemoclaw-blueprint", "policies", "openclaw-sandbox.yaml"); const createArgs = [ `--from "${buildCtx}/Dockerfile"`, - `--name ${sandboxName}`, + `--name "${sandboxName}"`, `--policy "${basePolicyPath}"`, ]; if (gpu && gpu.nimCapable) createArgs.push("--gpu"); @@ -195,7 +204,7 @@ async function createSandbox(gpu) { run(`openshell sandbox create ${createArgs.join(" ")} -- env ${envArgs.join(" ")} nemoclaw-start 2>&1 | awk '/Sandbox allocated/{if(!seen){print;seen=1}next}1'`); // Forward dashboard port separately - run(`openshell forward start --background 18789 ${sandboxName}`, { ignoreError: true }); + run(`openshell forward start --background 18789 "${sandboxName}"`, { ignoreError: true }); // Clean up build context run(`rm -rf "${buildCtx}"`, { ignoreError: true }); diff --git a/bin/lib/policies.js b/bin/lib/policies.js index 575fcdee27d..c997ca0a462 100644 --- a/bin/lib/policies.js +++ b/bin/lib/policies.js @@ -86,7 +86,7 @@ function applyPreset(sandboxName, presetName) { let rawPolicy = ""; try { rawPolicy = runCapture( - `openshell policy get --full ${sandboxName} 2>/dev/null`, + `openshell policy get --full "${sandboxName}" 2>/dev/null`, { ignoreError: true } ); } catch {} @@ -146,7 +146,7 @@ function applyPreset(sandboxName, presetName) { fs.writeFileSync(tmpFile, merged, "utf-8"); try { - run(`openshell policy set --policy "${tmpFile}" --wait ${sandboxName}`); + run(`openshell policy set --policy "${tmpFile}" --wait "${sandboxName}"`); console.log(` Applied preset: ${presetName}`); } finally { fs.unlinkSync(tmpFile); diff --git a/bin/nemoclaw.js b/bin/nemoclaw.js index a8a31188a1b..07bb3d5b5a2 100755 --- a/bin/nemoclaw.js +++ b/bin/nemoclaw.js @@ -188,8 +188,8 @@ function listSandboxes() { function sandboxConnect(sandboxName) { // Ensure port forward is alive before connecting - run(`openshell forward start --background 18789 ${sandboxName} 2>/dev/null || true`, { ignoreError: true }); - run(`openshell sandbox connect ${sandboxName}`); + run(`openshell forward start --background 18789 "${sandboxName}" 2>/dev/null || true`, { ignoreError: true }); + run(`openshell sandbox connect "${sandboxName}"`); } function sandboxStatus(sandboxName) { @@ -204,7 +204,7 @@ function sandboxStatus(sandboxName) { } // openshell info - run(`openshell sandbox get ${sandboxName} 2>/dev/null || true`, { ignoreError: true }); + run(`openshell sandbox get "${sandboxName}" 2>/dev/null || true`, { ignoreError: true }); // NIM health const nimStat = nim.nimStatus(sandboxName); @@ -217,7 +217,7 @@ function sandboxStatus(sandboxName) { function sandboxLogs(sandboxName, follow) { const followFlag = follow ? " --follow" : ""; - run(`openshell sandbox logs ${sandboxName}${followFlag}`); + run(`openshell sandbox logs "${sandboxName}"${followFlag}`); } async function sandboxPolicyAdd(sandboxName) { @@ -260,7 +260,7 @@ function sandboxDestroy(sandboxName) { nim.stopNimContainer(sandboxName); console.log(` Deleting sandbox '${sandboxName}'...`); - run(`openshell sandbox delete ${sandboxName} 2>/dev/null || true`, { ignoreError: true }); + run(`openshell sandbox delete "${sandboxName}" 2>/dev/null || true`, { ignoreError: true }); registry.removeSandbox(sandboxName); console.log(` ✓ Sandbox '${sandboxName}' destroyed`);