vssh 3.3.4__tar.gz → 3.3.7__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.7}/PKG-INFO +1 -1
- {vssh-3.3.4 → vssh-3.3.7}/pyproject.toml +1 -1
- {vssh-3.3.4 → vssh-3.3.7/vssh.egg-info}/PKG-INFO +1 -1
- {vssh-3.3.4 → vssh-3.3.7}/vssh.py +175 -4
- {vssh-3.3.4 → vssh-3.3.7}/LICENSE +0 -0
- {vssh-3.3.4 → vssh-3.3.7}/README.md +0 -0
- {vssh-3.3.4 → vssh-3.3.7}/setup.cfg +0 -0
- {vssh-3.3.4 → vssh-3.3.7}/vssh.egg-info/SOURCES.txt +0 -0
- {vssh-3.3.4 → vssh-3.3.7}/vssh.egg-info/dependency_links.txt +0 -0
- {vssh-3.3.4 → vssh-3.3.7}/vssh.egg-info/entry_points.txt +0 -0
- {vssh-3.3.4 → vssh-3.3.7}/vssh.egg-info/top_level.txt +0 -0
- {vssh-3.3.4 → vssh-3.3.7}/vssh_mcp_server.py +0 -0
- {vssh-3.3.4 → vssh-3.3.7}/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,117 @@ 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
|
+
|
|
3442
|
+
|
|
3443
|
+
def _get_vssh_version():
|
|
3444
|
+
try:
|
|
3445
|
+
from importlib.metadata import version as _v
|
|
3446
|
+
return _v('vssh')
|
|
3447
|
+
except Exception:
|
|
3448
|
+
return '?'
|
|
3449
|
+
|
|
3450
|
+
|
|
3282
3451
|
def main():
|
|
3283
3452
|
args = sys.argv[1:]
|
|
3284
3453
|
# Handle --version and --help flags
|
|
@@ -3594,6 +3763,8 @@ Env: VSSH_SECRET
|
|
|
3594
3763
|
sys.exit(0 if result['fail'] == 0 else 1)
|
|
3595
3764
|
elif cmd == 'server':
|
|
3596
3765
|
server()
|
|
3766
|
+
elif cmd == 'install':
|
|
3767
|
+
_cmd_install(args)
|
|
3597
3768
|
elif cmd == 'help' or cmd == '-h' or cmd == '--help':
|
|
3598
3769
|
main_args_bak = sys.argv[1:]
|
|
3599
3770
|
sys.argv = sys.argv[:1]
|
|
@@ -3615,7 +3786,7 @@ Env: VSSH_SECRET
|
|
|
3615
3786
|
result = {'name': name, 'ip': ip, 'online': False, 'latency': 0}
|
|
3616
3787
|
try:
|
|
3617
3788
|
s = _sock.socket(_sock.AF_INET, _sock.SOCK_STREAM)
|
|
3618
|
-
s.settimeout(
|
|
3789
|
+
s.settimeout(1)
|
|
3619
3790
|
t0 = time.time()
|
|
3620
3791
|
s.connect((ip, 48291))
|
|
3621
3792
|
result['latency'] = (time.time() - t0) * 1000
|
|
@@ -3649,9 +3820,9 @@ Env: VSSH_SECRET
|
|
|
3649
3820
|
return result
|
|
3650
3821
|
|
|
3651
3822
|
if full_mode:
|
|
3652
|
-
print("vssh
|
|
3823
|
+
print(f"vssh v{_get_vssh_version()} - Cluster Status (full)")
|
|
3653
3824
|
else:
|
|
3654
|
-
print("vssh
|
|
3825
|
+
print(f"vssh v{_get_vssh_version()} - Connection Status")
|
|
3655
3826
|
print("=" * 70)
|
|
3656
3827
|
|
|
3657
3828
|
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
|