multiSSH3 5.73__tar.gz → 5.74__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.

Potentially problematic release.


This version of multiSSH3 might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 5.73
3
+ Version: 5.74
4
4
  Summary: Run commands on multiple hosts via SSH
5
5
  Home-page: https://github.com/yufei-pan/multiSSH3
6
6
  Author: Yufei Pan
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 5.73
3
+ Version: 5.74
4
4
  Summary: Run commands on multiple hosts via SSH
5
5
  Home-page: https://github.com/yufei-pan/multiSSH3
6
6
  Author: Yufei Pan
@@ -10,6 +10,7 @@ __curses_available = False
10
10
  __resource_lib_available = False
11
11
  try:
12
12
  import curses
13
+ import curses.panel
13
14
  __curses_available = True
14
15
  except ImportError:
15
16
  pass
@@ -54,10 +55,10 @@ except AttributeError:
54
55
  # If neither is available, use a dummy decorator
55
56
  def cache_decorator(func):
56
57
  return func
57
- version = '5.73'
58
+ version = '5.74'
58
59
  VERSION = version
59
60
  __version__ = version
60
- COMMIT_DATE = '2025-05-21'
61
+ COMMIT_DATE = '2025-06-03'
61
62
 
62
63
  CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
63
64
  '~/multiSSH3.config.json',
@@ -1868,6 +1869,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1868
1869
  _ = config_reason
1869
1870
  try:
1870
1871
  box_ansi_color = None
1872
+ refresh_all = True
1871
1873
  org_dim = stdscr.getmaxyx()
1872
1874
  # To do this, first we need to know the size of the terminal
1873
1875
  max_y, max_x = org_dim
@@ -1951,6 +1953,33 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1951
1953
  #bottom_border.addnstr(0, 0, '-' * (max_x - 1), max_x - 1)
1952
1954
  _curses_add_string_to_window(window=bottom_border, y=0, line='-' * (max_x - 1),fill_char='-',box_ansi_color=box_ansi_color)
1953
1955
  bottom_border.refresh()
1956
+ help_window_hight = min(14, max_y)
1957
+ help_window_width = min(31, max_x)
1958
+ # Create a centered help window
1959
+ help_window_y = (max_y - help_window_hight) // 2
1960
+ help_window_x = (max_x - help_window_width) // 2
1961
+ help_window = curses.newwin(help_window_hight, help_window_width, help_window_y, help_window_x)
1962
+ help_window.leaveok(True)
1963
+ help_window.scrollok(True)
1964
+ help_window.idlok(True)
1965
+ help_window.box()
1966
+ _curses_add_string_to_window(window=help_window,y=0,line='Help', color_pair_list=[-1,-1,1], centered=True, fill_char='─', lead_str='┌', box_ansi_color=box_ansi_color)
1967
+ _curses_add_string_to_window(window=help_window,y=1,line='? : Toggle Help Menu', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1968
+ _curses_add_string_to_window(window=help_window,y=2,line='_ or + : Change window hight', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1969
+ _curses_add_string_to_window(window=help_window,y=3,line='{ or } : Change window width', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1970
+ _curses_add_string_to_window(window=help_window,y=4,line='< or > : Change host index', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1971
+ _curses_add_string_to_window(window=help_window,y=5,line='|(pipe) : Toggle single host', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1972
+ _curses_add_string_to_window(window=help_window,y=6,line='Ctrl+D : Exit', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1973
+ _curses_add_string_to_window(window=help_window,y=7,line='Ctrl+R : Force refresh', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1974
+ _curses_add_string_to_window(window=help_window,y=8,line='↑ or ↓ : Navigate history', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1975
+ _curses_add_string_to_window(window=help_window,y=9,line='← or → : Move cursor', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1976
+ _curses_add_string_to_window(window=help_window,y=10,line='PgUp/Dn : Scroll history by 5', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1977
+ _curses_add_string_to_window(window=help_window,y=11,line='Home/End: Jump cursor', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1978
+ _curses_add_string_to_window(window=help_window,y=12,line='Esc : Clear line', color_pair_list=[-1,-1,1], lead_str='│', box_ansi_color=box_ansi_color)
1979
+ help_panel = curses.panel.new_panel(help_window)
1980
+ help_panel.hide()
1981
+ help_shown = False
1982
+ curses.panel.update_panels()
1954
1983
  indexOffset = 0
1955
1984
  while host_stats['running'] > 0 or host_stats['waiting'] > 0:
1956
1985
  # Check for keypress
@@ -1961,7 +1990,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1961
1990
  # When we encounter a newline, we add a new list to the list of lists. ( a new line of input )
1962
1991
  # with open('keylog.txt','a') as f:
1963
1992
  # f.write(str(key)+'\n')
1964
- if key == 410: # 410 is the key code for resize
1993
+ if key == 410 or key == curses.KEY_RESIZE: # 410 is the key code for resize
1965
1994
  return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal resize requested')
1966
1995
  # if the user pressed ctrl + d and the last line is empty, we will exit by adding 'exit\n' to the last line
1967
1996
  elif key == 4 and not __keyPressesIn[-1]:
@@ -1991,12 +2020,15 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1991
2020
  # We handle positional keys
1992
2021
  # if the key is up arrow, we will move the line to display up
1993
2022
  elif key == 259: # 259 is the key code for up arrow
2023
+ # also scroll curserPosition to last if it is currently at the last line and curserPosition is at 0
1994
2024
  lineToDisplay = max(lineToDisplay - 1, -len(__keyPressesIn))
2025
+ if lineToDisplay == -2 and not __keyPressesIn[-1]:
2026
+ curserPosition = len(__keyPressesIn[lineToDisplay])
1995
2027
  # if the key is down arrow, we will move the line to display down
1996
2028
  elif key == 258: # 258 is the key code for down arrow
1997
2029
  lineToDisplay = min(lineToDisplay + 1, -1)
1998
2030
  # if the key is left arrow, we will move the cursor left
1999
- elif key == 260: # 260 is the key code for left arrow
2031
+ elif key == 260: # 260 is the key code for left arrow
2000
2032
  curserPosition = min(max(curserPosition - 1, 0), len(__keyPressesIn[lineToDisplay]) -1)
2001
2033
  # if the key is right arrow, we will move the cursor right
2002
2034
  elif key == 261: # 261 is the key code for right arrow
@@ -2013,6 +2045,23 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2013
2045
  # if the key is end, we will move the cursor to the end of the line
2014
2046
  elif key == 360: # 360 is the key code for end
2015
2047
  curserPosition = len(__keyPressesIn[lineToDisplay])
2048
+ elif key == curses.KEY_REFRESH or key == curses.KEY_F5 or key == 18: # 18 is the key code for ctrl + R
2049
+ # if the key is refresh, we will refresh the screen
2050
+ return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Refresh requested')
2051
+ elif key == curses.KEY_EXIT or key == 27: # 27 is the key code for ESC
2052
+ # if the key is exit, we will exit the program
2053
+ return
2054
+ elif key == curses.KEY_HELP or key == 63 or key == curses.KEY_F1: # 63 is the key code for ?
2055
+ # if the key is help, we will display the help message
2056
+ if not help_shown:
2057
+ help_panel.show()
2058
+ help_shown = True
2059
+ else:
2060
+ help_panel.hide()
2061
+ help_shown = False
2062
+ refresh_all = True
2063
+ #return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Help closed')
2064
+ curses.panel.update_panels()
2016
2065
  # We are left with these are keys that mofidy the current line.
2017
2066
  else:
2018
2067
  # This means the user have done scrolling and is committing to modify the current line.
@@ -2058,7 +2107,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2058
2107
  # displayCurserPosition is needed as the curserPosition can be larger than the length of the encodedLine. This is wanted to keep scrolling through the history less painful
2059
2108
  displayCurserPosition = min(curserPosition,len(encodedLine) -1)
2060
2109
  stats = f'Send CMD: {encodedLine[:displayCurserPosition]}\x1b[7m{encodedLine[displayCurserPosition]}\x1b[0m{encodedLine[displayCurserPosition + 1:]}'
2061
- if stats != old_stat :
2110
+ if stats != old_stat or refresh_all:
2062
2111
  old_stat = stats
2063
2112
  # calculate the real curser position in stats as we centered the stats
2064
2113
  # if 'Send CMD: ' in stats:
@@ -2078,7 +2127,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2078
2127
  #target_length = max_x - 2 + len('\x1b[33m\x1b[0m\x1b[31m\x1b[0m\x1b[32m\x1b[0m')
2079
2128
  #bottom_stats = '└'+ f" Total: {len(hosts)} Running: \x1b[33m{host_stats['running']}\x1b[0m Failed: \x1b[31m{host_stats['failed']}\x1b[0m Finished: \x1b[32m{host_stats['finished']}\x1b[0m Waiting: {host_stats['waiting']} "[:target_length].center(target_length, "─")
2080
2129
  bottom_stats = f" Total: {len(hosts)} Running: \x1b[33m{host_stats['running']}\x1b[0m Failed: \x1b[31m{host_stats['failed']}\x1b[0m Finished: \x1b[32m{host_stats['finished']}\x1b[0m Waiting: {host_stats['waiting']} "
2081
- if bottom_stats != old_bottom_stat:
2130
+ if bottom_stats != old_bottom_stat or refresh_all:
2082
2131
  old_bottom_stat = bottom_stats
2083
2132
  #bottom_border.clear()
2084
2133
  #bottom_border.addnstr(0, 0, bottom_stats, max_x - 1)
@@ -2087,6 +2136,9 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2087
2136
  # set the maximum refresh rate to 100 Hz
2088
2137
  if time.perf_counter() - last_refresh_time < 0.01:
2089
2138
  time.sleep(max(0,0.01 - time.perf_counter() + last_refresh_time))
2139
+ if refresh_all:
2140
+ rearrangedHosts = set(hosts_to_display)
2141
+ refresh_all = False
2090
2142
  #stdscr.clear()
2091
2143
  for host_window, host in zip(host_windows, hosts_to_display):
2092
2144
  # we will only update the window if there is new output or the window is not fully printed
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes