diff --git a/pyproject.toml b/pyproject.toml index 2f0a635..848c93b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "nexusai" -version = "0.5.28" +version = "0.5.29" description = "Add your description here" readme = "README.md" requires-python = ">=3.9" diff --git a/src/nexus/cli/config.py b/src/nexus/cli/config.py index d6ab0d8..3a1e327 100644 --- a/src/nexus/cli/config.py +++ b/src/nexus/cli/config.py @@ -13,6 +13,7 @@ class TargetConfig(pyd.BaseModel): host: str port: int = pyd.Field(default=54323) ssh_user: str + ssh_port: int = pyd.Field(default=22) REQUIRED_ENV_VARS = { diff --git a/src/nexus/cli/setup.py b/src/nexus/cli/setup.py index d6073fd..14cb58d 100644 --- a/src/nexus/cli/setup.py +++ b/src/nexus/cli/setup.py @@ -305,7 +305,7 @@ def open_jobrc_editor() -> None: utils.open_file_in_editor(jobrc_path) -def _test_ssh_connection(host: str, ssh_user: str) -> bool: +def _test_ssh_connection(host: str, ssh_user: str, ssh_port: int = 22) -> bool: import subprocess print(colored("\nTesting SSH connection...", "cyan")) @@ -313,6 +313,8 @@ def _test_ssh_connection(host: str, ssh_user: str) -> bool: result = subprocess.run( [ "ssh", + "-p", + str(ssh_port), "-o", "ConnectTimeout=10", "-o", @@ -397,19 +399,21 @@ def add_target() -> None: return host = utils.get_user_input("Remote server address (hostname or IP)", required=True) - port = int(utils.get_user_input("Remote server port", default="54323")) + ssh_port = int(utils.get_user_input("SSH port", default="22")) ssh_user = utils.get_user_input("SSH username", default=os.environ.get("USER", "")) + port = int(utils.get_user_input("Remote server port", default="54323")) print(colored("\nValidating connection...", "cyan", attrs=["bold"])) - if not _test_ssh_connection(host, ssh_user): + if not _test_ssh_connection(host, ssh_user, ssh_port): print(colored("\n✗ SSH connection failed", "red")) print(colored("\nTo fix:", "yellow")) print(colored(f" 1. Ensure your SSH key is in ~/.ssh/authorized_keys on {host}", "yellow")) - print(colored(f" 2. Test manually: ssh {ssh_user}@{host} echo ok", "yellow")) + port_flag = f"-p {ssh_port} " if ssh_port != 22 else "" + print(colored(f" 2. Test manually: ssh {port_flag}{ssh_user}@{host} echo ok", "yellow")) return - target_cfg = config.TargetConfig(host=host, port=port, ssh_user=ssh_user) + target_cfg = config.TargetConfig(host=host, port=port, ssh_user=ssh_user, ssh_port=ssh_port) cfg.targets[target_name] = target_cfg config.save_config(cfg) diff --git a/src/nexus/cli/tunnel_manager.py b/src/nexus/cli/tunnel_manager.py index 6703832..17964ee 100644 --- a/src/nexus/cli/tunnel_manager.py +++ b/src/nexus/cli/tunnel_manager.py @@ -156,6 +156,8 @@ def _start_control_master(target_name: str, target_cfg: config.TargetConfig) -> "-S", str(socket_path), "-fN", + "-p", + str(target_cfg.ssh_port), "-L", f"{local_port}:127.0.0.1:{target_cfg.port}", "-o", @@ -184,10 +186,11 @@ def _start_control_master(target_name: str, target_cfg: config.TargetConfig) -> time.sleep(0.1) last_error = SSHTunnelError(f"Port {local_port} already in use, retrying...") continue + port_flag = f"-p {target_cfg.ssh_port}" if target_cfg.ssh_port != 22 else "" raise SSHTunnelError( f"Failed to start SSH tunnel\n" f"Error: {error_msg}\n" - f"Hint: Verify SSH access with: ssh {target_cfg.ssh_user}@{target_cfg.host} echo ok" + f"Hint: Verify SSH access with: ssh {port_flag} {target_cfg.ssh_user}@{target_cfg.host} echo ok".strip() ) if not _wait_for_tunnel(local_port, timeout=10.0): diff --git a/uv.lock b/uv.lock index 0ec63d7..73575db 100644 --- a/uv.lock +++ b/uv.lock @@ -852,7 +852,7 @@ wheels = [ [[package]] name = "nexusai" -version = "0.5.28" +version = "0.5.29" source = { editable = "." } dependencies = [ { name = "aiohttp" },