@pyrokine/mcp-ssh 1.1.0 → 1.1.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 +81 -72
- package/README_zh.md +81 -73
- package/dist/file-ops.js +36 -20
- package/dist/index.js +24 -9
- package/dist/session-manager.d.ts +62 -51
- package/dist/session-manager.js +201 -168
- package/dist/ssh-config.js +2 -2
- package/package.json +1 -1
- package/src/file-ops.ts +602 -577
- package/src/index.ts +971 -948
- package/src/session-manager.ts +986 -945
- package/src/ssh-config.ts +185 -185
- package/src/types.ts +89 -89
- package/tsconfig.json +7 -2
package/README.md
CHANGED
|
@@ -19,16 +19,16 @@ A comprehensive SSH MCP Server for AI assistants (Claude, Cursor, Windsurf, etc.
|
|
|
19
19
|
- **Connection Management**: Connection pooling, keepalive, auto-reconnect
|
|
20
20
|
- **Session Persistence**: Sessions info saved for reconnection
|
|
21
21
|
- **Command Execution**:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
- Basic exec with timeout
|
|
23
|
+
- PTY mode (for interactive commands like `top`, `htop`)
|
|
24
|
+
- `sudo` execution
|
|
25
|
+
- `su` (switch user) execution - *run commands as different user*
|
|
26
|
+
- Batch execution
|
|
27
|
+
- Parallel execution on multiple hosts
|
|
28
28
|
- **Persistent PTY Sessions**: For long-running interactive commands (top, htop, tmux, vim, etc.)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
- Output buffering with polling read
|
|
30
|
+
- Send keystrokes and commands
|
|
31
|
+
- Window resize support
|
|
32
32
|
- **File Operations**: Upload, download, read, write, list directory (via SFTP)
|
|
33
33
|
- **Smart Sync**: Directory sync with rsync (auto-fallback to SFTP if rsync unavailable)
|
|
34
34
|
- **Environment Configuration**: LANG, LC_ALL, custom env vars
|
|
@@ -36,15 +36,15 @@ A comprehensive SSH MCP Server for AI assistants (Claude, Cursor, Windsurf, etc.
|
|
|
36
36
|
|
|
37
37
|
## Compatible Clients
|
|
38
38
|
|
|
39
|
-
| Client
|
|
40
|
-
|
|
41
|
-
| Claude Code
|
|
42
|
-
| Claude Desktop
|
|
43
|
-
| Cursor
|
|
44
|
-
| Windsurf
|
|
45
|
-
| Continue.dev
|
|
46
|
-
| Cline
|
|
47
|
-
| Any MCP-compatible client | ✅
|
|
39
|
+
| Client | Status |
|
|
40
|
+
|---------------------------|--------|
|
|
41
|
+
| Claude Code | ✅ |
|
|
42
|
+
| Claude Desktop | ✅ |
|
|
43
|
+
| Cursor | ✅ |
|
|
44
|
+
| Windsurf | ✅ |
|
|
45
|
+
| Continue.dev | ✅ |
|
|
46
|
+
| Cline | ✅ |
|
|
47
|
+
| Any MCP-compatible client | ✅ |
|
|
48
48
|
|
|
49
49
|
## Installation
|
|
50
50
|
|
|
@@ -90,57 +90,57 @@ Add to your MCP settings (e.g., `~/.claude/settings.json` or client-specific con
|
|
|
90
90
|
|
|
91
91
|
### Connection Management
|
|
92
92
|
|
|
93
|
-
| Tool
|
|
94
|
-
|
|
95
|
-
| `ssh_connect`
|
|
96
|
-
| `ssh_disconnect`
|
|
97
|
-
| `ssh_list_sessions` | List active sessions
|
|
98
|
-
| `ssh_reconnect`
|
|
99
|
-
| `ssh_config_list`
|
|
93
|
+
| Tool | Description |
|
|
94
|
+
|---------------------|---------------------------------------------------|
|
|
95
|
+
| `ssh_connect` | Establish SSH connection (supports ~/.ssh/config) |
|
|
96
|
+
| `ssh_disconnect` | Close connection |
|
|
97
|
+
| `ssh_list_sessions` | List active sessions |
|
|
98
|
+
| `ssh_reconnect` | Reconnect a disconnected session |
|
|
99
|
+
| `ssh_config_list` | List hosts from ~/.ssh/config |
|
|
100
100
|
|
|
101
101
|
### Command Execution
|
|
102
102
|
|
|
103
|
-
| Tool
|
|
104
|
-
|
|
105
|
-
| `ssh_exec`
|
|
106
|
-
| `ssh_exec_as_user`
|
|
107
|
-
| `ssh_exec_sudo`
|
|
108
|
-
| `ssh_exec_batch`
|
|
103
|
+
| Tool | Description |
|
|
104
|
+
|---------------------|-----------------------------------------------|
|
|
105
|
+
| `ssh_exec` | Execute command (supports PTY mode) |
|
|
106
|
+
| `ssh_exec_as_user` | Execute as different user (via `su`) |
|
|
107
|
+
| `ssh_exec_sudo` | Execute with `sudo` |
|
|
108
|
+
| `ssh_exec_batch` | Execute multiple commands sequentially |
|
|
109
109
|
| `ssh_exec_parallel` | Execute command on multiple hosts in parallel |
|
|
110
|
-
| `ssh_quick_exec`
|
|
110
|
+
| `ssh_quick_exec` | One-shot: connect, execute, disconnect |
|
|
111
111
|
|
|
112
112
|
### File Operations
|
|
113
113
|
|
|
114
|
-
| Tool
|
|
115
|
-
|
|
116
|
-
| `ssh_upload`
|
|
117
|
-
| `ssh_download`
|
|
118
|
-
| `ssh_read_file`
|
|
119
|
-
| `ssh_write_file` | Write content to remote file
|
|
120
|
-
| `ssh_list_dir`
|
|
121
|
-
| `ssh_file_info`
|
|
122
|
-
| `ssh_mkdir`
|
|
123
|
-
| `ssh_sync`
|
|
114
|
+
| Tool | Description |
|
|
115
|
+
|------------------|------------------------------------------|
|
|
116
|
+
| `ssh_upload` | Upload local file to remote server |
|
|
117
|
+
| `ssh_download` | Download remote file to local |
|
|
118
|
+
| `ssh_read_file` | Read remote file content |
|
|
119
|
+
| `ssh_write_file` | Write content to remote file |
|
|
120
|
+
| `ssh_list_dir` | List remote directory contents |
|
|
121
|
+
| `ssh_file_info` | Get file/directory metadata |
|
|
122
|
+
| `ssh_mkdir` | Create remote directory |
|
|
123
|
+
| `ssh_sync` | Smart sync with rsync (fallback to SFTP) |
|
|
124
124
|
|
|
125
125
|
### PTY Sessions (Persistent Interactive Terminal)
|
|
126
126
|
|
|
127
|
-
| Tool
|
|
128
|
-
|
|
129
|
-
| `ssh_pty_start`
|
|
130
|
-
| `ssh_pty_write`
|
|
131
|
-
| `ssh_pty_read`
|
|
132
|
-
| `ssh_pty_resize` | Resize PTY window
|
|
133
|
-
| `ssh_pty_close`
|
|
134
|
-
| `ssh_pty_list`
|
|
127
|
+
| Tool | Description |
|
|
128
|
+
|------------------|----------------------------------------------------------------------|
|
|
129
|
+
| `ssh_pty_start` | Start persistent PTY session (for top, htop, tmux, etc.) |
|
|
130
|
+
| `ssh_pty_write` | Send data to PTY (keystrokes, commands) |
|
|
131
|
+
| `ssh_pty_read` | Read PTY output (screen mode: current screen, raw mode: ANSI stream) |
|
|
132
|
+
| `ssh_pty_resize` | Resize PTY window |
|
|
133
|
+
| `ssh_pty_close` | Close PTY session |
|
|
134
|
+
| `ssh_pty_list` | List all PTY sessions |
|
|
135
135
|
|
|
136
136
|
### Port Forwarding
|
|
137
137
|
|
|
138
|
-
| Tool
|
|
139
|
-
|
|
140
|
-
| `ssh_forward_local`
|
|
138
|
+
| Tool | Description |
|
|
139
|
+
|----------------------|--------------------------------------------------------|
|
|
140
|
+
| `ssh_forward_local` | Local port forwarding (ssh -L): access remote services |
|
|
141
141
|
| `ssh_forward_remote` | Remote port forwarding (ssh -R): expose local services |
|
|
142
|
-
| `ssh_forward_close`
|
|
143
|
-
| `ssh_forward_list`
|
|
142
|
+
| `ssh_forward_close` | Close port forwarding |
|
|
143
|
+
| `ssh_forward_list` | List all port forwards |
|
|
144
144
|
|
|
145
145
|
## Usage Examples
|
|
146
146
|
|
|
@@ -161,6 +161,7 @@ ssh_connect(configHost="myserver", configPath="/custom/path/config")
|
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
Supported SSH config features:
|
|
164
|
+
|
|
164
165
|
- `Host` with multiple aliases (e.g., `Host a b c`)
|
|
165
166
|
- `Host *` global defaults inheritance (first `Host *` block only)
|
|
166
167
|
- `HostName`, `User`, `Port`, `IdentityFile`
|
|
@@ -168,11 +169,13 @@ Supported SSH config features:
|
|
|
168
169
|
- Explicit parameters override config values (e.g., `ssh_connect(configHost="x", user="override")`)
|
|
169
170
|
|
|
170
171
|
**Not supported** (skipped):
|
|
172
|
+
|
|
171
173
|
- `Include` directive
|
|
172
174
|
- `Match` blocks (entire block skipped until next `Host`)
|
|
173
175
|
- Wildcard patterns (e.g., `Host *.example.com`)
|
|
174
176
|
|
|
175
177
|
**Behavior notes**:
|
|
178
|
+
|
|
176
179
|
- Multiple `Host *` blocks: only first is used
|
|
177
180
|
- Duplicate Host definitions: `ssh_config_list` shows all, `ssh_connect` uses first
|
|
178
181
|
- IPv6 in ProxyJump: use bracket notation `[2001:db8::1]:22`
|
|
@@ -224,6 +227,10 @@ Perfect for scenarios where you SSH as root but need to run commands as another
|
|
|
224
227
|
// Output: appuser
|
|
225
228
|
```
|
|
226
229
|
|
|
230
|
+
By default, shell profile is loaded to ensure environment variables are available (since `su -c` creates a
|
|
231
|
+
non-interactive shell which doesn't execute rc files). Supports bash (`.bashrc`), zsh (`.zshrc`), and other shells (
|
|
232
|
+
`.profile`). Disable with `loadProfile=false` if not needed.
|
|
233
|
+
|
|
227
234
|
### Interactive Commands (PTY mode)
|
|
228
235
|
|
|
229
236
|
For commands that need a terminal:
|
|
@@ -306,7 +313,8 @@ ssh_sync(..., dryRun=true)
|
|
|
306
313
|
|
|
307
314
|
If rsync is not available on remote or local, it automatically falls back to SFTP.
|
|
308
315
|
|
|
309
|
-
**Note**: rsync mode uses SSH key/agent authentication and disables strict host key checking (
|
|
316
|
+
**Note**: rsync mode uses SSH key/agent authentication and disables strict host key checking (
|
|
317
|
+
`StrictHostKeyChecking=no`) for convenience. If you require host key verification, use SFTP mode instead.
|
|
310
318
|
|
|
311
319
|
### Persistent PTY Sessions (top, tmux, etc.)
|
|
312
320
|
|
|
@@ -345,6 +353,7 @@ ssh_pty_write(ptyId="pty_1_xxx", data="\x02d")
|
|
|
345
353
|
```
|
|
346
354
|
|
|
347
355
|
Common control sequences:
|
|
356
|
+
|
|
348
357
|
- Enter: `\r` or `\n`
|
|
349
358
|
- Ctrl+C: `\x03`
|
|
350
359
|
- Ctrl+D: `\x04`
|
|
@@ -374,25 +383,25 @@ ssh_forward_close(forwardId="fwd_1_xxx")
|
|
|
374
383
|
|
|
375
384
|
### Connection Options
|
|
376
385
|
|
|
377
|
-
| Option
|
|
378
|
-
|
|
379
|
-
| `host`
|
|
380
|
-
| `user`
|
|
381
|
-
| `password`
|
|
382
|
-
| `keyPath`
|
|
383
|
-
| `port`
|
|
384
|
-
| `alias`
|
|
385
|
-
| `env`
|
|
386
|
-
| `keepaliveInterval` | number | 30000
|
|
386
|
+
| Option | Type | Default | Description |
|
|
387
|
+
|---------------------|--------|----------------|--------------------------------|
|
|
388
|
+
| `host` | string | *required* | Server address |
|
|
389
|
+
| `user` | string | *required* | Username |
|
|
390
|
+
| `password` | string | - | Password authentication |
|
|
391
|
+
| `keyPath` | string | - | Path to SSH private key |
|
|
392
|
+
| `port` | number | 22 | SSH port |
|
|
393
|
+
| `alias` | string | auto-generated | Connection alias for reference |
|
|
394
|
+
| `env` | object | - | Environment variables |
|
|
395
|
+
| `keepaliveInterval` | number | 30000 | Keepalive interval in ms |
|
|
387
396
|
|
|
388
397
|
### Exec Options
|
|
389
398
|
|
|
390
|
-
| Option
|
|
391
|
-
|
|
392
|
-
| `timeout` | number
|
|
393
|
-
| `cwd`
|
|
394
|
-
| `env`
|
|
395
|
-
| `pty`
|
|
399
|
+
| Option | Type | Default | Description |
|
|
400
|
+
|-----------|---------|---------|------------------------------------------|
|
|
401
|
+
| `timeout` | number | 30000 | Command timeout in ms |
|
|
402
|
+
| `cwd` | string | - | Working directory |
|
|
403
|
+
| `env` | object | - | Additional environment variables |
|
|
404
|
+
| `pty` | boolean | false | Enable PTY mode for interactive commands |
|
|
396
405
|
|
|
397
406
|
## Project Structure
|
|
398
407
|
|
package/README_zh.md
CHANGED
|
@@ -19,16 +19,16 @@
|
|
|
19
19
|
- **连接管理**:连接池复用、心跳保持、自动重连
|
|
20
20
|
- **会话持久化**:会话信息保存,支持重连
|
|
21
21
|
- **命令执行**:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
- 基础执行(带超时)
|
|
23
|
+
- PTY 模式(用于 `top`、`htop` 等交互式命令)
|
|
24
|
+
- `sudo` 执行
|
|
25
|
+
- `su` 切换用户执行
|
|
26
|
+
- 批量执行
|
|
27
|
+
- 多主机并行执行
|
|
28
28
|
- **持久化 PTY 会话**:用于长时间运行的交互式命令(top、htop、tmux、vim 等)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
- 输出缓冲区,支持轮询读取
|
|
30
|
+
- 发送按键和命令
|
|
31
|
+
- 窗口大小调整
|
|
32
32
|
- **文件操作**:上传、下载、读取、写入、目录列表(通过 SFTP)
|
|
33
33
|
- **智能同步**:目录同步,优先使用 rsync(无 rsync 时自动回退到 SFTP)
|
|
34
34
|
- **环境配置**:LANG、LC_ALL、自定义环境变量
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
|
|
37
37
|
## 兼容客户端
|
|
38
38
|
|
|
39
|
-
| 客户端
|
|
40
|
-
|
|
41
|
-
| Claude Code
|
|
42
|
-
| Claude Desktop | ✅
|
|
43
|
-
| Cursor
|
|
44
|
-
| Windsurf
|
|
45
|
-
| Continue.dev
|
|
46
|
-
| Cline
|
|
47
|
-
| 其他 MCP 兼容客户端
|
|
39
|
+
| 客户端 | 状态 |
|
|
40
|
+
|----------------|----|
|
|
41
|
+
| Claude Code | ✅ |
|
|
42
|
+
| Claude Desktop | ✅ |
|
|
43
|
+
| Cursor | ✅ |
|
|
44
|
+
| Windsurf | ✅ |
|
|
45
|
+
| Continue.dev | ✅ |
|
|
46
|
+
| Cline | ✅ |
|
|
47
|
+
| 其他 MCP 兼容客户端 | ✅ |
|
|
48
48
|
|
|
49
49
|
## 安装
|
|
50
50
|
|
|
@@ -90,57 +90,57 @@ claude mcp add ssh -- node /path/to/mcp-ssh/dist/index.js
|
|
|
90
90
|
|
|
91
91
|
### 连接管理
|
|
92
92
|
|
|
93
|
-
| 工具
|
|
94
|
-
|
|
95
|
-
| `ssh_connect`
|
|
96
|
-
| `ssh_disconnect`
|
|
97
|
-
| `ssh_list_sessions` | 列出活跃会话
|
|
98
|
-
| `ssh_reconnect`
|
|
99
|
-
| `ssh_config_list`
|
|
93
|
+
| 工具 | 描述 |
|
|
94
|
+
|---------------------|-----------------------------|
|
|
95
|
+
| `ssh_connect` | 建立 SSH 连接(支持 ~/.ssh/config) |
|
|
96
|
+
| `ssh_disconnect` | 关闭连接 |
|
|
97
|
+
| `ssh_list_sessions` | 列出活跃会话 |
|
|
98
|
+
| `ssh_reconnect` | 重新连接断开的会话 |
|
|
99
|
+
| `ssh_config_list` | 列出 ~/.ssh/config 中的 Host |
|
|
100
100
|
|
|
101
101
|
### 命令执行
|
|
102
102
|
|
|
103
|
-
| 工具
|
|
104
|
-
|
|
105
|
-
| `ssh_exec`
|
|
106
|
-
| `ssh_exec_as_user`
|
|
107
|
-
| `ssh_exec_sudo`
|
|
108
|
-
| `ssh_exec_batch`
|
|
109
|
-
| `ssh_exec_parallel` | 在多台主机上并行执行命令
|
|
110
|
-
| `ssh_quick_exec`
|
|
103
|
+
| 工具 | 描述 |
|
|
104
|
+
|---------------------|--------------------|
|
|
105
|
+
| `ssh_exec` | 执行命令(支持 PTY 模式) |
|
|
106
|
+
| `ssh_exec_as_user` | 以其他用户身份执行(通过 `su`) |
|
|
107
|
+
| `ssh_exec_sudo` | 使用 `sudo` 执行 |
|
|
108
|
+
| `ssh_exec_batch` | 批量执行多条命令 |
|
|
109
|
+
| `ssh_exec_parallel` | 在多台主机上并行执行命令 |
|
|
110
|
+
| `ssh_quick_exec` | 一次性执行:连接、执行、断开 |
|
|
111
111
|
|
|
112
112
|
### 文件操作
|
|
113
113
|
|
|
114
|
-
| 工具
|
|
115
|
-
|
|
116
|
-
| `ssh_upload`
|
|
117
|
-
| `ssh_download`
|
|
118
|
-
| `ssh_read_file`
|
|
119
|
-
| `ssh_write_file` | 写入内容到远程文件
|
|
120
|
-
| `ssh_list_dir`
|
|
121
|
-
| `ssh_file_info`
|
|
122
|
-
| `ssh_mkdir`
|
|
123
|
-
| `ssh_sync`
|
|
114
|
+
| 工具 | 描述 |
|
|
115
|
+
|------------------|------------------------|
|
|
116
|
+
| `ssh_upload` | 上传本地文件到远程 |
|
|
117
|
+
| `ssh_download` | 从远程下载文件到本地 |
|
|
118
|
+
| `ssh_read_file` | 读取远程文件内容 |
|
|
119
|
+
| `ssh_write_file` | 写入内容到远程文件 |
|
|
120
|
+
| `ssh_list_dir` | 列出远程目录内容 |
|
|
121
|
+
| `ssh_file_info` | 获取文件/目录元数据 |
|
|
122
|
+
| `ssh_mkdir` | 创建远程目录 |
|
|
123
|
+
| `ssh_sync` | 智能同步(优先 rsync,回退 SFTP) |
|
|
124
124
|
|
|
125
125
|
### PTY 会话(持久化交互式终端)
|
|
126
126
|
|
|
127
|
-
| 工具
|
|
128
|
-
|
|
129
|
-
| `ssh_pty_start`
|
|
130
|
-
| `ssh_pty_write`
|
|
131
|
-
| `ssh_pty_read`
|
|
132
|
-
| `ssh_pty_resize` | 调整 PTY 窗口大小
|
|
133
|
-
| `ssh_pty_close`
|
|
134
|
-
| `ssh_pty_list`
|
|
127
|
+
| 工具 | 描述 |
|
|
128
|
+
|------------------|-----------------------------------------|
|
|
129
|
+
| `ssh_pty_start` | 启动持久化 PTY 会话(用于 top、htop、tmux 等) |
|
|
130
|
+
| `ssh_pty_write` | 向 PTY 发送数据(按键、命令) |
|
|
131
|
+
| `ssh_pty_read` | 读取 PTY 输出(screen 模式:当前屏幕,raw 模式:ANSI 流) |
|
|
132
|
+
| `ssh_pty_resize` | 调整 PTY 窗口大小 |
|
|
133
|
+
| `ssh_pty_close` | 关闭 PTY 会话 |
|
|
134
|
+
| `ssh_pty_list` | 列出所有 PTY 会话 |
|
|
135
135
|
|
|
136
136
|
### 端口转发
|
|
137
137
|
|
|
138
|
-
| 工具
|
|
139
|
-
|
|
140
|
-
| `ssh_forward_local`
|
|
138
|
+
| 工具 | 描述 |
|
|
139
|
+
|----------------------|-----------------------|
|
|
140
|
+
| `ssh_forward_local` | 本地端口转发(ssh -L):访问远程服务 |
|
|
141
141
|
| `ssh_forward_remote` | 远程端口转发(ssh -R):暴露本地服务 |
|
|
142
|
-
| `ssh_forward_close`
|
|
143
|
-
| `ssh_forward_list`
|
|
142
|
+
| `ssh_forward_close` | 关闭端口转发 |
|
|
143
|
+
| `ssh_forward_list` | 列出所有端口转发 |
|
|
144
144
|
|
|
145
145
|
## 使用示例
|
|
146
146
|
|
|
@@ -161,6 +161,7 @@ ssh_connect(configHost="myserver", configPath="/custom/path/config")
|
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
支持的 SSH config 特性:
|
|
164
|
+
|
|
164
165
|
- `Host` 多别名(如 `Host a b c`)
|
|
165
166
|
- `Host *` 全局默认继承(仅第一个 `Host *` 块)
|
|
166
167
|
- `HostName`、`User`、`Port`、`IdentityFile`
|
|
@@ -168,11 +169,13 @@ ssh_connect(configHost="myserver", configPath="/custom/path/config")
|
|
|
168
169
|
- 显式参数优先于 config 值(如 `ssh_connect(configHost="x", user="覆盖值")`)
|
|
169
170
|
|
|
170
171
|
**不支持**(跳过):
|
|
172
|
+
|
|
171
173
|
- `Include` 指令
|
|
172
174
|
- `Match` 块(整个块跳过直到下一个 `Host`)
|
|
173
175
|
- 通配符模式(如 `Host *.example.com`)
|
|
174
176
|
|
|
175
177
|
**行为说明**:
|
|
178
|
+
|
|
176
179
|
- 多个 `Host *` 块:仅使用第一个
|
|
177
180
|
- 重复的 Host 定义:`ssh_config_list` 显示全部,`ssh_connect` 使用第一个
|
|
178
181
|
- ProxyJump 中的 IPv6:使用方括号格式 `[2001:db8::1]:22`
|
|
@@ -224,6 +227,9 @@ ssh_connect(
|
|
|
224
227
|
// 输出: appuser
|
|
225
228
|
```
|
|
226
229
|
|
|
230
|
+
默认加载 shell 配置以确保环境变量可用(`su -c` 创建非交互式 shell,不会自动执行 rc 文件)。支持 bash(`.bashrc`)、zsh(`.zshrc`
|
|
231
|
+
)及其他 shell(`.profile`)。如不需要可设置 `loadProfile=false`。
|
|
232
|
+
|
|
227
233
|
### 交互式命令(PTY 模式)
|
|
228
234
|
|
|
229
235
|
用于需要终端的命令:
|
|
@@ -306,7 +312,8 @@ ssh_sync(..., dryRun=true)
|
|
|
306
312
|
|
|
307
313
|
如果远程或本地没有 rsync,会自动回退到 SFTP。
|
|
308
314
|
|
|
309
|
-
**注意**:rsync 模式使用 SSH 密钥/代理认证,并禁用严格主机密钥检查(`StrictHostKeyChecking=no`)以方便使用。如需主机密钥验证,请使用
|
|
315
|
+
**注意**:rsync 模式使用 SSH 密钥/代理认证,并禁用严格主机密钥检查(`StrictHostKeyChecking=no`)以方便使用。如需主机密钥验证,请使用
|
|
316
|
+
SFTP 模式。
|
|
310
317
|
|
|
311
318
|
### 持久化 PTY 会话(top、tmux 等)
|
|
312
319
|
|
|
@@ -345,6 +352,7 @@ ssh_pty_write(ptyId="pty_1_xxx", data="\x02d")
|
|
|
345
352
|
```
|
|
346
353
|
|
|
347
354
|
常用控制序列:
|
|
355
|
+
|
|
348
356
|
- 回车: `\r` 或 `\n`
|
|
349
357
|
- Ctrl+C: `\x03`
|
|
350
358
|
- Ctrl+D: `\x04`
|
|
@@ -374,25 +382,25 @@ ssh_forward_close(forwardId="fwd_1_xxx")
|
|
|
374
382
|
|
|
375
383
|
### 连接选项
|
|
376
384
|
|
|
377
|
-
| 选项
|
|
378
|
-
|
|
379
|
-
| `host`
|
|
380
|
-
| `user`
|
|
381
|
-
| `password`
|
|
382
|
-
| `keyPath`
|
|
383
|
-
| `port`
|
|
384
|
-
| `alias`
|
|
385
|
-
| `env`
|
|
386
|
-
| `keepaliveInterval` | number | 30000 | 心跳间隔(毫秒)
|
|
385
|
+
| 选项 | 类型 | 默认值 | 描述 |
|
|
386
|
+
|---------------------|--------|-------|-------------|
|
|
387
|
+
| `host` | string | *必需* | 服务器地址 |
|
|
388
|
+
| `user` | string | *必需* | 用户名 |
|
|
389
|
+
| `password` | string | - | 密码认证 |
|
|
390
|
+
| `keyPath` | string | - | SSH 私钥路径 |
|
|
391
|
+
| `port` | number | 22 | SSH 端口 |
|
|
392
|
+
| `alias` | string | 自动生成 | 连接别名,用于后续引用 |
|
|
393
|
+
| `env` | object | - | 环境变量 |
|
|
394
|
+
| `keepaliveInterval` | number | 30000 | 心跳间隔(毫秒) |
|
|
387
395
|
|
|
388
396
|
### 执行选项
|
|
389
397
|
|
|
390
|
-
| 选项
|
|
391
|
-
|
|
392
|
-
| `timeout` | number
|
|
393
|
-
| `cwd`
|
|
394
|
-
| `env`
|
|
395
|
-
| `pty`
|
|
398
|
+
| 选项 | 类型 | 默认值 | 描述 |
|
|
399
|
+
|-----------|---------|-------|--------------------|
|
|
400
|
+
| `timeout` | number | 30000 | 命令超时(毫秒) |
|
|
401
|
+
| `cwd` | string | - | 工作目录 |
|
|
402
|
+
| `env` | object | - | 额外环境变量 |
|
|
403
|
+
| `pty` | boolean | false | 启用 PTY 模式(用于交互式命令) |
|
|
396
404
|
|
|
397
405
|
## 项目结构
|
|
398
406
|
|
package/dist/file-ops.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SSH File Operations - 文件操作
|
|
3
3
|
*/
|
|
4
|
+
import { execSync } from 'child_process';
|
|
4
5
|
import * as fs from 'fs';
|
|
5
6
|
import * as path from 'path';
|
|
6
|
-
import { execSync } from 'child_process';
|
|
7
7
|
import { sessionManager } from './session-manager.js';
|
|
8
8
|
/**
|
|
9
9
|
* 上传文件
|
|
@@ -20,12 +20,14 @@ export async function uploadFile(alias, localPath, remotePath, onProgress) {
|
|
|
20
20
|
const writeStream = sftp.createWriteStream(remotePath);
|
|
21
21
|
let settled = false;
|
|
22
22
|
const cleanup = (err) => {
|
|
23
|
-
if (settled)
|
|
23
|
+
if (settled) {
|
|
24
24
|
return;
|
|
25
|
+
}
|
|
25
26
|
settled = true;
|
|
26
27
|
sftp.end();
|
|
27
|
-
if (err)
|
|
28
|
+
if (err) {
|
|
28
29
|
reject(err);
|
|
30
|
+
}
|
|
29
31
|
};
|
|
30
32
|
let transferred = 0;
|
|
31
33
|
readStream.on('data', (chunk) => {
|
|
@@ -58,10 +60,12 @@ export async function downloadFile(alias, remotePath, localPath, onProgress) {
|
|
|
58
60
|
// 获取远程文件大小
|
|
59
61
|
const stats = await new Promise((resolve, reject) => {
|
|
60
62
|
sftp.stat(remotePath, (err, stats) => {
|
|
61
|
-
if (err)
|
|
63
|
+
if (err) {
|
|
62
64
|
reject(err);
|
|
63
|
-
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
64
67
|
resolve(stats);
|
|
68
|
+
}
|
|
65
69
|
});
|
|
66
70
|
});
|
|
67
71
|
const totalSize = stats.size;
|
|
@@ -75,12 +79,14 @@ export async function downloadFile(alias, remotePath, localPath, onProgress) {
|
|
|
75
79
|
const writeStream = fs.createWriteStream(localPath);
|
|
76
80
|
let settled = false;
|
|
77
81
|
const cleanup = (err) => {
|
|
78
|
-
if (settled)
|
|
82
|
+
if (settled) {
|
|
79
83
|
return;
|
|
84
|
+
}
|
|
80
85
|
settled = true;
|
|
81
86
|
sftp.end();
|
|
82
|
-
if (err)
|
|
87
|
+
if (err) {
|
|
83
88
|
reject(err);
|
|
89
|
+
}
|
|
84
90
|
};
|
|
85
91
|
let transferred = 0;
|
|
86
92
|
readStream.on('data', (chunk) => {
|
|
@@ -108,16 +114,17 @@ export async function downloadFile(alias, remotePath, localPath, onProgress) {
|
|
|
108
114
|
/**
|
|
109
115
|
* 读取远程文件内容
|
|
110
116
|
*/
|
|
111
|
-
export async function readFile(alias, remotePath, maxBytes = 1024 * 1024
|
|
112
|
-
) {
|
|
117
|
+
export async function readFile(alias, remotePath, maxBytes = 1024 * 1024) {
|
|
113
118
|
const sftp = await sessionManager.getSftp(alias);
|
|
114
119
|
// 获取文件大小
|
|
115
120
|
const stats = await new Promise((resolve, reject) => {
|
|
116
121
|
sftp.stat(remotePath, (err, stats) => {
|
|
117
|
-
if (err)
|
|
122
|
+
if (err) {
|
|
118
123
|
reject(err);
|
|
119
|
-
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
120
126
|
resolve(stats);
|
|
127
|
+
}
|
|
121
128
|
});
|
|
122
129
|
});
|
|
123
130
|
const actualSize = stats.size;
|
|
@@ -264,10 +271,12 @@ export async function mkdir(alias, remotePath, recursive = false) {
|
|
|
264
271
|
return new Promise((resolve, reject) => {
|
|
265
272
|
sftp.mkdir(remotePath, (err) => {
|
|
266
273
|
sftp.end();
|
|
267
|
-
if (err)
|
|
274
|
+
if (err) {
|
|
268
275
|
reject(err);
|
|
269
|
-
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
270
278
|
resolve(true);
|
|
279
|
+
}
|
|
271
280
|
});
|
|
272
281
|
});
|
|
273
282
|
}
|
|
@@ -279,10 +288,12 @@ export async function removeFile(alias, remotePath) {
|
|
|
279
288
|
return new Promise((resolve, reject) => {
|
|
280
289
|
sftp.unlink(remotePath, (err) => {
|
|
281
290
|
sftp.end();
|
|
282
|
-
if (err)
|
|
291
|
+
if (err) {
|
|
283
292
|
reject(err);
|
|
284
|
-
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
285
295
|
resolve(true);
|
|
296
|
+
}
|
|
286
297
|
});
|
|
287
298
|
});
|
|
288
299
|
}
|
|
@@ -323,7 +334,7 @@ export async function syncFiles(alias, localPath, remotePath, direction, options
|
|
|
323
334
|
* 转义 shell 路径参数
|
|
324
335
|
*/
|
|
325
336
|
function escapeShellPath(p) {
|
|
326
|
-
return `'${p.replace(/'/g,
|
|
337
|
+
return `'${p.replace(/'/g, '\'\\\'\'')}'`;
|
|
327
338
|
}
|
|
328
339
|
/**
|
|
329
340
|
* 使用 rsync 同步文件
|
|
@@ -336,7 +347,8 @@ async function syncWithRsync(alias, localPath, remotePath, direction, options) {
|
|
|
336
347
|
execSync('which rsync', { stdio: 'pipe' });
|
|
337
348
|
hasLocalRsync = true;
|
|
338
349
|
}
|
|
339
|
-
catch {
|
|
350
|
+
catch {
|
|
351
|
+
}
|
|
340
352
|
if (!hasLocalRsync) {
|
|
341
353
|
// 本地没有 rsync,回退到 SFTP
|
|
342
354
|
return syncWithSftp(alias, localPath, remotePath, direction, options);
|
|
@@ -374,13 +386,16 @@ async function syncWithRsync(alias, localPath, remotePath, direction, options) {
|
|
|
374
386
|
const result = execSync(rsyncCmd, {
|
|
375
387
|
encoding: 'utf-8',
|
|
376
388
|
timeout: 600000, // 10 分钟超时
|
|
377
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
389
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
378
390
|
});
|
|
379
391
|
// 解析 rsync 输出统计文件数
|
|
380
392
|
const lines = result.split('\n');
|
|
381
393
|
let filesTransferred = 0;
|
|
382
394
|
for (const line of lines) {
|
|
383
|
-
if (line.trim() &&
|
|
395
|
+
if (line.trim() &&
|
|
396
|
+
!line.startsWith('sending') &&
|
|
397
|
+
!line.startsWith('receiving') &&
|
|
398
|
+
!line.startsWith('total')) {
|
|
384
399
|
filesTransferred++;
|
|
385
400
|
}
|
|
386
401
|
}
|
|
@@ -409,7 +424,8 @@ async function syncWithSftp(alias, localPath, remotePath, direction, options) {
|
|
|
409
424
|
return {
|
|
410
425
|
success: true,
|
|
411
426
|
method: 'sftp',
|
|
412
|
-
output: 'Dry run mode: would transfer files via SFTP' +
|
|
427
|
+
output: 'Dry run mode: would transfer files via SFTP' +
|
|
428
|
+
(warnings.length ? `. Warning: ${warnings.join('; ')}` : ''),
|
|
413
429
|
};
|
|
414
430
|
}
|
|
415
431
|
try {
|