pywebexec 1.2.6__py3-none-any.whl → 1.2.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
@@ -580,6 +580,7 @@ def get_command_output(command_id):
580
580
  @app.route('/executables', methods=['GET'])
581
581
  def list_executables():
582
582
  executables = [f for f in os.listdir('.') if os.path.isfile(f) and os.access(f, os.X_OK)]
583
+ executables.sort() # Sort the list of executables alphabetically
583
584
  return jsonify(executables)
584
585
 
585
586
  def main():
@@ -34,6 +34,8 @@ select { /* Safari bug */
34
34
  font-size: 15px;
35
35
  border: #aaa solid 1px;
36
36
  border-radius: 5px;
37
+ padding: 0px 5px 5px 0px;
38
+ min-width: 150px;
37
39
  }
38
40
  .output {
39
41
  white-space: pre-wrap;
@@ -62,6 +64,11 @@ button {
62
64
  form {
63
65
  padding-bottom: 15px;
64
66
  }
67
+ .form-inline {
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 10px;
71
+ }
65
72
  .status-icon {
66
73
  display: inline-block;
67
74
  width: 16px;
@@ -103,21 +110,89 @@ form {
103
110
  .copy_clip_ok, .copy_clip_ok:hover {
104
111
  background-image: url("/static/images/copy_ok.svg");
105
112
  }
106
- input {
107
- width: 50%;
113
+ #params, #commandName {
108
114
  -webkit-appearance: none;
109
115
  -webkit-border-radius: none;
110
116
  appearance: none;
111
117
  border-radius: 15px;
112
118
  padding: 3px;
113
- padding-right: 13px;
114
119
  border: 1px #aaa solid;
115
120
  height: 15px;
116
121
  font-size: 15px;
117
122
  outline: none;
123
+ background-color: white;
124
+ margin: 0; /* Remove margin */
125
+ }
126
+
127
+ #commandName {
128
+ width: auto; /* Allow dynamic width */
129
+ background-size: 16px 16px;
130
+ border-radius: 15px 0px 0px 15px;
131
+ border-right: 0px;
118
132
  text-indent: 5px;
133
+ padding-right: 0px;
134
+ }
135
+ #params {
136
+ width: auto; /* Allow dynamic width */
137
+ border-radius: 0px 15px 15px 0px;
138
+ border-left: 0px;
139
+ }
140
+
141
+ .input-group {
142
+ display: flex;
143
+ align-items: center;
144
+ /*width: 60%;*/
145
+ }
146
+
147
+ .show-command-list-button {
119
148
  background-color: white;
149
+ cursor: pointer;
150
+ height: 21px;
151
+ font-size: 15px;
152
+ border-top: 1px solid #aaa;
153
+ border-bottom: 1px solid #aaa;
154
+ border-radius: 0px;
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ }
159
+
160
+ .show-command-list-button .arrow {
161
+ visibility: hidden; /* Hide arrow by default */
162
+ }
163
+
164
+ .show-command-list-button:hover .arrow {
165
+ visibility: visible; /* Show arrow on hover */
166
+ }
167
+
168
+ #commandName:focus + .show-command-list-button .arrow {
169
+ visibility: visible; /* Show arrow when input has focus */
170
+ }
171
+
172
+ #commandList.show + .show-command-list-button .arrow {
173
+ display: none; /* Hide arrow when list is shown */
174
+ }
175
+
176
+ .command-list {
177
+ display: none;
178
+ position: absolute;
179
+ background-color: white;
180
+ border: 1px solid #aaa;
181
+ border-radius: 5px;
182
+ padding: 0px 5px 0px 5px;
183
+ overflow-y: auto;
184
+ z-index: 1000;
185
+ }
186
+
187
+ .command-item {
188
+ padding: 3px 5px 3px 5px;
189
+ cursor: pointer;
190
+ }
191
+
192
+ .command-item:hover, .command-item.highlighted {
193
+ background-color: #eee;
120
194
  }
195
+
121
196
  .currentcommand {
122
197
  background-color: #eef;
123
198
  }
@@ -172,6 +247,8 @@ body.dimmed * {
172
247
  visibility: hidden;
173
248
  height: 0px;
174
249
  }
175
- span { /* allow wide chars in terminal */
176
- letter-spacing: unset !important;
250
+ /* allow wide chars in terminal */
251
+ span {
252
+ letter-spacing: -0.03px !important;
177
253
  }
254
+
@@ -0,0 +1 @@
1
+ <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg"><polygon points="4 3 12 3 8 10 4 3" fill="black" /></svg>
@@ -0,0 +1,205 @@
1
+ // commands.js
2
+ let commandInput = document.getElementById('commandName');
3
+ let paramsInput = document.getElementById('params');
4
+ let commandListSelect = document.getElementById('commandList');
5
+ let showCommandListButton = document.getElementById('showCommandListButton');
6
+ let isHandlingKeydown = false;
7
+
8
+ function unfilterCommands() {
9
+ const options = commandListSelect.options;
10
+ for (let i = 0; i < options.length; i++) {
11
+ options[i].style.display = 'block';
12
+ }
13
+ commandListSelect.size = Math.min(20, commandListSelect.options.length);
14
+ }
15
+
16
+ function filterCommands() {
17
+ const value = commandInput.value.slice(0, commandInput.selectionStart);
18
+ const options = commandListSelect.options;
19
+ let nbVisibleItems = 0;
20
+ for (let i = 0; i < options.length; i++) {
21
+ if (options[i].text.startsWith(value)) {
22
+ options[i].style.display = 'block';
23
+ nbVisibleItems += 1;
24
+ } else {
25
+ options[i].style.display = 'none';
26
+ }
27
+ }
28
+ if (nbVisibleItems>1) {
29
+ commandListSelect.size = Math.min(20, nbVisibleItems);
30
+ } else {
31
+ commandListSelect.style.display = 'none';
32
+ }
33
+ }
34
+
35
+ function setCommandListPosition() {
36
+ const rect = commandInput.getBoundingClientRect();
37
+ commandListSelect.style.left = `${rect.left}px`;
38
+ commandListSelect.style.top = `${rect.bottom}px`;
39
+ }
40
+
41
+ function adjustInputWidth(input) {
42
+ input.style.width = 'auto';
43
+ input.style.width = `${input.scrollWidth}px`;
44
+ }
45
+
46
+
47
+ paramsInput.addEventListener('input', () => adjustInputWidth(paramsInput));
48
+ commandInput.addEventListener('input', () => {
49
+ adjustInputWidth(commandInput);
50
+ filterCommands(); // Filter commands on input
51
+ });
52
+
53
+ paramsInput.addEventListener('mouseover', () => {
54
+ paramsInput.focus();
55
+ paramsInput.setSelectionRange(0, paramsInput.value.length);
56
+ });
57
+
58
+ commandInput.addEventListener('mouseover', () => {
59
+ commandInput.focus();
60
+ commandInput.setSelectionRange(0, commandInput.value.length);
61
+ });
62
+
63
+ commandInput.addEventListener('input', (event) => {
64
+ if (event.inputType === 'deleteContentBackward') {
65
+ const newValue = commandInput.value.slice(0, -1);
66
+ commandInput.value = newValue;
67
+ commandInput.setSelectionRange(newValue.length, newValue.length);
68
+ }
69
+ const value = commandInput.value;
70
+ const options = commandListSelect.options;
71
+ if (value) {
72
+ const match = Array.from(options).find(option => option.text.startsWith(value));
73
+ if (match) {
74
+ commandInput.value = match.text;
75
+ commandInput.setSelectionRange(value.length, match.text.length);
76
+ } else {
77
+ commandInput.value = value.slice(0, -1);
78
+ }
79
+ }
80
+ filterCommands();
81
+ adjustInputWidth(commandInput); // Adjust width on input
82
+ });
83
+
84
+ commandInput.addEventListener('keydown', (event) => {
85
+ if (event.key === ' ' || event.key === 'ArrowRight') {
86
+ event.preventDefault();
87
+ paramsInput.focus();
88
+ paramsInput.setSelectionRange(0, paramsInput.value.length);
89
+ } else if (event.key === 'ArrowDown') {
90
+ /*setCommandListPosition();*/
91
+ unfilterCommands();
92
+ commandListSelect.style.display = 'block';
93
+ commandListSelect.focus();
94
+ commandListSelect.selectedIndex = 0;
95
+ event.preventDefault();
96
+ }
97
+ });
98
+
99
+ paramsInput.addEventListener('keydown', (event) => {
100
+ if (event.key === 'ArrowLeft' && paramsInput.selectionStart === 0) {
101
+ commandInput.focus();
102
+ commandInput.setSelectionRange(0, commandInput.value.length);
103
+ event.preventDefault();
104
+ }
105
+ });
106
+
107
+ commandListSelect.addEventListener('keydown', (event) => {
108
+ if (event.key === 'Enter') {
109
+ event.preventDefault(); // Prevent form submission
110
+ const selectedOption = commandListSelect.options[commandListSelect.selectedIndex];
111
+ commandInput.value = selectedOption.text;
112
+ commandListSelect.style.display = 'none';
113
+ adjustInputWidth(commandInput);
114
+ paramsInput.focus();
115
+ return;
116
+ }
117
+ if (event.key === 'ArrowUp' && commandListSelect.selectedIndex == 0) {
118
+ commandInput.focus();
119
+ commandListSelect.style.display = 'none'
120
+ }
121
+ });
122
+
123
+ commandListSelect.addEventListener('click', (event) => {
124
+ event.preventDefault(); // Prevent form submission
125
+ const selectedOption = commandListSelect.options[commandListSelect.selectedIndex];
126
+ commandInput.value = selectedOption.text;
127
+ commandListSelect.style.display = 'none';
128
+ adjustInputWidth(commandInput);
129
+ paramsInput.focus();
130
+ });
131
+
132
+
133
+ commandInput.addEventListener('click', () => {
134
+ setCommandListPosition();
135
+ if (commandListSelect.style.display == 'none')
136
+ commandListSelect.style.display = 'block';
137
+ else
138
+ commandListSelect.style.display = 'none';
139
+ filterCommands();
140
+ });
141
+
142
+ commandInput.addEventListener('blur', (event) => {
143
+ if (event.relatedTarget === showCommandListButton || event.relatedTarget === commandListSelect) {
144
+ event.preventDefault();
145
+ return;
146
+ }
147
+ commandListSelect.style.display = 'none';
148
+ adjustInputWidth(commandInput);
149
+ });
150
+
151
+ showCommandListButton.addEventListener('click', (event) => {
152
+ event.preventDefault();
153
+ setCommandListPosition();
154
+ if (commandListSelect.style.display == 'none')
155
+ commandListSelect.style.display = 'block';
156
+ else
157
+ commandListSelect.style.display = 'none';
158
+ unfilterCommands();
159
+ });
160
+
161
+ window.addEventListener('click', (event) => {
162
+ if (!commandInput.contains(event.target) && !commandListSelect.contains(event.target) && !showCommandListButton.contains(event.target)) {
163
+ commandListSelect.style.display = 'none';
164
+ }
165
+ });
166
+
167
+ window.addEventListener('keydown', (event) => {
168
+ if ([commandInput, paramsInput, commandListSelect].includes(document.activeElement)) return;
169
+ if (event.code === `Key${event.key.toUpperCase()}`) {
170
+ commandInput.focus();
171
+ commandInput.dispatchEvent(new KeyboardEvent('keydown', event));
172
+ }
173
+ });
174
+
175
+ window.addEventListener('resize', () => {
176
+ setCommandListPosition();
177
+ });
178
+
179
+ window.addEventListener('load', () => {
180
+ fetchExecutables();
181
+ adjustInputWidth(paramsInput); // Adjust width on load
182
+ adjustInputWidth(commandInput); // Adjust width on load
183
+ setCommandListPosition();
184
+ });
185
+
186
+ async function fetchExecutables() {
187
+ try {
188
+ const response = await fetch('/executables');
189
+ if (!response.ok) {
190
+ throw new Error('Failed to fetch command status');
191
+ }
192
+ const executables = await response.json();
193
+ commandListSelect.innerHTML = '';
194
+ executables.forEach(executable => {
195
+ const option = document.createElement('option');
196
+ option.className = 'command-item';
197
+ option.text = executable;
198
+ commandListSelect.appendChild(option);
199
+ });
200
+ } catch (error) {
201
+ alert("Failed to fetch executables");
202
+ }
203
+ commandListSelect.size = Math.min(20, commandListSelect.options.length)
204
+
205
+ }
@@ -7,7 +7,21 @@ const terminal = new Terminal({
7
7
  cursorHidden: true,
8
8
  disableStdin: true,
9
9
  convertEol: true,
10
- fontFamily: 'Consolas NF, monospace, courier-new, courier'
10
+ fontFamily: 'Consolas NF, monospace, courier-new, courier',
11
+ scrollBack: 999999,
12
+ theme: {
13
+ background: '#111412',
14
+ black: '#111412',
15
+ green: '#088a5b',
16
+ blue: "#2760aa",
17
+ red: '#ba1611',
18
+ yellow: "#cf8700",
19
+ magenta: "#4c3d80",
20
+ cyan: "#00a7aa",
21
+ brightBlack: "#243C4F",
22
+ brightBlue: "#5584b1",
23
+ },
24
+ rescaleOverlappingGlyphs: true,
11
25
  });
12
26
  const fitAddon = new FitAddon.FitAddon();
13
27
  terminal.loadAddon(fitAddon);
@@ -42,6 +56,8 @@ document.getElementById('launchForm').addEventListener('submit', async (event) =
42
56
  await new Promise(r => setTimeout(r, 200));
43
57
  fetchCommands();
44
58
  viewOutput(data.command_id);
59
+ commandInput.focus()
60
+ commandInput.setSelectionRange(0, commandInput.value.length)
45
61
  } catch (error) {
46
62
  console.log('Error running command:', error);
47
63
  }
@@ -75,7 +91,7 @@ async function fetchCommands() {
75
91
  <td>${command.command.replace(/^\.\//, '')}</td>
76
92
  <td><span class="status-icon status-${command.status}"></span>${command.status}${command.status === 'failed' ? ` (${command.exit_code})` : ''}</td>
77
93
  <td>
78
- ${command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}')">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}')">Run</button>`}
94
+ ${command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}', event)">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}', event)">Run</button>`}
79
95
  </td>
80
96
  <td class="monospace outcol">${command.last_output_line || ''}</td>
81
97
  `;
@@ -88,27 +104,6 @@ async function fetchCommands() {
88
104
  }
89
105
  }
90
106
 
91
- async function fetchExecutables() {
92
- try {
93
- const response = await fetch('/executables');
94
- if (!response.ok) {
95
- throw new Error('Failed to fetch command status');
96
- }
97
- const executables = await response.json();
98
- const commandNameSelect = document.getElementById('commandName');
99
- commandNameSelect.innerHTML = '';
100
- executables.forEach(executable => {
101
- const option = document.createElement('option');
102
- option.value = executable;
103
- option.textContent = executable;
104
- commandNameSelect.appendChild(option);
105
- });
106
- } catch (error) {
107
- console.log('Error fetching executables:', error);
108
- alert("Failed to fetch executables");
109
- }
110
- }
111
-
112
107
  async function fetchOutput(url) {
113
108
  try {
114
109
  const response = await fetch(url);
@@ -155,7 +150,9 @@ async function viewOutput(command_id) {
155
150
  }
156
151
  }
157
152
 
158
- async function relaunchCommand(command_id) {
153
+ async function relaunchCommand(command_id, event) {
154
+ event.stopPropagation();
155
+ event.stopImmediatePropagation();
159
156
  try {
160
157
  const response = await fetch(`/command_status/${command_id}`);
161
158
  if (!response.ok) {
@@ -188,7 +185,9 @@ async function relaunchCommand(command_id) {
188
185
  }
189
186
  }
190
187
 
191
- async function stopCommand(command_id) {
188
+ async function stopCommand(command_id, event) {
189
+ event.stopPropagation();
190
+ event.stopImmediatePropagation();
192
191
  try {
193
192
  const response = await fetch(`/stop_command/${command_id}`, {
194
193
  method: 'POST'
@@ -269,11 +268,7 @@ function initResizer() {
269
268
  }
270
269
 
271
270
  window.addEventListener('resize', adjustOutputHeight);
272
- window.addEventListener('load', () => {
273
- adjustOutputHeight();
274
- initResizer();
275
- });
271
+ window.addEventListener('load', initResizer);
276
272
 
277
- fetchExecutables();
278
273
  fetchCommands();
279
- setInterval(fetchCommands, 5000);
274
+ setInterval(fetchCommands, 5000);
@@ -12,11 +12,16 @@
12
12
  <div class="dimmer-text">Server not reachable</div>
13
13
  </div>
14
14
  <h2><span class="status-icon title-icon"></span>{{ title }}</h2>
15
- <form id="launchForm">
15
+ <form id="launchForm" class="form-inline">
16
16
  <label for="commandName">Command</label>
17
- <select id="commandName" name="commandName"></select>
18
- <label for="params">Params</label>
19
- <input type="text" id="params" name="params">
17
+ <div class="input-group">
18
+ <input type="text" id="commandName" name="commandName" size=5 autocomplete="off" style="width: 100px;" autofocus>
19
+ <div id="showCommandListButton" class="show-command-list-button">
20
+ <span class="arrow">&#9660;</span>
21
+ </div>
22
+ <select id="commandList" class="command-list" size="20"></select>
23
+ <input type="text" id="params" name="params" oninput="this.style.width = ((this.value.length + 1) * 8) + 'px';">
24
+ </div>
20
25
  <button type="submit">Run</button>
21
26
  </form>
22
27
  <div class="table-container" id="tableContainer">
@@ -43,5 +48,6 @@
43
48
  <script src="/static/js/xterm/xterm.js"></script>
44
49
  <script src="/static/js/xterm/xterm-addon-fit.js"></script>
45
50
  <script type="text/javascript" src="/static/js/script.js"></script>
51
+ <script type="text/javascript" src="/static/js/commands.js"></script>
46
52
  </body>
47
53
  </html>
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.2.6'
16
- __version_tuple__ = version_tuple = (1, 2, 6)
15
+ __version__ = version = '1.2.8'
16
+ __version_tuple__ = version_tuple = (1, 2, 8)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.2.6
3
+ Version: 1.2.8
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -83,7 +83,7 @@ $ pywebexec
83
83
  ```
84
84
 
85
85
  * Launch commands with params/view live output/Status using browser
86
- ![pywebexecnew5](https://github.com/user-attachments/assets/cb443a6f-e364-4302-98de-f0330d763289)
86
+ ![pywebexecnew6](https://github.com/user-attachments/assets/11415e1f-9f5f-409e-a04c-51eb062a9780)
87
87
 
88
88
  all commands output / statuses are available in the executables directory in subdirectory `.web_status`
89
89
 
@@ -1,25 +1,27 @@
1
1
  pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
2
- pywebexec/pywebexec.py,sha256=nVc6fm5jGgPLc4bv4EMVaR84qJN-yy8q9teFOD_LcRE,22665
3
- pywebexec/version.py,sha256=lMW_5tymyxUCLB-4LUHlm-nxaXdxL9eMN-CPoWJeaIw,411
4
- pywebexec/static/css/style.css,sha256=mKatR7_j_byear3GNtYMC_4e9XtXirZDL9yGeZRsDQ0,3592
2
+ pywebexec/pywebexec.py,sha256=LRKJ2KgklZsHawd97mSKodBpH5gLm3oMza30hcLcekc,22735
3
+ pywebexec/version.py,sha256=s3iHFZtIHrLA43BEwwqCJ5Ch9SA-UTWHAi57WzfyPLM,411
4
+ pywebexec/static/css/style.css,sha256=s3rpd-sbnu_T4MogbVjjT05edhmGdMtlwAMx_9n8834,5177
5
5
  pywebexec/static/css/xterm.css,sha256=gy8_LGA7Q61DUf8ElwFQzHqHMBQnbbEmpgZcbdgeSHI,5383
6
6
  pywebexec/static/images/aborted.svg,sha256=_mP43hU5QdRLFZIknBgjx-dIXrHgQG23-QV27ApXK2A,381
7
7
  pywebexec/static/images/copy.svg,sha256=d9OwtGh5GzzZHzYcDrLfNxZYLth1Q64x7bRyYxu4Px0,622
8
8
  pywebexec/static/images/copy_ok.svg,sha256=mEqUVUhSq8xaJK2msQkxRawnz_KwlCZ-tok8QS6hJ3g,451
9
+ pywebexec/static/images/down-arrow.svg,sha256=4TclEmntMvKk_F_ADXgTpGtviYo826EDmmZiGE7HQBI,121
9
10
  pywebexec/static/images/failed.svg,sha256=ADZ7IKrUyOXtqpivnz3VcH0-Wru-I5MOi3OJAkI3hxk,1439
10
11
  pywebexec/static/images/favicon.svg,sha256=ti80IfuDZwIvQcmJxkOeUaB1iMsiyOPmQmVO-h0y1IU,1126
11
12
  pywebexec/static/images/running.gif,sha256=iYuzQGkMxrakSIwt6gPieKCImGZoSAHmU5MUNZa7cpw,25696
12
13
  pywebexec/static/images/success.svg,sha256=PJDcCSTevJh7rkfSFLtc7P0pbeh8PVQBS8DaOLQemmc,489
13
- pywebexec/static/js/script.js,sha256=rvAXV7z1kzThVyeAdX55a7ss7Eemr6ibAdvOV0cFukI,9979
14
+ pywebexec/static/js/commands.js,sha256=XfrnyRVjfZ8eOq9XpJXNVVzuvzLHqa81Ot99DSRL_Rk,6954
15
+ pywebexec/static/js/script.js,sha256=1ypziT5pOmHj_15MUvBf0ChjzEpAQ-ji4W9nhxoI_iM,9775
14
16
  pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
15
17
  pywebexec/static/js/xterm/ansi_up.min.js,sha256=KNGV0vEr30hNqKQimTAvGVy-icD5A1JqMQTtvYtKR2Y,13203
16
18
  pywebexec/static/js/xterm/xterm-addon-fit.js,sha256=Pprm9pZe4SadVXS5Bc8b9VnC9Ex4QlWwA0pxOH53Gck,1460
17
19
  pywebexec/static/js/xterm/xterm.js,sha256=Bzka76jZwEhVt_LlS0e0qMw7ryGa1p5qfxFyeohphBo,283371
18
20
  pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- pywebexec/templates/index.html,sha256=3tQwVTcyK_r5BW1gHViuza59Gh-4pUZQQl7jqfvjNMY,1655
20
- pywebexec-1.2.6.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
21
- pywebexec-1.2.6.dist-info/METADATA,sha256=F2qZ480h1eB7Y3qnkF3MCgxsbzQLHr0wlDH6wS8EziE,6936
22
- pywebexec-1.2.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
23
- pywebexec-1.2.6.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
24
- pywebexec-1.2.6.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
25
- pywebexec-1.2.6.dist-info/RECORD,,
21
+ pywebexec/templates/index.html,sha256=M-9JUAR5vrk-19oF8CpxkWJvyWJkZIa71AI-1jpsMWE,2116
22
+ pywebexec-1.2.8.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
23
+ pywebexec-1.2.8.dist-info/METADATA,sha256=lNTx0DEKtyTGW4huTsiNf-ZPfoer9Ieaz5uTndBglCE,6936
24
+ pywebexec-1.2.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
25
+ pywebexec-1.2.8.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
26
+ pywebexec-1.2.8.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
27
+ pywebexec-1.2.8.dist-info/RECORD,,