Skip to content

Commit acad2e8

Browse files
committed
allow dropping into an ssh shell straight from the dashboard
1 parent 71b72bd commit acad2e8

File tree

3 files changed

+53
-6
lines changed

3 files changed

+53
-6
lines changed

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,24 @@ The tool will:
6868
- While in the dashboard, press `c` to return to host selection to add/remove hosts
6969
- Press `n` to cycle through connected hosts (like tmux sessions)
7070
- Press `t` to toggle overview mode, showing all selected hosts at once with GPU pressure summaries
71+
- Press `s` to exit the dashboard and drop into an interactive SSH shell with the current host
7172
- All connections remain active - no need to reconnect!
7273

7374
### Configuration
7475

7576
**Update Interval:**
7677

77-
Control how often the dashboard refreshes (default: 5 seconds):
78+
Control how often the dashboard refreshes in seconds (default: 5). Supports decimal values for sub-second updates:
7879

7980
```bash
80-
ssh-dashboard -interval 1
81+
# Update every second
82+
ssh-dashboard -n 1
83+
84+
# Update 10 times per second (100ms)
85+
ssh-dashboard -n 0.1
8186

8287
# or with an env var
83-
export SSH_DASHBOARD_INTERVAL=1
88+
export SSH_DASHBOARD_INTERVAL=0.5
8489
ssh-dashboard
8590
```
8691

@@ -90,6 +95,7 @@ ssh-dashboard
9095
- `Enter` - Connect to selected host(s)
9196
- `n` - Switch to next host (when multiple hosts selected)
9297
- `t` - Toggle overview screen (shows all hosts at once)
98+
- `s` - Exit and SSH into current host
9399
- `c` - Add hosts (from dashboard, returns to host selection)
94100

95101
## SSH Configuration

cmd/ssh_dashboard/main.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"flag"
55
"fmt"
66
"os"
7+
"os/exec"
78
"strconv"
9+
"syscall"
810
"time"
911

1012
"github.com/alpindale/ssh-dashboard/internal"
@@ -58,9 +60,30 @@ func main() {
5860
os.Exit(1)
5961
}
6062

61-
p := tea.NewProgram(internal.InitialModel(hosts, interval), tea.WithAltScreen())
62-
if _, err := p.Run(); err != nil {
63+
initialModel := internal.InitialModel(hosts, interval)
64+
p := tea.NewProgram(initialModel, tea.WithAltScreen())
65+
finalModel, err := p.Run()
66+
if err != nil {
6367
fmt.Fprintf(os.Stderr, "Error running program: %v\n", err)
6468
os.Exit(1)
6569
}
70+
71+
if m, ok := finalModel.(internal.Model); ok {
72+
if sshHost := m.GetSSHOnExit(); sshHost != "" {
73+
sshPath, err := exec.LookPath("ssh")
74+
if err != nil {
75+
fmt.Fprintf(os.Stderr, "Error finding ssh: %v\n", err)
76+
os.Exit(1)
77+
}
78+
79+
args := []string{"ssh", sshHost}
80+
env := os.Environ()
81+
82+
err = syscall.Exec(sshPath, args, env)
83+
if err != nil {
84+
fmt.Fprintf(os.Stderr, "Error executing ssh: %v\n", err)
85+
os.Exit(1)
86+
}
87+
}
88+
}
6689
}

internal/ui.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type Model struct {
3636
err error
3737
width int
3838
height int
39+
sshOnExit string
3940
}
4041

4142
type TickMsg time.Time
@@ -141,6 +142,10 @@ func InitialModel(hosts []SSHHost, updateInterval time.Duration) Model {
141142
}
142143
}
143144

145+
func (m Model) GetSSHOnExit() string {
146+
return m.sshOnExit
147+
}
148+
144149
func (m *Model) updateListSelection() {
145150
items := m.list.Items()
146151
selectedMap := make(map[string]bool)
@@ -232,6 +237,19 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
232237
} else if m.screen == ScreenOverview {
233238
m.screen = ScreenDashboard
234239
}
240+
case "s":
241+
if m.screen == ScreenDashboard {
242+
if len(m.selectedHosts) > 0 {
243+
currentHost := m.selectedHosts[m.currentHostIdx]
244+
m.sshOnExit = currentHost.Name
245+
for _, client := range m.clients {
246+
if client != nil {
247+
client.Close()
248+
}
249+
}
250+
return m, tea.Quit
251+
}
252+
}
235253
}
236254

237255
case tea.WindowSizeMsg:
@@ -625,7 +643,7 @@ func renderDashboard(hostName string, info *SystemInfo, updateInterval time.Dura
625643
if multiHost {
626644
navHint = " | 'n' next | 't' overview"
627645
}
628-
subtitle := fmt.Sprintf("Last Updated: %s | Interval: %s%s | 'c' add hosts | 'q' quit",
646+
subtitle := fmt.Sprintf("Last Updated: %s | Interval: %s%s | 's' shell | 'c' add hosts | 'q' quit",
629647
lastUpdate.Format("15:04:05"), formatInterval(updateInterval), navHint)
630648

631649
b.WriteString(titleStyle.Render(title))

0 commit comments

Comments
 (0)