pywebexec 2.1.19__py3-none-any.whl → 2.2.1__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 +23 -22
- pywebexec/static/css/markdown.css +1 -1
- pywebexec/static/css/style.css +1 -0
- pywebexec/static/js/executables.js +30 -36
- pywebexec/static/js/popup.js +24 -8
- pywebexec/static/js/schemaform.js +86 -9
- pywebexec/static/js/script.js +22 -6
- pywebexec/static/js/swagger-form.js +40 -20
- pywebexec/swagger.yaml +190 -170
- pywebexec/version.py +2 -2
- {pywebexec-2.1.19.dist-info → pywebexec-2.2.1.dist-info}/METADATA +1 -1
- {pywebexec-2.1.19.dist-info → pywebexec-2.2.1.dist-info}/RECORD +16 -16
- {pywebexec-2.1.19.dist-info → pywebexec-2.2.1.dist-info}/LICENSE +0 -0
- {pywebexec-2.1.19.dist-info → pywebexec-2.2.1.dist-info}/WHEEL +0 -0
- {pywebexec-2.1.19.dist-info → pywebexec-2.2.1.dist-info}/entry_points.txt +0 -0
- {pywebexec-2.1.19.dist-info → pywebexec-2.2.1.dist-info}/top_level.txt +0 -0
pywebexec/pywebexec.py
CHANGED
@@ -1038,15 +1038,12 @@ def swagger_yaml():
|
|
1038
1038
|
swagger_spec = yaml.safe_load(swagger_spec_str)
|
1039
1039
|
# Update existing POST /commands enum if present
|
1040
1040
|
executables = get_executables()
|
1041
|
-
post_cmd = swagger_spec.get('paths', {}).get('/commands', {}).get('post')
|
1042
|
-
if post_cmd:
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
if 'command' in props:
|
1048
|
-
props['command']['enum'] = [e['command'] for e in executables]
|
1049
|
-
# Add dynamic paths for each
|
1041
|
+
post_cmd = swagger_spec.get('paths', {}).get('/commands', {}).get('post', {})
|
1042
|
+
if post_cmd and 'requestBody' in post_cmd:
|
1043
|
+
schema = post_cmd['requestBody'].get('content', {}).get('application/json', {}).get('schema', {})
|
1044
|
+
if 'properties' in schema and 'command' in schema['properties']:
|
1045
|
+
schema['properties']['command']['enum'] = [e['command'] for e in executables]
|
1046
|
+
|
1050
1047
|
# Add dynamic paths for each executable:
|
1051
1048
|
for exe in executables:
|
1052
1049
|
dynamic_path = "/commands/" + exe["command"]
|
@@ -1065,28 +1062,32 @@ def swagger_yaml():
|
|
1065
1062
|
"parallel": {"type": "integer", "description": 'nb parallel jobs', "default": 1, "required": True, "minimum": 1, "maximum": 100},
|
1066
1063
|
"delay": {"type": "number", "description": "initial delay in s between jobs", "default": 10, "required": True, "minimum": 0, "maximum": 600},
|
1067
1064
|
})
|
1065
|
+
|
1068
1066
|
swagger_spec.setdefault("paths", {})[dynamic_path] = {
|
1069
1067
|
"post": {
|
1070
1068
|
"summary": f"Run command {exe['command']}",
|
1071
1069
|
"tags": ["run_commands"],
|
1072
1070
|
"description": f"{exe['help']}",
|
1073
|
-
"
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
"schema": cmd_schema
|
1071
|
+
"requestBody": {
|
1072
|
+
"required": True,
|
1073
|
+
"content": {
|
1074
|
+
"application/json": {
|
1075
|
+
"schema": cmd_schema
|
1076
|
+
}
|
1080
1077
|
}
|
1081
|
-
|
1078
|
+
},
|
1082
1079
|
"responses": {
|
1083
1080
|
"200": {
|
1084
1081
|
"description": "Command started",
|
1085
|
-
"
|
1086
|
-
"
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1082
|
+
"content": {
|
1083
|
+
"application/json": {
|
1084
|
+
"schema": {
|
1085
|
+
"type": "object",
|
1086
|
+
"properties": {
|
1087
|
+
"message": {"type": "string"},
|
1088
|
+
"command_id": {"type": "string"}
|
1089
|
+
}
|
1090
|
+
}
|
1090
1091
|
}
|
1091
1092
|
}
|
1092
1093
|
}
|
pywebexec/static/css/style.css
CHANGED
@@ -264,44 +264,38 @@ paramsInput.addEventListener('focus', () => {
|
|
264
264
|
}
|
265
265
|
if (gExecutables[currentCmd] && gExecutables[currentCmd].schema && gExecutables[currentCmd].schema.properties && paramsContainer.style.display == 'none') {
|
266
266
|
createSchemaForm($('#schemaForm'), gExecutables[currentCmd].schema, async function (errors, values) {
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
payload
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
body: JSON.stringify(payload)
|
289
|
-
});
|
290
|
-
|
291
|
-
if (!response.ok) {
|
292
|
-
throw new Error('Failed to launch command');
|
293
|
-
}
|
294
|
-
|
295
|
-
const data = await response.json();
|
296
|
-
viewOutput(data.command_id);
|
297
|
-
fetchCommands();
|
298
|
-
commandInput.focus();
|
299
|
-
commandInput.setSelectionRange(0, commandInput.value.length);
|
300
|
-
} catch (error) {
|
301
|
-
console.log('Error running command:', error);
|
267
|
+
const commandName = commandInput.value;
|
268
|
+
fitAddon.fit();
|
269
|
+
terminal.clear();
|
270
|
+
payload = { params: values, rows: terminal.rows, cols: terminal.cols }
|
271
|
+
if ('parallel' in values) {
|
272
|
+
payload['parallel'] = values['parallel'];
|
273
|
+
payload['delay'] = values['delay'];
|
274
|
+
delete payload['params']['parallel'];
|
275
|
+
delete payload['params']['delay'];
|
276
|
+
}
|
277
|
+
try {
|
278
|
+
const response = await fetch(`/commands/${commandName}`, {
|
279
|
+
method: 'POST',
|
280
|
+
headers: {
|
281
|
+
'Content-Type': 'application/json'
|
282
|
+
},
|
283
|
+
body: JSON.stringify(payload)
|
284
|
+
});
|
285
|
+
|
286
|
+
if (!response.ok) {
|
287
|
+
throw new Error('Failed to launch command');
|
302
288
|
}
|
289
|
+
|
290
|
+
const data = await response.json();
|
291
|
+
viewOutput(data.command_id);
|
292
|
+
fetchCommands();
|
293
|
+
commandInput.focus();
|
294
|
+
commandInput.setSelectionRange(0, commandInput.value.length);
|
295
|
+
} catch (error) {
|
296
|
+
console.log('Error running command:', error);
|
303
297
|
}
|
304
|
-
}, currentCmd
|
298
|
+
}, currentCmd);
|
305
299
|
setHelpDivPosition();
|
306
300
|
paramsContainer.style.display = 'block';
|
307
301
|
const input1 = schemaFormPW.querySelector('input, select, textarea');
|
pywebexec/static/js/popup.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
const maxScrollback = 99999;
|
2
2
|
const maxSize = 10485760; // 10MB
|
3
|
-
let fontSize = 14;
|
3
|
+
let fontSize = +localStorage.getItem('popupFontSize') || 14;
|
4
4
|
let terminal = new Terminal({
|
5
5
|
cursorBlink: false,
|
6
6
|
cursorInactiveStyle: 'none',
|
@@ -75,8 +75,8 @@ let slider = null;
|
|
75
75
|
let isPaused = false;
|
76
76
|
let cols = 0;
|
77
77
|
let rows = 0;
|
78
|
-
let fitWindow = false;
|
79
|
-
|
78
|
+
let fitWindow = localStorage.getItem('popupFitWindow') === 'false' ? false : true;
|
79
|
+
console.log(localStorage.getItem('popupFitWindow'));
|
80
80
|
const toggleButton = document.getElementById('toggleFetch');
|
81
81
|
const pausedMessage = document.getElementById('pausedMessage');
|
82
82
|
const toggleFitButton = document.getElementById('toggleFit');
|
@@ -169,9 +169,13 @@ async function viewOutput(command_id) {
|
|
169
169
|
}
|
170
170
|
const data = await response.json();
|
171
171
|
const commandInfo = document.getElementById('commandInfo');
|
172
|
-
|
172
|
+
if (data.command.endsWith('/run-para')) {
|
173
|
+
command = `${data.params.join(' ').replace(/^.* -- run .\//, 'batch ')}`;
|
174
|
+
} else {
|
175
|
+
command = `${data.command.replace(/^\.\//, '')} ${data.params.join(' ')}`;
|
176
|
+
}
|
173
177
|
setCommandStatus(data.status);
|
174
|
-
commandInfo.
|
178
|
+
commandInfo.innerHTML = command;
|
175
179
|
commandInfo.setAttribute('title', command);
|
176
180
|
document.title = `${data.command} ${data.params.join(' ')} - [${data.status}]`;
|
177
181
|
if (data.command == 'term')
|
@@ -232,21 +236,31 @@ function toggleFetchOutput() {
|
|
232
236
|
}
|
233
237
|
isPaused = !isPaused;
|
234
238
|
}
|
235
|
-
function
|
236
|
-
|
239
|
+
function setFitIcon()
|
240
|
+
{
|
241
|
+
console.log(fitWindow);
|
237
242
|
if (fitWindow) {
|
243
|
+
toggleFitButton.classList.remove('fit-window');
|
238
244
|
toggleFitButton.classList.add('fit-tty');
|
239
245
|
toggleFitButton.setAttribute('title', 'terminal fit tty');
|
240
246
|
} else {
|
241
247
|
toggleFitButton.classList.remove('fit-tty');
|
248
|
+
toggleFitButton.classList.add('fit-window');
|
242
249
|
toggleFitButton.setAttribute('title', 'terminal fit window');
|
243
|
-
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
|
253
|
+
function toggleFit() {
|
254
|
+
fitWindow = ! fitWindow;
|
255
|
+
setFitIcon();
|
256
|
+
localStorage.setItem('popupFitWindow', fitWindow);
|
244
257
|
autoFit();
|
245
258
|
viewOutput(currentCommandId);
|
246
259
|
}
|
247
260
|
|
248
261
|
toggleButton.addEventListener('click', toggleFetchOutput);
|
249
262
|
toggleFitButton.addEventListener('click', toggleFit);
|
263
|
+
setFitIcon();
|
250
264
|
window.addEventListener('resize', adjustOutputHeight);
|
251
265
|
window.addEventListener('load', () => {
|
252
266
|
slider = document.getElementById('outputSlider');
|
@@ -259,11 +273,13 @@ window.addEventListener('load', () => {
|
|
259
273
|
document.getElementById('decreaseFontSize').addEventListener('click', () => {
|
260
274
|
fontSize = Math.max(8, fontSize - 1);
|
261
275
|
terminal.options.fontSize = fontSize;
|
276
|
+
localStorage.setItem('popupFontSize', fontSize);
|
262
277
|
autoFit();
|
263
278
|
});
|
264
279
|
|
265
280
|
document.getElementById('increaseFontSize').addEventListener('click', () => {
|
266
281
|
fontSize = Math.min(32, fontSize + 1);
|
267
282
|
terminal.options.fontSize = fontSize;
|
283
|
+
localStorage.setItem('popupFontSize', fontSize);
|
268
284
|
autoFit();
|
269
285
|
});
|
@@ -11,7 +11,7 @@ function adjustInputWidth(input) {
|
|
11
11
|
function formInputHandle() {
|
12
12
|
schemaForm.querySelectorAll('input[type="text"], input[type="number"]').forEach(input => {
|
13
13
|
if (! inputHandlers.includes(input)) {
|
14
|
-
val = input.
|
14
|
+
val = input.placeholder;
|
15
15
|
if (val) {
|
16
16
|
size = Math.max(val.length, 2)
|
17
17
|
if (input.type== 'number') {
|
@@ -54,12 +54,46 @@ function extractKeysAndPlaceholders(obj, formoptions, prefix = '') {
|
|
54
54
|
return result;
|
55
55
|
}
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
// ...existing code...
|
58
|
+
|
59
|
+
function convertTextareaToArray(values, formDesc, schema) {
|
60
|
+
// Helper function to get schema type for a key path
|
61
|
+
function getSchemaType(schema, keyPath) {
|
62
|
+
const keys = keyPath.split('.');
|
63
|
+
let current = schema.properties;
|
64
|
+
for (const key of keys) {
|
65
|
+
if (!current || !current[key] || !current[key].properties) {
|
66
|
+
return current?.[key]?.type;
|
67
|
+
}
|
68
|
+
current = current[key].properties;
|
69
|
+
}
|
70
|
+
return null;
|
71
|
+
}
|
72
|
+
|
73
|
+
// Convert textarea values to arrays if schema type matches
|
74
|
+
for (let i = 0; i < formDesc.length; i++) {
|
75
|
+
if (formDesc[i].type == 'textarea') {
|
76
|
+
const schemaType = getSchemaType(schema, formDesc[i].key);
|
77
|
+
if (schemaType === 'array') {
|
78
|
+
const keys = formDesc[i].key.split('.');
|
79
|
+
let obj = values;
|
80
|
+
for (let j = 0; j < keys.length - 1; j++) {
|
81
|
+
obj = obj[keys[j]];
|
82
|
+
}
|
83
|
+
const lastKey = keys[keys.length - 1];
|
84
|
+
const val = obj[lastKey];
|
85
|
+
if (val) {
|
86
|
+
obj[lastKey] = val.trim().split(/[\s\r,]+/).filter(x => x);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
62
90
|
}
|
91
|
+
return values;
|
92
|
+
}
|
93
|
+
|
94
|
+
// ...existing code...
|
95
|
+
|
96
|
+
function createSchemaForm(form, schema, onSubmit, schemaName) {
|
63
97
|
if (schema && schema.schema_options) {
|
64
98
|
schema_options = schema.schema_options;
|
65
99
|
} else {
|
@@ -80,6 +114,33 @@ function createSchemaForm(form, schema, onSubmit, schemaName) {
|
|
80
114
|
}
|
81
115
|
}
|
82
116
|
formDesc = extractKeysAndPlaceholders(schema, formoptions);
|
117
|
+
if (schemaValues[schemaName]) {
|
118
|
+
value = schemaValues[schemaName];
|
119
|
+
// convert array for textarea formDesc type to string separated by newlines
|
120
|
+
// if in formDesc a key has type textarea, convert the value to string separated by newlines
|
121
|
+
// formDesc=[{key: 'db.sid', type: 'textarea'}]
|
122
|
+
// value = {db: {sid: ['AA', 'BB']}}
|
123
|
+
// convert to
|
124
|
+
// value = {db: {sid: 'AA\nBB'}}
|
125
|
+
for (let i = 0; i < formDesc.length; i++) {
|
126
|
+
if (formDesc[i].type === 'textarea') {
|
127
|
+
const keys = formDesc[i].key.split('.');
|
128
|
+
let obj = value;
|
129
|
+
for (let j = 0; j < keys.length - 1; j++) {
|
130
|
+
if (!(keys[j] in obj)) obj[keys[j]] = {};
|
131
|
+
obj = obj[keys[j]];
|
132
|
+
}
|
133
|
+
const lastKey = keys[keys.length - 1];
|
134
|
+
const val = obj[lastKey];
|
135
|
+
if (val && Array.isArray(val)) {
|
136
|
+
obj[lastKey] = val.join('\n');
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
} else {
|
141
|
+
value = {};
|
142
|
+
}
|
143
|
+
|
83
144
|
schemaForm = form[0];
|
84
145
|
if (onSubmit != null) {
|
85
146
|
if (schema_options && schema_options.batch_param) {
|
@@ -149,9 +210,20 @@ function createSchemaForm(form, schema, onSubmit, schemaName) {
|
|
149
210
|
form[0].classList.add('form-inline');
|
150
211
|
jsform = form.jsonForm({
|
151
212
|
schema: schema,
|
152
|
-
onSubmit:
|
213
|
+
onSubmit: function (errors, values) {
|
214
|
+
convertTextareaToArray(values, formDesc, schema);
|
215
|
+
env = JSV.createEnvironment();
|
216
|
+
report = env.validate(values, schema);
|
217
|
+
errors = report.errors;
|
218
|
+
if (errors.length > 0) {
|
219
|
+
alert(errors[0].message);
|
220
|
+
return false;
|
221
|
+
}
|
222
|
+
onSubmit(errors, values);
|
223
|
+
},
|
153
224
|
form: formDesc,
|
154
225
|
value: value,
|
226
|
+
validate: false,
|
155
227
|
// params: {
|
156
228
|
// fieldHtmlClass: "input-small",
|
157
229
|
// }
|
@@ -169,15 +241,20 @@ function createSchemaForm(form, schema, onSubmit, schemaName) {
|
|
169
241
|
txt.addEventListener("input", () => adjustTxtHeight(txt));
|
170
242
|
});
|
171
243
|
form[0].addEventListener('input', () => {
|
172
|
-
schemaValues[schemaName] = jsform.root.getFormValues();
|
244
|
+
schemaValues[schemaName] = convertTextareaToArray(jsform.root.getFormValues(), formDesc, schema);
|
173
245
|
localStorage.setItem('schemaValues', JSON.stringify(schemaValues));
|
174
246
|
});
|
175
247
|
|
176
248
|
return jsform;
|
177
249
|
}
|
178
250
|
function adjustTxtHeight(txt) {
|
251
|
+
if (txt.value.includes('\n')) {
|
252
|
+
delta = 2;
|
253
|
+
} else {
|
254
|
+
delta = 0;
|
255
|
+
}
|
179
256
|
txt.style.height = "0";
|
180
|
-
txt.style.height = txt.scrollHeight
|
257
|
+
txt.style.height = `${txt.scrollHeight+delta}px`;
|
181
258
|
}
|
182
259
|
async function getSwaggerSpec() {
|
183
260
|
const response = await fetch('/swagger.yaml');
|
pywebexec/static/js/script.js
CHANGED
@@ -7,11 +7,11 @@ let fullOutput = '';
|
|
7
7
|
let outputLength = 0;
|
8
8
|
const maxScrollback = 99999;
|
9
9
|
const maxSize = 10485760; // 10MB
|
10
|
-
let fontSize = 14;
|
10
|
+
let fontSize = +localStorage.getItem('fontSize') || 14;
|
11
11
|
let isPaused = false;
|
12
12
|
let showRunningOnly = false;
|
13
13
|
let hiddenCommandIds = [];
|
14
|
-
let fitWindow = false;
|
14
|
+
let fitWindow = localStorage.getItem('fitWindow') === 'false' ? false : true;
|
15
15
|
let cols = 0;
|
16
16
|
let rows = 0;
|
17
17
|
|
@@ -260,7 +260,11 @@ async function viewOutput(command_id) {
|
|
260
260
|
}
|
261
261
|
const data = await response.json();
|
262
262
|
const commandInfo = document.getElementById('commandInfo');
|
263
|
-
|
263
|
+
if (data.command.endsWith('/run-para')) {
|
264
|
+
command = `${data.params.join(' ').replace(/^.* -- run .\//, 'batch ')}`;
|
265
|
+
} else {
|
266
|
+
command = `${data.command.replace(/^\.\//, '')} ${data.params.join(' ')}`;
|
267
|
+
}
|
264
268
|
setCommandStatus(data.status)
|
265
269
|
commandInfo.innerHTML = command;
|
266
270
|
commandInfo.setAttribute('title', command);
|
@@ -415,12 +419,14 @@ slider.addEventListener('input', sliderUpdateOutput);
|
|
415
419
|
document.getElementById('decreaseFontSize').addEventListener('click', () => {
|
416
420
|
fontSize = Math.max(8, fontSize - 1);
|
417
421
|
terminal.options.fontSize = fontSize;
|
422
|
+
localStorage.setItem('fontSize', fontSize);
|
418
423
|
autoFit();
|
419
424
|
});
|
420
425
|
|
421
426
|
document.getElementById('increaseFontSize').addEventListener('click', () => {
|
422
427
|
fontSize = Math.min(32, fontSize + 1);
|
423
428
|
terminal.options.fontSize = fontSize;
|
429
|
+
localStorage.setItem('fontSize', fontSize);
|
424
430
|
autoFit();
|
425
431
|
});
|
426
432
|
|
@@ -451,21 +457,31 @@ function toggleFetchOutput() {
|
|
451
457
|
}
|
452
458
|
isPaused = !isPaused;
|
453
459
|
}
|
454
|
-
|
455
|
-
|
460
|
+
|
461
|
+
function setFitIcon()
|
462
|
+
{
|
456
463
|
if (fitWindow) {
|
464
|
+
toggleFitButton.classList.remove('fit-window');
|
457
465
|
toggleFitButton.classList.add('fit-tty');
|
458
466
|
toggleFitButton.setAttribute('title', 'terminal fit tty');
|
459
467
|
} else {
|
460
468
|
toggleFitButton.classList.remove('fit-tty');
|
469
|
+
toggleFitButton.classList.add('fit-window');
|
461
470
|
toggleFitButton.setAttribute('title', 'terminal fit window');
|
462
|
-
}
|
471
|
+
}
|
472
|
+
}
|
473
|
+
|
474
|
+
function toggleFit() {
|
475
|
+
fitWindow = ! fitWindow;
|
476
|
+
setFitIcon();
|
477
|
+
localStorage.setItem('fitWindow', fitWindow);
|
463
478
|
autoFit();
|
464
479
|
viewOutput(currentCommandId);
|
465
480
|
}
|
466
481
|
|
467
482
|
toggleButton.addEventListener('click', toggleFetchOutput);
|
468
483
|
toggleFitButton.addEventListener('click', toggleFit);
|
484
|
+
setFitIcon();
|
469
485
|
|
470
486
|
document.getElementById('thStatus').addEventListener('click', () => {
|
471
487
|
showRunningOnly = !showRunningOnly;
|
@@ -1,8 +1,9 @@
|
|
1
1
|
let ui;
|
2
2
|
let swaggerSchemas = {};
|
3
|
+
|
3
4
|
function addFormInputListener(textArea, jsform){
|
4
5
|
return function (event) {
|
5
|
-
jsonString = JSON.stringify(jsform.root.getFormValues(), null, 2);
|
6
|
+
jsonString = JSON.stringify(convertTextareaToArray(jsform.root.getFormValues(), jsform.formDesc.form, jsform.formDesc.schema), null, 2);
|
6
7
|
textArea.value = jsonString;
|
7
8
|
|
8
9
|
// Find the React fiber node
|
@@ -26,6 +27,28 @@ function addFormInputListener(textArea, jsform){
|
|
26
27
|
};
|
27
28
|
}
|
28
29
|
|
30
|
+
async function getPostParametersSchema() {
|
31
|
+
const swaggerSpec = await getSwaggerSpec();
|
32
|
+
const result = {};
|
33
|
+
for (const path in swaggerSpec.paths) {
|
34
|
+
const pathItem = swaggerSpec.paths[path];
|
35
|
+
if (pathItem.post) {
|
36
|
+
const postDef = pathItem.post;
|
37
|
+
// Look for requestBody with JSON schema in OpenAPI 3.0
|
38
|
+
if (postDef.requestBody && postDef.requestBody.content && postDef.requestBody.content['application/json']) {
|
39
|
+
result[path] = postDef.requestBody.content['application/json'].schema;
|
40
|
+
} else {
|
41
|
+
result[path] = null;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
return result;
|
46
|
+
}
|
47
|
+
function adjustTxtHeight2(paramtext) {
|
48
|
+
paramtext.style.height = "0";
|
49
|
+
paramtext.style.height = `${paramtext.scrollHeight+2}px`;
|
50
|
+
}
|
51
|
+
|
29
52
|
window.onload = function() {
|
30
53
|
ui = SwaggerUIBundle({
|
31
54
|
url: "/swagger.yaml",
|
@@ -69,29 +92,26 @@ window.onload = function() {
|
|
69
92
|
const observer = new MutationObserver((mutations) => {
|
70
93
|
mutations.forEach(mutation => {
|
71
94
|
mutation.addedNodes.forEach(node => {
|
72
|
-
if (node.
|
73
|
-
node.
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
prevForm
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
e.target.style.height = e.target.scrollHeight + "px";
|
85
|
-
});
|
86
|
-
node.style.height = "0"
|
87
|
-
node.style.height = node.scrollHeight + "px";
|
95
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
96
|
+
const paramtext = node.querySelector(".body-param__text");
|
97
|
+
if (paramtext) {
|
98
|
+
// Retrieve the data-path attribute from the first opblock-summary-path element
|
99
|
+
const routePath = $(node).closest('.opblock').find('.opblock-summary-path').first().attr('data-path');
|
100
|
+
const routePathId = `schemaForm${routePath.replaceAll("/", "_")}`;
|
101
|
+
const prevForm = paramtext.parentNode.querySelector(`#${routePathId}`)
|
102
|
+
if (prevForm) {
|
103
|
+
prevForm.remove();
|
104
|
+
}
|
105
|
+
paramtext.addEventListener("input", () => adjustTxtHeight2(paramtext));
|
106
|
+
setTimeout(() => adjustTxtHeight2(paramtext), 100);
|
88
107
|
const form = document.createElement("form");
|
89
108
|
form.id = routePathId;
|
90
109
|
form.classList.add("schema-form");
|
91
110
|
jsform = createSchemaForm($(form), swaggerSchemas[routePath], null, routePath);
|
92
|
-
// form.addEventListener("input", formInput(node, jsform));
|
93
|
-
|
94
|
-
|
111
|
+
// form.addEventListener("input", formInput(node, jsform));
|
112
|
+
setTimeout(() => addFormInputListener(paramtext, jsform)(), 100);
|
113
|
+
form.addEventListener("input", addFormInputListener(paramtext, jsform));
|
114
|
+
paramtext.parentNode.insertBefore(form, paramtext.nextSibling);
|
95
115
|
item1 = form.querySelector("input, select, textarea");
|
96
116
|
if (item1) {
|
97
117
|
item1.focus();
|
pywebexec/swagger.yaml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
openapi: "3.0.0"
|
2
2
|
info:
|
3
3
|
title: PyWebExec API
|
4
4
|
version: "1.0"
|
@@ -11,77 +11,80 @@ paths:
|
|
11
11
|
responses:
|
12
12
|
"200":
|
13
13
|
description: "List of all commands status"
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
14
|
+
content:
|
15
|
+
application/json:
|
16
|
+
schema:
|
17
|
+
type: object
|
18
|
+
properties:
|
19
|
+
commands:
|
20
|
+
type: array
|
21
|
+
items:
|
22
|
+
type: object
|
23
|
+
properties:
|
24
|
+
command_id:
|
25
|
+
type: string
|
26
|
+
command:
|
27
|
+
type: string
|
28
|
+
status:
|
29
|
+
type: string
|
30
|
+
start_time:
|
31
|
+
type: string
|
32
|
+
format: date-time
|
33
|
+
end_time:
|
34
|
+
type: string
|
35
|
+
format: date-time
|
36
|
+
exit_code:
|
37
|
+
type: integer
|
38
|
+
last_output_line:
|
39
|
+
type: string
|
40
|
+
user:
|
41
|
+
type: string
|
40
42
|
post:
|
41
43
|
summary: "Run a command"
|
42
44
|
tags:
|
43
45
|
- commands
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
type: object
|
53
|
-
properties:
|
54
|
-
command:
|
55
|
-
type: string
|
56
|
-
description: "Command to run"
|
57
|
-
# Enum will be added dynamically by the APIs
|
58
|
-
default: commandName
|
59
|
-
params:
|
60
|
-
type: array
|
61
|
-
description: "Command parameters"
|
62
|
-
items:
|
46
|
+
requestBody:
|
47
|
+
required: true
|
48
|
+
content:
|
49
|
+
application/json:
|
50
|
+
schema:
|
51
|
+
type: object
|
52
|
+
properties:
|
53
|
+
command:
|
63
54
|
type: string
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
55
|
+
description: "Command to run"
|
56
|
+
# Enum will be added dynamically by the APIs
|
57
|
+
default: commandName
|
58
|
+
required: true
|
59
|
+
params:
|
60
|
+
type: array
|
61
|
+
description: "Command parameters"
|
62
|
+
items:
|
63
|
+
type: string
|
64
|
+
default: []
|
65
|
+
rows:
|
66
|
+
type: integer
|
67
|
+
description: "tty nb rows"
|
68
|
+
default: 24
|
69
|
+
cols:
|
70
|
+
type: integer
|
71
|
+
description: "tty nb columns"
|
72
|
+
default: 125
|
73
|
+
required:
|
74
|
+
- command
|
75
75
|
responses:
|
76
76
|
"200":
|
77
77
|
description: "Command started"
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
78
|
+
content:
|
79
|
+
application/json:
|
80
|
+
schema:
|
81
|
+
type: object
|
82
|
+
properties:
|
83
|
+
command_id:
|
84
|
+
type: string
|
85
|
+
message:
|
86
|
+
type: string
|
87
|
+
|
85
88
|
/commands/{command_id}:
|
86
89
|
get:
|
87
90
|
summary: "Get command status"
|
@@ -91,43 +94,46 @@ paths:
|
|
91
94
|
- in: path
|
92
95
|
name: command_id
|
93
96
|
required: true
|
94
|
-
|
97
|
+
schema:
|
98
|
+
type: string
|
95
99
|
responses:
|
96
100
|
"200":
|
97
101
|
description: "Command status returned"
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
102
|
+
content:
|
103
|
+
application/json:
|
104
|
+
schema:
|
105
|
+
type: object
|
106
|
+
properties:
|
107
|
+
command_id:
|
108
|
+
type: string
|
109
|
+
command:
|
110
|
+
type: string
|
111
|
+
params:
|
112
|
+
type: array
|
113
|
+
items:
|
114
|
+
type: string
|
115
|
+
status:
|
116
|
+
type: string
|
117
|
+
start_time:
|
118
|
+
type: string
|
119
|
+
format: date-time
|
120
|
+
end_time:
|
121
|
+
type: string
|
122
|
+
format: date-time
|
123
|
+
exit_code:
|
124
|
+
type: integer
|
125
|
+
last_output_line:
|
126
|
+
type: string
|
127
|
+
cols:
|
128
|
+
type: integer
|
129
|
+
rows:
|
130
|
+
type: integer
|
131
|
+
user:
|
132
|
+
type: string
|
133
|
+
from:
|
134
|
+
type: string
|
135
|
+
pid:
|
136
|
+
type: integer
|
131
137
|
|
132
138
|
/commands/{command_id}/output:
|
133
139
|
get:
|
@@ -138,44 +144,49 @@ paths:
|
|
138
144
|
- in: path
|
139
145
|
name: command_id
|
140
146
|
required: true
|
141
|
-
|
147
|
+
schema:
|
148
|
+
type: string
|
142
149
|
- in: query
|
143
150
|
name: offset
|
144
|
-
|
145
|
-
|
151
|
+
schema:
|
152
|
+
type: integer
|
153
|
+
default: 0
|
146
154
|
- in: query
|
147
155
|
name: maxsize
|
148
|
-
|
149
|
-
|
156
|
+
schema:
|
157
|
+
type: integer
|
158
|
+
default: 10485760
|
150
159
|
- in: query
|
151
160
|
name: maxlines
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
- application/json
|
156
|
-
produces:
|
157
|
-
- application/json
|
158
|
-
- text/plain
|
161
|
+
schema:
|
162
|
+
type: integer
|
163
|
+
default: 5000
|
159
164
|
responses:
|
160
165
|
"200":
|
161
166
|
description: "Command output returned"
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
output:
|
166
|
-
type: string
|
167
|
-
status:
|
168
|
-
type: string
|
169
|
-
rows:
|
170
|
-
type: integer
|
171
|
-
cols:
|
172
|
-
type: integer
|
173
|
-
links:
|
167
|
+
content:
|
168
|
+
application/json:
|
169
|
+
schema:
|
174
170
|
type: object
|
175
171
|
properties:
|
176
|
-
|
172
|
+
output:
|
177
173
|
type: string
|
178
|
-
|
174
|
+
status:
|
175
|
+
type: string
|
176
|
+
rows:
|
177
|
+
type: integer
|
178
|
+
cols:
|
179
|
+
type: integer
|
180
|
+
links:
|
181
|
+
type: object
|
182
|
+
properties:
|
183
|
+
next:
|
184
|
+
type: string
|
185
|
+
format: uri
|
186
|
+
text/plain:
|
187
|
+
schema:
|
188
|
+
type: string
|
189
|
+
|
179
190
|
/commands/{command_id}/stop:
|
180
191
|
patch:
|
181
192
|
summary: "Stop a running command"
|
@@ -185,15 +196,19 @@ paths:
|
|
185
196
|
- in: path
|
186
197
|
name: command_id
|
187
198
|
required: true
|
188
|
-
|
199
|
+
schema:
|
200
|
+
type: string
|
189
201
|
responses:
|
190
202
|
"200":
|
191
203
|
description: "Command stopped successfully"
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
204
|
+
content:
|
205
|
+
application/json:
|
206
|
+
schema:
|
207
|
+
type: object
|
208
|
+
properties:
|
209
|
+
message:
|
210
|
+
type: string
|
211
|
+
|
197
212
|
/commands/{command_id}/run:
|
198
213
|
post:
|
199
214
|
summary: "Relaunch a command"
|
@@ -203,56 +218,61 @@ paths:
|
|
203
218
|
- in: path
|
204
219
|
name: command_id
|
205
220
|
required: true
|
206
|
-
type: string
|
207
|
-
- in: body
|
208
|
-
name: requestBody
|
209
221
|
schema:
|
210
|
-
type:
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
222
|
+
type: string
|
223
|
+
requestBody:
|
224
|
+
required: true
|
225
|
+
content:
|
226
|
+
application/json:
|
227
|
+
schema:
|
228
|
+
type: object
|
229
|
+
properties:
|
230
|
+
rows:
|
231
|
+
type: integer
|
232
|
+
description: "tty nb rows"
|
233
|
+
default: 24
|
234
|
+
cols:
|
235
|
+
type: integer
|
236
|
+
description: "tty nb columns"
|
237
|
+
default: 125
|
220
238
|
responses:
|
221
239
|
"200":
|
222
240
|
description: "Command relaunched"
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
241
|
+
content:
|
242
|
+
application/json:
|
243
|
+
schema:
|
244
|
+
type: object
|
245
|
+
properties:
|
246
|
+
message:
|
247
|
+
type: string
|
248
|
+
command_id:
|
249
|
+
type: string
|
250
|
+
|
230
251
|
/commands/exposed:
|
231
252
|
get:
|
232
253
|
summary: "List available executable commands"
|
233
254
|
tags:
|
234
255
|
- commands
|
235
|
-
produces:
|
236
|
-
- application/json
|
237
256
|
responses:
|
238
257
|
"200":
|
239
258
|
description: "List of commands with markdown help"
|
240
|
-
|
241
|
-
type: object
|
242
|
-
properties:
|
243
|
-
commands:
|
244
|
-
type: array
|
245
|
-
items:
|
246
|
-
type: object
|
247
|
-
properties:
|
248
|
-
command:
|
249
|
-
type: string
|
250
|
-
help:
|
251
|
-
type: string
|
252
|
-
examples:
|
259
|
+
content:
|
253
260
|
application/json:
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
261
|
+
schema:
|
262
|
+
type: object
|
263
|
+
properties:
|
264
|
+
commands:
|
265
|
+
type: array
|
266
|
+
items:
|
267
|
+
type: object
|
268
|
+
properties:
|
269
|
+
command:
|
270
|
+
type: string
|
271
|
+
help:
|
272
|
+
type: string
|
273
|
+
example:
|
274
|
+
commands:
|
275
|
+
- command: ls
|
276
|
+
help: "List directory contents \n**params**:\n* `<ls_params>`"
|
277
|
+
- command: cat
|
278
|
+
help: "Concatenate and display files \n**params**:\n* `<cat_params>`"
|
pywebexec/version.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
pywebexec/__init__.py,sha256=197fHJy0UDBwTTpGCGortZRr-w2kTaD7MxqdbVmTEi0,61
|
2
2
|
pywebexec/host_ip.py,sha256=Ud_HTflWVQ8789aoQ2RZdT1wGI-ccvrwSWGz_c7T3TI,1241
|
3
|
-
pywebexec/pywebexec.py,sha256=
|
4
|
-
pywebexec/swagger.yaml,sha256=
|
5
|
-
pywebexec/version.py,sha256=
|
3
|
+
pywebexec/pywebexec.py,sha256=R-jp9BiUMJZW7m9N9kQC6dpIueUgFVDo4n9_GFb1rOs,45734
|
4
|
+
pywebexec/swagger.yaml,sha256=I_oLpp7Hqel8SDEEykvpmCT-Gv3ytGlziq9bvQOrtZY,7598
|
5
|
+
pywebexec/version.py,sha256=wUUonx5HNvzUsCYrR_oek7De76IsD1eqg46c4ZbWiDg,511
|
6
6
|
pywebexec/static/css/form.css,sha256=eApJskeJ3MCzC7_p4gXgoIbvbG5s8m4bP1O4KHHqeiA,5293
|
7
|
-
pywebexec/static/css/markdown.css,sha256=
|
8
|
-
pywebexec/static/css/style.css,sha256=
|
7
|
+
pywebexec/static/css/markdown.css,sha256=br4-iK9wigTs54N2KHtjgZ4KLH0THVSvJo-XZAdMHiE,1970
|
8
|
+
pywebexec/static/css/style.css,sha256=R1VOPNV2ztROKy9Fgf3tvUrtuKagY027tFJ8C866yWU,9991
|
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
|
@@ -33,11 +33,11 @@ pywebexec/static/images/resume.svg,sha256=99LP1Ya2JXakRCO9kW8JMuT_4a_CannF65Eiuw
|
|
33
33
|
pywebexec/static/images/running.svg,sha256=fBCYwYb2O9K4N3waC2nURP25NRwZlqR4PbDZy6JQMww,610
|
34
34
|
pywebexec/static/images/success.svg,sha256=NVwezvVMplt46ElW798vqGfrL21Mw_DWHUp_qiD_FU8,489
|
35
35
|
pywebexec/static/images/swagger-ui.svg,sha256=FR0yeOVwe4zCYKZAjCGcT_m0Mf25NexIVaSXifIkoU0,2117
|
36
|
-
pywebexec/static/js/executables.js,sha256=
|
37
|
-
pywebexec/static/js/popup.js,sha256=
|
38
|
-
pywebexec/static/js/schemaform.js,sha256=
|
39
|
-
pywebexec/static/js/script.js,sha256=
|
40
|
-
pywebexec/static/js/swagger-form.js,sha256=
|
36
|
+
pywebexec/static/js/executables.js,sha256=lF9Icb4VzbxSCTmcz-TUjvFKQ_ygmSgPq0Q_uaTRR-g,11866
|
37
|
+
pywebexec/static/js/popup.js,sha256=O3DEWnyb5yGW9tjODYycc-ujWndyAfnJMxulaQeogtc,9700
|
38
|
+
pywebexec/static/js/schemaform.js,sha256=uYlu8P6Y1adEGefzsRhLT-gwS27TZsLwzDGs2UBgDGk,8808
|
39
|
+
pywebexec/static/js/script.js,sha256=X5TN2q1m6ZGPK72e1MWgyQWk1agOrcOWuGdwVWB9HJk,18204
|
40
|
+
pywebexec/static/js/swagger-form.js,sha256=EYXqHIE105DkXPCPi5-eYzux9nPLmJuisf-5EwFypsQ,4578
|
41
41
|
pywebexec/static/js/js-yaml/LICENSE,sha256=oHvCRGi5ZUznalR9R6LbKC0HcztxXbTHOpi9Y5YflVA,1084
|
42
42
|
pywebexec/static/js/js-yaml/js-yaml.min.js,sha256=Rdw90D3AegZwWiwpibjH9wkBPwS9U4bjJ51ORH8H69c,39430
|
43
43
|
pywebexec/static/js/marked/LICENSE.md,sha256=jjo_gvWaYJWPVsoI9EVkfDKkcz3HymwsRvbriYRxq5w,2942
|
@@ -67,9 +67,9 @@ pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
67
67
|
pywebexec/templates/index.html,sha256=w18O2plH_yS8bqlPsu5hwFFmCj9H2hWLSV8B6ADcSwU,3900
|
68
68
|
pywebexec/templates/popup.html,sha256=3kpMccKD_OLLhJ4Y9KRw6Ny8wQWjVaRrUfV9y5-bDiQ,1580
|
69
69
|
pywebexec/templates/swagger_ui.html,sha256=9ngyldkyEdLonBjl97mbIZUlVk-jxwcHrvFzMSrveyU,1067
|
70
|
-
pywebexec-2.1.
|
71
|
-
pywebexec-2.1.
|
72
|
-
pywebexec-2.1.
|
73
|
-
pywebexec-2.1.
|
74
|
-
pywebexec-2.1.
|
75
|
-
pywebexec-2.1.
|
70
|
+
pywebexec-2.2.1.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
|
71
|
+
pywebexec-2.2.1.dist-info/METADATA,sha256=UWTzS7NVz171aeuAw2z8nEdeg5ggBeKEEISktYdU8ms,12810
|
72
|
+
pywebexec-2.2.1.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
|
73
|
+
pywebexec-2.2.1.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
|
74
|
+
pywebexec-2.2.1.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
|
75
|
+
pywebexec-2.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|