ssh-agent-workspace 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.
Files changed (188) hide show
  1. package/README.md +319 -0
  2. package/dist/__tests__/SSHManager.test.d.ts +2 -0
  3. package/dist/__tests__/SSHManager.test.d.ts.map +1 -0
  4. package/dist/__tests__/SSHManager.test.js +134 -0
  5. package/dist/__tests__/SSHManager.test.js.map +1 -0
  6. package/dist/__tests__/SessionManager.test.d.ts +2 -0
  7. package/dist/__tests__/SessionManager.test.d.ts.map +1 -0
  8. package/dist/__tests__/SessionManager.test.js +141 -0
  9. package/dist/__tests__/SessionManager.test.js.map +1 -0
  10. package/dist/__tests__/StorageManager.test.d.ts +2 -0
  11. package/dist/__tests__/StorageManager.test.d.ts.map +1 -0
  12. package/dist/__tests__/StorageManager.test.js +171 -0
  13. package/dist/__tests__/StorageManager.test.js.map +1 -0
  14. package/dist/__tests__/ansi.test.d.ts +2 -0
  15. package/dist/__tests__/ansi.test.d.ts.map +1 -0
  16. package/dist/__tests__/ansi.test.js +41 -0
  17. package/dist/__tests__/ansi.test.js.map +1 -0
  18. package/dist/__tests__/security.test.d.ts +2 -0
  19. package/dist/__tests__/security.test.d.ts.map +1 -0
  20. package/dist/__tests__/security.test.js +87 -0
  21. package/dist/__tests__/security.test.js.map +1 -0
  22. package/dist/__tests__/validation.test.d.ts +2 -0
  23. package/dist/__tests__/validation.test.d.ts.map +1 -0
  24. package/dist/__tests__/validation.test.js +23 -0
  25. package/dist/__tests__/validation.test.js.map +1 -0
  26. package/dist/core/HostSecurityManager.d.ts +25 -0
  27. package/dist/core/HostSecurityManager.d.ts.map +1 -0
  28. package/dist/core/HostSecurityManager.js +76 -0
  29. package/dist/core/HostSecurityManager.js.map +1 -0
  30. package/dist/core/SSHManager.d.ts +48 -0
  31. package/dist/core/SSHManager.d.ts.map +1 -0
  32. package/dist/core/SSHManager.js +288 -0
  33. package/dist/core/SSHManager.js.map +1 -0
  34. package/dist/core/SessionManager.d.ts +15 -0
  35. package/dist/core/SessionManager.d.ts.map +1 -0
  36. package/dist/core/SessionManager.js +96 -0
  37. package/dist/core/SessionManager.js.map +1 -0
  38. package/dist/core/StorageManager.d.ts +27 -0
  39. package/dist/core/StorageManager.d.ts.map +1 -0
  40. package/dist/core/StorageManager.js +87 -0
  41. package/dist/core/StorageManager.js.map +1 -0
  42. package/dist/core/TmuxManager.d.ts +21 -0
  43. package/dist/core/TmuxManager.d.ts.map +1 -0
  44. package/dist/core/TmuxManager.js +110 -0
  45. package/dist/core/TmuxManager.js.map +1 -0
  46. package/dist/core/ToolConfigManager.d.ts +15 -0
  47. package/dist/core/ToolConfigManager.d.ts.map +1 -0
  48. package/dist/core/ToolConfigManager.js +57 -0
  49. package/dist/core/ToolConfigManager.js.map +1 -0
  50. package/dist/index.d.ts +3 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +169 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/server.d.ts +44 -0
  55. package/dist/server.d.ts.map +1 -0
  56. package/dist/server.js +152 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/tools/backup.d.ts +74 -0
  59. package/dist/tools/backup.d.ts.map +1 -0
  60. package/dist/tools/backup.js +152 -0
  61. package/dist/tools/backup.js.map +1 -0
  62. package/dist/tools/connect.d.ts +46 -0
  63. package/dist/tools/connect.d.ts.map +1 -0
  64. package/dist/tools/connect.js +235 -0
  65. package/dist/tools/connect.js.map +1 -0
  66. package/dist/tools/connection_status.d.ts +39 -0
  67. package/dist/tools/connection_status.d.ts.map +1 -0
  68. package/dist/tools/connection_status.js +67 -0
  69. package/dist/tools/connection_status.js.map +1 -0
  70. package/dist/tools/db_query.d.ts +103 -0
  71. package/dist/tools/db_query.d.ts.map +1 -0
  72. package/dist/tools/db_query.js +194 -0
  73. package/dist/tools/db_query.js.map +1 -0
  74. package/dist/tools/deploy.d.ts +127 -0
  75. package/dist/tools/deploy.d.ts.map +1 -0
  76. package/dist/tools/deploy.js +201 -0
  77. package/dist/tools/deploy.js.map +1 -0
  78. package/dist/tools/disconnect.d.ts +46 -0
  79. package/dist/tools/disconnect.d.ts.map +1 -0
  80. package/dist/tools/disconnect.js +77 -0
  81. package/dist/tools/disconnect.js.map +1 -0
  82. package/dist/tools/exec.d.ts +69 -0
  83. package/dist/tools/exec.d.ts.map +1 -0
  84. package/dist/tools/exec.js +188 -0
  85. package/dist/tools/exec.js.map +1 -0
  86. package/dist/tools/group_exec.d.ts +80 -0
  87. package/dist/tools/group_exec.d.ts.map +1 -0
  88. package/dist/tools/group_exec.js +150 -0
  89. package/dist/tools/group_exec.js.map +1 -0
  90. package/dist/tools/health_check.d.ts +38 -0
  91. package/dist/tools/health_check.d.ts.map +1 -0
  92. package/dist/tools/health_check.js +161 -0
  93. package/dist/tools/health_check.js.map +1 -0
  94. package/dist/tools/host_security.d.ts +52 -0
  95. package/dist/tools/host_security.d.ts.map +1 -0
  96. package/dist/tools/host_security.js +127 -0
  97. package/dist/tools/host_security.js.map +1 -0
  98. package/dist/tools/index.d.ts +24 -0
  99. package/dist/tools/index.d.ts.map +1 -0
  100. package/dist/tools/index.js +24 -0
  101. package/dist/tools/index.js.map +1 -0
  102. package/dist/tools/interrupt.d.ts +47 -0
  103. package/dist/tools/interrupt.d.ts.map +1 -0
  104. package/dist/tools/interrupt.js +77 -0
  105. package/dist/tools/interrupt.js.map +1 -0
  106. package/dist/tools/list_hosts.d.ts +15 -0
  107. package/dist/tools/list_hosts.d.ts.map +1 -0
  108. package/dist/tools/list_hosts.js +18 -0
  109. package/dist/tools/list_hosts.js.map +1 -0
  110. package/dist/tools/list_sessions.d.ts +16 -0
  111. package/dist/tools/list_sessions.d.ts.map +1 -0
  112. package/dist/tools/list_sessions.js +20 -0
  113. package/dist/tools/list_sessions.js.map +1 -0
  114. package/dist/tools/read_output.d.ts +46 -0
  115. package/dist/tools/read_output.d.ts.map +1 -0
  116. package/dist/tools/read_output.js +73 -0
  117. package/dist/tools/read_output.js.map +1 -0
  118. package/dist/tools/reconnect_to_tmux.d.ts +53 -0
  119. package/dist/tools/reconnect_to_tmux.d.ts.map +1 -0
  120. package/dist/tools/reconnect_to_tmux.js +199 -0
  121. package/dist/tools/reconnect_to_tmux.js.map +1 -0
  122. package/dist/tools/send_input.d.ts +45 -0
  123. package/dist/tools/send_input.d.ts.map +1 -0
  124. package/dist/tools/send_input.js +83 -0
  125. package/dist/tools/send_input.js.map +1 -0
  126. package/dist/tools/sftp_download.d.ts +52 -0
  127. package/dist/tools/sftp_download.d.ts.map +1 -0
  128. package/dist/tools/sftp_download.js +90 -0
  129. package/dist/tools/sftp_download.js.map +1 -0
  130. package/dist/tools/sftp_list.d.ts +46 -0
  131. package/dist/tools/sftp_list.d.ts.map +1 -0
  132. package/dist/tools/sftp_list.js +93 -0
  133. package/dist/tools/sftp_list.js.map +1 -0
  134. package/dist/tools/sftp_upload.d.ts +52 -0
  135. package/dist/tools/sftp_upload.d.ts.map +1 -0
  136. package/dist/tools/sftp_upload.js +98 -0
  137. package/dist/tools/sftp_upload.js.map +1 -0
  138. package/dist/tools/ssh_tunnel.d.ts +116 -0
  139. package/dist/tools/ssh_tunnel.d.ts.map +1 -0
  140. package/dist/tools/ssh_tunnel.js +282 -0
  141. package/dist/tools/ssh_tunnel.js.map +1 -0
  142. package/dist/tools/sync.d.ts +71 -0
  143. package/dist/tools/sync.d.ts.map +1 -0
  144. package/dist/tools/sync.js +310 -0
  145. package/dist/tools/sync.js.map +1 -0
  146. package/dist/tools/tail_log.d.ts +61 -0
  147. package/dist/tools/tail_log.d.ts.map +1 -0
  148. package/dist/tools/tail_log.js +111 -0
  149. package/dist/tools/tail_log.js.map +1 -0
  150. package/dist/tools/tools_config.d.ts +34 -0
  151. package/dist/tools/tools_config.d.ts.map +1 -0
  152. package/dist/tools/tools_config.js +98 -0
  153. package/dist/tools/tools_config.js.map +1 -0
  154. package/dist/types/index.d.ts +21 -0
  155. package/dist/types/index.d.ts.map +1 -0
  156. package/dist/types/index.js +2 -0
  157. package/dist/types/index.js.map +1 -0
  158. package/dist/utils/ansi.d.ts +2 -0
  159. package/dist/utils/ansi.d.ts.map +1 -0
  160. package/dist/utils/ansi.js +7 -0
  161. package/dist/utils/ansi.js.map +1 -0
  162. package/dist/utils/logger.d.ts +3 -0
  163. package/dist/utils/logger.d.ts.map +1 -0
  164. package/dist/utils/logger.js +8 -0
  165. package/dist/utils/logger.js.map +1 -0
  166. package/dist/utils/security.d.ts +7 -0
  167. package/dist/utils/security.d.ts.map +1 -0
  168. package/dist/utils/security.js +58 -0
  169. package/dist/utils/security.js.map +1 -0
  170. package/dist/utils/ssh.d.ts +4 -0
  171. package/dist/utils/ssh.d.ts.map +1 -0
  172. package/dist/utils/ssh.js +29 -0
  173. package/dist/utils/ssh.js.map +1 -0
  174. package/dist/utils/sshConfig.d.ts +4 -0
  175. package/dist/utils/sshConfig.d.ts.map +1 -0
  176. package/dist/utils/sshConfig.js +85 -0
  177. package/dist/utils/sshConfig.js.map +1 -0
  178. package/dist/utils/validation.d.ts +4 -0
  179. package/dist/utils/validation.d.ts.map +1 -0
  180. package/dist/utils/validation.js +12 -0
  181. package/dist/utils/validation.js.map +1 -0
  182. package/docs/SECURITY.md +213 -0
  183. package/docs/TOOLS.md +425 -0
  184. package/keygen.bat +325 -0
  185. package/package.json +48 -0
  186. package/test_check.bat +9 -0
  187. package/test_delayed.bat +12 -0
  188. package/vitest.config.ts +14 -0
package/keygen.bat ADDED
@@ -0,0 +1,325 @@
1
+ @echo off
2
+ setlocal EnableDelayedExpansion
3
+ title SSH MCP Setup
4
+ color 07
5
+ cls
6
+
7
+ :: ================================================
8
+ :: SSH MCP Setup (Windows)
9
+ :: Generates an ed25519 key and adds an SSH alias
10
+ :: ================================================
11
+
12
+ :: ------------------------------------------------
13
+ :: [0/4] Locate ssh-keygen
14
+ :: ------------------------------------------------
15
+ set "SSHKEYGEN="
16
+
17
+ for %%I in (ssh-keygen.exe) do (
18
+ if not "%%~$PATH:I"=="" set "SSHKEYGEN=%%~$PATH:I"
19
+ )
20
+
21
+ if not defined SSHKEYGEN (
22
+ if exist "%SystemRoot%\System32\OpenSSH\ssh-keygen.exe" (
23
+ set "SSHKEYGEN=%SystemRoot%\System32\OpenSSH\ssh-keygen.exe"
24
+ )
25
+ )
26
+
27
+ if not defined SSHKEYGEN (
28
+ echo.
29
+ echo [X] ssh-keygen not found.
30
+ echo.
31
+ echo Please install one of:
32
+ echo - OpenSSH Client ^(Windows Settings ^> Optional Features^)
33
+ echo - Git for Windows ^(https://git-scm.com^)
34
+ echo.
35
+ pause
36
+ exit /b 1
37
+ )
38
+
39
+ echo [OK] ssh-keygen: !SSHKEYGEN!
40
+ echo.
41
+
42
+ :: ------------------------------------------------
43
+ :: Paths
44
+ :: ------------------------------------------------
45
+ set "SSHDIR=%USERPROFILE%\.ssh"
46
+ set "PRIVKEY=%SSHDIR%\id_ed25519"
47
+ set "PUBKEY=%SSHDIR%\id_ed25519.pub"
48
+ set "CONFIG=%SSHDIR%\config"
49
+
50
+ echo ================================================
51
+ echo SSH MCP Setup ^(Windows^)
52
+ echo ================================================
53
+ echo.
54
+ echo This script will:
55
+ echo 1. Create ~/.ssh folder
56
+ echo 2. Generate ed25519 SSH key ^(if missing^)
57
+ echo 3. Add host alias to SSH config
58
+ echo.
59
+ echo Press Ctrl+C to cancel, or
60
+ pause
61
+ echo.
62
+ echo ------------------------------------------------
63
+
64
+ :: ------------------------------------------------
65
+ :: Prompt: Alias
66
+ :: ------------------------------------------------
67
+ :ask_alias
68
+ set "ALIAS="
69
+ set /p "ALIAS= Alias (e.g. prod) : "
70
+
71
+ :: Trim leading/trailing spaces via a for loop trick
72
+ for /f "tokens=* delims= " %%A in ("!ALIAS!") do set "ALIAS=%%A"
73
+
74
+ if "!ALIAS!"=="" (
75
+ echo [!] Alias cannot be empty.
76
+ goto :ask_alias
77
+ )
78
+
79
+ :: Reject spaces inside alias
80
+ echo !ALIAS!| find " " >nul 2>nul && (
81
+ echo [!] Alias cannot contain spaces.
82
+ goto :ask_alias
83
+ )
84
+
85
+ :: Reject characters that break SSH config or findstr: * ? \ / " < > |
86
+ echo !ALIAS!| findstr /r "[*?/\\\""<>|]" >nul 2>nul && (
87
+ echo [!] Alias contains invalid characters.
88
+ goto :ask_alias
89
+ )
90
+
91
+ :: ------------------------------------------------
92
+ :: Prompt: Host
93
+ :: ------------------------------------------------
94
+ :ask_host
95
+ set "HOST="
96
+ set /p "HOST= IP / Hostname : "
97
+ for /f "tokens=* delims= " %%A in ("!HOST!") do set "HOST=%%A"
98
+
99
+ if "!HOST!"=="" (
100
+ echo [!] Host cannot be empty.
101
+ goto :ask_host
102
+ )
103
+
104
+ echo !HOST!| find " " >nul 2>nul && (
105
+ echo [!] Hostname cannot contain spaces.
106
+ goto :ask_host
107
+ )
108
+
109
+ :: ------------------------------------------------
110
+ :: Prompt: User
111
+ :: ------------------------------------------------
112
+ :ask_user
113
+ set "SSHUSER="
114
+ set /p "SSHUSER= SSH Username : "
115
+ for /f "tokens=* delims= " %%A in ("!SSHUSER!") do set "SSHUSER=%%A"
116
+
117
+ if "!SSHUSER!"=="" (
118
+ echo [!] Username cannot be empty.
119
+ goto :ask_user
120
+ )
121
+
122
+ echo !SSHUSER!| find " " >nul 2>nul && (
123
+ echo [!] Username cannot contain spaces.
124
+ goto :ask_user
125
+ )
126
+
127
+ :: ------------------------------------------------
128
+ :: Prompt: Port (validated via PowerShell)
129
+ :: ------------------------------------------------
130
+ :ask_port
131
+ set "PORT="
132
+ set /p "PORT= SSH Port [default 22] : "
133
+ for /f "tokens=* delims= " %%A in ("!PORT!") do set "PORT=%%A"
134
+
135
+ if "!PORT!"=="" set "PORT=22"
136
+
137
+ :: Use PowerShell to safely validate numeric range (avoids batch integer parsing bugs)
138
+ powershell -NoProfile -Command ^
139
+ "param([string]$p) $n=0; if(-not[int]::TryParse($p,[ref]$n)-or $n -lt 1 -or $n -gt 65535){exit 1}" ^
140
+ -p "!PORT!" >nul 2>nul
141
+
142
+ if errorlevel 1 (
143
+ echo [!] Invalid port. Must be a number between 1 and 65535. Using 22.
144
+ set "PORT=22"
145
+ )
146
+
147
+ echo.
148
+
149
+ :: ------------------------------------------------
150
+ :: [1/4] Prepare .ssh folder
151
+ :: ------------------------------------------------
152
+ echo [1/4] Preparing .ssh folder ...
153
+ if not exist "%SSHDIR%\" (
154
+ mkdir "%SSHDIR%"
155
+ if errorlevel 1 (
156
+ echo [X] Failed to create: %SSHDIR%
157
+ pause & exit /b 1
158
+ )
159
+ echo [+] Created: %SSHDIR%
160
+ ) else (
161
+ echo [=] Already exists: %SSHDIR%
162
+ )
163
+ echo.
164
+
165
+ :: ------------------------------------------------
166
+ :: [2/4] Generate SSH key pair
167
+ :: ------------------------------------------------
168
+ echo [2/4] Generating SSH key pair ...
169
+ if not exist "%PRIVKEY%" (
170
+ if exist "%PUBKEY%" (
171
+ echo [W] Public key found but private key is missing.
172
+ echo Delete %PUBKEY% manually and re-run this script.
173
+ pause & exit /b 1
174
+ )
175
+ "!SSHKEYGEN!" -t ed25519 -f "%PRIVKEY%" -N "" -C "mcp-!ALIAS!"
176
+ if errorlevel 1 (
177
+ echo [X] ssh-keygen failed.
178
+ pause & exit /b 1
179
+ )
180
+ echo [+] Key saved: %PRIVKEY%
181
+ ) else (
182
+ if not exist "%PUBKEY%" (
183
+ echo [W] Private key found but public key is missing.
184
+ echo Delete %PRIVKEY% manually and re-run this script.
185
+ pause & exit /b 1
186
+ )
187
+ echo [=] Key already exists, skipping.
188
+ echo %PRIVKEY%
189
+ )
190
+ echo.
191
+
192
+ :: ------------------------------------------------
193
+ :: [3/4] Update SSH config
194
+ :: ------------------------------------------------
195
+ echo [3/4] Updating SSH config ...
196
+
197
+ if not exist "%CONFIG%" (
198
+ type nul > "%CONFIG%"
199
+ echo [+] Config file created: %CONFIG%
200
+ )
201
+
202
+ :: Use word-boundary match: "^Host <alias>$" to avoid partial matches
203
+ :: (e.g. alias "prod" must not match "production")
204
+ findstr /r /c:"^Host !ALIAS!$" "%CONFIG%" >nul 2>nul
205
+ if not errorlevel 1 (
206
+ echo [=] Alias [!ALIAS!] already exists in config, skipped.
207
+ goto :config_done
208
+ )
209
+
210
+ :: Write each line individually — parenthesised blocks are unreliable
211
+ :: with delayed expansion enabled on some Windows versions.
212
+ :: NOTE: no space before ">>" so trailing spaces are not written to the file.
213
+ >>"%CONFIG%" echo.
214
+ >>"%CONFIG%" echo Host !ALIAS!
215
+ >>"%CONFIG%" echo HostName !HOST!
216
+ >>"%CONFIG%" echo User !SSHUSER!
217
+ >>"%CONFIG%" echo Port !PORT!
218
+ >>"%CONFIG%" echo IdentityFile !PRIVKEY!
219
+ >>"%CONFIG%" echo ServerAliveInterval 60
220
+ >>"%CONFIG%" echo ServerAliveCountMax 3
221
+ >>"%CONFIG%" echo StrictHostKeyChecking accept-new
222
+
223
+ if errorlevel 1 (
224
+ echo [X] Failed to write to: %CONFIG%
225
+ pause & exit /b 1
226
+ )
227
+ echo [+] Alias [!ALIAS!] added to config.
228
+
229
+ :config_done
230
+ echo.
231
+
232
+ :: ------------------------------------------------
233
+ :: [4/4] Key permission hint
234
+ :: ------------------------------------------------
235
+ echo [4/4] Key permissions ...
236
+ echo [i] Windows OpenSSH enforces strict ACLs.
237
+ echo If ssh fails with "Bad permissions", run:
238
+ echo.
239
+ echo icacls "%PRIVKEY%" /inheritance:r /grant:r "%USERNAME%:R"
240
+ echo.
241
+
242
+ :: ------------------------------------------------
243
+ :: Summary
244
+ :: ------------------------------------------------
245
+ echo ================================================
246
+ echo Summary
247
+ echo ================================================
248
+ echo.
249
+ echo Alias : !ALIAS!
250
+ echo Host : !HOST!
251
+ echo User : !SSHUSER!
252
+ echo Port : !PORT!
253
+ echo Private Key : !PRIVKEY!
254
+ echo Config : !CONFIG!
255
+ echo.
256
+ echo Test your connection:
257
+ echo ssh !ALIAS!
258
+ echo.
259
+
260
+ :: ------------------------------------------------
261
+ :: Read public key — write to temp file first
262
+ :: so we never echo a raw key string directly
263
+ :: (avoids issues with = signs and long lines)
264
+ :: ------------------------------------------------
265
+ set "PUBKEYLINE="
266
+ if exist "%PUBKEY%" (
267
+ for /f "usebackq delims=" %%i in ("%PUBKEY%") do (
268
+ if "!PUBKEYLINE!"=="" set "PUBKEYLINE=%%i"
269
+ )
270
+ )
271
+
272
+ if "!PUBKEYLINE!"=="" (
273
+ echo [X] Could not read public key: %PUBKEY%
274
+ echo [i] Copy the key manually to your server.
275
+ ) else (
276
+ echo ================================================
277
+ echo Add your public key to the server
278
+ echo ================================================
279
+ echo.
280
+ echo Option A — paste this command on the server:
281
+ echo.
282
+
283
+ :: Write the server command to a temp file to avoid echo mangling long keys
284
+ set "TMPFILE=%TEMP%\mcp_pubkey_cmd.txt"
285
+ (
286
+ echo mkdir -p ~/.ssh ^&^& echo !PUBKEYLINE! ^>^> ~/.ssh/authorized_keys ^&^& chmod 700 ~/.ssh ^&^& chmod 600 ~/.ssh/authorized_keys
287
+ ) > "!TMPFILE!"
288
+ type "!TMPFILE!"
289
+ del "!TMPFILE!" >nul 2>nul
290
+
291
+ echo.
292
+ echo Option B — if ssh-copy-id is available ^(e.g. via Git Bash^):
293
+ echo.
294
+ echo ssh-copy-id -i "%PUBKEY%" !SSHUSER!@!HOST! -p !PORT!
295
+ )
296
+
297
+ echo.
298
+
299
+ :: ------------------------------------------------
300
+ :: Optional: Passwordless sudo
301
+ :: ------------------------------------------------
302
+ echo ================================================
303
+ echo OPTIONAL: Passwordless sudo for AI agent
304
+ echo ================================================
305
+ echo.
306
+ echo Allows the agent to run sudo without a password prompt.
307
+ echo.
308
+ powershell -NoProfile -Command "Write-Host ' WARNING: USE AT YOUR OWN RISK!' -ForegroundColor Red"
309
+ powershell -NoProfile -Command "Write-Host ' The AI agent can execute privileged commands.' -ForegroundColor Red"
310
+ powershell -NoProfile -Command "Write-Host ' Recommended only for local/dev/homelab servers.' -ForegroundColor Yellow"
311
+ echo.
312
+ echo [FULL ACCESS — easiest, least safe]
313
+ echo sudo bash -c "echo '!SSHUSER! ALL=(ALL) NOPASSWD: ALL' ^> /etc/sudoers.d/99-mcp-!SSHUSER!"
314
+ echo.
315
+ echo [LIMITED ACCESS — safer, restrict to specific commands]
316
+ echo sudo bash -c "echo '!SSHUSER! ALL=(ALL) NOPASSWD: /usr/bin/systemctl, /usr/bin/apt' ^> /etc/sudoers.d/99-mcp-!SSHUSER!"
317
+ echo.
318
+ echo Validate after applying:
319
+ echo sudo visudo -cf /etc/sudoers.d/99-mcp-!SSHUSER!
320
+ echo.
321
+ echo ================================================
322
+ echo.
323
+
324
+ pause
325
+ endlocal
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "ssh-agent-workspace",
3
+ "version": "1.0.0",
4
+ "description": "Persistent SSH workspaces for AI agents. Stateful tmux-backed sessions that survive reconnects, restarts, and network drops — with runtime security and tool configuration.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "ssh-agent-workspace": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "dev": "tsc --watch",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "ssh",
20
+ "tmux",
21
+ "stateful",
22
+ "workspace",
23
+ "persistent",
24
+ "opencode",
25
+ "claude",
26
+ "ai-agent"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.0.4",
32
+ "pino": "^9.5.0",
33
+ "ssh-config": "^5.0.0",
34
+ "ssh2": "^1.16.0",
35
+ "uuid": "^11.0.0",
36
+ "zod": "^3.23.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^22.0.0",
40
+ "@types/ssh2": "^1.15.0",
41
+ "@types/uuid": "^10.0.0",
42
+ "typescript": "^5.6.0",
43
+ "vitest": "^4.1.7"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
48
+ }
package/test_check.bat ADDED
@@ -0,0 +1,9 @@
1
+ @echo off
2
+ setlocal EnableDelayedExpansion
3
+ set "SSHKEYGEN="
4
+ where ssh-keygen >nul 2>&1
5
+ echo errorlevel after where: %errorlevel%
6
+ if not errorlevel 1 set "SSHKEYGEN=ssh-keygen"
7
+ echo SSHKEYGEN after where check: [!SSHKEYGEN!]
8
+ echo defined check:
9
+ if defined SSHKEYGEN (echo YES) else (echo NO)
@@ -0,0 +1,12 @@
1
+ @echo off
2
+ setlocal EnableDelayedExpansion
3
+ set BAR=worked
4
+ echo NORMAL: [%BAR%]
5
+ echo DELAYED: [!BAR!]
6
+ echo WHERE:
7
+ where ssh-keygen >nul 2>&1 && echo FOUND || echo NOTFOUND
8
+ set SSHKEYGEN=
9
+ where ssh-keygen >nul 2>&1 && set "SSHKEYGEN=ssh-keygen"
10
+ if defined SSHKEYGEN (echo SSHKEYGEN defined: [!SSHKEYGEN!]) else (echo SSHKEYGEN NOT defined)
11
+ echo.
12
+ if exist "%SYSTEMROOT%\System32\OpenSSH\ssh-keygen.exe" (echo FILE EXISTS) else (echo FILE NOT EXISTS)
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ include: ['src/**/*.test.ts'],
8
+ coverage: {
9
+ provider: 'v8',
10
+ include: ['src/**/*.ts'],
11
+ exclude: ['src/**/*.test.ts', 'src/index.ts', 'src/server.ts'],
12
+ },
13
+ },
14
+ });