gssh-agent 1.0.0

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.
@@ -0,0 +1,206 @@
1
+ package rpc
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "io"
7
+ "log"
8
+ "net"
9
+ "net/http"
10
+ "os"
11
+
12
+ "gssh/internal/protocol"
13
+ "gssh/internal/session"
14
+ )
15
+
16
+ // Handler handles JSON-RPC requests
17
+ type Handler struct {
18
+ manager *session.Manager
19
+ }
20
+
21
+ // NewHandler creates a new RPC handler
22
+ func NewHandler(manager *session.Manager) *Handler {
23
+ return &Handler{manager: manager}
24
+ }
25
+
26
+ // Handle processes a JSON-RPC request
27
+ func (h *Handler) Handle(data []byte) ([]byte, error) {
28
+ var req protocol.Request
29
+ if err := json.Unmarshal(data, &req); err != nil {
30
+ return h.errorResponse(nil, -32700, "Parse error")
31
+ }
32
+
33
+ var result interface{}
34
+ var err error
35
+
36
+ switch req.Method {
37
+ case "connect":
38
+ var params protocol.ConnectParams
39
+ if err := json.Unmarshal(req.Params, &params); err != nil {
40
+ return h.errorResponse(req.ID, -32602, "Invalid params")
41
+ }
42
+ result, err = h.manager.Connect(params.User, params.Host, params.Port, params.Password, params.KeyPath)
43
+
44
+ case "disconnect":
45
+ var params protocol.DisconnectParams
46
+ if err := json.Unmarshal(req.Params, &params); err != nil {
47
+ return h.errorResponse(req.ID, -32602, "Invalid params")
48
+ }
49
+ err = h.manager.Disconnect(params.SessionID)
50
+
51
+ case "reconnect":
52
+ var params protocol.ReconnectParams
53
+ if err := json.Unmarshal(req.Params, &params); err != nil {
54
+ return h.errorResponse(req.ID, -32602, "Invalid params")
55
+ }
56
+ result, err = h.manager.Reconnect(params.SessionID)
57
+
58
+ case "exec":
59
+ var params protocol.ExecParams
60
+ if err := json.Unmarshal(req.Params, &params); err != nil {
61
+ return h.errorResponse(req.ID, -32602, "Invalid params")
62
+ }
63
+ result, err = h.manager.Exec(params.SessionID, params.Command)
64
+
65
+ case "list":
66
+ result = h.manager.List()
67
+
68
+ case "use":
69
+ var params protocol.UseParams
70
+ if err := json.Unmarshal(req.Params, &params); err != nil {
71
+ return h.errorResponse(req.ID, -32602, "Invalid params")
72
+ }
73
+ err = h.manager.Use(params.SessionID)
74
+
75
+ case "forward":
76
+ var params protocol.ForwardParams
77
+ if err := json.Unmarshal(req.Params, &params); err != nil {
78
+ return h.errorResponse(req.ID, -32602, "Invalid params")
79
+ }
80
+ result, err = h.manager.AddForward(params.SessionID, params.Type, params.Local, params.Remote)
81
+
82
+ case "forwards":
83
+ result = h.manager.ListForwards()
84
+
85
+ case "forward_close":
86
+ var params protocol.ForwardCloseParams
87
+ if err := json.Unmarshal(req.Params, &params); err != nil {
88
+ return h.errorResponse(req.ID, -32602, "Invalid params")
89
+ }
90
+ err = h.manager.CloseForward(params.ForwardID)
91
+
92
+ default:
93
+ return h.errorResponse(req.ID, -32601, fmt.Sprintf("Method not found: %s", req.Method))
94
+ }
95
+
96
+ if err != nil {
97
+ return h.errorResponse(req.ID, -32000, err.Error())
98
+ }
99
+
100
+ return h.successResponse(req.ID, result)
101
+ }
102
+
103
+ func (h *Handler) successResponse(id interface{}, result interface{}) ([]byte, error) {
104
+ resp := protocol.Response{
105
+ JSONRPC: "2.0",
106
+ Result: result,
107
+ ID: id,
108
+ }
109
+ return json.Marshal(resp)
110
+ }
111
+
112
+ func (h *Handler) errorResponse(id interface{}, code int, message string) ([]byte, error) {
113
+ resp := protocol.Response{
114
+ JSONRPC: "2.0",
115
+ Error: &protocol.Error{
116
+ Code: code,
117
+ Message: message,
118
+ },
119
+ ID: id,
120
+ }
121
+ return json.Marshal(resp)
122
+ }
123
+
124
+ // ServeUnixSocket starts the RPC server on a Unix socket
125
+ func ServeUnixSocket(manager *session.Manager, socketPath string) error {
126
+ // Remove existing socket file
127
+ os.Remove(socketPath)
128
+
129
+ listener, err := net.Listen("unix", socketPath)
130
+ if err != nil {
131
+ return fmt.Errorf("failed to listen on socket: %w", err)
132
+ }
133
+
134
+ // Set socket permissions
135
+ os.Chmod(socketPath, 0700)
136
+
137
+ handler := NewHandler(manager)
138
+
139
+ log.Printf("RPC server listening on %s", socketPath)
140
+
141
+ for {
142
+ conn, err := listener.Accept()
143
+ if err != nil {
144
+ log.Printf("Accept error: %v", err)
145
+ continue
146
+ }
147
+
148
+ go handleConnection(conn, handler)
149
+ }
150
+ }
151
+
152
+ func handleConnection(conn net.Conn, handler *Handler) {
153
+ defer conn.Close()
154
+
155
+ buf := make([]byte, 4096)
156
+ for {
157
+ n, err := conn.Read(buf)
158
+ if err == io.EOF {
159
+ return
160
+ }
161
+ if err != nil {
162
+ log.Printf("Read error: %v", err)
163
+ return
164
+ }
165
+
166
+ data := buf[:n]
167
+ response, err := handler.Handle(data)
168
+ if err != nil {
169
+ log.Printf("Handle error: %v", err)
170
+ continue
171
+ }
172
+
173
+ // Add newline to flush
174
+ response = append(response, '\n')
175
+ _, err = conn.Write(response)
176
+ if err != nil {
177
+ log.Printf("Write error: %v", err)
178
+ }
179
+ }
180
+ }
181
+
182
+ // HTTPHandler creates an HTTP handler for testing
183
+ func HTTPHandler(manager *session.Manager) http.Handler {
184
+ handler := NewHandler(manager)
185
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
186
+ if r.Method != http.MethodPost {
187
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
188
+ return
189
+ }
190
+
191
+ data, err := io.ReadAll(r.Body)
192
+ if err != nil {
193
+ http.Error(w, "Read error", http.StatusBadRequest)
194
+ return
195
+ }
196
+
197
+ response, err := handler.Handle(data)
198
+ if err != nil {
199
+ http.Error(w, err.Error(), http.StatusInternalServerError)
200
+ return
201
+ }
202
+
203
+ w.Header().Set("Content-Type", "application/json")
204
+ w.Write(response)
205
+ })
206
+ }
package/skill.md ADDED
@@ -0,0 +1,96 @@
1
+ # gssh 使用指南
2
+
3
+ ## 快速开始
4
+
5
+ ### 1. 安装
6
+
7
+ ```bash
8
+ git clone <repository-url>
9
+ cd gssh
10
+ go build -o bin/daemon cmd/daemon/main.go
11
+ go build -o bin/gssh cmd/gssh/main.go
12
+ ./homebrew/install.sh
13
+ ```
14
+
15
+ ### 2. 基本使用
16
+
17
+ ```bash
18
+ # 连接 SSH
19
+ gssh connect -u admin1 -h 192.168.1.100 -P 1234
20
+
21
+ # 执行命令
22
+ gssh exec "ls -la"
23
+
24
+ # 列出 session
25
+ gssh list
26
+ ```
27
+
28
+ ## 核心概念
29
+
30
+ - **daemon**: 后台服务,管理 SSH 连接
31
+ - **session**: 一个 SSH 连接会话
32
+ - **默认 session**: 不指定 session ID 时使用的会话
33
+
34
+ ## 命令列表
35
+
36
+ | 命令 | 说明 |
37
+ |------|------|
38
+ | `connect` | 建立新 SSH 连接 |
39
+ | `exec` | 执行命令 |
40
+ | `list` | 列出所有 session |
41
+ | `use` | 切换默认 session |
42
+ | `disconnect` | 断开 session |
43
+ | `reconnect` | 重连 session |
44
+ | `forward` | 端口转发 |
45
+ | `forwards` | 列出转发 |
46
+ | `forward-close` | 关闭转发 |
47
+
48
+ ## 典型工作流
49
+
50
+ ```bash
51
+ # 1. 连接远程主机
52
+ gssh connect -u admin -h 192.168.1.100 -P password
53
+
54
+ # 2. 执行命令(使用默认 session)
55
+ gssh exec "pwd"
56
+ gssh exec "uname -a"
57
+
58
+ # 3. 断开连接
59
+ gssh disconnect
60
+
61
+ # 4. 重连
62
+ gssh reconnect -s <session-id>
63
+ ```
64
+
65
+ ## SSH 密钥认证
66
+
67
+ ```bash
68
+ # 使用密钥连接
69
+ gssh connect -u admin -h 192.168.1.100 -i ~/.ssh/id_rsa
70
+ ```
71
+
72
+ ## 端口转发
73
+
74
+ ```bash
75
+ # 本地转发:localhost:8080 -> remote:80
76
+ gssh forward -l 8080 -r 80
77
+ ```
78
+
79
+ ## 服务管理
80
+
81
+ ```bash
82
+ # 启动
83
+ brew services start gssh
84
+
85
+ # 停止
86
+ brew services stop gssh
87
+
88
+ # 状态
89
+ brew services list
90
+ ```
91
+
92
+ ## 注意事项
93
+
94
+ - Agent 使用场景:无状态,每次请求独立完成
95
+ - sudo 命令:需要配置 passwordless sudo 或使用密钥认证
96
+ - Session 状态由 daemon 维护,CLI 只负责发送请求