gssh-agent 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/bin/daemon +0 -0
- package/bin/gssh +0 -0
- package/bin/gssh-daemon +0 -0
- package/cmd/gssh/main.go +235 -12
- package/go.mod +6 -1
- package/go.sum +46 -0
- package/internal/client/ssh.go +252 -2
- package/internal/portforward/forwarder.go +88 -19
- package/internal/protocol/types.go +23 -0
- package/internal/session/manager.go +203 -20
- package/package.json +21 -6
- package/pkg/rpc/handler.go +28 -0
- package/skill.md +65 -65
- package/gssh-darwin-arm64.tar.gz +0 -0
- package/homebrew/gssh.plist +0 -22
- package/homebrew/install.sh +0 -43
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ gssh 是一个供 Agent 使用的 SSH Session 管理工具。通过 Go 语言实
|
|
|
10
10
|
- 断线自动重连
|
|
11
11
|
- 支持密码认证和 SSH 密钥认证
|
|
12
12
|
- 被连接机器零配置(普通 SSH 即可)
|
|
13
|
+
- SFTP 文件传输支持(上传/下载)
|
|
13
14
|
|
|
14
15
|
## 安装
|
|
15
16
|
|
|
@@ -124,6 +125,25 @@ gssh exec -s <session_id> "pwd"
|
|
|
124
125
|
gssh forward -l 8080 -r 80
|
|
125
126
|
```
|
|
126
127
|
|
|
128
|
+
### 文件传输(SFTP/SCP)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# 上传文件(本地 -> 远程)
|
|
132
|
+
gssh scp -put /path/to/local/file.txt /path/to/remote/file.txt
|
|
133
|
+
|
|
134
|
+
# 下载文件(远程 -> 本地)
|
|
135
|
+
gssh scp -get /path/to/remote/file.txt /path/to/local/file.txt
|
|
136
|
+
|
|
137
|
+
# 列出远程目录
|
|
138
|
+
gssh sftp -c ls -p /path/to/remote/dir
|
|
139
|
+
|
|
140
|
+
# 创建远程目录
|
|
141
|
+
gssh sftp -c mkdir -p /path/to/remote/newdir
|
|
142
|
+
|
|
143
|
+
# 删除远程文件
|
|
144
|
+
gssh sftp -c rm -p /path/to/remote/file.txt
|
|
145
|
+
```
|
|
146
|
+
|
|
127
147
|
### Session 管理
|
|
128
148
|
|
|
129
149
|
```bash
|
|
@@ -154,6 +174,10 @@ gssh reconnect -s <session_id>
|
|
|
154
174
|
| `-l local` | 本地端口 |
|
|
155
175
|
| `-r remote` | 远程端口 |
|
|
156
176
|
| `-R` | 远程端口转发 |
|
|
177
|
+
| `-put` | 上传模式(本地 -> 远程) |
|
|
178
|
+
| `-get` | 下载模式(远程 -> 本地) |
|
|
179
|
+
| `-c command` | SFTP 命令(ls/mkdir/rm) |
|
|
180
|
+
| `-p path` | SFTP 路径 |
|
|
157
181
|
|
|
158
182
|
## 开发
|
|
159
183
|
|
package/bin/daemon
CHANGED
|
Binary file
|
package/bin/gssh
CHANGED
|
Binary file
|
package/bin/gssh-daemon
CHANGED
|
Binary file
|
package/cmd/gssh/main.go
CHANGED
|
@@ -7,9 +7,13 @@ import (
|
|
|
7
7
|
"fmt"
|
|
8
8
|
"net"
|
|
9
9
|
"os"
|
|
10
|
+
"os/signal"
|
|
10
11
|
"strings"
|
|
12
|
+
"syscall"
|
|
11
13
|
|
|
12
14
|
"gssh/internal/protocol"
|
|
15
|
+
|
|
16
|
+
"golang.org/x/term"
|
|
13
17
|
)
|
|
14
18
|
|
|
15
19
|
const (
|
|
@@ -50,6 +54,10 @@ func main() {
|
|
|
50
54
|
err = handleForwards()
|
|
51
55
|
case "forward-close":
|
|
52
56
|
err = handleForwardClose()
|
|
57
|
+
case "scp":
|
|
58
|
+
err = handleSCP()
|
|
59
|
+
case "sftp":
|
|
60
|
+
err = handleSFTP()
|
|
53
61
|
case "help", "-h", "--help":
|
|
54
62
|
printUsage()
|
|
55
63
|
default:
|
|
@@ -64,6 +72,50 @@ func main() {
|
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
|
|
75
|
+
// readPassword reads password from terminal without echo
|
|
76
|
+
func readPassword() (string, error) {
|
|
77
|
+
// Check if stdin is a terminal
|
|
78
|
+
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
|
79
|
+
// Try to read from stdin
|
|
80
|
+
reader := bufio.NewReader(os.Stdin)
|
|
81
|
+
fmt.Print("Password: ")
|
|
82
|
+
password, err := reader.ReadString('\n')
|
|
83
|
+
if err != nil {
|
|
84
|
+
return "", err
|
|
85
|
+
}
|
|
86
|
+
return strings.TrimSuffix(password, "\n"), nil
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fmt.Print("Password: ")
|
|
90
|
+
bytePassword, err := term.ReadPassword(int(os.Stdin.Fd()))
|
|
91
|
+
fmt.Println()
|
|
92
|
+
if err != nil {
|
|
93
|
+
return "", err
|
|
94
|
+
}
|
|
95
|
+
return string(bytePassword), nil
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// readPassphrase reads passphrase from terminal without echo
|
|
99
|
+
func readPassphrase() (string, error) {
|
|
100
|
+
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
|
101
|
+
reader := bufio.NewReader(os.Stdin)
|
|
102
|
+
fmt.Print("Key passphrase: ")
|
|
103
|
+
passphrase, err := reader.ReadString('\n')
|
|
104
|
+
if err != nil {
|
|
105
|
+
return "", err
|
|
106
|
+
}
|
|
107
|
+
return strings.TrimSuffix(passphrase, "\n"), nil
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fmt.Print("Key passphrase: ")
|
|
111
|
+
bytePassphrase, err := term.ReadPassword(int(os.Stdin.Fd()))
|
|
112
|
+
fmt.Println()
|
|
113
|
+
if err != nil {
|
|
114
|
+
return "", err
|
|
115
|
+
}
|
|
116
|
+
return string(bytePassphrase), nil
|
|
117
|
+
}
|
|
118
|
+
|
|
67
119
|
func sendRequest(method string, params interface{}) ([]byte, error) {
|
|
68
120
|
conn, err := net.Dial("unix", *socketPath)
|
|
69
121
|
if err != nil {
|
|
@@ -129,6 +181,8 @@ func handleConnect() error {
|
|
|
129
181
|
port := flag.Int("p", 22, "Port")
|
|
130
182
|
password := flag.String("P", "", "Password")
|
|
131
183
|
keyPath := flag.String("i", "", "SSH key path")
|
|
184
|
+
askPassword := flag.Bool("ask-pass", false, "Ask for password interactively")
|
|
185
|
+
askPassphrase := flag.Bool("ask-passphrase", false, "Ask for key passphrase interactively")
|
|
132
186
|
|
|
133
187
|
flag.CommandLine.Parse(flag.Args()[1:])
|
|
134
188
|
|
|
@@ -136,6 +190,28 @@ func handleConnect() error {
|
|
|
136
190
|
return fmt.Errorf("user and host are required")
|
|
137
191
|
}
|
|
138
192
|
|
|
193
|
+
// If no password provided via flag and -ask-pass is set, read interactively
|
|
194
|
+
if *password == "" && *askPassword {
|
|
195
|
+
p, err := readPassword()
|
|
196
|
+
if err != nil {
|
|
197
|
+
return fmt.Errorf("failed to read password: %w", err)
|
|
198
|
+
}
|
|
199
|
+
*password = p
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// If key path provided and -ask-passphrase is set, read interactively
|
|
203
|
+
if *keyPath != "" && *askPassphrase {
|
|
204
|
+
passphrase, err := readPassphrase()
|
|
205
|
+
if err != nil {
|
|
206
|
+
return fmt.Errorf("failed to read passphrase: %w", err)
|
|
207
|
+
}
|
|
208
|
+
// Note: Passphrase support would require modifying the SSH client
|
|
209
|
+
// For now, we'll just warn that it's not supported
|
|
210
|
+
if passphrase != "" {
|
|
211
|
+
fmt.Println("Note: Passphrase for keys is not yet supported, ignoring")
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
139
215
|
params := protocol.ConnectParams{
|
|
140
216
|
User: *user,
|
|
141
217
|
Host: *host,
|
|
@@ -202,6 +278,9 @@ func handleExec() error {
|
|
|
202
278
|
args := flag.Args()[1:]
|
|
203
279
|
|
|
204
280
|
sessionID := flag.String("s", "", "Session ID")
|
|
281
|
+
sudoPassword := flag.String("S", "", "sudo password (will prompt if empty and -s flag used)")
|
|
282
|
+
askSudoPassword := flag.Bool("ask-sudo-pass", false, "Interactively ask for sudo password")
|
|
283
|
+
|
|
205
284
|
flag.CommandLine.Parse(args)
|
|
206
285
|
|
|
207
286
|
if flag.NArg() < 1 {
|
|
@@ -209,6 +288,21 @@ func handleExec() error {
|
|
|
209
288
|
}
|
|
210
289
|
command := strings.Join(flag.Args(), " ")
|
|
211
290
|
|
|
291
|
+
// If sudo password needed but not provided, prompt for it
|
|
292
|
+
if *askSudoPassword && *sudoPassword == "" {
|
|
293
|
+
p, err := readPassword()
|
|
294
|
+
if err != nil {
|
|
295
|
+
return fmt.Errorf("failed to read sudo password: %w", err)
|
|
296
|
+
}
|
|
297
|
+
*sudoPassword = p
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// If command contains sudo and password provided, wrap the command
|
|
301
|
+
if *sudoPassword != "" && strings.Contains(command, "sudo") {
|
|
302
|
+
// Use printf to pipe password to sudo -S
|
|
303
|
+
command = fmt.Sprintf("printf '%%s\\n' '%s' | %s -S", *sudoPassword, command)
|
|
304
|
+
}
|
|
305
|
+
|
|
212
306
|
params := protocol.ExecParams{
|
|
213
307
|
SessionID: *sessionID,
|
|
214
308
|
Command: command,
|
|
@@ -364,31 +458,160 @@ func handleForwardClose() error {
|
|
|
364
458
|
return nil
|
|
365
459
|
}
|
|
366
460
|
|
|
461
|
+
func handleSCP() error {
|
|
462
|
+
sessionID := flag.String("s", "", "Session ID")
|
|
463
|
+
isUpload := flag.Bool("put", false, "Upload mode (local->remote)")
|
|
464
|
+
isDownload := flag.Bool("get", false, "Download mode (remote->local)")
|
|
465
|
+
|
|
466
|
+
flag.CommandLine.Parse(flag.Args()[1:])
|
|
467
|
+
|
|
468
|
+
if flag.NArg() < 2 {
|
|
469
|
+
return fmt.Errorf("source and destination paths required")
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
source := flag.Arg(0)
|
|
473
|
+
dest := flag.Arg(1)
|
|
474
|
+
|
|
475
|
+
if !*isUpload && !*isDownload {
|
|
476
|
+
return fmt.Errorf("must specify -put or -get")
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
params := protocol.SCPParams{
|
|
480
|
+
SessionID: *sessionID,
|
|
481
|
+
Source: source,
|
|
482
|
+
Dest: dest,
|
|
483
|
+
IsUpload: *isUpload,
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
result, err := sendRequest("scp", params)
|
|
487
|
+
if err != nil {
|
|
488
|
+
return err
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
var scpResult protocol.SCPResult
|
|
492
|
+
if err := json.Unmarshal(result, &scpResult); err != nil {
|
|
493
|
+
return fmt.Errorf("failed to parse result: %w", err)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if scpResult.Success {
|
|
497
|
+
fmt.Printf("Success: %s\n", scpResult.Message)
|
|
498
|
+
} else {
|
|
499
|
+
return fmt.Errorf("failed: %s", scpResult.Message)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return nil
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
func handleSFTP() error {
|
|
506
|
+
sessionID := flag.String("s", "", "Session ID")
|
|
507
|
+
command := flag.String("c", "", "SFTP command (ls, mkdir, rm)")
|
|
508
|
+
path := flag.String("p", ".", "Path")
|
|
509
|
+
|
|
510
|
+
flag.CommandLine.Parse(flag.Args()[1:])
|
|
511
|
+
|
|
512
|
+
if *command == "" {
|
|
513
|
+
return fmt.Errorf("SFTP command required (-c ls|mkdir|rm)")
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
switch *command {
|
|
517
|
+
case "ls":
|
|
518
|
+
params := protocol.SFTPParams{
|
|
519
|
+
SessionID: *sessionID,
|
|
520
|
+
Command: "ls",
|
|
521
|
+
Path: *path,
|
|
522
|
+
}
|
|
523
|
+
result, err := sendRequest("sftp_list", params)
|
|
524
|
+
if err != nil {
|
|
525
|
+
return err
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
var files []string
|
|
529
|
+
if err := json.Unmarshal(result, &files); err != nil {
|
|
530
|
+
return fmt.Errorf("failed to parse result: %w", err)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
for _, f := range files {
|
|
534
|
+
fmt.Println(f)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
case "mkdir":
|
|
538
|
+
params := protocol.SFTPParams{
|
|
539
|
+
SessionID: *sessionID,
|
|
540
|
+
Command: "mkdir",
|
|
541
|
+
Path: *path,
|
|
542
|
+
}
|
|
543
|
+
_, err := sendRequest("sftp_mkdir", params)
|
|
544
|
+
if err != nil {
|
|
545
|
+
return err
|
|
546
|
+
}
|
|
547
|
+
fmt.Printf("Directory created: %s\n", *path)
|
|
548
|
+
|
|
549
|
+
case "rm":
|
|
550
|
+
params := protocol.SFTPParams{
|
|
551
|
+
SessionID: *sessionID,
|
|
552
|
+
Command: "rm",
|
|
553
|
+
Path: *path,
|
|
554
|
+
}
|
|
555
|
+
_, err := sendRequest("sftp_remove", params)
|
|
556
|
+
if err != nil {
|
|
557
|
+
return err
|
|
558
|
+
}
|
|
559
|
+
fmt.Printf("File removed: %s\n", *path)
|
|
560
|
+
|
|
561
|
+
default:
|
|
562
|
+
return fmt.Errorf("unknown SFTP command: %s", *command)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return nil
|
|
566
|
+
}
|
|
567
|
+
|
|
367
568
|
func printUsage() {
|
|
368
569
|
fmt.Println(`gssh - SSH Session Manager for Agents (Stateless)
|
|
369
570
|
|
|
370
571
|
Usage:
|
|
371
|
-
gssh connect -u user -h host [-p port] [-i key_path] [-P password]
|
|
572
|
+
gssh connect -u user -h host [-p port] [-i key_path] [-P password] [--ask-pass]
|
|
372
573
|
gssh disconnect [-s session_id]
|
|
373
574
|
gssh reconnect [-s session_id]
|
|
374
|
-
gssh exec [-s session_id] "command"
|
|
575
|
+
gssh exec [-s session_id] [-S password | --ask-sudo-pass] "sudo command"
|
|
375
576
|
gssh list
|
|
376
577
|
gssh use <session_id>
|
|
377
578
|
gssh forward [-s session_id] -l local_port -r remote_port
|
|
378
579
|
gssh forwards
|
|
379
580
|
gssh forward-close <forward_id>
|
|
581
|
+
gssh scp [-s session_id] [-put|-get] <source> <dest>
|
|
582
|
+
gssh sftp [-s session_id] -c <command> -p <path>
|
|
380
583
|
|
|
381
584
|
Note: For sudo commands, use key-based authentication or configure passwordless sudo.
|
|
382
585
|
|
|
383
586
|
Options:
|
|
384
|
-
-socket path
|
|
385
|
-
-s session_id
|
|
386
|
-
-u user
|
|
387
|
-
-h host
|
|
388
|
-
-p port
|
|
389
|
-
-P password
|
|
390
|
-
-i key_path
|
|
391
|
-
-
|
|
392
|
-
-
|
|
393
|
-
-
|
|
587
|
+
-socket path Unix socket path (default: /tmp/gssh.sock)
|
|
588
|
+
-s session_id Session ID
|
|
589
|
+
-u user Username
|
|
590
|
+
-h host Host
|
|
591
|
+
-p port Port (default: 22)
|
|
592
|
+
-P password Password (or use --ask-pass for interactive input)
|
|
593
|
+
-i key_path SSH key path
|
|
594
|
+
-S password sudo password (for executing sudo commands)
|
|
595
|
+
--ask-sudo-pass Interactively ask for sudo password
|
|
596
|
+
-l local Local port
|
|
597
|
+
-r remote Remote port
|
|
598
|
+
-R Remote port forward
|
|
599
|
+
-put Upload mode (local -> remote)
|
|
600
|
+
-get Download mode (remote -> local)
|
|
601
|
+
-c command SFTP command (ls, mkdir, rm)
|
|
602
|
+
-p path Path for SFTP command
|
|
603
|
+
--ask-pass Interactively ask for SSH password (secure)
|
|
604
|
+
--ask-passphrase Interactively ask for key passphrase (secure)`)
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Restore terminal on exit
|
|
608
|
+
func init() {
|
|
609
|
+
// Setup cleanup to restore terminal state on unexpected exit
|
|
610
|
+
c := make(chan os.Signal, 1)
|
|
611
|
+
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
|
612
|
+
go func() {
|
|
613
|
+
<-c
|
|
614
|
+
term.Restore(int(os.Stdin.Fd()), &term.State{})
|
|
615
|
+
os.Exit(1)
|
|
616
|
+
}()
|
|
394
617
|
}
|
package/go.mod
CHANGED
|
@@ -4,7 +4,12 @@ go 1.21
|
|
|
4
4
|
|
|
5
5
|
require (
|
|
6
6
|
github.com/google/uuid v1.5.0
|
|
7
|
+
github.com/pkg/sftp v1.13.6
|
|
7
8
|
golang.org/x/crypto v0.18.0
|
|
9
|
+
golang.org/x/term v0.16.0
|
|
8
10
|
)
|
|
9
11
|
|
|
10
|
-
require
|
|
12
|
+
require (
|
|
13
|
+
github.com/kr/fs v0.1.0 // indirect
|
|
14
|
+
golang.org/x/sys v0.16.0 // indirect
|
|
15
|
+
)
|
package/go.sum
CHANGED
|
@@ -1,8 +1,54 @@
|
|
|
1
|
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
2
|
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
3
|
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
1
4
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
|
2
5
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
6
|
+
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
|
7
|
+
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
|
8
|
+
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
|
|
9
|
+
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
|
|
10
|
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
11
|
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
12
|
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
13
|
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
14
|
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
15
|
+
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
16
|
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
17
|
+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
18
|
+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
19
|
+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
20
|
+
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
|
3
21
|
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
|
4
22
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
|
23
|
+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
24
|
+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
25
|
+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
26
|
+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
|
27
|
+
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
|
28
|
+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
29
|
+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
30
|
+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
31
|
+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
32
|
+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
33
|
+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
34
|
+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
35
|
+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
5
36
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
|
6
37
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
38
|
+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
39
|
+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
40
|
+
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
7
41
|
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
|
8
42
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
|
43
|
+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
44
|
+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
45
|
+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
46
|
+
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
47
|
+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
48
|
+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
49
|
+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
|
50
|
+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
51
|
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
52
|
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
53
|
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
54
|
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|