seahorse-bash-client 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.
- package/DESIGN.md +463 -0
- package/bin/cli.js +2 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +190 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/pty-manager.d.ts +52 -0
- package/dist/pty-manager.d.ts.map +1 -0
- package/dist/pty-manager.js +387 -0
- package/dist/pty-manager.js.map +1 -0
- package/dist/pty-manager.test.d.ts +8 -0
- package/dist/pty-manager.test.d.ts.map +1 -0
- package/dist/pty-manager.test.js +427 -0
- package/dist/pty-manager.test.js.map +1 -0
- package/dist/tools.d.ts +6 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +212 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/websocket-client.d.ts +73 -0
- package/dist/websocket-client.d.ts.map +1 -0
- package/dist/websocket-client.js +318 -0
- package/dist/websocket-client.js.map +1 -0
- package/package.json +55 -0
- package/src/cli.ts +179 -0
- package/src/index.ts +10 -0
- package/src/pty-manager.test.ts +555 -0
- package/src/pty-manager.ts +430 -0
- package/src/tools.ts +217 -0
- package/src/types.ts +221 -0
- package/src/websocket-client.ts +374 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +10 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
# Seahorse Bash Client - MCP Tool Design
|
|
2
|
+
|
|
3
|
+
## 개요
|
|
4
|
+
|
|
5
|
+
Stateful bash 머신에서 Seahorse Agent에 역방향 연결하여 PTY 기반 bash 도구를 제공하는 MCP 클라이언트.
|
|
6
|
+
|
|
7
|
+
## 아키텍처
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
11
|
+
│ Seahorse Agent Server │
|
|
12
|
+
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
|
|
13
|
+
│ │ RemoteMCPProvider│◄───│ RemoteMCPConnectionManager │ │
|
|
14
|
+
│ │ (call_tool) │ │ (WebSocket, tool registry) │ │
|
|
15
|
+
│ └─────────────────┘ └───────────────▲─────────────────┘ │
|
|
16
|
+
└──────────────────────────────────────────│──────────────────┘
|
|
17
|
+
│ WebSocket (wss://)
|
|
18
|
+
│ Reverse Connection
|
|
19
|
+
┌──────────────────────────────────────────│──────────────────┐
|
|
20
|
+
│ Seahorse Bash Client │ │
|
|
21
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
22
|
+
│ │ WebSocket Client │ │
|
|
23
|
+
│ │ - 서버에 연결 및 tool 등록 │ │
|
|
24
|
+
│ │ - tool_call 수신 → PTY Manager 호출 → 응답 전송 │ │
|
|
25
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
26
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
27
|
+
│ │ PTY Manager (node-pty) │ │
|
|
28
|
+
│ │ - 다중 PTY 세션 관리 │ │
|
|
29
|
+
│ │ - Rolling output buffer (50,000줄) │ │
|
|
30
|
+
│ │ - Exit notification │ │
|
|
31
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
32
|
+
└─────────────────────────────────────────────────────────────┘
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## MCP Tools 정의
|
|
36
|
+
|
|
37
|
+
### 1. `bash_spawn` - PTY 세션 생성 및 명령 실행
|
|
38
|
+
|
|
39
|
+
**용도**: 새로운 PTY 세션을 생성하고 백그라운드에서 명령 실행
|
|
40
|
+
|
|
41
|
+
**Input Schema**:
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"command": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "실행할 명령어 (예: 'npm run dev', 'python server.py')"
|
|
49
|
+
},
|
|
50
|
+
"args": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": { "type": "string" },
|
|
53
|
+
"description": "명령어 인자 (선택)"
|
|
54
|
+
},
|
|
55
|
+
"cwd": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "작업 디렉토리 (기본: 현재 디렉토리)"
|
|
58
|
+
},
|
|
59
|
+
"env": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"additionalProperties": { "type": "string" },
|
|
62
|
+
"description": "환경 변수 (기존 환경에 추가)"
|
|
63
|
+
},
|
|
64
|
+
"name": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "세션 이름/식별자 (선택, 자동 생성)"
|
|
67
|
+
},
|
|
68
|
+
"shell": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"enum": ["bash", "sh", "zsh"],
|
|
71
|
+
"default": "bash",
|
|
72
|
+
"description": "사용할 쉘"
|
|
73
|
+
},
|
|
74
|
+
"cols": {
|
|
75
|
+
"type": "integer",
|
|
76
|
+
"default": 120,
|
|
77
|
+
"description": "터미널 컬럼 수"
|
|
78
|
+
},
|
|
79
|
+
"rows": {
|
|
80
|
+
"type": "integer",
|
|
81
|
+
"default": 30,
|
|
82
|
+
"description": "터미널 행 수"
|
|
83
|
+
},
|
|
84
|
+
"notifyOnExit": {
|
|
85
|
+
"type": "boolean",
|
|
86
|
+
"default": true,
|
|
87
|
+
"description": "프로세스 종료 시 알림 여부"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"required": ["command"]
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Response**:
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"sessionId": "pty_a1b2c3d4",
|
|
98
|
+
"pid": 12345,
|
|
99
|
+
"command": "npm run dev",
|
|
100
|
+
"status": "running",
|
|
101
|
+
"message": "Session created and command started"
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### 2. `bash_exec` - 동기 명령 실행 (단일 명령)
|
|
108
|
+
|
|
109
|
+
**용도**: 단일 명령을 실행하고 완료될 때까지 대기 (기존 bash 세션 재사용)
|
|
110
|
+
|
|
111
|
+
**Input Schema**:
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"type": "object",
|
|
115
|
+
"properties": {
|
|
116
|
+
"command": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"description": "실행할 bash 명령어"
|
|
119
|
+
},
|
|
120
|
+
"cwd": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"description": "작업 디렉토리 (선택)"
|
|
123
|
+
},
|
|
124
|
+
"timeout": {
|
|
125
|
+
"type": "integer",
|
|
126
|
+
"default": 30000,
|
|
127
|
+
"description": "타임아웃 (밀리초, 기본 30초, 최대 600초)"
|
|
128
|
+
},
|
|
129
|
+
"env": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"additionalProperties": { "type": "string" },
|
|
132
|
+
"description": "추가 환경 변수"
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"required": ["command"]
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Response**:
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"exitCode": 0,
|
|
143
|
+
"stdout": "명령 출력...",
|
|
144
|
+
"stderr": "",
|
|
145
|
+
"duration": 1234,
|
|
146
|
+
"timedOut": false
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### 3. `bash_write` - PTY 세션에 입력 전송
|
|
153
|
+
|
|
154
|
+
**용도**: 실행 중인 PTY 세션에 키 입력 전송 (Ctrl+C, 텍스트 입력 등)
|
|
155
|
+
|
|
156
|
+
**Input Schema**:
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"type": "object",
|
|
160
|
+
"properties": {
|
|
161
|
+
"sessionId": {
|
|
162
|
+
"type": "string",
|
|
163
|
+
"description": "대상 세션 ID"
|
|
164
|
+
},
|
|
165
|
+
"data": {
|
|
166
|
+
"type": "string",
|
|
167
|
+
"description": "전송할 데이터 (텍스트 또는 이스케이프 시퀀스)"
|
|
168
|
+
},
|
|
169
|
+
"specialKey": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"enum": ["ctrl+c", "ctrl+d", "ctrl+z", "enter", "tab", "up", "down", "left", "right"],
|
|
172
|
+
"description": "특수 키 (data 대신 사용 가능)"
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"required": ["sessionId"],
|
|
176
|
+
"oneOf": [
|
|
177
|
+
{ "required": ["data"] },
|
|
178
|
+
{ "required": ["specialKey"] }
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Special Key Mappings**:
|
|
184
|
+
- `ctrl+c` → `\x03` (SIGINT)
|
|
185
|
+
- `ctrl+d` → `\x04` (EOF)
|
|
186
|
+
- `ctrl+z` → `\x1a` (SIGTSTP - 백그라운드 전환)
|
|
187
|
+
- `enter` → `\r`
|
|
188
|
+
- `tab` → `\t`
|
|
189
|
+
- `up` → `\x1b[A`
|
|
190
|
+
- `down` → `\x1b[B`
|
|
191
|
+
|
|
192
|
+
**Response**:
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"success": true,
|
|
196
|
+
"bytesWritten": 1,
|
|
197
|
+
"sessionStatus": "running"
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### 4. `bash_read` - PTY 세션 출력 읽기
|
|
204
|
+
|
|
205
|
+
**용도**: PTY 세션의 버퍼된 출력을 페이지네이션과 필터링으로 조회
|
|
206
|
+
|
|
207
|
+
**Input Schema**:
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"type": "object",
|
|
211
|
+
"properties": {
|
|
212
|
+
"sessionId": {
|
|
213
|
+
"type": "string",
|
|
214
|
+
"description": "대상 세션 ID"
|
|
215
|
+
},
|
|
216
|
+
"offset": {
|
|
217
|
+
"type": "integer",
|
|
218
|
+
"default": 0,
|
|
219
|
+
"description": "시작 줄 번호 (0부터)"
|
|
220
|
+
},
|
|
221
|
+
"limit": {
|
|
222
|
+
"type": "integer",
|
|
223
|
+
"default": 100,
|
|
224
|
+
"maximum": 1000,
|
|
225
|
+
"description": "반환할 최대 줄 수"
|
|
226
|
+
},
|
|
227
|
+
"pattern": {
|
|
228
|
+
"type": "string",
|
|
229
|
+
"description": "필터링 정규식 (선택)"
|
|
230
|
+
},
|
|
231
|
+
"ignoreCase": {
|
|
232
|
+
"type": "boolean",
|
|
233
|
+
"default": false,
|
|
234
|
+
"description": "대소문자 무시"
|
|
235
|
+
},
|
|
236
|
+
"since": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"format": "date-time",
|
|
239
|
+
"description": "특정 시간 이후의 출력만 (선택)"
|
|
240
|
+
},
|
|
241
|
+
"tail": {
|
|
242
|
+
"type": "boolean",
|
|
243
|
+
"default": false,
|
|
244
|
+
"description": "true면 마지막 N줄 반환"
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"required": ["sessionId"]
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Response**:
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"sessionId": "pty_a1b2c3d4",
|
|
255
|
+
"lines": [
|
|
256
|
+
{"line": 1, "text": "Server starting...", "timestamp": "2024-01-01T00:00:00Z"},
|
|
257
|
+
{"line": 2, "text": "Listening on port 3000", "timestamp": "2024-01-01T00:00:01Z"}
|
|
258
|
+
],
|
|
259
|
+
"totalLines": 150,
|
|
260
|
+
"hasMore": true,
|
|
261
|
+
"sessionStatus": "running"
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
### 5. `bash_list` - 모든 PTY 세션 목록
|
|
268
|
+
|
|
269
|
+
**용도**: 현재 활성화된 모든 PTY 세션 정보 조회
|
|
270
|
+
|
|
271
|
+
**Input Schema**:
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"type": "object",
|
|
275
|
+
"properties": {
|
|
276
|
+
"status": {
|
|
277
|
+
"type": "string",
|
|
278
|
+
"enum": ["all", "running", "exited", "killed"],
|
|
279
|
+
"default": "all",
|
|
280
|
+
"description": "필터링할 상태"
|
|
281
|
+
},
|
|
282
|
+
"includeOutput": {
|
|
283
|
+
"type": "boolean",
|
|
284
|
+
"default": false,
|
|
285
|
+
"description": "각 세션의 최근 출력 포함 여부"
|
|
286
|
+
},
|
|
287
|
+
"outputLines": {
|
|
288
|
+
"type": "integer",
|
|
289
|
+
"default": 5,
|
|
290
|
+
"description": "includeOutput=true일 때 포함할 줄 수"
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Response**:
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"sessions": [
|
|
300
|
+
{
|
|
301
|
+
"sessionId": "pty_a1b2c3d4",
|
|
302
|
+
"name": "dev-server",
|
|
303
|
+
"pid": 12345,
|
|
304
|
+
"command": "npm run dev",
|
|
305
|
+
"status": "running",
|
|
306
|
+
"createdAt": "2024-01-01T00:00:00Z",
|
|
307
|
+
"lineCount": 150,
|
|
308
|
+
"cwd": "/home/user/project",
|
|
309
|
+
"recentOutput": ["Server running on port 3000"]
|
|
310
|
+
}
|
|
311
|
+
],
|
|
312
|
+
"summary": {
|
|
313
|
+
"total": 3,
|
|
314
|
+
"running": 2,
|
|
315
|
+
"exited": 1,
|
|
316
|
+
"killed": 0
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
### 6. `bash_kill` - PTY 세션 종료
|
|
324
|
+
|
|
325
|
+
**용도**: 실행 중인 PTY 세션 종료 및 정리
|
|
326
|
+
|
|
327
|
+
**Input Schema**:
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"type": "object",
|
|
331
|
+
"properties": {
|
|
332
|
+
"sessionId": {
|
|
333
|
+
"type": "string",
|
|
334
|
+
"description": "종료할 세션 ID"
|
|
335
|
+
},
|
|
336
|
+
"signal": {
|
|
337
|
+
"type": "string",
|
|
338
|
+
"enum": ["SIGTERM", "SIGKILL", "SIGINT"],
|
|
339
|
+
"default": "SIGTERM",
|
|
340
|
+
"description": "전송할 시그널"
|
|
341
|
+
},
|
|
342
|
+
"cleanup": {
|
|
343
|
+
"type": "boolean",
|
|
344
|
+
"default": false,
|
|
345
|
+
"description": "true면 세션 목록에서도 제거 (출력 버퍼 삭제)"
|
|
346
|
+
},
|
|
347
|
+
"gracePeriod": {
|
|
348
|
+
"type": "integer",
|
|
349
|
+
"default": 5000,
|
|
350
|
+
"description": "SIGTERM 후 SIGKILL까지 대기 시간 (ms)"
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
"required": ["sessionId"]
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Response**:
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"sessionId": "pty_a1b2c3d4",
|
|
361
|
+
"exitCode": 143,
|
|
362
|
+
"signal": "SIGTERM",
|
|
363
|
+
"cleaned": false,
|
|
364
|
+
"finalLineCount": 200,
|
|
365
|
+
"message": "Session terminated successfully"
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## WebSocket 프로토콜
|
|
372
|
+
|
|
373
|
+
### 연결 시 등록 메시지
|
|
374
|
+
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"type": "register",
|
|
378
|
+
"server_name": "bash-client-hostname",
|
|
379
|
+
"tools": [
|
|
380
|
+
{
|
|
381
|
+
"name": "bash_spawn",
|
|
382
|
+
"description": "Create a new PTY session and run a command in background",
|
|
383
|
+
"inputSchema": { /* 위의 스키마 */ }
|
|
384
|
+
},
|
|
385
|
+
// ... 나머지 tools
|
|
386
|
+
],
|
|
387
|
+
"metadata": {
|
|
388
|
+
"version": "1.0.0",
|
|
389
|
+
"hostname": "my-dev-machine",
|
|
390
|
+
"platform": "darwin",
|
|
391
|
+
"shell": "/bin/bash"
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Tool Call 요청 (Agent → Client)
|
|
397
|
+
|
|
398
|
+
```json
|
|
399
|
+
{
|
|
400
|
+
"type": "tool_call",
|
|
401
|
+
"request_id": "server_name:uuid",
|
|
402
|
+
"method": "tools/call",
|
|
403
|
+
"params": {
|
|
404
|
+
"name": "bash_spawn",
|
|
405
|
+
"arguments": {
|
|
406
|
+
"command": "npm run dev",
|
|
407
|
+
"cwd": "/home/user/project",
|
|
408
|
+
"notifyOnExit": true
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Tool Call 응답 (Client → Agent)
|
|
415
|
+
|
|
416
|
+
```json
|
|
417
|
+
{
|
|
418
|
+
"type": "tool_response",
|
|
419
|
+
"request_id": "server_name:uuid",
|
|
420
|
+
"content": [
|
|
421
|
+
{
|
|
422
|
+
"type": "text",
|
|
423
|
+
"text": "{\"sessionId\": \"pty_a1b2c3d4\", \"pid\": 12345, ...}"
|
|
424
|
+
}
|
|
425
|
+
],
|
|
426
|
+
"isError": false
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Exit Notification (Client → Agent)
|
|
431
|
+
|
|
432
|
+
```json
|
|
433
|
+
{
|
|
434
|
+
"type": "notification",
|
|
435
|
+
"method": "session/exited",
|
|
436
|
+
"params": {
|
|
437
|
+
"sessionId": "pty_a1b2c3d4",
|
|
438
|
+
"exitCode": 0,
|
|
439
|
+
"signal": null,
|
|
440
|
+
"duration": 12345,
|
|
441
|
+
"finalLineCount": 500
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## 보안 고려사항
|
|
449
|
+
|
|
450
|
+
1. **명령 필터링**: 위험한 명령 (`rm -rf /`, `:(){ :|:& };:` 등) 차단 옵션
|
|
451
|
+
2. **작업 디렉토리 제한**: 특정 디렉토리 하위로만 cwd 제한 가능
|
|
452
|
+
3. **리소스 제한**: ulimit을 통한 메모리/CPU 제한
|
|
453
|
+
4. **감사 로깅**: 모든 명령 실행 기록
|
|
454
|
+
|
|
455
|
+
## 구현 우선순위
|
|
456
|
+
|
|
457
|
+
1. ✅ WebSocket 역방향 연결
|
|
458
|
+
2. ✅ `bash_exec` (동기 명령 - 가장 기본)
|
|
459
|
+
3. ✅ `bash_spawn` (백그라운드 실행)
|
|
460
|
+
4. ✅ `bash_read` (출력 읽기)
|
|
461
|
+
5. ✅ `bash_write` (입력 전송)
|
|
462
|
+
6. ✅ `bash_list` (세션 목록)
|
|
463
|
+
7. ✅ `bash_kill` (세션 종료)
|
package/bin/cli.js
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;GAIG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Seahorse Bash Client CLI
|
|
5
|
+
*
|
|
6
|
+
* Connects to Seahorse Agent and provides PTY-based bash tools.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
const commander_1 = require("commander");
|
|
46
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
+
const ora_1 = __importDefault(require("ora"));
|
|
48
|
+
const os_1 = __importDefault(require("os"));
|
|
49
|
+
const websocket_client_1 = require("./websocket-client");
|
|
50
|
+
const VERSION = '1.0.0';
|
|
51
|
+
const program = new commander_1.Command();
|
|
52
|
+
program
|
|
53
|
+
.name('seahorse-bash')
|
|
54
|
+
.description('Seahorse Bash Client - PTY-based bash tools for Seahorse Agent')
|
|
55
|
+
.version(VERSION);
|
|
56
|
+
program
|
|
57
|
+
.command('connect')
|
|
58
|
+
.description('Connect to Seahorse Agent and provide bash tools')
|
|
59
|
+
.requiredOption('-u, --url <url>', 'Seahorse Agent WebSocket URL (e.g., wss://agent.example.com/ws/remote-mcp)')
|
|
60
|
+
.option('-n, --name <name>', 'Server name for identification', `bash-${os_1.default.hostname()}`)
|
|
61
|
+
.option('-s, --shell <shell>', 'Default shell to use', process.env.SHELL || '/bin/bash')
|
|
62
|
+
.option('--reconnect-interval <ms>', 'Reconnect interval in milliseconds', '5000')
|
|
63
|
+
.option('--heartbeat-interval <ms>', 'Heartbeat interval in milliseconds', '30000')
|
|
64
|
+
.option('--max-output-lines <lines>', 'Maximum output lines to buffer per session', '50000')
|
|
65
|
+
.option('--allowed-commands <cmds>', 'Comma-separated list of allowed command prefixes')
|
|
66
|
+
.option('--blocked-commands <cmds>', 'Comma-separated list of blocked command patterns')
|
|
67
|
+
.option('--allowed-cwd <paths>', 'Comma-separated list of allowed working directory paths')
|
|
68
|
+
.option('-k, --insecure', 'Skip SSL certificate verification (for self-signed certs)')
|
|
69
|
+
.action(async (options) => {
|
|
70
|
+
const spinner = (0, ora_1.default)('Connecting to Seahorse Agent...').start();
|
|
71
|
+
const config = {
|
|
72
|
+
serverUrl: options.url,
|
|
73
|
+
serverName: options.name,
|
|
74
|
+
defaultShell: options.shell,
|
|
75
|
+
reconnectInterval: parseInt(options.reconnectInterval, 10),
|
|
76
|
+
heartbeatInterval: parseInt(options.heartbeatInterval, 10),
|
|
77
|
+
maxOutputLines: parseInt(options.maxOutputLines, 10),
|
|
78
|
+
allowedCommands: options.allowedCommands?.split(',').map((s) => s.trim()),
|
|
79
|
+
blockedCommands: options.blockedCommands?.split(',').map((s) => s.trim()),
|
|
80
|
+
allowedCwdPaths: options.allowedCwd?.split(',').map((s) => s.trim()),
|
|
81
|
+
insecure: options.insecure || false,
|
|
82
|
+
};
|
|
83
|
+
const client = new websocket_client_1.WebSocketClient(config);
|
|
84
|
+
// Event handlers
|
|
85
|
+
client.on('connected', () => {
|
|
86
|
+
spinner.succeed(chalk_1.default.green('Connected to Seahorse Agent'));
|
|
87
|
+
console.log(chalk_1.default.gray(` Server name: ${config.serverName}`));
|
|
88
|
+
console.log(chalk_1.default.gray(` Shell: ${config.defaultShell}`));
|
|
89
|
+
console.log(chalk_1.default.gray(` Platform: ${process.platform}`));
|
|
90
|
+
console.log();
|
|
91
|
+
console.log(chalk_1.default.cyan('Listening for tool calls...'));
|
|
92
|
+
console.log(chalk_1.default.gray('Press Ctrl+C to disconnect'));
|
|
93
|
+
console.log();
|
|
94
|
+
});
|
|
95
|
+
client.on('registered', ({ tools }) => {
|
|
96
|
+
console.log(chalk_1.default.green(`Registered ${tools.length} tools:`));
|
|
97
|
+
tools.forEach((tool) => {
|
|
98
|
+
console.log(chalk_1.default.gray(` - ${tool.name}`));
|
|
99
|
+
});
|
|
100
|
+
console.log();
|
|
101
|
+
});
|
|
102
|
+
client.on('disconnected', ({ code, reason }) => {
|
|
103
|
+
console.log(chalk_1.default.yellow(`Disconnected (code: ${code}, reason: ${reason})`));
|
|
104
|
+
});
|
|
105
|
+
client.on('reconnecting', ({ interval }) => {
|
|
106
|
+
console.log(chalk_1.default.yellow(`Reconnecting in ${interval}ms...`));
|
|
107
|
+
});
|
|
108
|
+
client.on('error', (error) => {
|
|
109
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
110
|
+
});
|
|
111
|
+
client.on('tool:call', ({ name, requestId }) => {
|
|
112
|
+
console.log(chalk_1.default.blue(`[${new Date().toISOString()}] Tool call: ${name} (${requestId})`));
|
|
113
|
+
});
|
|
114
|
+
client.on('tool:success', ({ name, requestId }) => {
|
|
115
|
+
console.log(chalk_1.default.green(`[${new Date().toISOString()}] Tool success: ${name} (${requestId})`));
|
|
116
|
+
});
|
|
117
|
+
client.on('tool:error', ({ name, requestId, error }) => {
|
|
118
|
+
console.log(chalk_1.default.red(`[${new Date().toISOString()}] Tool error: ${name} (${requestId}): ${error}`));
|
|
119
|
+
});
|
|
120
|
+
client.on('notification:sent', ({ method }) => {
|
|
121
|
+
console.log(chalk_1.default.magenta(`[${new Date().toISOString()}] Notification: ${method}`));
|
|
122
|
+
});
|
|
123
|
+
// Graceful shutdown
|
|
124
|
+
const shutdown = async () => {
|
|
125
|
+
console.log();
|
|
126
|
+
console.log(chalk_1.default.yellow('Shutting down...'));
|
|
127
|
+
await client.disconnect();
|
|
128
|
+
console.log(chalk_1.default.green('Disconnected. Goodbye!'));
|
|
129
|
+
process.exit(0);
|
|
130
|
+
};
|
|
131
|
+
process.on('SIGINT', shutdown);
|
|
132
|
+
process.on('SIGTERM', shutdown);
|
|
133
|
+
// Connect
|
|
134
|
+
try {
|
|
135
|
+
await client.connect();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
spinner.fail(chalk_1.default.red(`Failed to connect: ${error instanceof Error ? error.message : error}`));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
program
|
|
143
|
+
.command('test')
|
|
144
|
+
.description('Test PTY functionality locally (without connecting to agent)')
|
|
145
|
+
.option('-c, --command <cmd>', 'Command to execute', 'echo "Hello from PTY!"')
|
|
146
|
+
.option('-s, --shell <shell>', 'Shell to use', process.env.SHELL || '/bin/bash')
|
|
147
|
+
.action(async (options) => {
|
|
148
|
+
const { PTYManager } = await Promise.resolve().then(() => __importStar(require('./pty-manager')));
|
|
149
|
+
const ptyManager = new PTYManager({ defaultShell: options.shell });
|
|
150
|
+
console.log(chalk_1.default.cyan('Testing PTY Manager...'));
|
|
151
|
+
console.log();
|
|
152
|
+
// Test bash_exec
|
|
153
|
+
console.log(chalk_1.default.yellow('1. Testing bash_exec:'));
|
|
154
|
+
const execResult = await ptyManager.exec({ command: options.command });
|
|
155
|
+
console.log(chalk_1.default.gray(` Exit code: ${execResult.exitCode}`));
|
|
156
|
+
console.log(chalk_1.default.gray(` Duration: ${execResult.duration}ms`));
|
|
157
|
+
console.log(chalk_1.default.gray(` Output: ${execResult.stdout}`));
|
|
158
|
+
console.log();
|
|
159
|
+
// Test bash_spawn
|
|
160
|
+
console.log(chalk_1.default.yellow('2. Testing bash_spawn:'));
|
|
161
|
+
const spawnResult = await ptyManager.spawn({
|
|
162
|
+
command: 'for i in 1 2 3; do echo "Count: $i"; sleep 0.5; done',
|
|
163
|
+
name: 'test-session',
|
|
164
|
+
});
|
|
165
|
+
console.log(chalk_1.default.gray(` Session ID: ${spawnResult.sessionId}`));
|
|
166
|
+
console.log(chalk_1.default.gray(` PID: ${spawnResult.pid}`));
|
|
167
|
+
// Wait for output
|
|
168
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
169
|
+
// Test bash_read
|
|
170
|
+
console.log();
|
|
171
|
+
console.log(chalk_1.default.yellow('3. Testing bash_read:'));
|
|
172
|
+
const readResult = ptyManager.read({ sessionId: spawnResult.sessionId, tail: true, limit: 10 });
|
|
173
|
+
console.log(chalk_1.default.gray(` Total lines: ${readResult.totalLines}`));
|
|
174
|
+
readResult.lines.forEach((line) => {
|
|
175
|
+
console.log(chalk_1.default.gray(` [${line.line}] ${line.text}`));
|
|
176
|
+
});
|
|
177
|
+
// Test bash_list
|
|
178
|
+
console.log();
|
|
179
|
+
console.log(chalk_1.default.yellow('4. Testing bash_list:'));
|
|
180
|
+
const listResult = ptyManager.list({});
|
|
181
|
+
console.log(chalk_1.default.gray(` Total sessions: ${listResult.summary.total}`));
|
|
182
|
+
console.log(chalk_1.default.gray(` Running: ${listResult.summary.running}`));
|
|
183
|
+
console.log(chalk_1.default.gray(` Exited: ${listResult.summary.exited}`));
|
|
184
|
+
// Cleanup
|
|
185
|
+
await ptyManager.shutdown();
|
|
186
|
+
console.log();
|
|
187
|
+
console.log(chalk_1.default.green('All tests passed!'));
|
|
188
|
+
});
|
|
189
|
+
program.parse();
|
|
190
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|