diff --git a/install.ps1 b/install.ps1 index 60d38e9d0..41d5142af 100644 --- a/install.ps1 +++ b/install.ps1 @@ -327,6 +327,34 @@ function Get-NonEmptyLines { return @($lines | ForEach-Object { "$($_)".Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) } +function Wait-ForReady { + param([string]$Url) + + $maxWait = 60 + $waited = 0 + + Write-Host "[INFO] Launching Agent Zero..." -ForegroundColor Green -NoNewline + while ($waited -lt $maxWait) { + try { + $response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop + if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 400) { + Write-Host '' + print_ok "Agent Zero is ready at $Url" + return $true + } + } + catch { } + + Start-Sleep -Seconds 1 + $waited++ + Write-Host '.' -NoNewline + } + + Write-Host '' + print_warn "Agent Zero did not respond within $maxWait seconds. It may still be starting up." + return $false +} + # Uses image name string matching instead of ancestor filter. # The ancestor filter matches by image ID, not name, so containers created # from a previous version of a tag become invisible after pulling a newer version. @@ -652,32 +680,11 @@ function create_instance { throw 'Failed to start Agent Zero.' } - Write-Host '' - Write-Host '==========================================' - Write-Host ' Installation Complete!' -ForegroundColor Green - Write-Host '==========================================' - Write-Host '' - Write-Host " Image tag : $script:SelectedTag" - Write-Host " Instance name : $containerName" - Write-Host " Compose file : $composeFile" - Write-Host " Data directory : $dataDir" - Write-Host " Web UI : http://localhost:$port" - if (-not [string]::IsNullOrWhiteSpace($authLogin)) { - Write-Host ' Authentication : enabled' - Write-Host " Login : $authLogin" - Write-Host " Password : $authPassword" - } - else { - Write-Host ' Authentication : none' - } - Write-Host '' - Write-Host ' Useful commands:' - Write-Host " docker compose -f `"$composeFile`" logs -f # View logs" - Write-Host " docker compose -f `"$composeFile`" down # Stop" - Write-Host " docker compose -f `"$composeFile`" up -d # Start" - Write-Host " docker compose -f `"$composeFile`" pull # Update image" - Write-Host '' - print_info 'Happy automating with Agent Zero!' + # Wait for the service to become ready + Wait-ForReady -Url "http://localhost:$port" + + # Store the created container name for the caller + $script:CreatedContainerName = $containerName return $true } @@ -742,140 +749,149 @@ function manage_instances { $selectedRow = $containerRows[$selectedIndex] $selectedParts = $selectedRow -split '\|', 3 $selectedName = if ($selectedParts.Count -ge 1) { $selectedParts[0] } else { '' } - $selectedImage = if ($selectedParts.Count -ge 2) { $selectedParts[1] } else { '' } - # Inner action loop for the selected container - while ($true) { - $statusOutput = & docker ps -a --filter "name=^/$selectedName$" --format '{{.Status}}' 2>$null - $selectedStatus = ((Get-NonEmptyLines $statusOutput) | Select-Object -First 1) + manage_single_instance -ContainerName $selectedName + } +} - # If container no longer exists (e.g. after delete), go back to instance list - if ([string]::IsNullOrWhiteSpace($selectedStatus)) { +# Show the action menu for a single container (open, start, stop, restart, delete). +# Can be called from manage_instances or directly after create_instance. +function manage_single_instance { + param([string]$ContainerName) + + # Look up the image for display + $selectedImage = ((Get-NonEmptyLines (& docker ps -a --filter "name=^/$ContainerName$" --format '{{.Image}}' 2>$null)) | Select-Object -First 1) + + while ($true) { + $statusOutput = & docker ps -a --filter "name=^/$ContainerName$" --format '{{.Status}}' 2>$null + $selectedStatus = ((Get-NonEmptyLines $statusOutput) | Select-Object -First 1) + + # If container no longer exists (e.g. after delete), return + if ([string]::IsNullOrWhiteSpace($selectedStatus)) { + break + } + + $isRunning = $selectedStatus -like 'Up*' + $instanceHeader = "Selected: $ContainerName ($selectedImage, $selectedStatus)" + + if ($isRunning) { + $actionOptions = @('Open in browser', 'Restart', 'Stop', 'Delete', 'Back') + $actionIndex = select_from_menu -Header $instanceHeader -Options $actionOptions + switch ($actionIndex) { + -1 { $actionKey = 'back' } # Escape/Backspace + 0 { $actionKey = 'open' } + 1 { $actionKey = 'restart' } + 2 { $actionKey = 'stop' } + 3 { $actionKey = 'delete' } + 4 { $actionKey = 'back' } + default { $actionKey = 'invalid' } + } + } + else { + $actionOptions = @('Start', 'Delete', 'Back') + $actionIndex = select_from_menu -Header $instanceHeader -Options $actionOptions + switch ($actionIndex) { + -1 { $actionKey = 'back' } # Escape/Backspace + 0 { $actionKey = 'start' } + 1 { $actionKey = 'delete' } + 2 { $actionKey = 'back' } + default { $actionKey = 'invalid' } + } + } + + switch ($actionKey) { + 'open' { + $portOutput = & docker port $ContainerName '80/tcp' 2>$null + $hostPort = '' + + foreach ($line in (Get-NonEmptyLines $portOutput)) { + if ($line -match ':(\d+)\s*$') { + $hostPort = $Matches[1] + break + } + } + + if ([string]::IsNullOrWhiteSpace($hostPort)) { + print_warn "Could not resolve a host port for '$ContainerName' on 80/tcp. Ensure it is running with a published port." + } + else { + $targetUrl = "http://localhost:$hostPort" + print_info "Opening $targetUrl" + open_browser -Url $targetUrl + } + Wait-ForKeypress + } + 'start' { + print_info "Starting '$ContainerName'..." + $startOutput = & docker start $ContainerName 2>&1 + # Verify actual container state + $runCheck = & docker ps --filter "name=^/$ContainerName$" --filter 'status=running' --format '{{.Names}}' 2>$null + if ((Get-NonEmptyLines $runCheck) -contains $ContainerName) { + print_ok "Started '$ContainerName'." + } + else { + print_error "Failed to start '$ContainerName'." + if ($startOutput) { + Write-Host " $startOutput" + } + } + Wait-ForKeypress + } + 'stop' { + print_info "Stopping '$ContainerName'..." + & docker stop $ContainerName *> $null + if ($LASTEXITCODE -eq 0) { + print_ok "Stopped '$ContainerName'." + } + else { + print_error "Failed to stop '$ContainerName'." + } + Wait-ForKeypress + } + 'restart' { + print_info "Restarting '$ContainerName'..." + $restartOutput = & docker restart $ContainerName 2>&1 + # Verify actual container state + $runCheck = & docker ps --filter "name=^/$ContainerName$" --filter 'status=running' --format '{{.Names}}' 2>$null + if ((Get-NonEmptyLines $runCheck) -contains $ContainerName) { + print_ok "Restarted '$ContainerName'." + } + else { + print_error "Failed to restart '$ContainerName'." + if ($restartOutput) { + Write-Host " $restartOutput" + } + } + Wait-ForKeypress + } + 'delete' { + Write-Host "Are you sure you want to delete '$ContainerName'? [y/N]: " -NoNewline + $confirmKey = [Console]::ReadKey($true) + Write-Host '' + if ($confirmKey.KeyChar -eq 'y' -or $confirmKey.KeyChar -eq 'Y') { + # Stop first if running + & docker stop $ContainerName *> $null + & docker rm $ContainerName *> $null + if ($LASTEXITCODE -eq 0) { + print_ok "Deleted '$ContainerName'." + } + else { + print_error "Failed to delete '$ContainerName'." + } + Wait-ForKeypress + break # Container no longer exists + } + else { + print_info 'Delete cancelled.' + Wait-ForKeypress + } + } + 'back' { break } - - $isRunning = $selectedStatus -like 'Up*' - $instanceHeader = "Selected: $selectedName ($selectedImage, $selectedStatus)" - - if ($isRunning) { - $actionOptions = @('Open in browser', 'Restart', 'Stop', 'Delete', 'Back') - $actionIndex = select_from_menu -Header $instanceHeader -Options $actionOptions - switch ($actionIndex) { - -1 { $actionKey = 'back' } # Escape/Backspace - 0 { $actionKey = 'open' } - 1 { $actionKey = 'restart' } - 2 { $actionKey = 'stop' } - 3 { $actionKey = 'delete' } - 4 { $actionKey = 'back' } - default { $actionKey = 'invalid' } - } - } - else { - $actionOptions = @('Start', 'Delete', 'Back') - $actionIndex = select_from_menu -Header $instanceHeader -Options $actionOptions - switch ($actionIndex) { - -1 { $actionKey = 'back' } # Escape/Backspace - 0 { $actionKey = 'start' } - 1 { $actionKey = 'delete' } - 2 { $actionKey = 'back' } - default { $actionKey = 'invalid' } - } - } - - switch ($actionKey) { - 'open' { - $portOutput = & docker port $selectedName '80/tcp' 2>$null - $hostPort = '' - - foreach ($line in (Get-NonEmptyLines $portOutput)) { - if ($line -match ':(\d+)\s*$') { - $hostPort = $Matches[1] - break - } - } - - if ([string]::IsNullOrWhiteSpace($hostPort)) { - print_warn "Could not resolve a host port for '$selectedName' on 80/tcp. Ensure it is running with a published port." - } - else { - $targetUrl = "http://localhost:$hostPort" - print_info "Opening $targetUrl" - open_browser -Url $targetUrl - } - Wait-ForKeypress - } - 'start' { - print_info "Starting '$selectedName'..." - $startOutput = & docker start $selectedName 2>&1 - # Verify actual container state - $runCheck = & docker ps --filter "name=^/$selectedName$" --filter 'status=running' --format '{{.Names}}' 2>$null - if ((Get-NonEmptyLines $runCheck) -contains $selectedName) { - print_ok "Started '$selectedName'." - } - else { - print_error "Failed to start '$selectedName'." - if ($startOutput) { - Write-Host " $startOutput" - } - } - Wait-ForKeypress - } - 'stop' { - print_info "Stopping '$selectedName'..." - & docker stop $selectedName *> $null - if ($LASTEXITCODE -eq 0) { - print_ok "Stopped '$selectedName'." - } - else { - print_error "Failed to stop '$selectedName'." - } - Wait-ForKeypress - } - 'restart' { - print_info "Restarting '$selectedName'..." - $restartOutput = & docker restart $selectedName 2>&1 - # Verify actual container state - $runCheck = & docker ps --filter "name=^/$selectedName$" --filter 'status=running' --format '{{.Names}}' 2>$null - if ((Get-NonEmptyLines $runCheck) -contains $selectedName) { - print_ok "Restarted '$selectedName'." - } - else { - print_error "Failed to restart '$selectedName'." - if ($restartOutput) { - Write-Host " $restartOutput" - } - } - Wait-ForKeypress - } - 'delete' { - Write-Host "Are you sure you want to delete '$selectedName'? [y/N]: " -NoNewline - $confirmKey = [Console]::ReadKey($true) - Write-Host '' - if ($confirmKey.KeyChar -eq 'y' -or $confirmKey.KeyChar -eq 'Y') { - # Stop first if running - & docker stop $selectedName *> $null - & docker rm $selectedName *> $null - if ($LASTEXITCODE -eq 0) { - print_ok "Deleted '$selectedName'." - } - else { - print_error "Failed to delete '$selectedName'." - } - Wait-ForKeypress - break # Back to instance list (container no longer exists) - } - else { - print_info 'Delete cancelled.' - Wait-ForKeypress - } - } - 'back' { - break # Break inner loop, go back to instance selection - } - default { - print_warn 'Invalid action. Please try again.' - Wait-ForKeypress - } + default { + print_warn 'Invalid action. Please try again.' + Wait-ForKeypress } } } @@ -894,11 +910,12 @@ function main_menu_for_existing { switch ($selectedIndex) { -1 { exit 0 } # Escape/Backspace - exit 0 { + $script:CreatedContainerName = '' $result = create_instance if ($result) { - return # Install completed successfully + manage_single_instance -ContainerName $script:CreatedContainerName } - # Escape pressed during create - loop back to menu + # Escape pressed during create or back from detail - loop back to menu } 1 { manage_instances } # loops back to this menu after returning 2 { exit 0 } # Exit option @@ -907,11 +924,12 @@ function main_menu_for_existing { } else { # All containers were deleted - go straight to install + $script:CreatedContainerName = '' $result = create_instance if (-not $result) { exit 0 } - return + manage_single_instance -ContainerName $script:CreatedContainerName } } } @@ -931,10 +949,12 @@ function main { else { # No existing containers - go straight to install. # If Escape pressed during create, exit gracefully. + $script:CreatedContainerName = '' $result = create_instance if (-not $result) { exit 0 } + manage_single_instance -ContainerName $script:CreatedContainerName } } diff --git a/install.sh b/install.sh index 7a8f5a52c..f96f9df01 100755 --- a/install.sh +++ b/install.sh @@ -348,6 +348,28 @@ check_docker() { fi } +wait_for_ready() { + URL="$1" + MAX_WAIT=60 + WAITED=0 + + printf "${GREEN}[INFO]${NC} Launching Agent Zero..." + while [ "$WAITED" -lt "$MAX_WAIT" ]; do + HTTP_CODE="$(curl -s -o /dev/null -w '%{http_code}' "$URL" 2>/dev/null || true)" + if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 400 ]; then + printf "\n" + print_ok "Agent Zero is ready at $URL" + return 0 + fi + sleep 1 + WAITED=$((WAITED + 1)) + printf "." + done + printf "\n" + print_warn "Agent Zero did not respond within ${MAX_WAIT} seconds. It may still be starting up." + return 1 +} + count_existing_agent_zero_containers() { docker ps -a --format '{{.Names}}|{{.Image}}' 2>/dev/null | awk -F'|' '$2 ~ /^agent0ai\/agent-zero(:|$)/ {count++} END {print count+0}' } @@ -627,33 +649,12 @@ create_instance() { docker compose -f "$COMPOSE_FILE" up -d # ----------------------------------------------------------- - # 5. Done! + # 5. Wait for the service to become ready # ----------------------------------------------------------- - echo "" - echo "==========================================" - printf " ${GREEN}${BOLD}Installation Complete!${NC}\n" - echo "==========================================" - echo "" - echo " 🏷️ Image tag : $SELECTED_TAG" - echo " 📦 Instance name : $CONTAINER_NAME" - echo " 🧩 Compose file : $COMPOSE_FILE" - echo " 📁 Data directory : $DATA_DIR" - echo " 🌐 Web UI : http://localhost:$PORT" - if [ -n "$AUTH_LOGIN" ]; then - echo " 🔐 Authentication : enabled" - echo " 👤 Login : $AUTH_LOGIN" - echo " 🔑 Password : $AUTH_PASSWORD" - else - echo " 🔓 Authentication : none" - fi - echo "" - echo " Useful commands:" - echo " docker compose -f $COMPOSE_FILE logs -f # View logs" - echo " docker compose -f $COMPOSE_FILE down # Stop" - echo " docker compose -f $COMPOSE_FILE up -d # Start" - echo " docker compose -f $COMPOSE_FILE pull # Update image" - echo "" - print_info "Happy automating with Agent Zero! 🤖" + wait_for_ready "http://localhost:$PORT" + + # Store the created container name for the caller + CREATED_CONTAINER_NAME="$CONTAINER_NAME" } manage_instances() { @@ -744,120 +745,131 @@ manage_instances() { SELECTED_IMAGE="$(printf "%s\n" "$SELECTED_ROW" | cut -d'|' -f2)" SELECTED_STATUS="$(printf "%s\n" "$SELECTED_ROW" | cut -d'|' -f3-)" - while :; do - SELECTED_STATUS="$(docker ps -a --filter "name=^/${SELECTED_NAME}$" --format '{{.Status}}' 2>/dev/null | head -n 1)" + manage_single_instance "$SELECTED_NAME" + done +} - # If container no longer exists (e.g. after delete), go back to instance list - if [ -z "$SELECTED_STATUS" ]; then +# Show the action menu for a single container (open, start, stop, restart, delete). +# Can be called from manage_instances or directly after create_instance. +manage_single_instance() { + SELECTED_NAME="$1" + + # Look up the image for display + SELECTED_IMAGE="$(docker ps -a --filter "name=^/${SELECTED_NAME}$" --format '{{.Image}}' 2>/dev/null | head -n 1)" + + while :; do + SELECTED_STATUS="$(docker ps -a --filter "name=^/${SELECTED_NAME}$" --format '{{.Status}}' 2>/dev/null | head -n 1)" + + # If container no longer exists (e.g. after delete), return + if [ -z "$SELECTED_STATUS" ]; then + break + fi + + case "$SELECTED_STATUS" in + Up*) IS_RUNNING=1 ;; + *) IS_RUNNING=0 ;; + esac + + INSTANCE_HEADER="Selected: $SELECTED_NAME ($SELECTED_IMAGE, $SELECTED_STATUS)" + + if [ "$IS_RUNNING" -eq 1 ]; then + ACTION_INDEX=$(select_from_menu "--header=$INSTANCE_HEADER" "Open in browser" "Restart" "Stop" "Delete" "Back") || true + case "$ACTION_INDEX" in + -1) ACTION_KEY="back" ;; # Escape/Backspace — go back + 0) ACTION_KEY="open" ;; + 1) ACTION_KEY="restart" ;; + 2) ACTION_KEY="stop" ;; + 3) ACTION_KEY="delete" ;; + 4) ACTION_KEY="back" ;; + *) ACTION_KEY="invalid" ;; + esac + else + ACTION_INDEX=$(select_from_menu "--header=$INSTANCE_HEADER" "Start" "Delete" "Back") || true + case "$ACTION_INDEX" in + -1) ACTION_KEY="back" ;; # Escape/Backspace — go back + 0) ACTION_KEY="start" ;; + 1) ACTION_KEY="delete" ;; + 2) ACTION_KEY="back" ;; + *) ACTION_KEY="invalid" ;; + esac + fi + + case "$ACTION_KEY" in + open) + PORT_OUTPUT="$(docker port "$SELECTED_NAME" 80/tcp 2>/dev/null || true)" + HOST_PORT="$(printf "%s\n" "$PORT_OUTPUT" | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p' | head -n 1)" + + if [ -z "$HOST_PORT" ]; then + print_warn "Could not resolve a host port for '$SELECTED_NAME' on 80/tcp. Ensure it is running with a published port." + else + TARGET_URL="http://localhost:$HOST_PORT" + print_info "Opening $TARGET_URL" + open_browser "$TARGET_URL" + fi + wait_for_keypress + ;; + start) + print_info "Starting '$SELECTED_NAME'..." + START_OUTPUT="$(docker start "$SELECTED_NAME" 2>&1)" || true + if docker ps --filter "name=^/${SELECTED_NAME}$" --filter "status=running" --format '{{.Names}}' 2>/dev/null | grep -q "^${SELECTED_NAME}$"; then + print_ok "Started '$SELECTED_NAME'." + else + print_error "Failed to start '$SELECTED_NAME'." + if [ -n "$START_OUTPUT" ]; then + printf " %s\n" "$START_OUTPUT" + fi + fi + wait_for_keypress + ;; + stop) + print_info "Stopping '$SELECTED_NAME'..." + if docker stop "$SELECTED_NAME" >/dev/null 2>&1; then + print_ok "Stopped '$SELECTED_NAME'." + else + print_error "Failed to stop '$SELECTED_NAME'." + fi + wait_for_keypress + ;; + restart) + print_info "Restarting '$SELECTED_NAME'..." + RESTART_OUTPUT="$(docker restart "$SELECTED_NAME" 2>&1)" || true + if docker ps --filter "name=^/${SELECTED_NAME}$" --filter "status=running" --format '{{.Names}}' 2>/dev/null | grep -q "^${SELECTED_NAME}$"; then + print_ok "Restarted '$SELECTED_NAME'." + else + print_error "Failed to restart '$SELECTED_NAME'." + if [ -n "$RESTART_OUTPUT" ]; then + printf " %s\n" "$RESTART_OUTPUT" + fi + fi + wait_for_keypress + ;; + delete) + printf "Are you sure you want to delete '%s'? [y/N]: " "$SELECTED_NAME" + IFS= read -rsn1 CONFIRM /dev/null 2>&1 || true + if docker rm "$SELECTED_NAME" >/dev/null 2>&1; then + print_ok "Deleted '$SELECTED_NAME'." + else + print_error "Failed to delete '$SELECTED_NAME'." + fi + wait_for_keypress + break # Container no longer exists + else + print_info "Delete cancelled." + wait_for_keypress + fi + ;; + back) break - fi - - case "$SELECTED_STATUS" in - Up*) IS_RUNNING=1 ;; - *) IS_RUNNING=0 ;; - esac - - INSTANCE_HEADER="Selected: $SELECTED_NAME ($SELECTED_IMAGE, $SELECTED_STATUS)" - - if [ "$IS_RUNNING" -eq 1 ]; then - ACTION_INDEX=$(select_from_menu "--header=$INSTANCE_HEADER" "Open in browser" "Restart" "Stop" "Delete" "Back") || true - case "$ACTION_INDEX" in - -1) ACTION_KEY="back" ;; # Escape/Backspace — go back to instance list - 0) ACTION_KEY="open" ;; - 1) ACTION_KEY="restart" ;; - 2) ACTION_KEY="stop" ;; - 3) ACTION_KEY="delete" ;; - 4) ACTION_KEY="back" ;; - *) ACTION_KEY="invalid" ;; - esac - else - ACTION_INDEX=$(select_from_menu "--header=$INSTANCE_HEADER" "Start" "Delete" "Back") || true - case "$ACTION_INDEX" in - -1) ACTION_KEY="back" ;; # Escape/Backspace — go back to instance list - 0) ACTION_KEY="start" ;; - 1) ACTION_KEY="delete" ;; - 2) ACTION_KEY="back" ;; - *) ACTION_KEY="invalid" ;; - esac - fi - - case "$ACTION_KEY" in - open) - PORT_OUTPUT="$(docker port "$SELECTED_NAME" 80/tcp 2>/dev/null || true)" - HOST_PORT="$(printf "%s\n" "$PORT_OUTPUT" | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p' | head -n 1)" - - if [ -z "$HOST_PORT" ]; then - print_warn "Could not resolve a host port for '$SELECTED_NAME' on 80/tcp. Ensure it is running with a published port." - else - TARGET_URL="http://localhost:$HOST_PORT" - print_info "Opening $TARGET_URL" - open_browser "$TARGET_URL" - fi - wait_for_keypress - ;; - start) - print_info "Starting '$SELECTED_NAME'..." - START_OUTPUT="$(docker start "$SELECTED_NAME" 2>&1)" || true - if docker ps --filter "name=^/${SELECTED_NAME}$" --filter "status=running" --format '{{.Names}}' 2>/dev/null | grep -q "^${SELECTED_NAME}$"; then - print_ok "Started '$SELECTED_NAME'." - else - print_error "Failed to start '$SELECTED_NAME'." - if [ -n "$START_OUTPUT" ]; then - printf " %s\n" "$START_OUTPUT" - fi - fi - wait_for_keypress - ;; - stop) - print_info "Stopping '$SELECTED_NAME'..." - if docker stop "$SELECTED_NAME" >/dev/null 2>&1; then - print_ok "Stopped '$SELECTED_NAME'." - else - print_error "Failed to stop '$SELECTED_NAME'." - fi - wait_for_keypress - ;; - restart) - print_info "Restarting '$SELECTED_NAME'..." - RESTART_OUTPUT="$(docker restart "$SELECTED_NAME" 2>&1)" || true - if docker ps --filter "name=^/${SELECTED_NAME}$" --filter "status=running" --format '{{.Names}}' 2>/dev/null | grep -q "^${SELECTED_NAME}$"; then - print_ok "Restarted '$SELECTED_NAME'." - else - print_error "Failed to restart '$SELECTED_NAME'." - if [ -n "$RESTART_OUTPUT" ]; then - printf " %s\n" "$RESTART_OUTPUT" - fi - fi - wait_for_keypress - ;; - delete) - printf "Are you sure you want to delete '%s'? [y/N]: " "$SELECTED_NAME" - IFS= read -rsn1 CONFIRM /dev/null 2>&1 || true - if docker rm "$SELECTED_NAME" >/dev/null 2>&1; then - print_ok "Deleted '$SELECTED_NAME'." - else - print_error "Failed to delete '$SELECTED_NAME'." - fi - wait_for_keypress - break # Back to instance list (container no longer exists) - else - print_info "Delete cancelled." - wait_for_keypress - fi - ;; - back) - break # Break inner loop, go back to instance selection - ;; - *) - print_warn "Invalid action. Please try again." - wait_for_keypress - ;; - esac - done + ;; + *) + print_warn "Invalid action. Please try again." + wait_for_keypress + ;; + esac done } @@ -876,10 +888,11 @@ main_menu_for_existing() { case "$SELECTED_INDEX" in -1) exit 0 ;; # Escape/Backspace — exit 0) + CREATED_CONTAINER_NAME="" if create_instance; then - return 0 # Install completed successfully + manage_single_instance "$CREATED_CONTAINER_NAME" fi - # Escape pressed during create — loop back to menu + # Escape pressed during create or back from detail — loop back to menu ;; 1) manage_instances ;; # loops back to this menu after returning 2) exit 0 ;; # Exit option @@ -887,10 +900,11 @@ main_menu_for_existing() { esac else # All containers were deleted — go straight to install + CREATED_CONTAINER_NAME="" if ! create_instance; then exit 0 fi - return 0 + manage_single_instance "$CREATED_CONTAINER_NAME" fi done } @@ -909,9 +923,11 @@ main() { else # No existing containers — go straight to install. # If Escape pressed during create, exit gracefully. + CREATED_CONTAINER_NAME="" if ! create_instance; then exit 0 fi + manage_single_instance "$CREATED_CONTAINER_NAME" fi }