pywebexec 1.7.5__py3-none-any.whl → 1.7.8__py3-none-any.whl

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.
pywebexec/pywebexec.py CHANGED
@@ -492,33 +492,31 @@ def script(output_file):
492
492
  p.interact()
493
493
 
494
494
 
495
- def run_command(fromip, user, command, params, command_id):
495
+ def run_command(fromip, user, command, params, command_id, rows, cols):
496
496
  # app.logger.info(f'{fromip} run_command {command_id} {user}: {command} {params}')
497
497
  log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}')
498
- start_time = datetime.now().isoformat()
498
+ start_time = datetime.now(timezone.utc).isoformat()
499
499
  update_command_status(command_id, {
500
500
  'status': 'running',
501
501
  'command': command,
502
502
  'params': params,
503
503
  'start_time': start_time,
504
504
  'user': user,
505
- 'cols': tty_cols,
506
- 'rows': tty_rows,
505
+ 'cols': cols,
506
+ 'rows': rows,
507
507
  })
508
508
  output_file_path = get_output_file_path(command_id)
509
509
  try:
510
510
  with open(output_file_path, 'wb') as fd:
511
- p = pexpect.spawn(command, params, ignore_sighup=True, timeout=None, dimensions=(tty_rows, tty_cols))
511
+ p = pexpect.spawn(command, params, ignore_sighup=True, timeout=None, dimensions=(rows, cols))
512
512
  update_command_status(command_id, {
513
- 'status': 'running',
514
513
  'pid': p.pid,
515
- 'user': user
516
514
  })
517
515
  p.logfile = fd
518
516
  p.expect(pexpect.EOF)
519
517
  fd.flush()
520
518
  status = p.wait()
521
- end_time = datetime.now().isoformat()
519
+ end_time = datetime.now(timezone.utc).isoformat()
522
520
  # Update the status based on the result
523
521
  if status is None:
524
522
  exit_code = -15
@@ -549,7 +547,7 @@ def run_command(fromip, user, command, params, command_id):
549
547
  log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: exit code {exit_code}')
550
548
 
551
549
  except Exception as e:
552
- end_time = datetime.now().isoformat()
550
+ end_time = datetime.now(timezone.utc).isoformat()
553
551
  update_command_status(command_id, {
554
552
  'status': 'failed',
555
553
  'end_time': end_time,
@@ -617,7 +615,7 @@ def check_processes():
617
615
  status = read_command_status(command_id)
618
616
  if status.get('status') == 'running' and 'pid' in status:
619
617
  if not is_process_alive(status['pid']):
620
- end_time = datetime.now().isoformat()
618
+ end_time = datetime.now(timezone.utc).isoformat()
621
619
  update_command_status(command_id, {
622
620
  'status': 'aborted',
623
621
  'end_time': end_time,
@@ -643,7 +641,7 @@ def stop_command(command_id):
643
641
  return jsonify({'error': 'Invalid command_id or command not running'}), 400
644
642
 
645
643
  pid = status['pid']
646
- end_time = datetime.now().isoformat()
644
+ end_time = datetime.now(timezone.utc).isoformat()
647
645
  try:
648
646
  #update_command_status(command_id, 'aborted', end_time=end_time, exit_code=-15)
649
647
  os.killpg(os.getpgid(pid), 15) # Send SIGTERM to the process group
@@ -725,6 +723,8 @@ def run_command_endpoint():
725
723
  data = request.json
726
724
  command = data.get('command')
727
725
  params = data.get('params', [])
726
+ rows = data.get('rows', tty_rows)
727
+ cols = data.get('cols', tty_cols)
728
728
 
729
729
  if not command:
730
730
  return jsonify({'error': 'command is required'}), 400
@@ -753,7 +753,7 @@ def run_command_endpoint():
753
753
  })
754
754
 
755
755
  # Run the command in a separate thread
756
- thread = threading.Thread(target=run_command, args=(request.remote_addr, user, command_path, params, command_id))
756
+ thread = threading.Thread(target=run_command, args=(request.remote_addr, user, command_path, params, command_id, rows, cols))
757
757
  thread.start()
758
758
 
759
759
  return jsonify({'message': 'Command is running', 'command_id': command_id})
@@ -134,12 +134,12 @@ async function fetchOutput(url) {
134
134
  fullOutput += data.output;
135
135
  if (fullOutput.length > maxSize)
136
136
  fullOutput = fullOutput.slice(-maxSize);
137
- if (percentage == 100)
137
+ if (percentage == 1000)
138
138
  terminal.write(data.output);
139
139
  else {
140
- percentage = Math.round((outputLength * 100)/fullOutput.length);
140
+ percentage = Math.round((outputLength * 1000)/fullOutput.length);
141
141
  slider.value = percentage;
142
- document.getElementById('outputPercentage').innerText = `${percentage}%`;
142
+ document.getElementById('outputPercentage').innerText = `${Math.floor(percentage/10)}%`;
143
143
  }
144
144
  nextOutputLink = data.links.next;
145
145
  if (data.status != 'running') {
@@ -160,7 +160,7 @@ async function fetchOutput(url) {
160
160
  }
161
161
 
162
162
  async function viewOutput(command_id) {
163
- slider.value = 100;
163
+ slider.value = 1000;
164
164
  adjustOutputHeight();
165
165
  currentCommandId = command_id;
166
166
  nextOutputLink = `/command_output/${command_id}${urlToken}`;
@@ -207,18 +207,18 @@ function adjustOutputHeight() {
207
207
  }
208
208
 
209
209
  function sliderUpdateOutput() {
210
- const percentage = slider.value;
210
+ const percentage = slider.value / 10;
211
211
  outputLength = Math.floor((fullOutput.length * percentage) / 100);
212
212
  const limitedOutput = fullOutput.slice(0, outputLength);
213
213
  terminal.clear();
214
214
  terminal.reset();
215
215
  terminal.write(limitedOutput);
216
- document.getElementById('outputPercentage').innerText = `${percentage}%`;
216
+ document.getElementById('outputPercentage').innerText = `${Math.floor(percentage)}%`;
217
217
  }
218
218
 
219
219
  function toggleFetchOutput() {
220
220
  if (isPaused) {
221
- slider.value = 100;
221
+ slider.value = 1000;
222
222
  document.getElementById('outputPercentage').innerText = '100%';
223
223
  terminal.clear();
224
224
  terminal.reset();
@@ -128,13 +128,15 @@ document.getElementById('launchForm').addEventListener('submit', async (event) =
128
128
  event.preventDefault();
129
129
  const commandName = document.getElementById('commandName').value;
130
130
  const params = document.getElementById('params').value.split(' ');
131
+ fitAddon.fit();
132
+ terminal.clear();
131
133
  try {
132
134
  const response = await fetch(`/run_command${urlToken}`, {
133
135
  method: 'POST',
134
136
  headers: {
135
137
  'Content-Type': 'application/json'
136
138
  },
137
- body: JSON.stringify({ command: commandName, params: params })
139
+ body: JSON.stringify({ command: commandName, params: params, rows: terminal.rows, cols: terminal.cols })
138
140
  });
139
141
  if (!response.ok) {
140
142
  throw new Error('Failed to launch command');
@@ -228,12 +230,12 @@ async function fetchOutput(url) {
228
230
  fullOutput += data.output;
229
231
  if (fullOutput.length > maxSize)
230
232
  fullOutput = fullOutput.slice(-maxSize);
231
- if (slider.value == 100)
232
- terminal.write(data.output); //.replace(/ \r/g, "\r\n")); tty size mismatch
233
+ if (slider.value == 1000)
234
+ terminal.write(data.output);
233
235
  else {
234
- percentage = Math.round((outputLength * 100)/fullOutput.length);
236
+ percentage = Math.round((outputLength * 1000)/fullOutput.length);
235
237
  slider.value = percentage;
236
- outputPercentage.innerText = `${percentage}%`;
238
+ outputPercentage.innerText = `${Math.floor(percentage/10)}%`;
237
239
  }
238
240
  nextOutputLink = data.links.next;
239
241
  if (data.status != 'running') {
@@ -256,7 +258,7 @@ function setCommandStatus(status) {
256
258
  }
257
259
 
258
260
  async function viewOutput(command_id) {
259
- slider.value = 100;
261
+ slider.value = 1000;
260
262
  outputPercentage.innerText = '100%';
261
263
  adjustOutputHeight();
262
264
  currentCommandId = command_id;
@@ -314,6 +316,8 @@ async function relaunchCommand(command_id, event) {
314
316
  alert(data.error);
315
317
  return;
316
318
  }
319
+ fitAddon.fit();
320
+ terminal.clear();
317
321
  const relaunchResponse = await fetch(`/run_command${urlToken}`, {
318
322
  method: 'POST',
319
323
  headers: {
@@ -321,7 +325,9 @@ async function relaunchCommand(command_id, event) {
321
325
  },
322
326
  body: JSON.stringify({
323
327
  command: data.command,
324
- params: data.params
328
+ params: data.params,
329
+ rows: terminal.rows,
330
+ cols: terminal.cols,
325
331
  })
326
332
  });
327
333
  if (!relaunchResponse.ok) {
@@ -420,13 +426,13 @@ function initResizer() {
420
426
 
421
427
  function sliderUpdateOutput()
422
428
  {
423
- const percentage = slider.value;
429
+ const percentage = slider.value/10;
424
430
  outputLength = Math.floor((fullOutput.length * percentage) / 100);
425
431
  const limitedOutput = fullOutput.slice(0, outputLength);
426
432
  terminal.clear();
427
433
  terminal.reset();
428
434
  terminal.write(limitedOutput);
429
- outputPercentage.innerText = `${percentage}%`;
435
+ outputPercentage.innerText = `${Math.floor(percentage)}%`;
430
436
  }
431
437
 
432
438
  slider.addEventListener('input', sliderUpdateOutput);
@@ -47,7 +47,7 @@
47
47
  <div id="pausedMessage" class="paused-message">Paused</div>
48
48
  <div class="slider-container">
49
49
  <span class="command-info"><span id="commandStatus" class="status-icon"></span><span id="commandInfo"></span></span>
50
- <input type="range" id="outputSlider" min="0" max="100" value="100">
50
+ <input type="range" id="outputSlider" min="0" max="1000" value="1000">
51
51
  <label for="outputSlider"><span id="outputPercentage">100%</span></label>
52
52
  <button id="toggleFetch" class="pause-resume-button pause"></button>
53
53
  <button id="decreaseFontSize" class="font-size-button font-decrease"></button>
@@ -15,7 +15,7 @@
15
15
  <div id="pausedMessage" class="paused-message">Paused</div>
16
16
  <div class="slider-container">
17
17
  <span class="command-info"><span id="commandStatus"></span><span id="commandInfo"></span></span>
18
- <input type="range" id="outputSlider" min="0" max="100" value="100">
18
+ <input type="range" id="outputSlider" min="0" max="1000" value="1000">
19
19
  <label for="outputSlider"><span id="outputPercentage">100%</span></label>
20
20
  <button id="toggleFetch" class="pause-resume-button pause"></button>
21
21
  <button id="decreaseFontSize" class="font-size-button font-decrease"></button>
pywebexec/version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.7.5'
16
- __version_tuple__ = version_tuple = (1, 7, 5)
15
+ __version__ = version = '1.7.8'
16
+ __version_tuple__ = version_tuple = (1, 7, 8)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.7.5
3
+ Version: 1.7.8
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -199,7 +199,7 @@ $ curl http://myhost:8080/command_output/<command_id> -H "Accept: text/plain"
199
199
 
200
200
  | method | route | params/payload | returns
201
201
  |-----------|-----------------------------|--------------------|---------------------|
202
- | POST | /run_command | command: str<br>params: array[str] | command_id: uuid<br>message: str |
202
+ | POST | /run_command | command: str<br>params: array[str]<br>rows: int<br>cols: int | command_id: uuid<br>message: str |
203
203
  | POST | /stop_command/command_id | | message: str |
204
204
  | GET | /command_status/command_id | | command_id: uuid<br>command: str<br>params: array[str]<br>start_time: isotime<br>end_time: isotime<br>status: str<br>exit_code: int<br>last_output_line: str |
205
205
  | GET | /commands | | array of<br>command_id: uuid<br>command: str<br>start_time: isotime<br>end_time: isotime<br>status: str<br>exit_code: int<br>last_output_line: str |
@@ -1,6 +1,6 @@
1
1
  pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
2
- pywebexec/pywebexec.py,sha256=VBR6KmRiHNCd0ZvGEu_5sTqzVrUEPBhM8Hi6knS1QcA,33526
3
- pywebexec/version.py,sha256=1PmSWdbIl6FG3GO33tTDihvotemU0-1S8uTOfaXKRIY,411
2
+ pywebexec/pywebexec.py,sha256=MmpHK2SscvIPj1gmV9AcJXYfeaL-at5BDgLhKIxzZBc,33604
3
+ pywebexec/version.py,sha256=NhHD0wua0K7wHcsWg_CFF2Q9IzP5fWP-0bSHZe_REbQ,411
4
4
  pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
5
5
  pywebexec/static/css/style.css,sha256=3s7QgbCh4wb7kfZ7Pjo-B6o3lDIBogZ-3j6AfaPdpzU,8209
6
6
  pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
@@ -21,8 +21,8 @@ pywebexec/static/images/resume.svg,sha256=99LP1Ya2JXakRCO9kW8JMuT_4a_CannF65Eiuw
21
21
  pywebexec/static/images/running.svg,sha256=fBCYwYb2O9K4N3waC2nURP25NRwZlqR4PbDZy6JQMww,610
22
22
  pywebexec/static/images/success.svg,sha256=NVwezvVMplt46ElW798vqGfrL21Mw_DWHUp_qiD_FU8,489
23
23
  pywebexec/static/js/commands.js,sha256=h2fkd9qpypLBxvhEEbay23nwuqUwcKJA0vHugcyL8pU,7961
24
- pywebexec/static/js/popup.js,sha256=oVkwnOKHae11omBiTZs-00NVeS9HGNEiUo71JSwOqn4,9382
25
- pywebexec/static/js/script.js,sha256=TtniIzVgYa9A0nXPS13MpcGyGZfrM-r8eb0pmpqgXJA,18301
24
+ pywebexec/static/js/popup.js,sha256=MFGDVjTrddLeeAWmUarWSSP1LiQBOJGPNaSGLn994V4,9418
25
+ pywebexec/static/js/script.js,sha256=J0kOMT5eZx-3YdZ4UGKJLYZ6CjtLzHUXm3laV1e1tUw,18496
26
26
  pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
27
27
  pywebexec/static/js/xterm/addon-canvas.js,sha256=ez6QTVvsmLVNJmdJlM-ZQ5bErwlxAQ_9DUmDIptl2TM,94607
28
28
  pywebexec/static/js/xterm/addon-canvas.js.map,sha256=ECBA4B-BqUpdFeRzlsEWLSQnudnhLP-yPQJ8_hKquMo,379537
@@ -33,11 +33,11 @@ pywebexec/static/js/xterm/addon-unicode11.js.map,sha256=paDj5KKtTIUGedQn2x7CaUTD
33
33
  pywebexec/static/js/xterm/xterm.js,sha256=H5kaw7Syg-v5bmCuI6AKUnZd06Lkb6b92p8aqwMvdJU,289441
34
34
  pywebexec/static/js/xterm/xterm.js.map,sha256=Y7O2Pb-fIS7Z8AC1D5s04_aiW_Jf1f4mCfN0U_OI6Zw,1118392
35
35
  pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- pywebexec/templates/index.html,sha256=2fEN8cggHBEd8-RamDFpnekVJtIbRembFSw0-1YEptc,2979
37
- pywebexec/templates/popup.html,sha256=GT2jY7oOxpCaBaRl924QJWdFBmfSOP952T13d37R_pY,1506
38
- pywebexec-1.7.5.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
39
- pywebexec-1.7.5.dist-info/METADATA,sha256=ueE1L7xvCOkQEjAF480gyouw1IiRKk0tVOUahe1dxU4,8122
40
- pywebexec-1.7.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
41
- pywebexec-1.7.5.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
42
- pywebexec-1.7.5.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
43
- pywebexec-1.7.5.dist-info/RECORD,,
36
+ pywebexec/templates/index.html,sha256=CcLN3v_42lnj4juiTntDTiGHJ8-zQnmB9oqpSK_0kks,2981
37
+ pywebexec/templates/popup.html,sha256=f5m4u8WKpkevL2mQamGqo4_y-rSuLOXGuNsezuUbniY,1508
38
+ pywebexec-1.7.8.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
39
+ pywebexec-1.7.8.dist-info/METADATA,sha256=K-4wEuwRvadD43jgSHJmQYSsJoRJUuU1zD-4STQgxbc,8148
40
+ pywebexec-1.7.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
41
+ pywebexec-1.7.8.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
42
+ pywebexec-1.7.8.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
43
+ pywebexec-1.7.8.dist-info/RECORD,,