qssh 0.2.2__tar.gz → 0.2.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qssh
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Quick SSH session manager - save your VM credentials and connect with a single command
5
5
  Author: joan-code6
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qssh"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "Quick SSH session manager - save your VM credentials and connect with a single command"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  """qssh - Quick SSH session manager."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.2.3"
4
4
  __author__ = "bennet"
5
5
 
6
6
  from .session import SessionManager
@@ -2,6 +2,7 @@
2
2
 
3
3
  import os
4
4
  import sys
5
+ import signal
5
6
  import select
6
7
  import subprocess
7
8
  import platform
@@ -173,12 +174,16 @@ class SSHConnector:
173
174
  """
174
175
  import threading
175
176
  import msvcrt
177
+ import time
176
178
 
177
- running = True
179
+ # Ignore SIGINT (Ctrl+C) at the Python level - we'll handle it manually
180
+ original_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN)
181
+
182
+ running = [True] # Use list to allow modification in nested function
178
183
 
179
184
  def read_output():
180
185
  """Read from channel and print to stdout."""
181
- while running:
186
+ while running[0]:
182
187
  try:
183
188
  if channel.recv_ready():
184
189
  data = channel.recv(4096)
@@ -186,8 +191,18 @@ class SSHConnector:
186
191
  sys.stdout.write(data.decode("utf-8", errors="replace"))
187
192
  sys.stdout.flush()
188
193
  else:
194
+ # Empty data means connection closed
195
+ running[0] = False
189
196
  break
197
+
198
+ # Check if channel is closed
199
+ if channel.closed or channel.exit_status_ready():
200
+ running[0] = False
201
+ break
202
+
203
+ time.sleep(0.01)
190
204
  except Exception:
205
+ running[0] = False
191
206
  break
192
207
 
193
208
  # Start output reader thread
@@ -195,16 +210,17 @@ class SSHConnector:
195
210
  output_thread.start()
196
211
 
197
212
  try:
198
- while running and not channel.closed:
199
- # Check for keyboard input
213
+ while running[0] and not channel.closed:
214
+ # Check for keyboard input (non-blocking)
200
215
  if msvcrt.kbhit():
216
+ # Use getch for raw byte input (better for control chars)
201
217
  char = msvcrt.getwch()
218
+
202
219
  if char == '\r':
203
- channel.send('\n')
204
- elif char == '\x03': # Ctrl+C
205
- channel.send('\x03')
206
- elif char == '\x00' or char == '\xe0': # Special keys
207
- # Read the second byte for arrow keys etc.
220
+ # Enter key - send carriage return
221
+ channel.send('\r')
222
+ elif char == '\x00' or char == '\xe0':
223
+ # Special keys (arrows, function keys, etc.)
208
224
  char2 = msvcrt.getwch()
209
225
  # Map arrow keys to ANSI escape sequences
210
226
  key_map = {
@@ -212,21 +228,31 @@ class SSHConnector:
212
228
  'P': '\x1b[B', # Down
213
229
  'M': '\x1b[C', # Right
214
230
  'K': '\x1b[D', # Left
231
+ 'G': '\x1b[H', # Home
232
+ 'O': '\x1b[F', # End
233
+ 'R': '\x1b[2~', # Insert
234
+ 'S': '\x1b[3~', # Delete
235
+ 'I': '\x1b[5~', # Page Up
236
+ 'Q': '\x1b[6~', # Page Down
215
237
  }
216
238
  if char2 in key_map:
217
239
  channel.send(key_map[char2])
240
+ elif char == '\x08':
241
+ # Backspace
242
+ channel.send('\x7f')
218
243
  else:
244
+ # Send character as-is (includes Ctrl+C as \x03, Ctrl+D as \x04, etc.)
219
245
  channel.send(char)
220
-
221
- # Small delay to prevent CPU spinning
222
- if not channel.recv_ready():
223
- import time
246
+ else:
247
+ # Small delay to prevent CPU spinning
224
248
  time.sleep(0.01)
225
249
 
226
- except KeyboardInterrupt:
227
- pass
228
250
  finally:
229
- running = False
251
+ running[0] = False
252
+ # Wait for output thread to finish
253
+ output_thread.join(timeout=1.0)
254
+ # Restore original signal handler
255
+ signal.signal(signal.SIGINT, original_sigint)
230
256
 
231
257
  def _unix_interactive_shell(self, channel) -> None:
232
258
  """Interactive shell for Unix systems using select.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qssh
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Quick SSH session manager - save your VM credentials and connect with a single command
5
5
  Author: joan-code6
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes