vssh 3.3.4__tar.gz → 3.3.6__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.3.4/vssh.egg-info → vssh-3.3.6}/PKG-INFO +1 -1
- {vssh-3.3.4 → vssh-3.3.6}/pyproject.toml +1 -1
- {vssh-3.3.4 → vssh-3.3.6/vssh.egg-info}/PKG-INFO +1 -1
- {vssh-3.3.4 → vssh-3.3.6}/vssh.py +166 -4
- {vssh-3.3.4 → vssh-3.3.6}/LICENSE +0 -0
- {vssh-3.3.4 → vssh-3.3.6}/README.md +0 -0
- {vssh-3.3.4 → vssh-3.3.6}/setup.cfg +0 -0
- {vssh-3.3.4 → vssh-3.3.6}/vssh.egg-info/SOURCES.txt +0 -0
- {vssh-3.3.4 → vssh-3.3.6}/vssh.egg-info/dependency_links.txt +0 -0
- {vssh-3.3.4 → vssh-3.3.6}/vssh.egg-info/entry_points.txt +0 -0
- {vssh-3.3.4 → vssh-3.3.6}/vssh.egg-info/top_level.txt +0 -0
- {vssh-3.3.4 → vssh-3.3.6}/vssh_mcp_server.py +0 -0
- {vssh-3.3.4 → vssh-3.3.6}/vssh_p2p.py +0 -0
|
@@ -35,7 +35,14 @@ def _load_vssh_secret():
|
|
|
35
35
|
s = sf.read_text().strip()
|
|
36
36
|
if s: return s
|
|
37
37
|
conf = _load_vssh_config()
|
|
38
|
-
|
|
38
|
+
s = conf.get('SECRET', '')
|
|
39
|
+
if s: return s
|
|
40
|
+
# Auto-generate on first install
|
|
41
|
+
import secrets as _sec
|
|
42
|
+
s = _sec.token_hex(16)
|
|
43
|
+
VSSH_DIR_CONF.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
sf.write_text(s)
|
|
45
|
+
return s
|
|
39
46
|
|
|
40
47
|
VSSH_CONFIG = _load_vssh_config()
|
|
41
48
|
SECRET = _load_vssh_secret()
|
|
@@ -2961,6 +2968,57 @@ def server():
|
|
|
2961
2968
|
result = subprocess.run(cmdline, shell=True, capture_output=True, timeout=60)
|
|
2962
2969
|
conn.sendall(result.stdout + result.stderr + b'__END__')
|
|
2963
2970
|
|
|
2971
|
+
elif cmd == 'PTY_SESSION':
|
|
2972
|
+
# PTY_SESSION:auth:rows:cols → full bash PTY relay
|
|
2973
|
+
try:
|
|
2974
|
+
rows = int(parts[2]) if len(parts) > 2 else 24
|
|
2975
|
+
cols = int(parts[3]) if len(parts) > 3 else 80
|
|
2976
|
+
except (IndexError, ValueError):
|
|
2977
|
+
rows, cols = 24, 80
|
|
2978
|
+
conn.sendall(b'PTY_OK\n')
|
|
2979
|
+
print(f'[PTY] {addr[0]} {rows}x{cols}')
|
|
2980
|
+
import pty as _pty, select as _sel, fcntl as _fcntl
|
|
2981
|
+
import termios as _ter, struct as _stru
|
|
2982
|
+
mfd, sfd = _pty.openpty()
|
|
2983
|
+
_fcntl.ioctl(sfd, _ter.TIOCSWINSZ,
|
|
2984
|
+
_stru.pack('HHHH', rows, cols, 0, 0))
|
|
2985
|
+
proc = subprocess.Popen(
|
|
2986
|
+
['/bin/bash', '-l'],
|
|
2987
|
+
stdin=sfd, stdout=sfd, stderr=sfd,
|
|
2988
|
+
preexec_fn=os.setsid, close_fds=True
|
|
2989
|
+
)
|
|
2990
|
+
os.close(sfd)
|
|
2991
|
+
cfd = conn.fileno()
|
|
2992
|
+
try:
|
|
2993
|
+
while proc.poll() is None:
|
|
2994
|
+
r, _, _ = _sel.select([cfd, mfd], [], [], 0.05)
|
|
2995
|
+
if cfd in r:
|
|
2996
|
+
data = conn.recv(4096)
|
|
2997
|
+
if not data:
|
|
2998
|
+
break
|
|
2999
|
+
try:
|
|
3000
|
+
os.write(mfd, data)
|
|
3001
|
+
except OSError:
|
|
3002
|
+
break
|
|
3003
|
+
if mfd in r:
|
|
3004
|
+
try:
|
|
3005
|
+
data = os.read(mfd, 4096)
|
|
3006
|
+
if data:
|
|
3007
|
+
conn.sendall(data)
|
|
3008
|
+
except OSError:
|
|
3009
|
+
break
|
|
3010
|
+
finally:
|
|
3011
|
+
try:
|
|
3012
|
+
proc.terminate()
|
|
3013
|
+
proc.wait(timeout=2)
|
|
3014
|
+
except Exception:
|
|
3015
|
+
pass
|
|
3016
|
+
try:
|
|
3017
|
+
os.close(mfd)
|
|
3018
|
+
except Exception:
|
|
3019
|
+
pass
|
|
3020
|
+
print(f'[PTY] {addr[0]} session ended')
|
|
3021
|
+
|
|
2964
3022
|
elif cmd == 'INFO':
|
|
2965
3023
|
conn.sendall(b'OK\n')
|
|
2966
3024
|
|
|
@@ -3279,6 +3337,108 @@ def server():
|
|
|
3279
3337
|
except KeyboardInterrupt:
|
|
3280
3338
|
print('\nStopped')
|
|
3281
3339
|
|
|
3340
|
+
|
|
3341
|
+
def _cmd_install(args):
|
|
3342
|
+
"""Install vssh as a system daemon (systemd on Linux, LaunchAgent on macOS)."""
|
|
3343
|
+
import sys as _sys, os as _os, platform as _plat, subprocess as _sp, stat as _stat
|
|
3344
|
+
|
|
3345
|
+
python_exe = _sys.executable
|
|
3346
|
+
vssh_py = _os.path.abspath(__file__)
|
|
3347
|
+
secret = _load_vssh_secret()
|
|
3348
|
+
|
|
3349
|
+
print(f"[vssh install]")
|
|
3350
|
+
print(f" python : {python_exe}")
|
|
3351
|
+
print(f" vssh.py : {vssh_py}")
|
|
3352
|
+
print(f" secret : {secret[:6]}...{secret[-4:]} ({'auto-generated' if not _os.environ.get('VSSH_SECRET') else 'from env'})")
|
|
3353
|
+
|
|
3354
|
+
if _plat.system() == 'Linux':
|
|
3355
|
+
service = f"""[Unit]
|
|
3356
|
+
Description=vssh daemon
|
|
3357
|
+
After=network.target
|
|
3358
|
+
|
|
3359
|
+
[Service]
|
|
3360
|
+
Type=simple
|
|
3361
|
+
ExecStart={python_exe} {vssh_py} server
|
|
3362
|
+
Environment="VSSH_SECRET={secret}"
|
|
3363
|
+
Restart=always
|
|
3364
|
+
RestartSec=5
|
|
3365
|
+
StandardOutput=journal
|
|
3366
|
+
StandardError=journal
|
|
3367
|
+
|
|
3368
|
+
[Install]
|
|
3369
|
+
WantedBy=multi-user.target
|
|
3370
|
+
"""
|
|
3371
|
+
svc_path = '/etc/systemd/system/vssh.service'
|
|
3372
|
+
try:
|
|
3373
|
+
with open(svc_path, 'w') as f:
|
|
3374
|
+
f.write(service)
|
|
3375
|
+
print(f" Created : {svc_path}")
|
|
3376
|
+
_sp.run(['systemctl', 'daemon-reload'], check=True, capture_output=True)
|
|
3377
|
+
_sp.run(['systemctl', 'enable', 'vssh'], check=True, capture_output=True)
|
|
3378
|
+
_sp.run(['systemctl', 'restart', 'vssh'], check=True, capture_output=True)
|
|
3379
|
+
import time; time.sleep(1)
|
|
3380
|
+
r = _sp.run(['systemctl', 'is-active', 'vssh'], capture_output=True, text=True)
|
|
3381
|
+
status = r.stdout.strip()
|
|
3382
|
+
print(f" Service : {status}")
|
|
3383
|
+
if status == 'active':
|
|
3384
|
+
print("[vssh install] ✓ Done. vssh daemon running.")
|
|
3385
|
+
else:
|
|
3386
|
+
print("[vssh install] ✗ Service not active — check: journalctl -u vssh -n 20")
|
|
3387
|
+
except PermissionError:
|
|
3388
|
+
print("[vssh install] ✗ Need root. Run: sudo vssh install")
|
|
3389
|
+
except Exception as e:
|
|
3390
|
+
print(f"[vssh install] ✗ Error: {e}")
|
|
3391
|
+
|
|
3392
|
+
elif _plat.system() == 'Darwin':
|
|
3393
|
+
home = _os.path.expanduser('~')
|
|
3394
|
+
plist_dir = _os.path.join(home, 'Library', 'LaunchAgents')
|
|
3395
|
+
_os.makedirs(plist_dir, exist_ok=True)
|
|
3396
|
+
plist_path = _os.path.join(plist_dir, 'com.vssh.server.plist')
|
|
3397
|
+
plist = f"""<?xml version="1.0" encoding="UTF-8"?>
|
|
3398
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3399
|
+
<plist version="1.0">
|
|
3400
|
+
<dict>
|
|
3401
|
+
<key>Label</key>
|
|
3402
|
+
<string>com.vssh.server</string>
|
|
3403
|
+
<key>ProgramArguments</key>
|
|
3404
|
+
<array>
|
|
3405
|
+
<string>{python_exe}</string>
|
|
3406
|
+
<string>{vssh_py}</string>
|
|
3407
|
+
<string>server</string>
|
|
3408
|
+
</array>
|
|
3409
|
+
<key>EnvironmentVariables</key>
|
|
3410
|
+
<dict>
|
|
3411
|
+
<key>VSSH_SECRET</key>
|
|
3412
|
+
<string>{secret}</string>
|
|
3413
|
+
</dict>
|
|
3414
|
+
<key>RunAtLoad</key>
|
|
3415
|
+
<true/>
|
|
3416
|
+
<key>KeepAlive</key>
|
|
3417
|
+
<true/>
|
|
3418
|
+
<key>StandardOutPath</key>
|
|
3419
|
+
<string>/tmp/vssh.log</string>
|
|
3420
|
+
<key>StandardErrorPath</key>
|
|
3421
|
+
<string>/tmp/vssh.err</string>
|
|
3422
|
+
</dict>
|
|
3423
|
+
</plist>"""
|
|
3424
|
+
# Unload existing if any
|
|
3425
|
+
_sp.run(['launchctl', 'unload', plist_path], capture_output=True)
|
|
3426
|
+
with open(plist_path, 'w') as f:
|
|
3427
|
+
f.write(plist)
|
|
3428
|
+
print(f" Created : {plist_path}")
|
|
3429
|
+
r = _sp.run(['launchctl', 'load', plist_path], capture_output=True, text=True)
|
|
3430
|
+
if r.returncode == 0:
|
|
3431
|
+
import time; time.sleep(1)
|
|
3432
|
+
r2 = _sp.run(['pgrep', '-f', 'vssh.py server'], capture_output=True, text=True)
|
|
3433
|
+
if r2.stdout.strip():
|
|
3434
|
+
print("[vssh install] ✓ Done. vssh daemon running.")
|
|
3435
|
+
else:
|
|
3436
|
+
print("[vssh install] ✗ Process not found — check: cat /tmp/vssh.err")
|
|
3437
|
+
else:
|
|
3438
|
+
print(f"[vssh install] ✗ launchctl error: {r.stderr.strip()}")
|
|
3439
|
+
else:
|
|
3440
|
+
print(f"[vssh install] ✗ Unsupported OS: {_plat.system()}")
|
|
3441
|
+
|
|
3282
3442
|
def main():
|
|
3283
3443
|
args = sys.argv[1:]
|
|
3284
3444
|
# Handle --version and --help flags
|
|
@@ -3594,6 +3754,8 @@ Env: VSSH_SECRET
|
|
|
3594
3754
|
sys.exit(0 if result['fail'] == 0 else 1)
|
|
3595
3755
|
elif cmd == 'server':
|
|
3596
3756
|
server()
|
|
3757
|
+
elif cmd == 'install':
|
|
3758
|
+
_cmd_install(args)
|
|
3597
3759
|
elif cmd == 'help' or cmd == '-h' or cmd == '--help':
|
|
3598
3760
|
main_args_bak = sys.argv[1:]
|
|
3599
3761
|
sys.argv = sys.argv[:1]
|
|
@@ -3615,7 +3777,7 @@ Env: VSSH_SECRET
|
|
|
3615
3777
|
result = {'name': name, 'ip': ip, 'online': False, 'latency': 0}
|
|
3616
3778
|
try:
|
|
3617
3779
|
s = _sock.socket(_sock.AF_INET, _sock.SOCK_STREAM)
|
|
3618
|
-
s.settimeout(
|
|
3780
|
+
s.settimeout(1)
|
|
3619
3781
|
t0 = time.time()
|
|
3620
3782
|
s.connect((ip, 48291))
|
|
3621
3783
|
result['latency'] = (time.time() - t0) * 1000
|
|
@@ -3649,9 +3811,9 @@ Env: VSSH_SECRET
|
|
|
3649
3811
|
return result
|
|
3650
3812
|
|
|
3651
3813
|
if full_mode:
|
|
3652
|
-
print("vssh
|
|
3814
|
+
print(f"vssh v{_get_vssh_version()} - Cluster Status (full)")
|
|
3653
3815
|
else:
|
|
3654
|
-
print("vssh
|
|
3816
|
+
print(f"vssh v{_get_vssh_version()} - Connection Status")
|
|
3655
3817
|
print("=" * 70)
|
|
3656
3818
|
|
|
3657
3819
|
online = 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|