mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-07 09:10:55 +00:00
feat: add spawn pick to shared _display_and_select (Hetzner + all clouds) (#1505)
* feat: add spawn pick to _display_and_select in shared/common.sh
All clouds using interactive_pick (Hetzner, DigitalOcean, AWS, fly, etc.)
now get the arrow-key picker UI when the user runs via `spawn`.
Placement: between fzf (rarely installed) and numbered list (plain fallback).
Priority: fzf > spawn pick > numbered list.
Pipe-delimited items "id|field2|field3..." are converted to tab-delimited
"id\tid\tfield2 · field3 · ..." so spawn pick displays:
> cx22 2 vCPU · 4.0 GB RAM · 40 GB disk · shared · $ 0.0057/hr
> fsn1 Falkenstein · DE
The --default flag uses default_id when set, otherwise default_value,
so the correct item is pre-selected when the picker opens.
No 2>/dev/tty redirect (avoids the zsh 'file exists' failure that broke
the GCP picker; spawn pick opens /dev/tty internally via fs.openSync).
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* refactor: replace custom _gcp_interactive_pick with shared interactive_pick
- Remove _gcp_interactive_pick (60 lines of custom picker logic)
- Convert option functions to pipe-delimited format (id|detail)
to match what interactive_pick / _display_and_select expect
- Replace _gcp_pick_{machine_type,zone,project} with direct
interactive_pick calls — same pattern as Hetzner
- _gcp_project_options: awk now outputs id|name instead of id\tid\tname
GCP now gets fzf → spawn pick → numbered list for free via the
shared helper, with no cloud-specific picker code.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d8785708c9
commit
ff261f3544
2 changed files with 47 additions and 74 deletions
|
|
@ -94,101 +94,55 @@ _gcp_check_auth() {
|
|||
# Interactive pickers for GCP project, zone, and machine type
|
||||
# ============================================================
|
||||
|
||||
# Curated list of popular GCP machine types (value\tLabel\tHint)
|
||||
# Curated list of popular GCP machine types (id|detail)
|
||||
_gcp_machine_type_options() {
|
||||
printf '%s\n' \
|
||||
"e2-micro e2-micro Shared CPU · 2 vCPU · 1 GB RAM (~\$7/mo)" \
|
||||
"e2-small e2-small Shared CPU · 2 vCPU · 2 GB RAM (~\$14/mo)" \
|
||||
"e2-medium e2-medium Shared CPU · 2 vCPU · 4 GB RAM (~\$28/mo) ← default" \
|
||||
"e2-standard-2 e2-standard-2 2 vCPU · 8 GB RAM (~\$49/mo)" \
|
||||
"e2-standard-4 e2-standard-4 4 vCPU · 16 GB RAM (~\$98/mo)" \
|
||||
"n2-standard-2 n2-standard-2 2 vCPU · 8 GB RAM, higher perf (~\$72/mo)" \
|
||||
"n2-standard-4 n2-standard-4 4 vCPU · 16 GB RAM, higher perf (~\$144/mo)" \
|
||||
"c4-standard-2 c4-standard-2 2 vCPU · 8 GB RAM, latest gen (~\$82/mo)"
|
||||
"e2-micro|Shared CPU · 2 vCPU · 1 GB RAM (~\$7/mo)" \
|
||||
"e2-small|Shared CPU · 2 vCPU · 2 GB RAM (~\$14/mo)" \
|
||||
"e2-medium|Shared CPU · 2 vCPU · 4 GB RAM (~\$28/mo)" \
|
||||
"e2-standard-2|2 vCPU · 8 GB RAM (~\$49/mo)" \
|
||||
"e2-standard-4|4 vCPU · 16 GB RAM (~\$98/mo)" \
|
||||
"n2-standard-2|2 vCPU · 8 GB RAM, higher perf (~\$72/mo)" \
|
||||
"n2-standard-4|4 vCPU · 16 GB RAM, higher perf (~\$144/mo)" \
|
||||
"c4-standard-2|2 vCPU · 8 GB RAM, latest gen (~\$82/mo)"
|
||||
}
|
||||
|
||||
# Curated list of popular GCP zones (value\tLabel\tHint)
|
||||
# Curated list of popular GCP zones (id|location)
|
||||
_gcp_zone_options() {
|
||||
printf '%s\n' \
|
||||
"us-central1-a us-central1-a Iowa, US ← default" \
|
||||
"us-east1-b us-east1-b South Carolina, US" \
|
||||
"us-east4-a us-east4-a N. Virginia, US" \
|
||||
"us-west1-a us-west1-a Oregon, US" \
|
||||
"us-west2-a us-west2-a Los Angeles, US" \
|
||||
"northamerica-northeast1-a northamerica-northeast1-a Montreal, Canada" \
|
||||
"europe-west1-b europe-west1-b Belgium" \
|
||||
"europe-west4-a europe-west4-a Netherlands" \
|
||||
"europe-west6-a europe-west6-a Zurich, Switzerland" \
|
||||
"asia-east1-a asia-east1-a Taiwan" \
|
||||
"asia-southeast1-a asia-southeast1-a Singapore" \
|
||||
"australia-southeast1-a australia-southeast1-a Sydney, Australia"
|
||||
"us-central1-a|Iowa, US" \
|
||||
"us-east1-b|South Carolina, US" \
|
||||
"us-east4-a|N. Virginia, US" \
|
||||
"us-west1-a|Oregon, US" \
|
||||
"us-west2-a|Los Angeles, US" \
|
||||
"northamerica-northeast1-a|Montreal, Canada" \
|
||||
"europe-west1-b|Belgium" \
|
||||
"europe-west4-a|Netherlands" \
|
||||
"europe-west6-a|Zurich, Switzerland" \
|
||||
"asia-east1-a|Taiwan" \
|
||||
"asia-southeast1-a|Singapore" \
|
||||
"australia-southeast1-a|Sydney, Australia"
|
||||
}
|
||||
|
||||
# Fetch active GCP projects accessible to the authenticated user (value\tLabel\tHint)
|
||||
# Fetch active GCP projects accessible to the authenticated user (id|name)
|
||||
_gcp_project_options() {
|
||||
gcloud projects list \
|
||||
--filter="lifecycleState=ACTIVE" \
|
||||
--format="value(projectId,name)" \
|
||||
2>/dev/null | \
|
||||
awk -F'\t' '{ print $1 "\t" $1 "\t" $2 }'
|
||||
}
|
||||
|
||||
# Generic GCP interactive picker.
|
||||
# Respects the named env var (skip picker if already set).
|
||||
# Tries `spawn pick` for a nice arrow-key UI, falls back to a numbered list.
|
||||
#
|
||||
# Usage: _gcp_interactive_pick DISPLAY_NAME ENV_VAR_NAME DEFAULT OPTIONS_FN
|
||||
# Outputs the selected value on stdout.
|
||||
_gcp_interactive_pick() {
|
||||
local display="${1}" # e.g. "GCP machine type"
|
||||
local env_var="${2}" # e.g. "GCP_MACHINE_TYPE"
|
||||
local default_val="${3}" # e.g. "e2-medium"
|
||||
local options_fn="${4}" # function name that prints "value\tLabel\tHint" lines
|
||||
|
||||
# Honour an explicit env var override — no prompt needed
|
||||
local current_val
|
||||
current_val="${!env_var:-}"
|
||||
if [[ -n "${current_val}" ]]; then
|
||||
echo "${current_val}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Fetch available options
|
||||
local options_text
|
||||
options_text=$("${options_fn}")
|
||||
if [[ -z "${options_text}" ]]; then
|
||||
log_warn "Could not list ${display} options — using default: ${default_val}"
|
||||
echo "${default_val}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Try `spawn pick` for a nicer arrow-key UI (available when user ran `spawn`)
|
||||
if command -v spawn >/dev/null 2>&1; then
|
||||
local picked
|
||||
picked=$(printf '%s\n' "${options_text}" | \
|
||||
spawn pick --prompt "Select ${display}" --default "${default_val}") && {
|
||||
echo "${picked}"
|
||||
return
|
||||
}
|
||||
fi
|
||||
|
||||
# Fallback: shared/common.sh numbered-list selector
|
||||
# Convert "value\tLabel\tHint" → "value|Label" for _display_and_select
|
||||
local items
|
||||
items=$(printf '%s\n' "${options_text}" | awk -F'\t' '{ print $1 "|" $2 }')
|
||||
_display_and_select "${display}" "${default_val}" "${default_val}" <<< "${items}"
|
||||
awk -F'\t' '{ print $1 "|" $2 }'
|
||||
}
|
||||
|
||||
_gcp_pick_machine_type() {
|
||||
_gcp_interactive_pick "GCP machine type" "GCP_MACHINE_TYPE" "e2-medium" "_gcp_machine_type_options"
|
||||
interactive_pick "GCP_MACHINE_TYPE" "e2-medium" "GCP machine types" _gcp_machine_type_options "e2-medium"
|
||||
}
|
||||
|
||||
_gcp_pick_zone() {
|
||||
_gcp_interactive_pick "GCP zone" "GCP_ZONE" "us-central1-a" "_gcp_zone_options"
|
||||
interactive_pick "GCP_ZONE" "us-central1-a" "GCP zones" _gcp_zone_options "us-central1-a"
|
||||
}
|
||||
|
||||
_gcp_pick_project() {
|
||||
_gcp_interactive_pick "GCP project" "GCP_PROJECT" "" "_gcp_project_options"
|
||||
interactive_pick "GCP_PROJECT" "" "GCP projects" _gcp_project_options
|
||||
}
|
||||
|
||||
# Resolve and export GCP_PROJECT — prompt interactively if not already set
|
||||
|
|
|
|||
|
|
@ -3459,7 +3459,26 @@ _display_and_select() {
|
|||
return
|
||||
fi
|
||||
|
||||
# Fallback to numbered list when fzf is not available
|
||||
# Try spawn pick for an arrow-key UI (available when the user ran `spawn`)
|
||||
if command -v spawn >/dev/null 2>&1; then
|
||||
# Convert pipe-delimited "id|label|extra..." → "id\tid\tlabel · extra · ..."
|
||||
# so spawn pick shows the id as label and all detail fields as hint.
|
||||
local spawn_input
|
||||
spawn_input=$(printf '%s\n' "${items_array[@]}" | awk -F'|' '{
|
||||
val=$1; hint="";
|
||||
for (i=2; i<=NF; i++) { hint = hint (hint ? " \xc2\xb7 " : "") $i }
|
||||
printf "%s\t%s\t%s\n", val, val, hint
|
||||
}')
|
||||
local picked
|
||||
local spawn_default="${default_id:-${default_value}}"
|
||||
picked=$(printf '%s\n' "${spawn_input}" | \
|
||||
spawn pick --prompt "Select ${prompt_text}" --default "${spawn_default}") && {
|
||||
echo "${picked}"
|
||||
return
|
||||
}
|
||||
fi
|
||||
|
||||
# Fallback to numbered list when neither fzf nor spawn pick is available
|
||||
_numbered_list_select "${prompt_text}" "${default_value}" "${default_id}" "${items_array[@]}"
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue