vssh 3.7.3__tar.gz → 3.7.4__tar.gz
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.
- {vssh-3.7.3/vssh.egg-info → vssh-3.7.4}/PKG-INFO +3 -31
- {vssh-3.7.3 → vssh-3.7.4}/README.md +2 -30
- {vssh-3.7.3 → vssh-3.7.4}/pyproject.toml +2 -2
- {vssh-3.7.3 → vssh-3.7.4/vssh.egg-info}/PKG-INFO +3 -31
- {vssh-3.7.3 → vssh-3.7.4}/vssh.egg-info/SOURCES.txt +0 -1
- {vssh-3.7.3 → vssh-3.7.4}/vssh.egg-info/top_level.txt +0 -1
- {vssh-3.7.3 → vssh-3.7.4}/vssh.py +0 -107
- {vssh-3.7.3 → vssh-3.7.4}/vssh_mcp_server.py +2 -30
- vssh-3.7.3/vssh_p2p.py +0 -933
- {vssh-3.7.3 → vssh-3.7.4}/LICENSE +0 -0
- {vssh-3.7.3 → vssh-3.7.4}/setup.cfg +0 -0
- {vssh-3.7.3 → vssh-3.7.4}/vssh.egg-info/dependency_links.txt +0 -0
- {vssh-3.7.3 → vssh-3.7.4}/vssh.egg-info/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vssh
|
|
3
|
-
Version: 3.7.
|
|
3
|
+
Version: 3.7.4
|
|
4
4
|
Summary: Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server
|
|
5
5
|
Author-email: MeshPOP <mpop@mpop.dev>
|
|
6
6
|
License: MIT
|
|
@@ -157,7 +157,7 @@ vssh history 20 web1 # Last 20 commands for specific host
|
|
|
157
157
|
|
|
158
158
|
**Example `vssh status` output:**
|
|
159
159
|
```
|
|
160
|
-
vssh v3.7.
|
|
160
|
+
vssh v3.7.4 - Connection Status
|
|
161
161
|
======================================================================
|
|
162
162
|
c1 10.99.248.51 ● online (12ms)
|
|
163
163
|
c2 10.99.14.187 ● online (11ms)
|
|
@@ -168,7 +168,7 @@ Total: 3/3 online
|
|
|
168
168
|
|
|
169
169
|
**Example `vssh status --full` output:**
|
|
170
170
|
```
|
|
171
|
-
vssh v3.7.
|
|
171
|
+
vssh v3.7.4 - Cluster Status (full)
|
|
172
172
|
======================================================================
|
|
173
173
|
web1 10.99.1.10 ● online (8ms)
|
|
174
174
|
disk: 45% used (120GB / 250GB) mem: 4.2GB / 16GB load: 0.42
|
|
@@ -213,7 +213,6 @@ vssh get --retry=3 <host>:<remote> <local> # Download with retry
|
|
|
213
213
|
vssh sync <local_dir> <host>:<remote_dir> # Directory sync (8 parallel streams)
|
|
214
214
|
vssh mput <host>:<base_path> <file1> <file2> ... # Multi-file single connection
|
|
215
215
|
vssh fast <local> <host>:<remote> # Auto-select best method
|
|
216
|
-
vssh p2p <local> <host>:<remote> # P2P direct (NAT hole-punch)
|
|
217
216
|
vssh rsync <local> <host>:<remote> # Delta sync (only changed blocks)
|
|
218
217
|
```
|
|
219
218
|
|
|
@@ -234,7 +233,6 @@ vssh get web1:/var/log/app.log ./app.log # Download
|
|
|
234
233
|
vssh sync ./config/ web1:/etc/app/ # Sync directory
|
|
235
234
|
vssh mput web1:/backup/ file1.txt file2.txt file3.log # Multi-file
|
|
236
235
|
vssh rsync ./src/ web1:/opt/src/ # Delta sync (only changes)
|
|
237
|
-
vssh p2p ./dataset.tar.gz web2:/data/ # P2P bypass VPN relay
|
|
238
236
|
```
|
|
239
237
|
|
|
240
238
|
vssh auto-compresses text files (`.py`, `.js`, `.json`, `.log`, `.md`, etc.) and skips upload for files with matching MD5.
|
|
@@ -623,16 +621,6 @@ Used internally for LAN-direct optimization.
|
|
|
623
621
|
|
|
624
622
|
---
|
|
625
623
|
|
|
626
|
-
#### `p2p_signal` — P2P Signaling
|
|
627
|
-
|
|
628
|
-
Used internally by `vssh p2p` to coordinate NAT hole-punch. Exchanges external IP/port info between peers via the VPN mesh relay.
|
|
629
|
-
|
|
630
|
-
```bash
|
|
631
|
-
vssh rpc relay p2p_signal '{"external_ip":"203.0.113.1","external_port":48300,"local_port":48300}'
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
---
|
|
635
|
-
|
|
636
624
|
### RPC via Session
|
|
637
625
|
|
|
638
626
|
You can run multiple RPC calls on a single persistent connection using `vssh session`:
|
|
@@ -899,22 +887,6 @@ Test upload and download speed to a server.
|
|
|
899
887
|
|
|
900
888
|
---
|
|
901
889
|
|
|
902
|
-
#### `vssh_p2p_status` — P2P Status
|
|
903
|
-
|
|
904
|
-
Check NAT hole-punch capability.
|
|
905
|
-
|
|
906
|
-
```json
|
|
907
|
-
// Output:
|
|
908
|
-
{
|
|
909
|
-
"p2p_available": true,
|
|
910
|
-
"external_ip": "203.0.113.1",
|
|
911
|
-
"external_port": 48310,
|
|
912
|
-
"nat_type": "cone"
|
|
913
|
-
}
|
|
914
|
-
```
|
|
915
|
-
|
|
916
|
-
---
|
|
917
|
-
|
|
918
890
|
#### `vssh_tunnel` — SSH Tunnel (Port Forwarding)
|
|
919
891
|
|
|
920
892
|
Create an SSH tunnel to access a remote service locally.
|
|
@@ -134,7 +134,7 @@ vssh history 20 web1 # Last 20 commands for specific host
|
|
|
134
134
|
|
|
135
135
|
**Example `vssh status` output:**
|
|
136
136
|
```
|
|
137
|
-
vssh v3.7.
|
|
137
|
+
vssh v3.7.4 - Connection Status
|
|
138
138
|
======================================================================
|
|
139
139
|
c1 10.99.248.51 ● online (12ms)
|
|
140
140
|
c2 10.99.14.187 ● online (11ms)
|
|
@@ -145,7 +145,7 @@ Total: 3/3 online
|
|
|
145
145
|
|
|
146
146
|
**Example `vssh status --full` output:**
|
|
147
147
|
```
|
|
148
|
-
vssh v3.7.
|
|
148
|
+
vssh v3.7.4 - Cluster Status (full)
|
|
149
149
|
======================================================================
|
|
150
150
|
web1 10.99.1.10 ● online (8ms)
|
|
151
151
|
disk: 45% used (120GB / 250GB) mem: 4.2GB / 16GB load: 0.42
|
|
@@ -190,7 +190,6 @@ vssh get --retry=3 <host>:<remote> <local> # Download with retry
|
|
|
190
190
|
vssh sync <local_dir> <host>:<remote_dir> # Directory sync (8 parallel streams)
|
|
191
191
|
vssh mput <host>:<base_path> <file1> <file2> ... # Multi-file single connection
|
|
192
192
|
vssh fast <local> <host>:<remote> # Auto-select best method
|
|
193
|
-
vssh p2p <local> <host>:<remote> # P2P direct (NAT hole-punch)
|
|
194
193
|
vssh rsync <local> <host>:<remote> # Delta sync (only changed blocks)
|
|
195
194
|
```
|
|
196
195
|
|
|
@@ -211,7 +210,6 @@ vssh get web1:/var/log/app.log ./app.log # Download
|
|
|
211
210
|
vssh sync ./config/ web1:/etc/app/ # Sync directory
|
|
212
211
|
vssh mput web1:/backup/ file1.txt file2.txt file3.log # Multi-file
|
|
213
212
|
vssh rsync ./src/ web1:/opt/src/ # Delta sync (only changes)
|
|
214
|
-
vssh p2p ./dataset.tar.gz web2:/data/ # P2P bypass VPN relay
|
|
215
213
|
```
|
|
216
214
|
|
|
217
215
|
vssh auto-compresses text files (`.py`, `.js`, `.json`, `.log`, `.md`, etc.) and skips upload for files with matching MD5.
|
|
@@ -600,16 +598,6 @@ Used internally for LAN-direct optimization.
|
|
|
600
598
|
|
|
601
599
|
---
|
|
602
600
|
|
|
603
|
-
#### `p2p_signal` — P2P Signaling
|
|
604
|
-
|
|
605
|
-
Used internally by `vssh p2p` to coordinate NAT hole-punch. Exchanges external IP/port info between peers via the VPN mesh relay.
|
|
606
|
-
|
|
607
|
-
```bash
|
|
608
|
-
vssh rpc relay p2p_signal '{"external_ip":"203.0.113.1","external_port":48300,"local_port":48300}'
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
---
|
|
612
|
-
|
|
613
601
|
### RPC via Session
|
|
614
602
|
|
|
615
603
|
You can run multiple RPC calls on a single persistent connection using `vssh session`:
|
|
@@ -876,22 +864,6 @@ Test upload and download speed to a server.
|
|
|
876
864
|
|
|
877
865
|
---
|
|
878
866
|
|
|
879
|
-
#### `vssh_p2p_status` — P2P Status
|
|
880
|
-
|
|
881
|
-
Check NAT hole-punch capability.
|
|
882
|
-
|
|
883
|
-
```json
|
|
884
|
-
// Output:
|
|
885
|
-
{
|
|
886
|
-
"p2p_available": true,
|
|
887
|
-
"external_ip": "203.0.113.1",
|
|
888
|
-
"external_port": 48310,
|
|
889
|
-
"nat_type": "cone"
|
|
890
|
-
}
|
|
891
|
-
```
|
|
892
|
-
|
|
893
|
-
---
|
|
894
|
-
|
|
895
867
|
#### `vssh_tunnel` — SSH Tunnel (Port Forwarding)
|
|
896
868
|
|
|
897
869
|
Create an SSH tunnel to access a remote service locally.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vssh"
|
|
7
|
-
version = "3.7.
|
|
7
|
+
version = "3.7.4"
|
|
8
8
|
description = "Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -38,4 +38,4 @@ Repository = "https://github.com/meshpop/vssh"
|
|
|
38
38
|
vssh = "vssh_mcp_server"
|
|
39
39
|
|
|
40
40
|
[tool.setuptools]
|
|
41
|
-
py-modules = ["vssh", "
|
|
41
|
+
py-modules = ["vssh", "vssh_mcp_server"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vssh
|
|
3
|
-
Version: 3.7.
|
|
3
|
+
Version: 3.7.4
|
|
4
4
|
Summary: Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server
|
|
5
5
|
Author-email: MeshPOP <mpop@mpop.dev>
|
|
6
6
|
License: MIT
|
|
@@ -157,7 +157,7 @@ vssh history 20 web1 # Last 20 commands for specific host
|
|
|
157
157
|
|
|
158
158
|
**Example `vssh status` output:**
|
|
159
159
|
```
|
|
160
|
-
vssh v3.7.
|
|
160
|
+
vssh v3.7.4 - Connection Status
|
|
161
161
|
======================================================================
|
|
162
162
|
c1 10.99.248.51 ● online (12ms)
|
|
163
163
|
c2 10.99.14.187 ● online (11ms)
|
|
@@ -168,7 +168,7 @@ Total: 3/3 online
|
|
|
168
168
|
|
|
169
169
|
**Example `vssh status --full` output:**
|
|
170
170
|
```
|
|
171
|
-
vssh v3.7.
|
|
171
|
+
vssh v3.7.4 - Cluster Status (full)
|
|
172
172
|
======================================================================
|
|
173
173
|
web1 10.99.1.10 ● online (8ms)
|
|
174
174
|
disk: 45% used (120GB / 250GB) mem: 4.2GB / 16GB load: 0.42
|
|
@@ -213,7 +213,6 @@ vssh get --retry=3 <host>:<remote> <local> # Download with retry
|
|
|
213
213
|
vssh sync <local_dir> <host>:<remote_dir> # Directory sync (8 parallel streams)
|
|
214
214
|
vssh mput <host>:<base_path> <file1> <file2> ... # Multi-file single connection
|
|
215
215
|
vssh fast <local> <host>:<remote> # Auto-select best method
|
|
216
|
-
vssh p2p <local> <host>:<remote> # P2P direct (NAT hole-punch)
|
|
217
216
|
vssh rsync <local> <host>:<remote> # Delta sync (only changed blocks)
|
|
218
217
|
```
|
|
219
218
|
|
|
@@ -234,7 +233,6 @@ vssh get web1:/var/log/app.log ./app.log # Download
|
|
|
234
233
|
vssh sync ./config/ web1:/etc/app/ # Sync directory
|
|
235
234
|
vssh mput web1:/backup/ file1.txt file2.txt file3.log # Multi-file
|
|
236
235
|
vssh rsync ./src/ web1:/opt/src/ # Delta sync (only changes)
|
|
237
|
-
vssh p2p ./dataset.tar.gz web2:/data/ # P2P bypass VPN relay
|
|
238
236
|
```
|
|
239
237
|
|
|
240
238
|
vssh auto-compresses text files (`.py`, `.js`, `.json`, `.log`, `.md`, etc.) and skips upload for files with matching MD5.
|
|
@@ -623,16 +621,6 @@ Used internally for LAN-direct optimization.
|
|
|
623
621
|
|
|
624
622
|
---
|
|
625
623
|
|
|
626
|
-
#### `p2p_signal` — P2P Signaling
|
|
627
|
-
|
|
628
|
-
Used internally by `vssh p2p` to coordinate NAT hole-punch. Exchanges external IP/port info between peers via the VPN mesh relay.
|
|
629
|
-
|
|
630
|
-
```bash
|
|
631
|
-
vssh rpc relay p2p_signal '{"external_ip":"203.0.113.1","external_port":48300,"local_port":48300}'
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
---
|
|
635
|
-
|
|
636
624
|
### RPC via Session
|
|
637
625
|
|
|
638
626
|
You can run multiple RPC calls on a single persistent connection using `vssh session`:
|
|
@@ -899,22 +887,6 @@ Test upload and download speed to a server.
|
|
|
899
887
|
|
|
900
888
|
---
|
|
901
889
|
|
|
902
|
-
#### `vssh_p2p_status` — P2P Status
|
|
903
|
-
|
|
904
|
-
Check NAT hole-punch capability.
|
|
905
|
-
|
|
906
|
-
```json
|
|
907
|
-
// Output:
|
|
908
|
-
{
|
|
909
|
-
"p2p_available": true,
|
|
910
|
-
"external_ip": "203.0.113.1",
|
|
911
|
-
"external_port": 48310,
|
|
912
|
-
"nat_type": "cone"
|
|
913
|
-
}
|
|
914
|
-
```
|
|
915
|
-
|
|
916
|
-
---
|
|
917
|
-
|
|
918
890
|
#### `vssh_tunnel` — SSH Tunnel (Port Forwarding)
|
|
919
891
|
|
|
920
892
|
Create an SSH tunnel to access a remote service locally.
|
|
@@ -1009,67 +1009,6 @@ def _rpc_get_network_info(payload: dict) -> dict:
|
|
|
1009
1009
|
'public_ip': get_cached_public_ip(),
|
|
1010
1010
|
}
|
|
1011
1011
|
|
|
1012
|
-
# P2P signaling storage (in-memory, temporary)
|
|
1013
|
-
_p2p_signals = {} # {peer_ip: {external_ip, external_port, local_port, timestamp}}
|
|
1014
|
-
_P2P_SIGNAL_TTL = 60 # 60 seconds
|
|
1015
|
-
|
|
1016
|
-
@rpc_method('p2p_signal', 'read')
|
|
1017
|
-
def _rpc_p2p_signal(payload: dict) -> dict:
|
|
1018
|
-
"""Exchange P2P signaling info for hole punching
|
|
1019
|
-
|
|
1020
|
-
Payload: {external_ip, external_port, local_port, peer_vpn_ip (optional)}
|
|
1021
|
-
Returns: Peer's info if available, or stores sender's info
|
|
1022
|
-
"""
|
|
1023
|
-
import time
|
|
1024
|
-
|
|
1025
|
-
sender_ip = payload.get('_sender_ip', 'unknown')
|
|
1026
|
-
ext_ip = payload.get('external_ip')
|
|
1027
|
-
ext_port = payload.get('external_port')
|
|
1028
|
-
local_port = payload.get('local_port')
|
|
1029
|
-
peer_vpn_ip = payload.get('peer_vpn_ip')
|
|
1030
|
-
|
|
1031
|
-
if not all([ext_ip, ext_port, local_port]):
|
|
1032
|
-
return {'error': 'external_ip, external_port, local_port required'}
|
|
1033
|
-
|
|
1034
|
-
now = time.time()
|
|
1035
|
-
|
|
1036
|
-
# Clean old signals
|
|
1037
|
-
expired = [k for k, v in _p2p_signals.items() if now - v.get('timestamp', 0) > _P2P_SIGNAL_TTL]
|
|
1038
|
-
for k in expired:
|
|
1039
|
-
del _p2p_signals[k]
|
|
1040
|
-
|
|
1041
|
-
# Store sender's info
|
|
1042
|
-
_p2p_signals[sender_ip] = {
|
|
1043
|
-
'external_ip': ext_ip,
|
|
1044
|
-
'external_port': ext_port,
|
|
1045
|
-
'local_port': local_port,
|
|
1046
|
-
'timestamp': now
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
# If peer_vpn_ip specified, return their info if available
|
|
1050
|
-
if peer_vpn_ip and peer_vpn_ip in _p2p_signals:
|
|
1051
|
-
peer_info = _p2p_signals[peer_vpn_ip]
|
|
1052
|
-
return {
|
|
1053
|
-
'status': 'peer_found',
|
|
1054
|
-
'external_ip': peer_info['external_ip'],
|
|
1055
|
-
'external_port': peer_info['external_port'],
|
|
1056
|
-
'local_port': peer_info['local_port']
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
return {'status': 'registered', 'waiting_for_peer': True}
|
|
1060
|
-
|
|
1061
|
-
@rpc_method('p2p_get_peer', 'read')
|
|
1062
|
-
def _rpc_p2p_get_peer(payload: dict) -> dict:
|
|
1063
|
-
"""Get peer's P2P signal info"""
|
|
1064
|
-
peer_vpn_ip = payload.get('peer_vpn_ip')
|
|
1065
|
-
if not peer_vpn_ip:
|
|
1066
|
-
return {'error': 'peer_vpn_ip required'}
|
|
1067
|
-
|
|
1068
|
-
if peer_vpn_ip in _p2p_signals:
|
|
1069
|
-
return _p2p_signals[peer_vpn_ip]
|
|
1070
|
-
|
|
1071
|
-
return {'error': 'peer not found'}
|
|
1072
|
-
|
|
1073
1012
|
@rpc_method('restart_service', 'admin')
|
|
1074
1013
|
def _rpc_restart_service(payload: dict) -> dict:
|
|
1075
1014
|
"""Restart a systemd service (admin only)"""
|
|
@@ -1618,43 +1557,6 @@ def put_fast(local: str, remote: str, lan: bool = True) -> bool:
|
|
|
1618
1557
|
return _put_file(path, host, rpath)
|
|
1619
1558
|
|
|
1620
1559
|
|
|
1621
|
-
# ========== P2P Transfer (NAT Hole-Punch) ==========
|
|
1622
|
-
def put_p2p(local: str, remote: str, fallback: bool = True) -> bool:
|
|
1623
|
-
"""Upload via P2P hole-punching (bypasses VPN relay)
|
|
1624
|
-
|
|
1625
|
-
Attempts P2P direct transfer for higher speed between NAT'd hosts.
|
|
1626
|
-
Falls back to regular VPN transfer if P2P fails.
|
|
1627
|
-
|
|
1628
|
-
Best for: Large files between NAT'd machines (e.g., local <-> remote)
|
|
1629
|
-
Speed: ~11MB/s P2P vs ~10MB/s VPN relay
|
|
1630
|
-
"""
|
|
1631
|
-
try:
|
|
1632
|
-
from vssh_p2p import p2p_transfer_tcp
|
|
1633
|
-
except ImportError:
|
|
1634
|
-
print('[P2P] vssh_p2p module not found, using VPN')
|
|
1635
|
-
return put_fast(local, remote)
|
|
1636
|
-
|
|
1637
|
-
path = Path(local)
|
|
1638
|
-
if not path.is_file():
|
|
1639
|
-
print(f'Not a file: {local}')
|
|
1640
|
-
return False
|
|
1641
|
-
|
|
1642
|
-
host, rpath = remote.split(':', 1)
|
|
1643
|
-
|
|
1644
|
-
# Try P2P first
|
|
1645
|
-
print(f'[P2P] Attempting direct transfer to {host}...')
|
|
1646
|
-
success = p2p_transfer_tcp(str(path), host, rpath)
|
|
1647
|
-
|
|
1648
|
-
if success:
|
|
1649
|
-
return True
|
|
1650
|
-
|
|
1651
|
-
if fallback:
|
|
1652
|
-
print('[P2P] Falling back to VPN relay...')
|
|
1653
|
-
return put_fast(local, remote)
|
|
1654
|
-
|
|
1655
|
-
return False
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
1560
|
# ========== Multi-connection Large File Transfer ==========
|
|
1659
1561
|
def put_large_file(path: Path, host: str, rpath: str) -> bool:
|
|
1660
1562
|
"""Large file multi-connection upload"""
|
|
@@ -3653,7 +3555,6 @@ File transfer:
|
|
|
3653
3555
|
sync <local_dir> <host>:<remote> Directory sync (8 parallel streams)
|
|
3654
3556
|
mput <host>:<base> file1 file2 Multi-file upload (single connection)
|
|
3655
3557
|
fast <local> <host>:<remote> Auto-select best transfer method
|
|
3656
|
-
p2p <local> <host>:<remote> P2P direct (NAT hole-punch)
|
|
3657
3558
|
rsync <local> <host>:<remote> Delta sync (only changed blocks)
|
|
3658
3559
|
|
|
3659
3560
|
Pipes:
|
|
@@ -3709,14 +3610,6 @@ Env: VSSH_SECRET
|
|
|
3709
3610
|
no_lan = '--no-lan' in args
|
|
3710
3611
|
fast_args = [a for a in args[1:] if not a.startswith('-')]
|
|
3711
3612
|
sys.exit(0 if put_fast(fast_args[0], fast_args[1], lan=not no_lan) else 1)
|
|
3712
|
-
elif cmd == 'p2p':
|
|
3713
|
-
# P2P direct transfer (NAT hole-punch, bypasses VPN relay)
|
|
3714
|
-
no_fallback = '--no-fallback' in args
|
|
3715
|
-
p2p_args = [a for a in args[1:] if not a.startswith('-')]
|
|
3716
|
-
if len(p2p_args) < 2:
|
|
3717
|
-
print('Usage: vssh p2p <local> <host>:<remote> [--no-fallback]')
|
|
3718
|
-
sys.exit(1)
|
|
3719
|
-
sys.exit(0 if put_p2p(p2p_args[0], p2p_args[1], fallback=not no_fallback) else 1)
|
|
3720
3613
|
elif cmd == 'rsync':
|
|
3721
3614
|
# Delta sync (only changed blocks)
|
|
3722
3615
|
no_lan = '--no-lan' in args
|
|
@@ -8,8 +8,9 @@ Tools:
|
|
|
8
8
|
- vssh_put: Upload file to server
|
|
9
9
|
- vssh_get: Download file from server
|
|
10
10
|
- vssh_sync: Sync directory between servers
|
|
11
|
-
- vssh_p2p_status: P2P connection status
|
|
12
11
|
- vssh_speed_test: Test transfer speed
|
|
12
|
+
- vssh_tunnel: SSH port forwarding tunnel
|
|
13
|
+
- vssh_keys: Manage vssh keys and secrets
|
|
13
14
|
|
|
14
15
|
Run: python3 vssh-mcp-server.py
|
|
15
16
|
"""
|
|
@@ -467,24 +468,6 @@ def tool_vssh_speed_test(server: str, size_mb: int = 10):
|
|
|
467
468
|
"latency_ms": round((upload_time + download_time) * 500 / size_mb, 1)
|
|
468
469
|
}
|
|
469
470
|
|
|
470
|
-
def tool_vssh_p2p_status():
|
|
471
|
-
"""Check P2P connection capabilities"""
|
|
472
|
-
# Check if P2P module is available
|
|
473
|
-
try:
|
|
474
|
-
from vssh_p2p import stun_get_external
|
|
475
|
-
external = stun_get_external()
|
|
476
|
-
return {
|
|
477
|
-
"p2p_available": True,
|
|
478
|
-
"external_ip": external[0] if external else "unknown",
|
|
479
|
-
"external_port": external[1] if external else 0,
|
|
480
|
-
"nat_type": "cone" if external else "unknown"
|
|
481
|
-
}
|
|
482
|
-
except Exception as e:
|
|
483
|
-
return {
|
|
484
|
-
"p2p_available": False,
|
|
485
|
-
"note": "P2P module not loaded"
|
|
486
|
-
}
|
|
487
|
-
|
|
488
471
|
def tool_vssh_tunnel(server: str, local_port: int, remote_port: int, remote_host: str = "localhost"):
|
|
489
472
|
"""Create SSH tunnel (port forwarding). Launches tunnel in background and returns PID."""
|
|
490
473
|
servers = get_servers()
|
|
@@ -732,15 +715,6 @@ TOOLS = [
|
|
|
732
715
|
"required": ["server"]
|
|
733
716
|
}
|
|
734
717
|
},
|
|
735
|
-
{
|
|
736
|
-
"name": "vssh_p2p_status",
|
|
737
|
-
"description": "Check P2P (NAT hole-punch) connection capabilities for direct peer-to-peer transfers.",
|
|
738
|
-
"inputSchema": {
|
|
739
|
-
"type": "object",
|
|
740
|
-
"properties": {},
|
|
741
|
-
"required": []
|
|
742
|
-
}
|
|
743
|
-
},
|
|
744
718
|
{
|
|
745
719
|
"name": "vssh_tunnel",
|
|
746
720
|
"description": "Create SSH tunnel (port forwarding) to access remote services locally. Launches tunnel in background, returns PID and access URL.",
|
|
@@ -822,8 +796,6 @@ def handle_request(request):
|
|
|
822
796
|
result = tool_vssh_sync(args.get("source"), args.get("dest"), args.get("path"))
|
|
823
797
|
elif tool_name == "vssh_speed_test":
|
|
824
798
|
result = tool_vssh_speed_test(args.get("server"), args.get("size_mb", 10))
|
|
825
|
-
elif tool_name == "vssh_p2p_status":
|
|
826
|
-
result = tool_vssh_p2p_status()
|
|
827
799
|
elif tool_name == "vssh_tunnel":
|
|
828
800
|
result = tool_vssh_tunnel(
|
|
829
801
|
args.get("server"),
|