pywebexec 2.3.13__py3-none-any.whl → 2.4.0__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
@@ -22,7 +22,7 @@ if platform.system() != 'Windows':
22
22
  import termios
23
23
  else:
24
24
  from waitress import serve
25
- from winpty import PtyProcess, WinptyError
25
+ from winpty import PTY, WinptyError
26
26
  import ipaddress
27
27
  from socket import socket, AF_INET, SOCK_STREAM
28
28
  import ssl
@@ -513,7 +513,6 @@ def script(output_file):
513
513
  signal.signal(signal.SIGWINCH, sigwinch_passthrough)
514
514
  p.interact()
515
515
 
516
-
517
516
  def run_command(fromip, user, command, params, command_id, rows, cols):
518
517
  log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}')
519
518
  start_time = datetime.now(timezone.utc).isoformat()
@@ -545,21 +544,25 @@ def run_command(fromip, user, command, params, command_id, rows, cols):
545
544
  elif platform.system() == 'Windows':
546
545
  # On Windows, use winpty
547
546
  with open(output_file_path, 'wb', buffering=0) as fd:
548
- p = PtyProcess.spawn([sys.executable, "-u", command, *params], dimensions=(rows, cols))
549
- pid = p.pid
547
+ p = PTY(cols, rows)
548
+ p.spawn(subprocess.list2cmdline([sys.executable, "-u", command, *params]))
550
549
  update_command_status(command_id, {
551
- 'pid': pid,
550
+ 'pid': p.pid,
552
551
  })
553
552
  while True:
554
553
  try:
555
- data = p.fileobj.recv(10485760)
556
- if not data:
554
+ # sleep less than 0.1s to get full output after command ends
555
+ # pty won't be readable 0.1s after command ends
556
+ time.sleep(0.09)
557
+ # cannot use blocking read as it will block forever if no output at end of commandgit d
558
+ data = p.read(10485760, blocking=False)
559
+ fd.write(data.encode())
560
+ if not p.isalive():
557
561
  break
558
- fd.write(data)
559
562
  except (EOFError, WinptyError):
560
563
  break
561
- status = p.exitstatus
562
- p.close()
564
+ status = p.get_exitstatus()
565
+ del p
563
566
  else:
564
567
  # On Unix, use pexpect
565
568
  with open(output_file_path, 'wb') as fd:
@@ -575,29 +578,29 @@ def run_command(fromip, user, command, params, command_id, rows, cols):
575
578
  end_time = datetime.now(timezone.utc).isoformat()
576
579
  # Update the status based on the result
577
580
  if status is None:
578
- exit_code = -15
581
+ status = -15
582
+ if status in [15, -15] :
579
583
  update_command_status(command_id, {
580
584
  'status': 'aborted',
581
585
  'end_time': end_time,
582
- 'exit_code': exit_code,
586
+ 'exit_code': status,
583
587
  })
584
588
  log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: command aborted')
585
589
  else:
586
- exit_code = status
587
- if exit_code == 0:
590
+ if status == 0:
588
591
  update_command_status(command_id, {
589
592
  'status': 'success',
590
593
  'end_time': end_time,
591
- 'exit_code': exit_code,
594
+ 'exit_code': status,
592
595
  })
593
596
  log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: completed successfully')
594
597
  else:
595
598
  update_command_status(command_id, {
596
599
  'status': 'failed',
597
600
  'end_time': end_time,
598
- 'exit_code': exit_code,
601
+ 'exit_code': status,
599
602
  })
600
- log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: exit code {exit_code}')
603
+ log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: exit code {status}')
601
604
 
602
605
  except Exception as e:
603
606
  end_time = datetime.now(timezone.utc).isoformat()
@@ -609,8 +612,8 @@ def run_command(fromip, user, command, params, command_id, rows, cols):
609
612
  with open(get_output_file_path(command_id), 'a') as output_file:
610
613
  output_file.write(str(e))
611
614
  app.logger.error(fromip, user, f'Error running command {command_id}: {e}')
612
- exit_code = 1
613
- return exit_code
615
+ status = 1
616
+ return status
614
617
 
615
618
  def command_str(command, params):
616
619
  try:
@@ -735,9 +738,9 @@ def stop_command(command_id):
735
738
  end_time = datetime.now(timezone.utc).isoformat()
736
739
  try:
737
740
  try:
738
- os.killpg(os.getpgid(pid), signal.SIGTERM) # Send SIGTERM to the process group
741
+ os.killpg(os.getpgid(pid), signal.SIGTERM)
739
742
  except:
740
- os.kill(pid, signal.SIGINT) # Send SIGTERM to the process
743
+ os.kill(pid, signal.SIGTERM)
741
744
  except Exception as e:
742
745
  update_command_status(command_id, {
743
746
  'status': 'aborted',
@@ -4,6 +4,27 @@
4
4
  src: url('../fonts/glyphicons-halflings-regular.eot');
5
5
  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
6
6
  }
7
+ .glyphicon-plus-sign:before {
8
+ content: "\e081";
9
+ }
10
+ .glyphicon-minus-sign:before {
11
+ content: "\e082";
12
+ }
13
+ .glyphicon-search:before {
14
+ content: "\e003";
15
+ }
16
+ .glyphicon {
17
+ position: relative;
18
+ top: 1px;
19
+ display: inline-block;
20
+ font-family: 'Glyphicons Halflings';
21
+ font-style: normal;
22
+ font-weight: normal;
23
+ line-height: 1;
24
+ -webkit-font-smoothing: antialiased;
25
+ -moz-osx-font-smoothing: grayscale;
26
+ }
27
+
7
28
 
8
29
  #schemaForm, .schema-form {
9
30
  font-size: 14px;
@@ -12,24 +33,6 @@
12
33
  div {
13
34
  scrollbar-width: thin;
14
35
  }
15
- .glyphicon-plus-sign:before {
16
- content: "\e081";
17
- }
18
- .glyphicon-minus-sign:before {
19
- content: "\e082";
20
- }
21
-
22
- .glyphicon {
23
- position: relative;
24
- top: 1px;
25
- display: inline-block;
26
- font-family: 'Glyphicons Halflings';
27
- font-style: normal;
28
- font-weight: normal;
29
- line-height: 1;
30
- -webkit-font-smoothing: antialiased;
31
- -moz-osx-font-smoothing: grayscale;
32
- }
33
36
 
34
37
  .btn {
35
38
  font-size: 14px;
@@ -71,6 +74,7 @@
71
74
  display: block;
72
75
  margin-top: 5px;
73
76
  margin-bottom: 10px;
77
+ max-width: 300px;
74
78
  }
75
79
  .form-control {
76
80
  font-family: Arial, Helvetica, sans-serif;
@@ -317,4 +321,5 @@
317
321
  .swagger-ui textarea {
318
322
  min-height: 130px;
319
323
  overflow-y: hidden;
320
- }
324
+ }
325
+
@@ -46,6 +46,57 @@ th {
46
46
  position: sticky;
47
47
  top: 0;
48
48
  z-index: 1;
49
+ vertical-align: top;
50
+ }
51
+ .th-content {
52
+ display: flex;
53
+ align-items: center;
54
+ gap: 5px;
55
+ white-space: nowrap;
56
+ }
57
+ .sort-btn {
58
+ color: #aaa;
59
+ font-size: 14px;
60
+ user-select: none;
61
+ display: inline-block;
62
+ width: 7px;
63
+ text-align: center;
64
+ flex-shrink: 0;
65
+ }
66
+ .sort-btn[data-sort-order="asc"] {
67
+ color: #5a5;
68
+ }
69
+ .sort-btn[data-sort-order="desc"] {
70
+ color: #5a5;
71
+ }
72
+ .column-filter {
73
+ display: block;
74
+ width: 90%;
75
+ max-width: 300px;
76
+ margin: 5px 0px 0px 0px;
77
+ padding: 2px 5px;
78
+ text-indent: 5px;
79
+ border: 1px solid #666;
80
+ border-radius: 15px;
81
+ font-size: 12px;
82
+ background: #444;
83
+ color: #eee
84
+ }
85
+ .column-filter:focus {
86
+ outline: none;
87
+ border-color: #666;
88
+ background: #ddd;
89
+ color: #222;
90
+ }
91
+ .column-filter:focus::placeholder {
92
+ color: transparent;
93
+ }
94
+ .column-filter::placeholder {
95
+ font-family: 'Glyphicons Halflings';
96
+ /* content: "\e003"; */
97
+ font-size: 12px;
98
+ /* text-align: right; */
99
+ color: #888;
49
100
  }
50
101
  .outcol {
51
102
  width: 100%;
@@ -133,6 +184,7 @@ form {
133
184
  width: 16px;
134
185
  height: 16px;
135
186
  min-width: 16px;
187
+ margin-left: 5px;
136
188
  margin-right: 5px;
137
189
  background-size: contain;
138
190
  background-repeat: no-repeat;
@@ -254,7 +306,7 @@ code {
254
306
  font-size: 13px;
255
307
  }
256
308
 
257
- .show-command-list-button .arrow {
309
+ .show-command-list-button .ar- {
258
310
  visibility: hidden; /* Hide arrow by default */
259
311
  }
260
312
 
@@ -469,13 +521,24 @@ body.dimmed * {
469
521
  }
470
522
  .nbrunning {
471
523
  display: inline-block;
472
- font-size: 13px;
524
+ font-size: 10px;
473
525
  font-weight: normal;
474
526
  border: 1px solid #aaa;
475
527
  border-radius: 17px;
476
528
  min-width: 17px;
477
529
  text-align: center;
478
530
  }
531
+ .row-count {
532
+ display: inline-block;
533
+ font-size: 11px;
534
+ font-weight: normal;
535
+ border: 1px solid #aaa;
536
+ border-radius: 17px;
537
+ /* min-width: 17px; */
538
+ text-align: center;
539
+ padding: 0px 4px;
540
+ margin-left: auto;
541
+ }
479
542
 
480
543
  .xterm-accessibility {
481
544
  display: none;
@@ -483,4 +546,8 @@ body.dimmed * {
483
546
  .swaggerlink {
484
547
  text-decoration: none;
485
548
  font-size: 10px;
486
- }
549
+ }
550
+
551
+ #statusRunning > span {
552
+ vertical-align: text-top;
553
+ }
@@ -181,14 +181,19 @@ async function fetchCommands(hide=false) {
181
181
  </td>
182
182
  `;
183
183
  commandsTbody.appendChild(commandRow);
184
+ const commandsTable = document.getElementById('commandsTable');
184
185
  });
185
186
  if (runningCommands.length) {
186
- document.getElementById('thStatus').innerHTML=`<span class="status-icon status-running"></span>Running <span class="system-font nbrunning">${runningCommands.length}</span>`;
187
- document.getElementById('thStatus').setAttribute('title', runningCommands.join("\n"));
187
+ const thStatus = document.getElementById('statusRunning');
188
+ thStatus.innerHTML = `Running <span class="status-icon status-running"></span><span class="system-font nbrunning">${runningCommands.length}</span>`;
189
+ thStatus.setAttribute('title', runningCommands.join("\n"));
188
190
  } else {
189
- document.getElementById('thStatus').innerHTML=`<span class="status-icon status-norun"></span>Status`;
190
- document.getElementById('thStatus').setAttribute('title', "no command running");
191
+ const thStatus = document.getElementById('statusRunning');
192
+ thStatus.innerHTML = `Status <span class="status-icon status-norun"></span>`;
193
+ thStatus.setAttribute('title', "no command running");
191
194
  }
195
+ // Apply filters after table update
196
+ applyFilters(document.getElementById('commandsTable'));
192
197
  document.getElementById('dimmer').style.display = 'none';
193
198
  } catch (error) {
194
199
  console.log('Error fetching commands:', error);
@@ -227,6 +232,10 @@ async function fetchOutput(url) {
227
232
  if (htmlContent) {
228
233
  document.getElementById('output').innerHTML = htmlContent;
229
234
  document.getElementById('output').classList.add('outputhtml');
235
+ const table = document.getElementById('output').querySelector('table');
236
+ if (table != undefined && table != null) {
237
+ initTableFilters(table);
238
+ }
230
239
  } else {
231
240
  if (slider.value == 1000)
232
241
  terminal.write(data.output);
@@ -321,6 +330,11 @@ async function viewOutput(command_id) {
321
330
  if (htmlContent) {
322
331
  document.getElementById('output').innerHTML = htmlContent;
323
332
  document.getElementById('output').classList.add('outputhtml');
333
+ const table = document.getElementById('output').querySelector('table');
334
+ console.log(table);
335
+ if (table != undefined && table != null) {
336
+ initTableFilters(table);
337
+ }
324
338
  } else {
325
339
  terminal.write(output);
326
340
  }
@@ -535,7 +549,7 @@ toggleButton.addEventListener('click', toggleFetchOutput);
535
549
  toggleFitButton.addEventListener('click', toggleFit);
536
550
  setFitIcon();
537
551
 
538
- document.getElementById('thStatus').addEventListener('click', () => {
552
+ document.getElementById('statusRunning').addEventListener('click', () => {
539
553
  showRunningOnly = !showRunningOnly;
540
554
  hiddenCommandIds = [];
541
555
  fetchCommands(showRunningOnly);
@@ -0,0 +1,149 @@
1
+ function initTableFilters(table) {
2
+ const headers = table.querySelectorAll('thead th');
3
+ headers.forEach((header, index) => {
4
+ if (index !== 4 || table!==commandsTable) { // Skip Action column
5
+ const contentSpan = document.createElement('span');
6
+ contentSpan.className = 'th-content';
7
+
8
+ // Add sort button first
9
+ const sortBtn = document.createElement('span');
10
+ sortBtn.className = 'sort-btn';
11
+ sortBtn.innerHTML = '⇕';
12
+ sortBtn.style.cursor = 'pointer';
13
+ sortBtn.setAttribute('data-sort-order', '');
14
+ sortBtn.onclick = () => toggleSort(table, index, sortBtn);
15
+
16
+ // Move existing elements into the content span
17
+ while (header.firstChild) {
18
+ contentSpan.appendChild(header.firstChild);
19
+ }
20
+
21
+ // Add sort button at the beginning
22
+ contentSpan.insertBefore(sortBtn, contentSpan.firstChild);
23
+
24
+ // Add row counter for last column
25
+ if (index === headers.length - 1) {
26
+ const rowCount = document.createElement('span');
27
+ rowCount.className = 'row-count';
28
+ rowCount.classList.add('system-font');
29
+ contentSpan.appendChild(rowCount);
30
+ }
31
+
32
+ header.appendChild(contentSpan);
33
+
34
+ // Add filter input
35
+ const input = document.createElement('input');
36
+ input.type = 'search';
37
+ input.className = 'column-filter';
38
+ input.placeholder = ''; // Unicode for magnifying glass
39
+ input.addEventListener('input', () => applyFilters(table));
40
+ header.appendChild(input);
41
+ }
42
+ });
43
+ // Initialize row count
44
+ updateRowCount(table, table.querySelectorAll('tbody tr').length);
45
+ }
46
+
47
+ function updateRowCount(table, count) {
48
+ const rowCount = table.querySelector('.row-count');
49
+ if (rowCount) {
50
+ rowCount.textContent = `${count}`;
51
+ }
52
+ }
53
+
54
+ function toggleSort(table, colIndex, sortBtn) {
55
+ // Reset other sort buttons
56
+ table.querySelectorAll('.sort-btn').forEach(btn => {
57
+ if (btn !== sortBtn) {
58
+ btn.setAttribute('data-sort-order', '');
59
+ btn.innerHTML = '⇕';
60
+ }
61
+ });
62
+
63
+ // Toggle sort order
64
+ const currentOrder = sortBtn.getAttribute('data-sort-order');
65
+ let newOrder = 'asc';
66
+ if (currentOrder === 'asc') {
67
+ newOrder = 'desc';
68
+ sortBtn.innerHTML = '⇓';
69
+ } else if (currentOrder === 'desc') {
70
+ newOrder = '';
71
+ sortBtn.innerHTML = '⇕';
72
+ } else {
73
+ sortBtn.innerHTML = '⇑';
74
+ }
75
+ sortBtn.setAttribute('data-sort-order', newOrder);
76
+ sortBtn.setAttribute('data-col-index', colIndex); // Store column index on the button
77
+ applyFilters(table);
78
+ }
79
+
80
+ function applyFilters(table) {
81
+ const rows = Array.from(table.querySelectorAll('tbody tr'));
82
+ const filters = Array.from(table.querySelectorAll('.column-filter'))
83
+ .map(filter => ({
84
+ value: filter.value.toLowerCase(),
85
+ index: filter.parentElement.cellIndex,
86
+ regexp: filter.value ? (() => {
87
+ try { return new RegExp(filter.value, 'i'); }
88
+ catch(e) { return null; }
89
+ })() : null
90
+ }));
91
+
92
+ // First apply filters
93
+ const filteredRows = rows.filter(row => {
94
+ // If no filters are active, show all rows
95
+ if (filters.every(f => !f.value)) {
96
+ row.style.display = '';
97
+ return true;
98
+ }
99
+ const cells = row.cells;
100
+ const shouldShow = !filters.some(filter => {
101
+ if (!filter.value) return false;
102
+ const cellText = cells[filter.index]?.innerText || '';
103
+ if (filter.regexp) return !filter.regexp.test(cellText);
104
+ return !cellText.toLowerCase().includes(filter.value);
105
+ });
106
+ row.style.display = shouldShow ? '' : 'none';
107
+ return shouldShow;
108
+ });
109
+
110
+ // Update row count
111
+ updateRowCount(table, filteredRows.length);
112
+
113
+ // Then apply sorting if active
114
+ const sortBtn = table.querySelector('.sort-btn[data-sort-order]:not([data-sort-order=""])');
115
+ if (sortBtn) {
116
+ const colIndex = parseInt(sortBtn.getAttribute('data-col-index'));
117
+ const sortOrder = sortBtn.getAttribute('data-sort-order');
118
+
119
+ filteredRows.sort((a, b) => {
120
+ const aVal = a.cells[colIndex]?.innerText.trim() || '';
121
+ const bVal = b.cells[colIndex]?.innerText.trim() || '';
122
+
123
+ // Check if both values are numeric
124
+ const aNum = !isNaN(aVal) && !isNaN(parseFloat(aVal));
125
+ const bNum = !isNaN(bVal) && !isNaN(parseFloat(bVal));
126
+
127
+ if (aNum && bNum) {
128
+ // Numeric comparison
129
+ return sortOrder === 'asc'
130
+ ? parseFloat(aVal) - parseFloat(bVal)
131
+ : parseFloat(bVal) - parseFloat(aVal);
132
+ }
133
+
134
+ // Fallback to string comparison
135
+ if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1;
136
+ if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1;
137
+ return 0;
138
+ });
139
+
140
+ // Reorder visible rows
141
+ const tbody = table.querySelector('tbody');
142
+ filteredRows.forEach(row => tbody.appendChild(row));
143
+ }
144
+ }
145
+
146
+ let commandsTable = document.querySelector('#commandsTable');
147
+ document.addEventListener('DOMContentLoaded', () => {
148
+ if (commandsTable) initTableFilters(commandsTable);
149
+ });
@@ -33,13 +33,13 @@
33
33
  <div id="paramsHelp" class="markdown"></div>
34
34
  </div>
35
35
  <div class="table-container" id="tableContainer">
36
- <table>
36
+ <table id="commandsTable">
37
37
  <thead>
38
38
  <tr>
39
39
  <th>ID</th>
40
40
  <th>Start Time</th>
41
41
  <th>Duration</th>
42
- <th id="thStatus"><span class="status-icon status-norun"></span>Status</th>
42
+ <th id="thStatus"><div id="statusRunning"></div></th>
43
43
  <th>Action</th>
44
44
  <th>Command</th>
45
45
  <th>Output</th>
@@ -75,6 +75,7 @@
75
75
  <script type="text/javascript" src="/static/js/script.js"></script>
76
76
  <script type="text/javascript" src="/static/js/executables.js"></script>
77
77
  <script type="text/javascript" src="/static/js/schemaform.js"></script>
78
+ <script type="text/javascript" src="/static/js/tablefilter.js"></script>
78
79
 
79
80
 
80
81
  </body>
pywebexec/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.3.13'
21
- __version_tuple__ = version_tuple = (2, 3, 13)
20
+ __version__ = version = '2.4.0'
21
+ __version_tuple__ = version_tuple = (2, 4, 0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywebexec
3
- Version: 2.3.13
3
+ Version: 2.4.0
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -1,11 +1,11 @@
1
1
  pywebexec/__init__.py,sha256=197fHJy0UDBwTTpGCGortZRr-w2kTaD7MxqdbVmTEi0,61
2
2
  pywebexec/host_ip.py,sha256=oiCMlo2o3AkkgXDarUSx8T3FWXKI0vk1-EPnx5FGBd8,1332
3
- pywebexec/pywebexec.py,sha256=iQsFNCjh_vWOVSPQuSJVHnNecYoM3i_o_fTIdcPLNEc,48241
3
+ pywebexec/pywebexec.py,sha256=NT4f7Xd4qMkAgjgwguqKKbUkOTCqT7ArRYlsW57Pfwg,48477
4
4
  pywebexec/swagger.yaml,sha256=I_oLpp7Hqel8SDEEykvpmCT-Gv3ytGlziq9bvQOrtZY,7598
5
- pywebexec/version.py,sha256=g7JB3-xSQEFZqMm5-StvX0lwySFF3upByJIPluxkg7Y,513
6
- pywebexec/static/css/form.css,sha256=riFFi02xtUXusTTZOU3RSZykutJWMFfK65-eR5RbhY8,7252
5
+ pywebexec/version.py,sha256=KyHVhKcn4Ob8_JP09Buz8x5uB4b7RJZLl3FBlZ8sWSY,511
6
+ pywebexec/static/css/form.css,sha256=2JUhraeL46JaiNoD_MSKA2JdouHkXaamhd77DnCqG8k,7291
7
7
  pywebexec/static/css/markdown.css,sha256=br4-iK9wigTs54N2KHtjgZ4KLH0THVSvJo-XZAdMHiE,1970
8
- pywebexec/static/css/style.css,sha256=pUmylXwbFIoXrdaJRVOUohlKIhOIilapH97NyIlgGV4,10343
8
+ pywebexec/static/css/style.css,sha256=mdLDqKIGLxJtR9tSaThL8po0CXoEkm9P0k_gS8_wPcA,11652
9
9
  pywebexec/static/css/swagger-ui.css,sha256=xhXN8fnUaIACGHuPIEIr9-qmyYr6Zx0k2wv4Qy7Bg1Y,154985
10
10
  pywebexec/static/css/swagger-ui.css.map,sha256=dJy-xBn_htK4BNupTMIl33ddse7BXsrCdDJWlTJodnw,258842
11
11
  pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
@@ -36,8 +36,9 @@ pywebexec/static/images/swagger-ui.svg,sha256=FR0yeOVwe4zCYKZAjCGcT_m0Mf25NexIVa
36
36
  pywebexec/static/js/executables.js,sha256=cTgCFHr_F9bFCirtfG_uR32vOY3vNUr4Ih3Wglj5lFc,11988
37
37
  pywebexec/static/js/popup.js,sha256=IaKmk2U2hEn-Nv6krf_PPW6LaG8NcpCkJKb7lUX0qZo,11457
38
38
  pywebexec/static/js/schemaform.js,sha256=2AIjwdjSDTE2ide8UMmQt4tS-7-JKqidKdopq9mNzvo,12458
39
- pywebexec/static/js/script.js,sha256=TI3TSylgBxh_a6QvYWlg4CyJ6LMPxnhFl8WRtRDGD0Y,20810
39
+ pywebexec/static/js/script.js,sha256=SpNmskHKJHza0Au7QWrb17EKqiMPbMz5CDmaLt_i3M4,21548
40
40
  pywebexec/static/js/swagger-form.js,sha256=CLcSHMhk5P4-_2MIRBoJLgEnIj_9keDDSzUugXHZjio,4565
41
+ pywebexec/static/js/tablefilter.js,sha256=ml6oGCik11W61ifGWqLLmY_sC7gOH1tsqOFXFQ336lo,5590
41
42
  pywebexec/static/js/js-yaml/LICENSE,sha256=oHvCRGi5ZUznalR9R6LbKC0HcztxXbTHOpi9Y5YflVA,1084
42
43
  pywebexec/static/js/js-yaml/js-yaml.min.js,sha256=Rdw90D3AegZwWiwpibjH9wkBPwS9U4bjJ51ORH8H69c,39430
43
44
  pywebexec/static/js/marked/LICENSE.md,sha256=jjo_gvWaYJWPVsoI9EVkfDKkcz3HymwsRvbriYRxq5w,2942
@@ -64,12 +65,12 @@ pywebexec/static/jsonform/deps/underscore.js,sha256=SzKOQsVYGX1bmddyfPzGC6yXY_rW
64
65
  pywebexec/static/jsonform/deps/img/glyphicons-halflings.png,sha256=hpJM0AbbMLnU8UGOBs172D7vK-dooQ8n0s_ybml3zO0,13826
65
66
  pywebexec/static/jsonform/lib/jsonform.js,sha256=U-BvOgq5gCvSUo36qSAK7Y91RPKOq7vZShkIYpzwlkk,138525
66
67
  pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
- pywebexec/templates/index.html,sha256=w18O2plH_yS8bqlPsu5hwFFmCj9H2hWLSV8B6ADcSwU,3900
68
+ pywebexec/templates/index.html,sha256=29-MBjeJ5mgYdrkRB27WiY0okQLYcFFycJvdCMuwvV0,3974
68
69
  pywebexec/templates/popup.html,sha256=3kpMccKD_OLLhJ4Y9KRw6Ny8wQWjVaRrUfV9y5-bDiQ,1580
69
70
  pywebexec/templates/swagger_ui.html,sha256=MAPr-z96VERAecDvX37V8q2Nxph-O0fNDBul1x2w9SI,1147
70
- pywebexec-2.3.13.dist-info/licenses/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
71
- pywebexec-2.3.13.dist-info/METADATA,sha256=wkQVPjtyJ3gvD-TRYBbkUr9506YH2jO2qUl5dxNxqak,13016
72
- pywebexec-2.3.13.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
73
- pywebexec-2.3.13.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
74
- pywebexec-2.3.13.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
75
- pywebexec-2.3.13.dist-info/RECORD,,
71
+ pywebexec-2.4.0.dist-info/licenses/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
72
+ pywebexec-2.4.0.dist-info/METADATA,sha256=of_DSpdHxXl41y6oQxQWaTvioxOEdYan-96zOHRIjz4,13015
73
+ pywebexec-2.4.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
74
+ pywebexec-2.4.0.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
75
+ pywebexec-2.4.0.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
76
+ pywebexec-2.4.0.dist-info/RECORD,,