diff --git a/cli/src/commands.ts b/cli/src/commands.ts index 60622d92..2059e81a 100644 --- a/cli/src/commands.ts +++ b/cli/src/commands.ts @@ -277,6 +277,29 @@ function validateImplementation(manifest: Manifest, cloud: string, agent: string // ── Interactive ──────────────────────────────────────────────────────────────── +/** Sort clouds by credential availability and build hint overrides for the picker */ +export function prioritizeCloudsByCredentials( + clouds: string[], + manifest: Manifest +): { sortedClouds: string[]; hintOverrides: Record; credCount: number } { + const withCreds: string[] = []; + const withoutCreds: string[] = []; + for (const c of clouds) { + if (hasCloudCredentials(manifest.clouds[c].auth)) { + withCreds.push(c); + } else { + withoutCreds.push(c); + } + } + + const hintOverrides: Record = {}; + for (const c of withCreds) { + hintOverrides[c] = `credentials detected -- ${manifest.clouds[c].description}`; + } + + return { sortedClouds: [...withCreds, ...withoutCreds], hintOverrides, credCount: withCreds.length }; +} + export async function cmdInteractive(): Promise { p.intro(pc.inverse(` spawn v${VERSION} `)); @@ -298,26 +321,10 @@ export async function cmdInteractive(): Promise { process.exit(1); } - // Prioritize clouds where the user already has credentials set - const withCreds: string[] = []; - const withoutCreds: string[] = []; - for (const c of clouds) { - if (hasCloudCredentials(manifest.clouds[c].auth)) { - withCreds.push(c); - } else { - withoutCreds.push(c); - } - } - const sortedClouds = [...withCreds, ...withoutCreds]; + const { sortedClouds, hintOverrides, credCount } = prioritizeCloudsByCredentials(clouds, manifest); - // Add credential hints to the select options - const hintOverrides: Record = {}; - for (const c of withCreds) { - hintOverrides[c] = `credentials detected -- ${manifest.clouds[c].description}`; - } - - if (withCreds.length > 0) { - p.log.info(`${withCreds.length} cloud${withCreds.length > 1 ? "s" : ""} with credentials detected`); + if (credCount > 0) { + p.log.info(`${credCount} cloud${credCount > 1 ? "s" : ""} with credentials detected`); } const cloudChoice = await p.select({ diff --git a/ramnode/lib/common.sh b/ramnode/lib/common.sh index a855c30e..263e0c63 100755 --- a/ramnode/lib/common.sh +++ b/ramnode/lib/common.sh @@ -214,43 +214,9 @@ for f in flavors: " } -# Interactive flavor picker +# Interactive flavor picker (delegates to shared interactive_pick) _pick_flavor() { - if [[ -n "${RAMNODE_FLAVOR:-}" ]]; then - echo "$RAMNODE_FLAVOR" - return - fi - - log_step "Fetching available instance types..." - local flavors - flavors=$(_list_flavors) - - if [[ -z "$flavors" ]]; then - log_warn "Could not fetch flavors, using default: 1GB" - echo "1GB" - return - fi - - log_step "Available instance types:" - local i=1 - local names=() - while IFS='|' read -r name cores ram disk; do - printf " %2d) %-12s %-8s %-12s %s\n" "$i" "$name" "$cores" "$ram" "$disk" >&2 - names+=("$name") - i=$((i + 1)) - done <<< "$flavors" - - local choice - printf "\n" >&2 - choice=$(safe_read "Select instance type [1]: ") || choice="" - choice="${choice:-1}" - - if [[ "$choice" -ge 1 && "$choice" -le "${#names[@]}" ]] 2>/dev/null; then - echo "${names[$((choice - 1))]}" - else - log_warn "Invalid choice, using default: 1GB" - echo "1GB" - fi + interactive_pick "RAMNODE_FLAVOR" "1GB" "instance types" _list_flavors } # List available images