pywebexec 1.2.6__tar.gz → 1.2.7__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {pywebexec-1.2.6/pywebexec.egg-info → pywebexec-1.2.7}/PKG-INFO +2 -2
- {pywebexec-1.2.6 → pywebexec-1.2.7}/README.md +1 -1
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/pywebexec.py +1 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/css/style.css +81 -5
- pywebexec-1.2.7/pywebexec/static/images/down-arrow.svg +1 -0
- pywebexec-1.2.7/pywebexec/static/js/commands.js +183 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/js/script.js +24 -31
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/templates/index.html +10 -4
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/version.py +2 -2
- {pywebexec-1.2.6 → pywebexec-1.2.7/pywebexec.egg-info}/PKG-INFO +2 -2
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec.egg-info/SOURCES.txt +2 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/.github/workflows/python-publish.yml +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/.gitignore +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/LICENSE +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pyproject.toml +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/__init__.py +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/css/xterm.css +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/images/aborted.svg +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/images/copy.svg +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/images/copy_ok.svg +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/images/failed.svg +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/images/favicon.svg +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/images/running.gif +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/images/success.svg +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/js/xterm/LICENSE +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/js/xterm/ansi_up.min.js +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/js/xterm/xterm-addon-fit.js +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/static/js/xterm/xterm.js +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec/templates/__init__.py +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec.egg-info/dependency_links.txt +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec.egg-info/entry_points.txt +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec.egg-info/requires.txt +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/pywebexec.egg-info/top_level.txt +0 -0
- {pywebexec-1.2.6 → pywebexec-1.2.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: pywebexec
|
3
|
-
Version: 1.2.
|
3
|
+
Version: 1.2.7
|
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
|
-
![
|
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
|
|
@@ -21,7 +21,7 @@ $ pywebexec
|
|
21
21
|
```
|
22
22
|
|
23
23
|
* Launch commands with params/view live output/Status using browser
|
24
|
-
![
|
24
|
+
![pywebexecnew6](https://github.com/user-attachments/assets/11415e1f-9f5f-409e-a04c-51eb062a9780)
|
25
25
|
|
26
26
|
all commands output / statuses are available in the executables directory in subdirectory `.web_status`
|
27
27
|
|
@@ -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():
|
@@ -62,6 +62,11 @@ button {
|
|
62
62
|
form {
|
63
63
|
padding-bottom: 15px;
|
64
64
|
}
|
65
|
+
.form-inline {
|
66
|
+
display: flex;
|
67
|
+
align-items: center;
|
68
|
+
gap: 10px;
|
69
|
+
}
|
65
70
|
.status-icon {
|
66
71
|
display: inline-block;
|
67
72
|
width: 16px;
|
@@ -103,21 +108,90 @@ form {
|
|
103
108
|
.copy_clip_ok, .copy_clip_ok:hover {
|
104
109
|
background-image: url("/static/images/copy_ok.svg");
|
105
110
|
}
|
106
|
-
|
107
|
-
width: 50%;
|
111
|
+
#params, #commandName {
|
108
112
|
-webkit-appearance: none;
|
109
113
|
-webkit-border-radius: none;
|
110
114
|
appearance: none;
|
111
115
|
border-radius: 15px;
|
112
116
|
padding: 3px;
|
113
|
-
padding-right: 13px;
|
114
117
|
border: 1px #aaa solid;
|
115
118
|
height: 15px;
|
116
119
|
font-size: 15px;
|
117
120
|
outline: none;
|
121
|
+
background-color: white;
|
122
|
+
margin: 0; /* Remove margin */
|
123
|
+
}
|
124
|
+
|
125
|
+
#commandName {
|
126
|
+
width: auto; /* Allow dynamic width */
|
127
|
+
background-size: 16px 16px;
|
128
|
+
border-radius: 15px 0px 0px 15px;
|
129
|
+
border-right: 0px;
|
118
130
|
text-indent: 5px;
|
131
|
+
padding-right: 0px;
|
132
|
+
}
|
133
|
+
#params {
|
134
|
+
width: auto; /* Allow dynamic width */
|
135
|
+
border-radius: 0px 15px 15px 0px;
|
136
|
+
border-left: 0px;
|
137
|
+
}
|
138
|
+
|
139
|
+
.input-group {
|
140
|
+
display: flex;
|
141
|
+
align-items: center;
|
142
|
+
/*width: 60%;*/
|
143
|
+
}
|
144
|
+
|
145
|
+
.show-command-list-button {
|
119
146
|
background-color: white;
|
147
|
+
cursor: pointer;
|
148
|
+
height: 21px;
|
149
|
+
font-size: 15px;
|
150
|
+
border-top: 1px solid #aaa;
|
151
|
+
border-bottom: 1px solid #aaa;
|
152
|
+
border-radius: 0px;
|
153
|
+
display: flex;
|
154
|
+
align-items: center;
|
155
|
+
justify-content: center;
|
156
|
+
}
|
157
|
+
|
158
|
+
.show-command-list-button .arrow {
|
159
|
+
visibility: hidden; /* Hide arrow by default */
|
160
|
+
}
|
161
|
+
|
162
|
+
.show-command-list-button:hover .arrow {
|
163
|
+
visibility: visible; /* Show arrow on hover */
|
164
|
+
}
|
165
|
+
|
166
|
+
#commandName:focus + .show-command-list-button .arrow {
|
167
|
+
visibility: visible; /* Show arrow when input has focus */
|
168
|
+
}
|
169
|
+
|
170
|
+
#commandList.show + .show-command-list-button .arrow {
|
171
|
+
display: none; /* Hide arrow when list is shown */
|
172
|
+
}
|
173
|
+
|
174
|
+
.command-list {
|
175
|
+
display: none;
|
176
|
+
position: absolute;
|
177
|
+
background-color: white;
|
178
|
+
border: 1px solid #aaa;
|
179
|
+
border-radius: 5px;
|
180
|
+
padding: 0px 5px 0px 5px;
|
181
|
+
max-height: 300px;
|
182
|
+
overflow-y: auto;
|
183
|
+
z-index: 1000;
|
184
|
+
}
|
185
|
+
|
186
|
+
.command-item {
|
187
|
+
padding: 3px 5px 3px 5px;
|
188
|
+
cursor: pointer;
|
189
|
+
}
|
190
|
+
|
191
|
+
.command-item:hover {
|
192
|
+
background-color: #eee;
|
120
193
|
}
|
194
|
+
|
121
195
|
.currentcommand {
|
122
196
|
background-color: #eef;
|
123
197
|
}
|
@@ -172,6 +246,8 @@ body.dimmed * {
|
|
172
246
|
visibility: hidden;
|
173
247
|
height: 0px;
|
174
248
|
}
|
175
|
-
|
176
|
-
|
249
|
+
/* allow wide chars in terminal */
|
250
|
+
span {
|
251
|
+
letter-spacing: -0.03px !important;
|
177
252
|
}
|
253
|
+
|
@@ -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,183 @@
|
|
1
|
+
// commands.js
|
2
|
+
let commandInput = document.getElementById('commandName');
|
3
|
+
let paramsInput = document.getElementById('params');
|
4
|
+
let commandListDiv = document.getElementById('commandList');
|
5
|
+
let showCommandListButton = document.getElementById('showCommandListButton');
|
6
|
+
|
7
|
+
function unfilterCommands() {
|
8
|
+
const items = commandListDiv.getElementsByClassName('command-item');
|
9
|
+
Array.from(items).forEach(item => {
|
10
|
+
item.style.display = 'block';
|
11
|
+
});
|
12
|
+
adjustCommandListWidth();
|
13
|
+
}
|
14
|
+
|
15
|
+
function filterCommands() {
|
16
|
+
const value = commandInput.value;
|
17
|
+
const items = commandListDiv.getElementsByClassName('command-item');
|
18
|
+
let hasVisibleItems = false;
|
19
|
+
Array.from(items).forEach(item => {
|
20
|
+
if (item.textContent.startsWith(value)) {
|
21
|
+
item.style.display = 'block';
|
22
|
+
hasVisibleItems = true;
|
23
|
+
} else {
|
24
|
+
item.style.display = 'none';
|
25
|
+
}
|
26
|
+
});
|
27
|
+
commandListDiv.style.display = hasVisibleItems ? 'block' : 'none';
|
28
|
+
if (hasVisibleItems) {
|
29
|
+
commandListDiv.classList.add('show');
|
30
|
+
} else {
|
31
|
+
commandListDiv.classList.remove('show');
|
32
|
+
}
|
33
|
+
adjustCommandListWidth(); // Adjust width after filtering commands
|
34
|
+
}
|
35
|
+
|
36
|
+
function setCommandListPosition() {
|
37
|
+
const rect = commandInput.getBoundingClientRect();
|
38
|
+
commandListDiv.style.left = `${rect.left}px`;
|
39
|
+
commandListDiv.style.top = `${rect.bottom}px`;
|
40
|
+
commandListDiv.style.minWidth = `${rect.width}px`;
|
41
|
+
}
|
42
|
+
|
43
|
+
function adjustInputWidth(input) {
|
44
|
+
input.style.width = 'auto';
|
45
|
+
input.style.width = `${input.scrollWidth}px`;
|
46
|
+
}
|
47
|
+
|
48
|
+
function adjustCommandListWidth() {
|
49
|
+
commandListDiv.style.width = 'auto'; // Reset width before recalculating
|
50
|
+
const items = Array.from(commandListDiv.getElementsByClassName('command-item'));
|
51
|
+
let maxWidth = 0;
|
52
|
+
items.forEach(item => {
|
53
|
+
if (item.style.display !== 'none') {
|
54
|
+
const itemWidth = item.scrollWidth;
|
55
|
+
if (itemWidth > maxWidth) {
|
56
|
+
maxWidth = itemWidth;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
});
|
60
|
+
commandListDiv.style.width = `${maxWidth}px`;
|
61
|
+
}
|
62
|
+
|
63
|
+
paramsInput.addEventListener('input', () => adjustInputWidth(paramsInput));
|
64
|
+
commandInput.addEventListener('input', () => {
|
65
|
+
adjustInputWidth(commandInput);
|
66
|
+
filterCommands(); // Filter commands on input
|
67
|
+
});
|
68
|
+
|
69
|
+
paramsInput.addEventListener('mouseover', () => {
|
70
|
+
paramsInput.focus();
|
71
|
+
paramsInput.setSelectionRange(0, paramsInput.value.length);
|
72
|
+
});
|
73
|
+
|
74
|
+
commandInput.addEventListener('mouseover', () => {
|
75
|
+
commandInput.focus();
|
76
|
+
commandInput.setSelectionRange(0, commandInput.value.length);
|
77
|
+
});
|
78
|
+
|
79
|
+
commandInput.addEventListener('input', (event) => {
|
80
|
+
if (event.inputType === 'deleteContentBackward') {
|
81
|
+
const newValue = commandInput.value.slice(0, -1);
|
82
|
+
commandInput.value = newValue;
|
83
|
+
commandInput.setSelectionRange(newValue.length, newValue.length);
|
84
|
+
}
|
85
|
+
const value = commandInput.value;
|
86
|
+
const items = Array.from(commandListDiv.getElementsByClassName('command-item'));
|
87
|
+
if (value) {
|
88
|
+
const match = items.find(item => item.textContent.startsWith(value));
|
89
|
+
if (match) {
|
90
|
+
commandInput.value = match.textContent;
|
91
|
+
commandInput.setSelectionRange(value.length, match.textContent.length);
|
92
|
+
} else {
|
93
|
+
commandInput.value = value.slice(0, -1);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
filterCommands();
|
97
|
+
adjustInputWidth(commandInput); // Adjust width on input
|
98
|
+
});
|
99
|
+
|
100
|
+
commandInput.addEventListener('click', () => {
|
101
|
+
setCommandListPosition();
|
102
|
+
commandListDiv.style.display = 'block';
|
103
|
+
filterCommands();
|
104
|
+
});
|
105
|
+
|
106
|
+
commandInput.addEventListener('keydown', (event) => {
|
107
|
+
if (event.key === 'ArrowDown') {
|
108
|
+
setCommandListPosition();
|
109
|
+
commandListDiv.style.display = 'block';
|
110
|
+
unfilterCommands();
|
111
|
+
}
|
112
|
+
});
|
113
|
+
|
114
|
+
commandInput.addEventListener('blur', (event) => {
|
115
|
+
if (event.relatedTarget === showCommandListButton) {
|
116
|
+
event.preventDefault();
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
commandListDiv.style.display = 'none';
|
120
|
+
commandListDiv.classList.remove('show');
|
121
|
+
adjustInputWidth(commandInput);
|
122
|
+
});
|
123
|
+
|
124
|
+
showCommandListButton.addEventListener('mousedown', (event) => {
|
125
|
+
event.preventDefault();
|
126
|
+
setCommandListPosition();
|
127
|
+
commandListDiv.style.display = 'block';
|
128
|
+
unfilterCommands();
|
129
|
+
});
|
130
|
+
|
131
|
+
window.addEventListener('click', (event) => {
|
132
|
+
if (!commandInput.contains(event.target) && !commandListDiv.contains(event.target) && !showCommandListButton.contains(event.target)) {
|
133
|
+
commandListDiv.style.display = 'none';
|
134
|
+
commandListDiv.classList.remove('show');
|
135
|
+
adjustInputWidth(commandInput);
|
136
|
+
}
|
137
|
+
});
|
138
|
+
|
139
|
+
window.addEventListener('keydown', (event) => {
|
140
|
+
if (document.activeElement !== paramsInput) {
|
141
|
+
commandInput.focus();
|
142
|
+
commandInput.dispatchEvent(new KeyboardEvent('keydown', event));
|
143
|
+
}
|
144
|
+
});
|
145
|
+
|
146
|
+
window.addEventListener('resize', () => {
|
147
|
+
setCommandListPosition();
|
148
|
+
});
|
149
|
+
|
150
|
+
window.addEventListener('load', () => {
|
151
|
+
fetchExecutables();
|
152
|
+
adjustInputWidth(paramsInput); // Adjust width on load
|
153
|
+
adjustInputWidth(commandInput); // Adjust width on load
|
154
|
+
setCommandListPosition();
|
155
|
+
});
|
156
|
+
|
157
|
+
async function fetchExecutables() {
|
158
|
+
try {
|
159
|
+
const response = await fetch('/executables');
|
160
|
+
if (!response.ok) {
|
161
|
+
throw new Error('Failed to fetch command status');
|
162
|
+
}
|
163
|
+
const executables = await response.json();
|
164
|
+
commandListDiv.innerHTML = '';
|
165
|
+
executables.forEach(executable => {
|
166
|
+
const div = document.createElement('div');
|
167
|
+
div.className = 'command-item';
|
168
|
+
div.textContent = executable;
|
169
|
+
div.addEventListener('mousedown', () => {
|
170
|
+
commandInput.value = executable;
|
171
|
+
commandListDiv.style.display = 'none';
|
172
|
+
commandListDiv.classList.remove('show');
|
173
|
+
adjustInputWidth(commandInput);
|
174
|
+
paramsInput.focus();
|
175
|
+
});
|
176
|
+
commandListDiv.appendChild(div);
|
177
|
+
});
|
178
|
+
// Ensure the elements are rendered before measuring their widths
|
179
|
+
requestAnimationFrame(adjustCommandListWidth);
|
180
|
+
} catch (error) {
|
181
|
+
alert("Failed to fetch executables");
|
182
|
+
}
|
183
|
+
}
|
@@ -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);
|
@@ -75,7 +89,7 @@ async function fetchCommands() {
|
|
75
89
|
<td>${command.command.replace(/^\.\//, '')}</td>
|
76
90
|
<td><span class="status-icon status-${command.status}"></span>${command.status}${command.status === 'failed' ? ` (${command.exit_code})` : ''}</td>
|
77
91
|
<td>
|
78
|
-
${command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}')">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}')">Run</button>`}
|
92
|
+
${command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}', event)">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}', event)">Run</button>`}
|
79
93
|
</td>
|
80
94
|
<td class="monospace outcol">${command.last_output_line || ''}</td>
|
81
95
|
`;
|
@@ -88,27 +102,6 @@ async function fetchCommands() {
|
|
88
102
|
}
|
89
103
|
}
|
90
104
|
|
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
105
|
async function fetchOutput(url) {
|
113
106
|
try {
|
114
107
|
const response = await fetch(url);
|
@@ -155,7 +148,9 @@ async function viewOutput(command_id) {
|
|
155
148
|
}
|
156
149
|
}
|
157
150
|
|
158
|
-
async function relaunchCommand(command_id) {
|
151
|
+
async function relaunchCommand(command_id, event) {
|
152
|
+
event.stopPropagation();
|
153
|
+
event.stopImmediatePropagation();
|
159
154
|
try {
|
160
155
|
const response = await fetch(`/command_status/${command_id}`);
|
161
156
|
if (!response.ok) {
|
@@ -188,7 +183,9 @@ async function relaunchCommand(command_id) {
|
|
188
183
|
}
|
189
184
|
}
|
190
185
|
|
191
|
-
async function stopCommand(command_id) {
|
186
|
+
async function stopCommand(command_id, event) {
|
187
|
+
event.stopPropagation();
|
188
|
+
event.stopImmediatePropagation();
|
192
189
|
try {
|
193
190
|
const response = await fetch(`/stop_command/${command_id}`, {
|
194
191
|
method: 'POST'
|
@@ -269,11 +266,7 @@ function initResizer() {
|
|
269
266
|
}
|
270
267
|
|
271
268
|
window.addEventListener('resize', adjustOutputHeight);
|
272
|
-
window.addEventListener('load',
|
273
|
-
adjustOutputHeight();
|
274
|
-
initResizer();
|
275
|
-
});
|
269
|
+
window.addEventListener('load', initResizer);
|
276
270
|
|
277
|
-
fetchExecutables();
|
278
271
|
fetchCommands();
|
279
|
-
setInterval(fetchCommands, 5000);
|
272
|
+
setInterval(fetchCommands, 5000);
|
@@ -12,13 +12,18 @@
|
|
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
|
-
<
|
18
|
-
|
19
|
-
|
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">▼</span>
|
21
|
+
</div>
|
22
|
+
<input type="text" id="params" name="params" oninput="this.style.width = ((this.value.length + 1) * 8) + 'px';">
|
23
|
+
</div>
|
20
24
|
<button type="submit">Run</button>
|
21
25
|
</form>
|
26
|
+
<div id="commandList" class="command-list"></div>
|
22
27
|
<div class="table-container" id="tableContainer">
|
23
28
|
<table>
|
24
29
|
<thead>
|
@@ -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>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: pywebexec
|
3
|
-
Version: 1.2.
|
3
|
+
Version: 1.2.7
|
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
|
-
![
|
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
|
|
@@ -18,10 +18,12 @@ pywebexec/static/css/xterm.css
|
|
18
18
|
pywebexec/static/images/aborted.svg
|
19
19
|
pywebexec/static/images/copy.svg
|
20
20
|
pywebexec/static/images/copy_ok.svg
|
21
|
+
pywebexec/static/images/down-arrow.svg
|
21
22
|
pywebexec/static/images/failed.svg
|
22
23
|
pywebexec/static/images/favicon.svg
|
23
24
|
pywebexec/static/images/running.gif
|
24
25
|
pywebexec/static/images/success.svg
|
26
|
+
pywebexec/static/js/commands.js
|
25
27
|
pywebexec/static/js/script.js
|
26
28
|
pywebexec/static/js/xterm/LICENSE
|
27
29
|
pywebexec/static/js/xterm/ansi_up.min.js
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|