diff --git a/cli/src/aws/aws.ts b/cli/src/aws/aws.ts index 83277056..7ce4fb5a 100644 --- a/cli/src/aws/aws.ts +++ b/cli/src/aws/aws.ts @@ -13,6 +13,7 @@ import { validateServerName, validateRegionName, toKebabCase, + defaultSpawnName, } from "../shared/ui"; import type { CloudInitTier } from "../shared/agents"; import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init"; @@ -805,47 +806,25 @@ export async function getServerName(): Promise { const kebab = process.env.SPAWN_NAME_KEBAB || (process.env.SPAWN_NAME ? toKebabCase(process.env.SPAWN_NAME) : ""); - const defaultName = kebab || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE === "1") { - return defaultName; - } - - const answer = await prompt(`Enter instance name [${defaultName}]: `); - const name = answer || defaultName; - - if (!validateServerName(name)) { - logError(`Invalid instance name: '${name}'`); - throw new Error("Invalid server name"); - } - return name; + return kebab || defaultSpawnName(); } export async function promptSpawnName(): Promise { if (process.env.SPAWN_NAME_KEBAB) return; - let displayName: string; + let kebab: string; if (process.env.SPAWN_NAME) { - displayName = process.env.SPAWN_NAME; - logInfo(`Spawn name: ${displayName}`); + kebab = toKebabCase(process.env.SPAWN_NAME) || defaultSpawnName(); } else if (process.env.SPAWN_NON_INTERACTIVE === "1") { - displayName = "spawn"; + kebab = defaultSpawnName(); } else { + const fallback = defaultSpawnName(); process.stderr.write("\n"); - displayName = await prompt('Spawn name (e.g. "My Dev Box"): '); - if (!displayName) displayName = "spawn"; + const answer = await prompt(`AWS instance name [${fallback}]: `); + kebab = toKebabCase(answer || fallback) || defaultSpawnName(); } - let kebab = toKebabCase(displayName) || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE !== "1") { - const confirmed = await prompt(`Resource name [${kebab}]: `); - if (confirmed) { - kebab = toKebabCase(confirmed) || "spawn"; - } - } - - process.env.SPAWN_NAME_DISPLAY = displayName; + process.env.SPAWN_NAME_DISPLAY = kebab; process.env.SPAWN_NAME_KEBAB = kebab; logInfo(`Using resource name: ${kebab}`); } diff --git a/cli/src/daytona/daytona.ts b/cli/src/daytona/daytona.ts index edfed4eb..91d692b4 100644 --- a/cli/src/daytona/daytona.ts +++ b/cli/src/daytona/daytona.ts @@ -10,6 +10,7 @@ import { jsonEscape, validateServerName, toKebabCase, + defaultSpawnName, } from "../shared/ui"; import type { CloudInitTier } from "../shared/agents"; import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init"; @@ -519,47 +520,25 @@ export async function getServerName(): Promise { const kebab = process.env.SPAWN_NAME_KEBAB || (process.env.SPAWN_NAME ? toKebabCase(process.env.SPAWN_NAME) : ""); - const defaultName = kebab || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE === "1") { - return defaultName; - } - - const answer = await prompt(`Enter sandbox name [${defaultName}]: `); - const name = answer || defaultName; - - if (!validateServerName(name)) { - logError(`Invalid sandbox name: '${name}'`); - throw new Error("Invalid server name"); - } - return name; + return kebab || defaultSpawnName(); } export async function promptSpawnName(): Promise { if (process.env.SPAWN_NAME_KEBAB) return; - let displayName: string; + let kebab: string; if (process.env.SPAWN_NAME) { - displayName = process.env.SPAWN_NAME; - logInfo(`Spawn name: ${displayName}`); + kebab = toKebabCase(process.env.SPAWN_NAME) || defaultSpawnName(); } else if (process.env.SPAWN_NON_INTERACTIVE === "1") { - displayName = "spawn"; + kebab = defaultSpawnName(); } else { + const fallback = defaultSpawnName(); process.stderr.write("\n"); - displayName = await prompt('Spawn name (e.g. "My Dev Box"): '); - if (!displayName) displayName = "spawn"; + const answer = await prompt(`Daytona workspace name [${fallback}]: `); + kebab = toKebabCase(answer || fallback) || defaultSpawnName(); } - let kebab = toKebabCase(displayName) || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE !== "1") { - const confirmed = await prompt(`Resource name [${kebab}]: `); - if (confirmed) { - kebab = toKebabCase(confirmed) || "spawn"; - } - } - - process.env.SPAWN_NAME_DISPLAY = displayName; + process.env.SPAWN_NAME_DISPLAY = kebab; process.env.SPAWN_NAME_KEBAB = kebab; logInfo(`Using resource name: ${kebab}`); } diff --git a/cli/src/digitalocean/digitalocean.ts b/cli/src/digitalocean/digitalocean.ts index 55907c08..aff09129 100644 --- a/cli/src/digitalocean/digitalocean.ts +++ b/cli/src/digitalocean/digitalocean.ts @@ -11,6 +11,7 @@ import { validateServerName, validateRegionName, toKebabCase, + defaultSpawnName, } from "../shared/ui"; import type { CloudInitTier } from "../shared/agents"; import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init"; @@ -945,47 +946,25 @@ export async function getServerName(): Promise { const kebab = process.env.SPAWN_NAME_KEBAB || (process.env.SPAWN_NAME ? toKebabCase(process.env.SPAWN_NAME) : ""); - const defaultName = kebab || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE === "1") { - return defaultName; - } - - const answer = await prompt(`Enter droplet name [${defaultName}]: `); - const name = answer || defaultName; - - if (!validateServerName(name)) { - logError(`Invalid droplet name: '${name}'`); - throw new Error("Invalid server name"); - } - return name; + return kebab || defaultSpawnName(); } export async function promptSpawnName(): Promise { if (process.env.SPAWN_NAME_KEBAB) return; - let displayName: string; + let kebab: string; if (process.env.SPAWN_NAME) { - displayName = process.env.SPAWN_NAME; - logInfo(`Spawn name: ${displayName}`); + kebab = toKebabCase(process.env.SPAWN_NAME) || defaultSpawnName(); } else if (process.env.SPAWN_NON_INTERACTIVE === "1") { - displayName = "spawn"; + kebab = defaultSpawnName(); } else { + const fallback = defaultSpawnName(); process.stderr.write("\n"); - displayName = await prompt('Spawn name (e.g. "My Dev Box"): '); - if (!displayName) displayName = "spawn"; + const answer = await prompt(`DigitalOcean droplet name [${fallback}]: `); + kebab = toKebabCase(answer || fallback) || defaultSpawnName(); } - let kebab = toKebabCase(displayName) || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE !== "1") { - const confirmed = await prompt(`Resource name [${kebab}]: `); - if (confirmed) { - kebab = toKebabCase(confirmed) || "spawn"; - } - } - - process.env.SPAWN_NAME_DISPLAY = displayName; + process.env.SPAWN_NAME_DISPLAY = kebab; process.env.SPAWN_NAME_KEBAB = kebab; logInfo(`Using resource name: ${kebab}`); } diff --git a/cli/src/fly/fly.ts b/cli/src/fly/fly.ts index 847d31a3..33c2b0cd 100644 --- a/cli/src/fly/fly.ts +++ b/cli/src/fly/fly.ts @@ -12,6 +12,7 @@ import { validateServerName, validateRegionName, toKebabCase, + defaultSpawnName, } from "../shared/ui"; import type { CloudInitTier } from "../shared/agents"; import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init"; @@ -908,51 +909,27 @@ export async function getServerName(): Promise { return name; } - // Derive from spawn name const kebab = process.env.SPAWN_NAME_KEBAB || (process.env.SPAWN_NAME ? toKebabCase(process.env.SPAWN_NAME) : ""); - const defaultName = kebab || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE === "1") { - return defaultName; - } - - const answer = await prompt(`Enter app name [${defaultName}]: `); - const name = answer || defaultName; - - if (!validateServerName(name)) { - logError(`Invalid app name: '${name}'`); - throw new Error("Invalid server name"); - } - return name; + return kebab || defaultSpawnName(); } -/** Prompt for a spawn display name, derive kebab-case resource name. */ export async function promptSpawnName(): Promise { if (process.env.SPAWN_NAME_KEBAB) return; - let displayName: string; + let kebab: string; if (process.env.SPAWN_NAME) { - displayName = process.env.SPAWN_NAME; - logInfo(`Spawn name: ${displayName}`); + kebab = toKebabCase(process.env.SPAWN_NAME) || defaultSpawnName(); } else if (process.env.SPAWN_NON_INTERACTIVE === "1") { - displayName = "spawn"; + kebab = defaultSpawnName(); } else { + const fallback = defaultSpawnName(); process.stderr.write("\n"); - displayName = await prompt('Spawn name (e.g. "My Dev Box"): '); - if (!displayName) displayName = "spawn"; + const answer = await prompt(`Fly machine name [${fallback}]: `); + kebab = toKebabCase(answer || fallback) || defaultSpawnName(); } - let kebab = toKebabCase(displayName) || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE !== "1") { - const confirmed = await prompt(`Resource name [${kebab}]: `); - if (confirmed) { - kebab = toKebabCase(confirmed) || "spawn"; - } - } - - process.env.SPAWN_NAME_DISPLAY = displayName; + process.env.SPAWN_NAME_DISPLAY = kebab; process.env.SPAWN_NAME_KEBAB = kebab; logInfo(`Using resource name: ${kebab}`); } diff --git a/cli/src/gcp/gcp.ts b/cli/src/gcp/gcp.ts index de6170af..82f54637 100644 --- a/cli/src/gcp/gcp.ts +++ b/cli/src/gcp/gcp.ts @@ -11,6 +11,7 @@ import { jsonEscape, validateServerName, toKebabCase, + defaultSpawnName, } from "../shared/ui"; import type { CloudInitTier } from "../shared/agents"; import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init"; @@ -379,48 +380,25 @@ export async function getServerName(): Promise { const kebab = process.env.SPAWN_NAME_KEBAB || (process.env.SPAWN_NAME ? toKebabCase(process.env.SPAWN_NAME) : ""); - const defaultName = kebab || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE === "1") { - return defaultName; - } - - const answer = await prompt(`Enter instance name [${defaultName}]: `); - const name = answer || defaultName; - - if (!validateServerName(name)) { - logError(`Invalid instance name: '${name}'`); - throw new Error("Invalid server name"); - } - return name; + return kebab || defaultSpawnName(); } -/** Prompt for a spawn display name, derive kebab-case resource name. */ export async function promptSpawnName(): Promise { if (process.env.SPAWN_NAME_KEBAB) return; - let displayName: string; + let kebab: string; if (process.env.SPAWN_NAME) { - displayName = process.env.SPAWN_NAME; - logInfo(`Spawn name: ${displayName}`); + kebab = toKebabCase(process.env.SPAWN_NAME) || defaultSpawnName(); } else if (process.env.SPAWN_NON_INTERACTIVE === "1") { - displayName = "spawn"; + kebab = defaultSpawnName(); } else { + const fallback = defaultSpawnName(); process.stderr.write("\n"); - displayName = await prompt('Spawn name (e.g. "My Dev Box"): '); - if (!displayName) displayName = "spawn"; + const answer = await prompt(`GCP instance name [${fallback}]: `); + kebab = toKebabCase(answer || fallback) || defaultSpawnName(); } - let kebab = toKebabCase(displayName) || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE !== "1") { - const confirmed = await prompt(`Resource name [${kebab}]: `); - if (confirmed) { - kebab = toKebabCase(confirmed) || "spawn"; - } - } - - process.env.SPAWN_NAME_DISPLAY = displayName; + process.env.SPAWN_NAME_DISPLAY = kebab; process.env.SPAWN_NAME_KEBAB = kebab; logInfo(`Using resource name: ${kebab}`); } diff --git a/cli/src/hetzner/hetzner.ts b/cli/src/hetzner/hetzner.ts index 92836e01..86e56e30 100644 --- a/cli/src/hetzner/hetzner.ts +++ b/cli/src/hetzner/hetzner.ts @@ -11,6 +11,7 @@ import { validateServerName, validateRegionName, toKebabCase, + defaultSpawnName, } from "../shared/ui"; import type { CloudInitTier } from "../shared/agents"; import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init"; @@ -576,47 +577,25 @@ export async function getServerName(): Promise { const kebab = process.env.SPAWN_NAME_KEBAB || (process.env.SPAWN_NAME ? toKebabCase(process.env.SPAWN_NAME) : ""); - const defaultName = kebab || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE === "1") { - return defaultName; - } - - const answer = await prompt(`Enter server name [${defaultName}]: `); - const name = answer || defaultName; - - if (!validateServerName(name)) { - logError(`Invalid server name: '${name}'`); - throw new Error("Invalid server name"); - } - return name; + return kebab || defaultSpawnName(); } export async function promptSpawnName(): Promise { if (process.env.SPAWN_NAME_KEBAB) return; - let displayName: string; + let kebab: string; if (process.env.SPAWN_NAME) { - displayName = process.env.SPAWN_NAME; - logInfo(`Spawn name: ${displayName}`); + kebab = toKebabCase(process.env.SPAWN_NAME) || defaultSpawnName(); } else if (process.env.SPAWN_NON_INTERACTIVE === "1") { - displayName = "spawn"; + kebab = defaultSpawnName(); } else { + const fallback = defaultSpawnName(); process.stderr.write("\n"); - displayName = await prompt('Spawn name (e.g. "My Dev Box"): '); - if (!displayName) displayName = "spawn"; + const answer = await prompt(`Hetzner server name [${fallback}]: `); + kebab = toKebabCase(answer || fallback) || defaultSpawnName(); } - let kebab = toKebabCase(displayName) || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE !== "1") { - const confirmed = await prompt(`Resource name [${kebab}]: `); - if (confirmed) { - kebab = toKebabCase(confirmed) || "spawn"; - } - } - - process.env.SPAWN_NAME_DISPLAY = displayName; + process.env.SPAWN_NAME_DISPLAY = kebab; process.env.SPAWN_NAME_KEBAB = kebab; logInfo(`Using resource name: ${kebab}`); } diff --git a/cli/src/shared/ui.ts b/cli/src/shared/ui.ts index 284b631d..6779796b 100644 --- a/cli/src/shared/ui.ts +++ b/cli/src/shared/ui.ts @@ -141,3 +141,9 @@ export function toKebabCase(name: string): string { .replace(/-{2,}/g, "-") .replace(/^-|-$/g, ""); } + +/** Generate a default spawn name with random suffix (e.g. "spawn-a1b2"). */ +export function defaultSpawnName(): string { + const suffix = Math.random().toString(36).slice(2, 6); + return `spawn-${suffix}`; +} diff --git a/cli/src/sprite/main.ts b/cli/src/sprite/main.ts index 7cf1d581..60a75280 100644 --- a/cli/src/sprite/main.ts +++ b/cli/src/sprite/main.ts @@ -5,7 +5,7 @@ import { ensureSpriteCli, ensureSpriteAuthenticated, promptSpawnName, - getSpriteName, + getServerName, createSprite, verifySpriteConnectivity, setupShellEnvironment, @@ -45,7 +45,7 @@ async function main() { await setupShellEnvironment(); saveVmConnection(); }, - getServerName: getSpriteName, + getServerName, async waitForReady() {}, interactiveSession, saveLaunchCmd, diff --git a/cli/src/sprite/sprite.ts b/cli/src/sprite/sprite.ts index 36f01529..dcaacb6f 100644 --- a/cli/src/sprite/sprite.ts +++ b/cli/src/sprite/sprite.ts @@ -9,6 +9,7 @@ import { prompt, validateServerName, toKebabCase, + defaultSpawnName, } from "../shared/ui"; // ─── Configurable Constants ────────────────────────────────────────────────── @@ -193,33 +194,24 @@ function orgFlags(): string[] { export async function promptSpawnName(): Promise { if (process.env.SPAWN_NAME_KEBAB) return; - let displayName: string; + let kebab: string; if (process.env.SPAWN_NAME) { - displayName = process.env.SPAWN_NAME; - logInfo(`Spawn name: ${displayName}`); + kebab = toKebabCase(process.env.SPAWN_NAME) || defaultSpawnName(); } else if (process.env.SPAWN_NON_INTERACTIVE === "1") { - displayName = "spawn"; + kebab = defaultSpawnName(); } else { + const fallback = defaultSpawnName(); process.stderr.write("\n"); - displayName = await prompt('Spawn name (e.g. "My Dev Box"): '); - if (!displayName) displayName = "spawn"; + const answer = await prompt(`Sprite name [${fallback}]: `); + kebab = toKebabCase(answer || fallback) || defaultSpawnName(); } - let kebab = toKebabCase(displayName) || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE !== "1") { - const confirmed = await prompt(`Resource name [${kebab}]: `); - if (confirmed) { - kebab = toKebabCase(confirmed) || "spawn"; - } - } - - process.env.SPAWN_NAME_DISPLAY = displayName; + process.env.SPAWN_NAME_DISPLAY = kebab; process.env.SPAWN_NAME_KEBAB = kebab; logInfo(`Using resource name: ${kebab}`); } -export async function getSpriteName(): Promise { +export async function getServerName(): Promise { if (process.env.SPRITE_NAME) { const name = process.env.SPRITE_NAME; if (!validateServerName(name)) { @@ -232,20 +224,7 @@ export async function getSpriteName(): Promise { const kebab = process.env.SPAWN_NAME_KEBAB || (process.env.SPAWN_NAME ? toKebabCase(process.env.SPAWN_NAME) : ""); - const defaultName = kebab || "spawn"; - - if (process.env.SPAWN_NON_INTERACTIVE === "1") { - return defaultName; - } - - const answer = await prompt(`Enter sprite name [${defaultName}]: `); - const name = answer || defaultName; - - if (!validateServerName(name)) { - logError(`Invalid sprite name: '${name}'`); - throw new Error("Invalid server name"); - } - return name; + return kebab || defaultSpawnName(); } // ─── Provisioning ────────────────────────────────────────────────────────────