Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use real pty #197

Merged
merged 38 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0b36a69
feat: use pty
caarlos0 Dec 4, 2023
aa0bb66
fix: deps
caarlos0 Dec 4, 2023
fc89255
fix: deps
caarlos0 Dec 4, 2023
0868b14
fix: update example
caarlos0 Dec 4, 2023
46447ee
fix: deps
caarlos0 Dec 4, 2023
9ba7e36
docs: fix example
caarlos0 Dec 5, 2023
ab27fe2
feat: pass lipgloss.Renderer down to the tea.App
caarlos0 Dec 8, 2023
020651e
Merge remote-tracking branch 'origin/main' into using-pty
caarlos0 Dec 8, 2023
c6e19fc
Merge remote-tracking branch 'origin/main' into using-pty
caarlos0 Dec 12, 2023
a70d0bd
fix: go mod tidy
caarlos0 Dec 12, 2023
fd4337e
improvements
caarlos0 Jan 4, 2024
488fd2f
Merge remote-tracking branch 'origin/main' into using-pty
caarlos0 Jan 4, 2024
c9ad8ee
Merge remote-tracking branch 'origin/main' into using-pty
caarlos0 Jan 5, 2024
83cc777
fix: pty
caarlos0 Jan 5, 2024
60e8254
fix: better diff
caarlos0 Jan 5, 2024
2cf714d
chore: typo
caarlos0 Jan 5, 2024
59c5884
fix: godocs
caarlos0 Jan 5, 2024
9ab015a
fix: allocate pty on macos
caarlos0 Jan 8, 2024
50a706e
fix: improvements
caarlos0 Jan 8, 2024
411f301
chore: godoc
caarlos0 Jan 8, 2024
9d68adf
Merge remote-tracking branch 'origin/main' into using-pty
caarlos0 Jan 9, 2024
47074ac
fix: review
caarlos0 Jan 9, 2024
5e7f41a
fix: example
caarlos0 Jan 9, 2024
4c7fb4f
fix: tea program handler
caarlos0 Jan 9, 2024
0431178
fix: examples
caarlos0 Jan 9, 2024
aed4f59
Merge remote-tracking branch 'origin/main' into using-pty
caarlos0 Jan 9, 2024
c40b251
Merge remote-tracking branch 'origin/main' into using-pty
caarlos0 Jan 9, 2024
d7ee1ec
refactor: improve p!=nil handling
caarlos0 Jan 9, 2024
c0d4269
fix: ensure session envs are available to renderer (#223)
aymanbagabas Jan 9, 2024
73189b2
fix: rename func to makeoptions
caarlos0 Jan 10, 2024
569d089
fix: not too much breaking
caarlos0 Jan 10, 2024
b0c56ba
chore: fix gitignore
caarlos0 Jan 10, 2024
c9668e0
Merge remote-tracking branch 'origin/main' into using-pty
caarlos0 Jan 17, 2024
91c322b
fix: dep
caarlos0 Jan 17, 2024
cd60d40
fix: update charmbracelet/ssh
aymanbagabas Jan 18, 2024
12a7a6a
fix: update dep
caarlos0 Jan 18, 2024
731c66e
chore: go mod tidy
caarlos0 Jan 18, 2024
20ca97a
chore: update example
caarlos0 Jan 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ coverage.txt

# MacOS specific
.DS_Store
id_ed25519
id_ed25519.pub
72 changes: 27 additions & 45 deletions bubbletea/tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"github.com/charmbracelet/log"
"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
"github.com/muesli/termenv"
)

// BubbleTeaHandler is the function Bubble Tea apps implement to hook into the
Expand All @@ -24,56 +23,34 @@
// start it with the tea.ProgramOptions returned.
type Handler func(ssh.Session) (tea.Model, []tea.ProgramOption)

// ProgramHandler is the function Bubble Tea apps implement to hook into the SSH
// Middleware. This should return a new tea.Program. This handler is different
// from the default handler in that it returns a tea.Program instead of
// (tea.Model, tea.ProgramOptions).
//
// Make sure to set the tea.WithInput and tea.WithOutput to the ssh.Session
// otherwise the program will not function properly.
type ProgramHandler func(ssh.Session) *tea.Program

// Middleware takes a Handler and hooks the input and output for the
// ssh.Session into the tea.Program. It also captures window resize events and
// sends them to the tea.Program as tea.WindowSizeMsgs. By default a 256 color
// profile will be used when rendering with Lip Gloss.
func Middleware(bth Handler) wish.Middleware {
return MiddlewareWithColorProfile(bth, termenv.ANSI256)
}

// MiddlewareWithColorProfile allows you to specify the number of colors
// returned by the server when using Lip Gloss. The number of colors supported
// by an SSH client's terminal cannot be detected by the server but this will
// allow for manually setting the color profile on all SSH connections.
func MiddlewareWithColorProfile(bth Handler, cp termenv.Profile) wish.Middleware {
h := func(s ssh.Session) *tea.Program {
m, opts := bth(s)
if m == nil {
return nil
}
opts = append(opts, tea.WithInput(s), tea.WithOutput(s))
return tea.NewProgram(m, opts...)
}
return MiddlewareWithProgramHandler(h, cp)
// NewRenderer returns a lipgloss renderer for the current session.
// This function handle PTYs as well, and should be used to style your application.
func NewRenderer(s ssh.Session) *lipgloss.Renderer {
aymanbagabas marked this conversation as resolved.
Show resolved Hide resolved
return newRenderer(s)
}

// MiddlewareWithProgramHandler allows you to specify the ProgramHandler to be
// able to access the underlying tea.Program. This is useful for creating custom
// middlewars that need access to tea.Program for instance to use p.Send() to
// send messages to tea.Program.
// Middleware takes a Handler and hooks the input and output for the
// ssh.Session into the tea.Program.
//
// Make sure to set the tea.WithInput and tea.WithOutput to the ssh.Session
// otherwise the program will not function properly.
func MiddlewareWithProgramHandler(bth ProgramHandler, cp termenv.Profile) wish.Middleware {
// XXX: This is a hack to make sure the default Termenv output color
// profile is set before the program starts. Ideally, we want a Lip Gloss
// renderer per session.
lipgloss.SetColorProfile(cp)
// It also captures window resize events and sends them to the tea.Program
// as tea.WindowSizeMsgs.
func Middleware(bth Handler) wish.Middleware {
return func(sh ssh.Handler) ssh.Handler {
return func(s ssh.Session) {
p := bth(s)
tty, windowChanges, ok := s.Pty()
if !ok {
wish.Fatalln(s, "no active terminal, skipping")
return
}

m, opts := bth(s)
if m == nil {
log.Error("no model returned by the handler")
return
}

p := tea.NewProgram(m, append(opts, makeIOOpts(s)...)...)
if p != nil {
_, windowChanges, _ := s.Pty()
ctx, cancel := context.WithCancel(s.Context())
go func() {
for {
Expand All @@ -98,6 +75,11 @@
// tui crash
p.Kill()
cancel()
if err := tty.Close(); err != nil {
aymanbagabas marked this conversation as resolved.
Show resolved Hide resolved
log.Error("could not close pty", "error", err)
return
}

Check failure on line 82 in bubbletea/tea.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary trailing newline (whitespace)
}
sh(s)
}
Expand Down
22 changes: 22 additions & 0 deletions bubbletea/tea_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris
// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris

package bubbletea

import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/ssh"
"github.com/muesli/termenv"
)

func makeIOOpts(s ssh.Session) []tea.ProgramOption {
return []tea.ProgramOption{
tea.WithInput(s),
tea.WithOutput(s),
}
}

func newRenderer(s ssh.Session) *lipgloss.Renderer {
return lipgloss.NewRenderer(s, termenv.WithColorCache(true))
aymanbagabas marked this conversation as resolved.
Show resolved Hide resolved
}
31 changes: 31 additions & 0 deletions bubbletea/tea_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris

package bubbletea

import (
tea "github.com/charmbracelet/bubbletea"

Check failure on line 7 in bubbletea/tea_unix.go

View workflow job for this annotation

GitHub Actions / lint

import 'github.com/charmbracelet/bubbletea' is not allowed from list 'Main' (depguard)
"github.com/charmbracelet/lipgloss"

Check failure on line 8 in bubbletea/tea_unix.go

View workflow job for this annotation

GitHub Actions / lint

import 'github.com/charmbracelet/lipgloss' is not allowed from list 'Main' (depguard)
"github.com/charmbracelet/ssh"

Check failure on line 9 in bubbletea/tea_unix.go

View workflow job for this annotation

GitHub Actions / lint

import 'github.com/charmbracelet/ssh' is not allowed from list 'Main' (depguard)
"github.com/muesli/termenv"

Check failure on line 10 in bubbletea/tea_unix.go

View workflow job for this annotation

GitHub Actions / lint

import 'github.com/muesli/termenv' is not allowed from list 'Main' (depguard)
)

func makeIOOpts(s ssh.Session) []tea.ProgramOption {
pty, _, ok := s.Pty()
if !ok || s.EmulatedPty() {
return []tea.ProgramOption{
tea.WithInput(s),
tea.WithOutput(s),
}
}

return []tea.ProgramOption{
tea.WithInput(pty.Slave),
tea.WithOutput(pty.Slave),
}
}

func newRenderer(s ssh.Session) *lipgloss.Renderer {
pty, _, _ := s.Pty()
return lipgloss.NewRenderer(pty.Slave, termenv.WithColorCache(true))
aymanbagabas marked this conversation as resolved.
Show resolved Hide resolved
}
2 changes: 2 additions & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id_ed25519*
file.txt
6 changes: 4 additions & 2 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/charmbracelet/log v0.3.1
github.com/charmbracelet/ssh v0.0.0-20240104172912-e11ae277b249
github.com/charmbracelet/ssh v0.0.0-20240108200552-7ffdd484aa1b
github.com/charmbracelet/wish v0.5.0
github.com/muesli/termenv v0.15.2
github.com/spf13/cobra v1.7.0
Expand All @@ -23,6 +23,7 @@ require (
github.com/charmbracelet/keygen v0.5.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/creack/pty v1.1.21 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
Expand All @@ -45,13 +46,14 @@ require (
github.com/sergi/go-diff v1.1.0 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/u-root/u-root v0.11.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.14.0 // indirect
Expand Down
13 changes: 9 additions & 4 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw=
github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
github.com/charmbracelet/ssh v0.0.0-20240104172912-e11ae277b249 h1:M1Q/UIbi9Cfla0HK3A9puhQhb8ZPkA5HQxeSYcVrGpo=
github.com/charmbracelet/ssh v0.0.0-20240104172912-e11ae277b249/go.mod h1:A1H384KV/cJcSKofWjdSIb+dfbikXiW6449EluL3qJI=
github.com/charmbracelet/ssh v0.0.0-20240108200552-7ffdd484aa1b h1:VPRNMDlAOQeptDNA7u3ci897ApTY7ay/aaImni+4f8U=
github.com/charmbracelet/ssh v0.0.0-20240108200552-7ffdd484aa1b/go.mod h1:H/cBXf4vsbGUlCLiR8cNGP7lpv8wuHbgD3GnWJ8quHA=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -106,6 +108,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/u-root/gobusybox/src v0.0.0-20221229083637-46b2883a7f90 h1:zTk5683I9K62wtZ6eUa6vu6IWwVHXPnoKK5n2unAwv0=
github.com/u-root/u-root v0.11.0 h1:6gCZLOeRyevw7gbTwMj3fKxnr9+yHFlgF3N7udUVNO8=
github.com/u-root/u-root v0.11.0/go.mod h1:DBkDtiZyONk9hzVEdB/PWI9B4TxDkElWlVTHseglrZY=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
Expand Down Expand Up @@ -150,8 +155,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
Expand Down
111 changes: 111 additions & 0 deletions examples/wish-exec/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package main

import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/log"
"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
bm "github.com/charmbracelet/wish/bubbletea"
lm "github.com/charmbracelet/wish/logging"
)

const (
host = "localhost"
port = 23235
)

func main() {
s, err := wish.NewServer(
wish.WithAddress(fmt.Sprintf("%s:%d", host, port)),
ssh.AllocatePty(),
wish.WithMiddleware(
bm.Middleware(teaHandler),
lm.Middleware(),
),
)
if err != nil {
log.Error("could not start server", "error", err)
}

done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
log.Info("Starting SSH server", "host", host, "port", port)
go func() {
if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
log.Error("could not start server", "error", err)
done <- nil
}
}()

<-done
log.Info("Stopping SSH server")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer func() { cancel() }()
if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
log.Error("could not stop server", "error", err)
}
}

func teaHandler(s ssh.Session) (tea.Model, []tea.ProgramOption) {
renderer := bm.NewRenderer(s)
m := model{
sess: s,
style: renderer.NewStyle().Foreground(lipgloss.Color("8")),
errStyle: renderer.NewStyle().Foreground(lipgloss.Color("3")),
}
return m, []tea.ProgramOption{tea.WithAltScreen()}
}

type model struct {
err error
sess ssh.Session
style lipgloss.Style
errStyle lipgloss.Style
}

func (m model) Init() tea.Cmd {
return nil
}

type VimFinishedMsg struct{ err error }

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "e":
c := exec.Command("vim", "file.txt")
cmd := tea.ExecProcess(c, func(err error) tea.Msg {
if err != nil {
log.Error("vim finished", "error", err)
}
return VimFinishedMsg{err: err}
})
return m, cmd
case "q", "ctrl+c":
return m, tea.Quit
}
case VimFinishedMsg:
m.err = msg.err
return m, nil
}

return m, nil
}

func (m model) View() string {
if m.err != nil {
return m.errStyle.Render(m.err.Error() + "\n")
}
return m.style.Render("Press 'e' to edit or 'q' to quit...\n")
}
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/charmbracelet/keygen v0.5.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/charmbracelet/log v0.3.1
github.com/charmbracelet/ssh v0.0.0-20240104172912-e11ae277b249
github.com/charmbracelet/ssh v0.0.0-20240108200552-7ffdd484aa1b
github.com/go-git/go-git/v5 v5.11.0
github.com/google/go-cmp v0.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7
Expand All @@ -26,6 +26,7 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/creack/pty v1.1.21 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
Expand All @@ -45,11 +46,12 @@ require (
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/u-root/u-root v0.11.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.14.0 // indirect
Expand Down
Loading
Loading