Skip to content

Commit a351552

Browse files
fix(onboard): reject sandbox names starting with a digit and allow retry
Names starting with a digit (e.g., '7racii') pass the current regex but fail downstream in Kubernetes. The user is forced to delete ~/.nemoclaw to recover because the invalid name is persisted in the session and reused on retry. Tighten the regex from ^[a-z0-9] to ^[a-z] so digit-prefixed names are caught at prompt time. Replace process.exit(1) with a retry loop (up to 3 attempts) so users can correct the name without restarting. Non-interactive mode still exits immediately on invalid input. Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
1 parent 711b98e commit a351552

2 files changed

Lines changed: 47 additions & 12 deletions

File tree

bin/lib/onboard.js

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,22 +1787,39 @@ async function recoverGatewayRuntime() {
17871787
// ── Step 3: Sandbox ──────────────────────────────────────────────
17881788

17891789
async function promptValidatedSandboxName() {
1790-
const nameAnswer = await promptOrDefault(
1791-
" Sandbox name (lowercase, numbers, hyphens) [my-assistant]: ",
1792-
"NEMOCLAW_SANDBOX_NAME", "my-assistant"
1793-
);
1794-
const sandboxName = (nameAnswer || "my-assistant").trim().toLowerCase();
1790+
const MAX_ATTEMPTS = 3;
1791+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
1792+
const nameAnswer = await promptOrDefault(
1793+
" Sandbox name (lowercase, starts with letter, hyphens ok) [my-assistant]: ",
1794+
"NEMOCLAW_SANDBOX_NAME", "my-assistant"
1795+
);
1796+
const sandboxName = (nameAnswer || "my-assistant").trim().toLowerCase();
1797+
1798+
// Validate: RFC 1123 subdomain — lowercase alphanumeric and hyphens,
1799+
// must start with a letter (not a digit) to satisfy Kubernetes naming.
1800+
if (/^[a-z]([a-z0-9-]*[a-z0-9])?$/.test(sandboxName)) {
1801+
return sandboxName;
1802+
}
17951803

1796-
// Validate: RFC 1123 subdomain — lowercase alphanumeric and hyphens,
1797-
// must start and end with alphanumeric (required by Kubernetes/OpenShell)
1798-
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(sandboxName)) {
17991804
console.error(` Invalid sandbox name: '${sandboxName}'`);
1800-
console.error(" Names must be lowercase, contain only letters, numbers, and hyphens,");
1801-
console.error(" and must start and end with a letter or number.");
1802-
process.exit(1);
1805+
if (/^[0-9]/.test(sandboxName)) {
1806+
console.error(" Names must start with a letter, not a digit.");
1807+
} else {
1808+
console.error(" Names must be lowercase, contain only letters, numbers, and hyphens,");
1809+
console.error(" and must start and end with a letter or number.");
1810+
}
1811+
1812+
if (isNonInteractive()) {
1813+
process.exit(1);
1814+
}
1815+
1816+
if (attempt < MAX_ATTEMPTS - 1) {
1817+
console.error(" Please try again.\n");
1818+
}
18031819
}
18041820

1805-
return sandboxName;
1821+
console.error(" Too many invalid attempts.");
1822+
process.exit(1);
18061823
}
18071824

18081825
// eslint-disable-next-line complexity

test/onboard.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,24 @@ describe("onboard helpers", () => {
440440
}
441441
});
442442

443+
it("rejects sandbox names starting with a digit", () => {
444+
// The validation regex must require names to start with a letter,
445+
// not a digit — Kubernetes rejects digit-prefixed names downstream.
446+
const SANDBOX_NAME_REGEX = /^[a-z]([a-z0-9-]*[a-z0-9])?$/;
447+
448+
expect(SANDBOX_NAME_REGEX.test("my-assistant")).toBe(true);
449+
expect(SANDBOX_NAME_REGEX.test("a")).toBe(true);
450+
expect(SANDBOX_NAME_REGEX.test("agent-1")).toBe(true);
451+
expect(SANDBOX_NAME_REGEX.test("test-sandbox-v2")).toBe(true);
452+
453+
expect(SANDBOX_NAME_REGEX.test("7racii")).toBe(false);
454+
expect(SANDBOX_NAME_REGEX.test("1sandbox")).toBe(false);
455+
expect(SANDBOX_NAME_REGEX.test("123")).toBe(false);
456+
expect(SANDBOX_NAME_REGEX.test("-start-hyphen")).toBe(false);
457+
expect(SANDBOX_NAME_REGEX.test("end-hyphen-")).toBe(false);
458+
expect(SANDBOX_NAME_REGEX.test("")).toBe(false);
459+
});
460+
443461
it("passes credential names to openshell without embedding secret values in argv", () => {
444462
const repoRoot = path.join(import.meta.dirname, "..");
445463
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-onboard-inference-"));

0 commit comments

Comments
 (0)