flake-update-20260201
1#!/usr/bin/env bash
2# shpool-connect - Interactive shpool session selector with fuzzel/fzf
3#
4# Features:
5# - Lists available hosts from SSH config (detects shpool-enabled hosts)
6# - Lists sessions on selected host (shows connected/disconnected status)
7# - Attaches to selected session using kitty (wayland) or current terminal
8# - Handles already-attached sessions (force detach/attach)
9set -euo pipefail
10
11# Error handler - show error and wait for user input before exiting
12error_handler() {
13 local exit_code=$?
14 local line_no=$1
15 if [[ $exit_code -ne 0 ]]; then
16 echo ""
17 echo "Error on line $line_no: Command exited with status $exit_code"
18 echo "Press Enter to close..."
19 read -r
20 fi
21 exit $exit_code
22}
23trap 'error_handler $LINENO' ERR
24
25# Extract shpool-enabled hosts from SSH config
26# These are hosts with the pattern "host/*" (shpool session aliases)
27get_shpool_hosts() {
28 ssh -G "dummy.home/test" 2>/dev/null | head -1 >/dev/null # validate ssh config works
29 # Parse SSH config for "Host X/*" patterns and extract X
30 grep -E '^Host [^*]+/\*$' ~/.ssh/config 2>/dev/null | \
31 sed 's/^Host //' | sed 's|/\*$||' | sort -u
32}
33
34# Detect environment
35DESKTOP="${XDG_CURRENT_DESKTOP:-}"
36GRAPHICS=1
37
38if [[ "$DESKTOP" != "sway" && "$DESKTOP" != "niri" ]]; then
39 if ! command -v xset &>/dev/null; then
40 GRAPHICS=0
41 elif ! timeout 1s xset q &>/dev/null 2>&1; then
42 GRAPHICS=0
43 fi
44fi
45
46# Select function based on environment
47select_item() {
48 local prompt="$1"
49 if [[ GRAPHICS -eq 0 ]]; then
50 fzf --prompt "$prompt"
51 elif [[ "$DESKTOP" == "sway" || "$DESKTOP" == "niri" ]]; then
52 fuzzel --dmenu --prompt "$prompt" --lines 15 --width 50
53 else
54 fzf --prompt "$prompt"
55 fi
56}
57
58notify() {
59 local title="$1"
60 local msg="$2"
61 if [[ GRAPHICS -eq 1 ]]; then
62 notify-send "$title" "$msg"
63 else
64 echo "$title: $msg"
65 fi
66}
67
68# Step 1: Select host
69shpool_hosts=$(get_shpool_hosts)
70
71if [[ -z "$shpool_hosts" ]]; then
72 notify "shpool" "No shpool-enabled hosts found in SSH config"
73 exit 1
74fi
75
76selected_host=$(echo "$shpool_hosts" | select_item "Host: ")
77
78if [[ -z "$selected_host" ]]; then
79 exit 0
80fi
81
82# Step 2: Get sessions on the host with status
83# Format: "session-name (status)" where status is connected/disconnected
84session_list=$(ssh "$selected_host" shpool list 2>/dev/null | tail -n +2 | awk '{
85 status = $3
86 if (status == "connected") {
87 print $1 " (attached)"
88 } else {
89 print $1
90 }
91}')
92
93if [[ -z "$session_list" ]]; then
94 notify "shpool" "No sessions on $selected_host"
95 exit 0
96fi
97
98# Add option to create new session
99sessions="[new session]"$'\n'"$session_list"
100
101selected_session=$(echo "$sessions" | select_item "Session on $selected_host: ")
102
103# Strip status suffix if present (e.g., "session (attached)" -> "session")
104selected_session="${selected_session% (attached)}"
105
106if [[ -z "$selected_session" ]]; then
107 exit 0
108fi
109
110# Handle new session creation
111if [[ "$selected_session" == "[new session]" ]]; then
112 # Prompt for session name
113 if [[ GRAPHICS -eq 0 ]]; then
114 echo -n "Session name: "
115 read -r selected_session
116 elif [[ "$DESKTOP" == "sway" || "$DESKTOP" == "niri" ]]; then
117 selected_session=$(echo "" | fuzzel --dmenu --prompt "New session name: " --width 30)
118 else
119 echo -n "Session name: "
120 read -r selected_session
121 fi
122
123 if [[ -z "$selected_session" ]]; then
124 exit 0
125 fi
126fi
127
128# Step 3: Attach to session
129ssh_target="${selected_host}/${selected_session}"
130
131if [[ GRAPHICS -eq 1 ]] && [[ "$DESKTOP" == "sway" || "$DESKTOP" == "niri" ]]; then
132 # Use kitty for graphical wayland environments
133 if command -v kitty &>/dev/null; then
134 exec kitty --single-instance --instance-group=shpool ssh "$ssh_target"
135 else
136 notify "shpool" "kitty not found, falling back to terminal"
137 exec ssh "$ssh_target"
138 fi
139else
140 # Terminal mode - just SSH directly
141 exec ssh "$ssh_target"
142fi