pywebexec 1.2.4__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- pywebexec/__init__.py +5 -0
- pywebexec/pywebexec.py +595 -0
- pywebexec/static/css/style.css +178 -0
- pywebexec/static/css/xterm.css +209 -0
- pywebexec/static/images/aborted.svg +1 -0
- pywebexec/static/images/copy.svg +1 -0
- pywebexec/static/images/copy_ok.svg +1 -0
- pywebexec/static/images/failed.svg +1 -0
- pywebexec/static/images/favicon.svg +1 -0
- pywebexec/static/images/running.gif +0 -0
- pywebexec/static/images/success.svg +1 -0
- pywebexec/static/js/script.js +280 -0
- pywebexec/static/js/xterm/LICENSE +21 -0
- pywebexec/static/js/xterm/ansi_up.min.js +7 -0
- pywebexec/static/js/xterm/xterm-addon-fit.js +1 -0
- pywebexec/static/js/xterm/xterm.js +1 -0
- pywebexec/templates/__init__.py +0 -0
- pywebexec/templates/index.html +48 -0
- pywebexec/version.py +16 -0
- pywebexec-1.2.4.dist-info/LICENSE +21 -0
- pywebexec-1.2.4.dist-info/METADATA +171 -0
- pywebexec-1.2.4.dist-info/RECORD +25 -0
- pywebexec-1.2.4.dist-info/WHEEL +5 -0
- pywebexec-1.2.4.dist-info/entry_points.txt +2 -0
- pywebexec-1.2.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
body {
|
2
|
+
font-family: Arial, sans-serif;
|
3
|
+
overflow: hidden;
|
4
|
+
}
|
5
|
+
.table-container {
|
6
|
+
height: 270px;
|
7
|
+
overflow-y: auto;
|
8
|
+
position: relative;
|
9
|
+
border-radius: 10px;
|
10
|
+
border: 1px solid #aaa;
|
11
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.30);
|
12
|
+
}
|
13
|
+
table {
|
14
|
+
width: 100%;
|
15
|
+
border-collapse: collapse;
|
16
|
+
}
|
17
|
+
th, td {
|
18
|
+
padding: 8px;
|
19
|
+
text-align: left;
|
20
|
+
border-bottom: 1px solid #ddd;
|
21
|
+
white-space: nowrap;
|
22
|
+
}
|
23
|
+
th {
|
24
|
+
background-color: #444;
|
25
|
+
color: #eee;
|
26
|
+
position: sticky;
|
27
|
+
top: 0;
|
28
|
+
z-index: 1;
|
29
|
+
}
|
30
|
+
.outcol {
|
31
|
+
width: 100%;
|
32
|
+
}
|
33
|
+
select { /* Safari bug */
|
34
|
+
font-size: 15px;
|
35
|
+
border: #aaa solid 1px;
|
36
|
+
border-radius: 5px;
|
37
|
+
}
|
38
|
+
.output {
|
39
|
+
white-space: pre-wrap;
|
40
|
+
background: #111;
|
41
|
+
padding: 10px;
|
42
|
+
border: 1px solid #ccc;
|
43
|
+
font-family: monospace;
|
44
|
+
border-radius: 10px;
|
45
|
+
overflow-y: hidden;
|
46
|
+
}
|
47
|
+
.copy-icon { cursor: pointer; }
|
48
|
+
.monospace { font-family: monospace; }
|
49
|
+
.copied { color: green; margin-left: 5px; }
|
50
|
+
button {
|
51
|
+
-webkit-appearance: none;
|
52
|
+
-webkit-border-radius: none;
|
53
|
+
appearance: none;
|
54
|
+
border-radius: 15px;
|
55
|
+
padding-right: 13px;
|
56
|
+
border: 1px #555 solid;
|
57
|
+
height: 22px;
|
58
|
+
font-size: 13px;
|
59
|
+
outline: none;
|
60
|
+
text-indent: 10px;
|
61
|
+
background-color: #eee;
|
62
|
+
}
|
63
|
+
form {
|
64
|
+
padding-bottom: 15px;
|
65
|
+
}
|
66
|
+
.status-icon {
|
67
|
+
display: inline-block;
|
68
|
+
width: 16px;
|
69
|
+
height: 16px;
|
70
|
+
margin-right: 5px;
|
71
|
+
background-size: contain;
|
72
|
+
background-repeat: no-repeat;
|
73
|
+
vertical-align: middle;
|
74
|
+
}
|
75
|
+
.title-icon {
|
76
|
+
width: 30px;
|
77
|
+
height: 30px;
|
78
|
+
background-image: url("/static/images/favicon.svg");
|
79
|
+
vertical-align: bottom;
|
80
|
+
}
|
81
|
+
.status-running {
|
82
|
+
background-image: url("/static/images/running.gif")
|
83
|
+
}
|
84
|
+
.status-success {
|
85
|
+
background-image: url("/static/images/success.svg")
|
86
|
+
}
|
87
|
+
.status-failed {
|
88
|
+
background-image: url("/static/images/failed.svg")
|
89
|
+
}
|
90
|
+
.status-aborted {
|
91
|
+
background-image: url("/static/images/aborted.svg")
|
92
|
+
}
|
93
|
+
.copy_clip {
|
94
|
+
padding-right: 20px;
|
95
|
+
background-repeat: no-repeat;
|
96
|
+
background-position: right top;
|
97
|
+
background-size: 20px 12px;
|
98
|
+
white-space: nowrap;
|
99
|
+
}
|
100
|
+
.copy_clip:hover {
|
101
|
+
cursor: pointer;
|
102
|
+
background-image: url("/static/images/copy.svg");
|
103
|
+
}
|
104
|
+
.copy_clip_ok, .copy_clip_ok:hover {
|
105
|
+
background-image: url("/static/images/copy_ok.svg");
|
106
|
+
}
|
107
|
+
input {
|
108
|
+
width: 50%;
|
109
|
+
-webkit-appearance: none;
|
110
|
+
-webkit-border-radius: none;
|
111
|
+
appearance: none;
|
112
|
+
border-radius: 15px;
|
113
|
+
padding: 3px;
|
114
|
+
padding-right: 13px;
|
115
|
+
border: 1px #aaa solid;
|
116
|
+
height: 15px;
|
117
|
+
font-size: 15px;
|
118
|
+
outline: none;
|
119
|
+
text-indent: 5px;
|
120
|
+
background-color: white;
|
121
|
+
}
|
122
|
+
.currentcommand {
|
123
|
+
background-color: #eef;
|
124
|
+
}
|
125
|
+
.resizer {
|
126
|
+
width: 100%;
|
127
|
+
height: 5px;
|
128
|
+
border-radius: 5px;
|
129
|
+
background: #aaa;
|
130
|
+
cursor: ns-resize;
|
131
|
+
position: absolute;
|
132
|
+
bottom: 0;
|
133
|
+
left: 0;
|
134
|
+
}
|
135
|
+
.resizer-container {
|
136
|
+
position: relative;
|
137
|
+
height: 5px;
|
138
|
+
margin: 5px;
|
139
|
+
/*margin-bottom: 10px;*/
|
140
|
+
}
|
141
|
+
tr.clickable-row {
|
142
|
+
cursor: pointer;
|
143
|
+
}
|
144
|
+
body.dimmed {
|
145
|
+
background-color: rgba(0, 0, 0, 0.5);
|
146
|
+
pointer-events: none;
|
147
|
+
}
|
148
|
+
body.dimmed * {
|
149
|
+
pointer-events: none;
|
150
|
+
}
|
151
|
+
.dimmer {
|
152
|
+
display: none;
|
153
|
+
position: fixed;
|
154
|
+
top: 0;
|
155
|
+
left: 0;
|
156
|
+
width: 100%;
|
157
|
+
height: 100%;
|
158
|
+
background-color: rgba(0, 0, 0, 0.5);
|
159
|
+
z-index: 1000;
|
160
|
+
overflow-y: hidden;
|
161
|
+
}
|
162
|
+
.dimmer-text {
|
163
|
+
color: white;
|
164
|
+
font-size: 24px;
|
165
|
+
text-align: center;
|
166
|
+
position: absolute;
|
167
|
+
top: 50%;
|
168
|
+
left: 50%;
|
169
|
+
transform: translate(-50%, -50%);
|
170
|
+
}
|
171
|
+
.xterm-cursor {
|
172
|
+
display: none;
|
173
|
+
visibility: hidden;
|
174
|
+
height: 0px;
|
175
|
+
}
|
176
|
+
span { /* allow wide chars in terminal */
|
177
|
+
letter-spacing: unset !important;
|
178
|
+
}
|
@@ -0,0 +1,209 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
3
|
+
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
4
|
+
* https://github.com/chjj/term.js
|
5
|
+
* @license MIT
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*
|
25
|
+
* Originally forked from (with the author's permission):
|
26
|
+
* Fabrice Bellard's javascript vt100 for jslinux:
|
27
|
+
* http://bellard.org/jslinux/
|
28
|
+
* Copyright (c) 2011 Fabrice Bellard
|
29
|
+
* The original design remains. The terminal itself
|
30
|
+
* has been extended to include xterm CSI codes, among
|
31
|
+
* other features.
|
32
|
+
*/
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Default styles for xterm.js
|
36
|
+
*/
|
37
|
+
|
38
|
+
.xterm {
|
39
|
+
cursor: text;
|
40
|
+
position: relative;
|
41
|
+
user-select: none;
|
42
|
+
-ms-user-select: none;
|
43
|
+
-webkit-user-select: none;
|
44
|
+
}
|
45
|
+
|
46
|
+
.xterm.focus,
|
47
|
+
.xterm:focus {
|
48
|
+
outline: none;
|
49
|
+
}
|
50
|
+
|
51
|
+
.xterm .xterm-helpers {
|
52
|
+
position: absolute;
|
53
|
+
top: 0;
|
54
|
+
/**
|
55
|
+
* The z-index of the helpers must be higher than the canvases in order for
|
56
|
+
* IMEs to appear on top.
|
57
|
+
*/
|
58
|
+
z-index: 5;
|
59
|
+
}
|
60
|
+
|
61
|
+
.xterm .xterm-helper-textarea {
|
62
|
+
padding: 0;
|
63
|
+
border: 0;
|
64
|
+
margin: 0;
|
65
|
+
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
|
66
|
+
position: absolute;
|
67
|
+
opacity: 0;
|
68
|
+
left: -9999em;
|
69
|
+
top: 0;
|
70
|
+
width: 0;
|
71
|
+
height: 0;
|
72
|
+
z-index: -5;
|
73
|
+
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
74
|
+
white-space: nowrap;
|
75
|
+
overflow: hidden;
|
76
|
+
resize: none;
|
77
|
+
}
|
78
|
+
|
79
|
+
.xterm .composition-view {
|
80
|
+
/* TODO: Composition position got messed up somewhere */
|
81
|
+
background: #000;
|
82
|
+
color: #FFF;
|
83
|
+
display: none;
|
84
|
+
position: absolute;
|
85
|
+
white-space: nowrap;
|
86
|
+
z-index: 1;
|
87
|
+
}
|
88
|
+
|
89
|
+
.xterm .composition-view.active {
|
90
|
+
display: block;
|
91
|
+
}
|
92
|
+
|
93
|
+
.xterm .xterm-viewport {
|
94
|
+
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
95
|
+
background-color: #000;
|
96
|
+
overflow-y: scroll;
|
97
|
+
cursor: default;
|
98
|
+
position: absolute;
|
99
|
+
right: 0;
|
100
|
+
left: 0;
|
101
|
+
top: 0;
|
102
|
+
bottom: 0;
|
103
|
+
}
|
104
|
+
|
105
|
+
.xterm .xterm-screen {
|
106
|
+
position: relative;
|
107
|
+
}
|
108
|
+
|
109
|
+
.xterm .xterm-screen canvas {
|
110
|
+
position: absolute;
|
111
|
+
left: 0;
|
112
|
+
top: 0;
|
113
|
+
}
|
114
|
+
|
115
|
+
.xterm .xterm-scroll-area {
|
116
|
+
visibility: hidden;
|
117
|
+
}
|
118
|
+
|
119
|
+
.xterm-char-measure-element {
|
120
|
+
display: inline-block;
|
121
|
+
visibility: hidden;
|
122
|
+
position: absolute;
|
123
|
+
top: 0;
|
124
|
+
left: -9999em;
|
125
|
+
line-height: normal;
|
126
|
+
}
|
127
|
+
|
128
|
+
.xterm.enable-mouse-events {
|
129
|
+
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
130
|
+
cursor: default;
|
131
|
+
}
|
132
|
+
|
133
|
+
.xterm.xterm-cursor-pointer,
|
134
|
+
.xterm .xterm-cursor-pointer {
|
135
|
+
cursor: pointer;
|
136
|
+
}
|
137
|
+
|
138
|
+
.xterm.column-select.focus {
|
139
|
+
/* Column selection mode */
|
140
|
+
cursor: crosshair;
|
141
|
+
}
|
142
|
+
|
143
|
+
.xterm .xterm-accessibility,
|
144
|
+
.xterm .xterm-message {
|
145
|
+
position: absolute;
|
146
|
+
left: 0;
|
147
|
+
top: 0;
|
148
|
+
bottom: 0;
|
149
|
+
right: 0;
|
150
|
+
z-index: 10;
|
151
|
+
color: transparent;
|
152
|
+
pointer-events: none;
|
153
|
+
}
|
154
|
+
|
155
|
+
.xterm .live-region {
|
156
|
+
position: absolute;
|
157
|
+
left: -9999px;
|
158
|
+
width: 1px;
|
159
|
+
height: 1px;
|
160
|
+
overflow: hidden;
|
161
|
+
}
|
162
|
+
|
163
|
+
.xterm-dim {
|
164
|
+
/* Dim should not apply to background, so the opacity of the foreground color is applied
|
165
|
+
* explicitly in the generated class and reset to 1 here */
|
166
|
+
opacity: 1 !important;
|
167
|
+
}
|
168
|
+
|
169
|
+
.xterm-underline-1 { text-decoration: underline; }
|
170
|
+
.xterm-underline-2 { text-decoration: double underline; }
|
171
|
+
.xterm-underline-3 { text-decoration: wavy underline; }
|
172
|
+
.xterm-underline-4 { text-decoration: dotted underline; }
|
173
|
+
.xterm-underline-5 { text-decoration: dashed underline; }
|
174
|
+
|
175
|
+
.xterm-overline {
|
176
|
+
text-decoration: overline;
|
177
|
+
}
|
178
|
+
|
179
|
+
.xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
|
180
|
+
.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
|
181
|
+
.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
|
182
|
+
.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
|
183
|
+
.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }
|
184
|
+
|
185
|
+
.xterm-strikethrough {
|
186
|
+
text-decoration: line-through;
|
187
|
+
}
|
188
|
+
|
189
|
+
.xterm-screen .xterm-decoration-container .xterm-decoration {
|
190
|
+
z-index: 6;
|
191
|
+
position: absolute;
|
192
|
+
}
|
193
|
+
|
194
|
+
.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
|
195
|
+
z-index: 7;
|
196
|
+
}
|
197
|
+
|
198
|
+
.xterm-decoration-overview-ruler {
|
199
|
+
z-index: 8;
|
200
|
+
position: absolute;
|
201
|
+
top: 0;
|
202
|
+
right: 0;
|
203
|
+
pointer-events: none;
|
204
|
+
}
|
205
|
+
|
206
|
+
.xterm-decoration-top {
|
207
|
+
z-index: 2;
|
208
|
+
position: relative;
|
209
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg viewBox="-1 -1 13 13" xmlns="http://www.w3.org/2000/svg" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 12A6 6 0 106 0a6 6 0 000 12zM3 5a1 1 0 000 2h6a1 1 0 100-2H3z" fill="#ff641a"></path></g></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" role="img" viewBox="0 0 16 16" width="25" height="16" fill="currentColor"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" role="img" viewBox="0 0 16 16" width="25" height="16" fill="currentColor" style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"><path fill="#118811" d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg viewBox="-2 -2 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><title>cross-circle</title><desc>Created with Sketch Beta.</desc><defs></defs><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"><g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-570.000000, -1089.000000)" fill="#ca0000"><path d="M591.657,1109.24 C592.048,1109.63 592.048,1110.27 591.657,1110.66 C591.267,1111.05 590.633,1111.05 590.242,1110.66 L586.006,1106.42 L581.74,1110.69 C581.346,1111.08 580.708,1111.08 580.314,1110.69 C579.921,1110.29 579.921,1109.65 580.314,1109.26 L584.58,1104.99 L580.344,1100.76 C579.953,1100.37 579.953,1099.73 580.344,1099.34 C580.733,1098.95 581.367,1098.95 581.758,1099.34 L585.994,1103.58 L590.292,1099.28 C590.686,1098.89 591.323,1098.89 591.717,1099.28 C592.11,1099.68 592.11,1100.31 591.717,1100.71 L587.42,1105.01 L591.657,1109.24 L591.657,1109.24 Z M586,1089 C577.163,1089 570,1096.16 570,1105 C570,1113.84 577.163,1121 586,1121 C594.837,1121 602,1113.84 602,1105 C602,1096.16 594.837,1089 586,1089 L586,1089 Z" id="cross-circle" sketch:type="MSShapeGroup"></path></g></g></g></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg fill="#000000" viewBox="-1 0 19 19" xmlns="http://www.w3.org/2000/svg" class="cf-icon-svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M16.5 9.5a8 8 0 1 1-8-8 8 8 0 0 1 8 8zm-2.97.006a5.03 5.03 0 1 0-5.03 5.03 5.03 5.03 0 0 0 5.03-5.03zm-7.383-.4H4.289a4.237 4.237 0 0 1 2.565-3.498q.1-.042.2-.079a7.702 7.702 0 0 0-.907 3.577zm0 .8a7.7 7.7 0 0 0 .908 3.577q-.102-.037-.201-.079a4.225 4.225 0 0 1-2.565-3.498zm.8-.8a9.04 9.04 0 0 1 .163-1.402 6.164 6.164 0 0 1 .445-1.415c.289-.615.66-1.013.945-1.013.285 0 .656.398.945 1.013a6.18 6.18 0 0 1 .445 1.415 9.078 9.078 0 0 1 .163 1.402zm3.106.8a9.073 9.073 0 0 1-.163 1.402 6.187 6.187 0 0 1-.445 1.415c-.289.616-.66 1.013-.945 1.013-.285 0-.656-.397-.945-1.013a6.172 6.172 0 0 1-.445-1.415 9.036 9.036 0 0 1-.163-1.402zm1.438-3.391a4.211 4.211 0 0 1 1.22 2.591h-1.858a7.698 7.698 0 0 0-.908-3.577q.102.037.201.08a4.208 4.208 0 0 1 1.345.906zm-.638 3.391h1.858a4.238 4.238 0 0 1-2.565 3.498q-.1.043-.2.08a7.697 7.697 0 0 0 .907-3.578z"></path></g></svg>
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg viewBox="0 75 949 949" xmlns="http://www.w3.org/2000/svg" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill="#00a600" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm-55.808 536.384-99.52-99.584a38.4 38.4 0 1 0-54.336 54.336l126.72 126.72a38.272 38.272 0 0 0 54.336 0l262.4-262.464a38.4 38.4 0 1 0-54.272-54.336L456.192 600.384z"></path></g></svg>
|
@@ -0,0 +1,280 @@
|
|
1
|
+
let currentCommandId = null;
|
2
|
+
let outputInterval = null;
|
3
|
+
let nextOutputLink = null;
|
4
|
+
|
5
|
+
const terminal = new Terminal({
|
6
|
+
cursorBlink: false,
|
7
|
+
cursorHidden: true,
|
8
|
+
disableStdin: true,
|
9
|
+
convertEol: true,
|
10
|
+
fontFamily: 'Consolas NF, monospace, courier-new, courier'
|
11
|
+
});
|
12
|
+
const fitAddon = new FitAddon.FitAddon();
|
13
|
+
terminal.loadAddon(fitAddon);
|
14
|
+
terminal.open(document.getElementById('output'));
|
15
|
+
fitAddon.fit();
|
16
|
+
|
17
|
+
terminal.onSelectionChange(() => {
|
18
|
+
const selectionText = terminal.getSelection();
|
19
|
+
if (selectionText) {
|
20
|
+
navigator.clipboard.writeText(selectionText).catch(err => {
|
21
|
+
console.error('Failed to copy text to clipboard:', err);
|
22
|
+
});
|
23
|
+
}
|
24
|
+
});
|
25
|
+
|
26
|
+
document.getElementById('launchForm').addEventListener('submit', async (event) => {
|
27
|
+
event.preventDefault();
|
28
|
+
const commandName = document.getElementById('commandName').value;
|
29
|
+
const params = document.getElementById('params').value.split(' ');
|
30
|
+
try {
|
31
|
+
const response = await fetch('/run_command', {
|
32
|
+
method: 'POST',
|
33
|
+
headers: {
|
34
|
+
'Content-Type': 'application/json'
|
35
|
+
},
|
36
|
+
body: JSON.stringify({ command: commandName, params: params })
|
37
|
+
});
|
38
|
+
if (!response.ok) {
|
39
|
+
throw new Error('Failed to launch command');
|
40
|
+
}
|
41
|
+
const data = await response.json();
|
42
|
+
await new Promise(r => setTimeout(r, 200));
|
43
|
+
fetchCommands();
|
44
|
+
viewOutput(data.command_id);
|
45
|
+
} catch (error) {
|
46
|
+
console.log('Error running command:', error);
|
47
|
+
}
|
48
|
+
});
|
49
|
+
|
50
|
+
async function fetchCommands() {
|
51
|
+
try {
|
52
|
+
const response = await fetch('/commands');
|
53
|
+
if (!response.ok) {
|
54
|
+
document.getElementById('dimmer').style.display = 'block';
|
55
|
+
return;
|
56
|
+
}
|
57
|
+
const commands = await response.json();
|
58
|
+
commands.sort((a, b) => new Date(b.start_time) - new Date(a.start_time));
|
59
|
+
const commandsTbody = document.getElementById('commands');
|
60
|
+
commandsTbody.innerHTML = '';
|
61
|
+
if (!currentCommandId && commands.length) {
|
62
|
+
currentCommandId = commands[0].command_id;
|
63
|
+
viewOutput(currentCommandId);
|
64
|
+
}
|
65
|
+
commands.forEach(command => {
|
66
|
+
const commandRow = document.createElement('tr');
|
67
|
+
commandRow.className = `clickable-row ${command.command_id === currentCommandId ? 'currentcommand' : ''}`;
|
68
|
+
commandRow.onclick = () => viewOutput(command.command_id);
|
69
|
+
commandRow.innerHTML = `
|
70
|
+
<td class="monospace">
|
71
|
+
${navigator.clipboard == undefined ? `${command.command_id.slice(0, 8)}` : `<span class="copy_clip" onclick="copyToClipboard('${command.command_id}', this, event)">${command.command_id.slice(0, 8)}</span>`}
|
72
|
+
</td>
|
73
|
+
<td>${formatTime(command.start_time)}</td>
|
74
|
+
<td>${command.status === 'running' ? formatDuration(command.start_time, new Date().toISOString()) : formatDuration(command.start_time, command.end_time)}</td>
|
75
|
+
<td>${command.command.replace(/^\.\//, '')}</td>
|
76
|
+
<td>${command.exit_code}</td>
|
77
|
+
<td><span class="status-icon status-${command.status}"></span>${command.status}</td>
|
78
|
+
<td>
|
79
|
+
${command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}')">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}')">Run</button>`}
|
80
|
+
</td>
|
81
|
+
<td class="monospace outcol">${command.last_output_line || ''}</td>
|
82
|
+
`;
|
83
|
+
commandsTbody.appendChild(commandRow);
|
84
|
+
});
|
85
|
+
document.getElementById('dimmer').style.display = 'none';
|
86
|
+
} catch (error) {
|
87
|
+
console.log('Error fetching commands:', error);
|
88
|
+
document.getElementById('dimmer').style.display = 'block';
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
async function fetchExecutables() {
|
93
|
+
try {
|
94
|
+
const response = await fetch('/executables');
|
95
|
+
if (!response.ok) {
|
96
|
+
throw new Error('Failed to fetch command status');
|
97
|
+
}
|
98
|
+
const executables = await response.json();
|
99
|
+
const commandNameSelect = document.getElementById('commandName');
|
100
|
+
commandNameSelect.innerHTML = '';
|
101
|
+
executables.forEach(executable => {
|
102
|
+
const option = document.createElement('option');
|
103
|
+
option.value = executable;
|
104
|
+
option.textContent = executable;
|
105
|
+
commandNameSelect.appendChild(option);
|
106
|
+
});
|
107
|
+
} catch (error) {
|
108
|
+
console.log('Error fetching executables:', error);
|
109
|
+
alert("Failed to fetch executables");
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
async function fetchOutput(url) {
|
114
|
+
try {
|
115
|
+
const response = await fetch(url);
|
116
|
+
if (!response.ok) {
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
const data = await response.json();
|
120
|
+
if (data.error) {
|
121
|
+
terminal.write(data.error);
|
122
|
+
clearInterval(outputInterval);
|
123
|
+
} else {
|
124
|
+
terminal.write(data.output);
|
125
|
+
nextOutputLink = data.links.next;
|
126
|
+
if (data.status != 'running') {
|
127
|
+
clearInterval(outputInterval);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
} catch (error) {
|
131
|
+
console.log('Error fetching output:', error);
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
async function viewOutput(command_id) {
|
136
|
+
adjustOutputHeight();
|
137
|
+
currentCommandId = command_id;
|
138
|
+
nextOutputLink = `/command_output/${command_id}`;
|
139
|
+
clearInterval(outputInterval);
|
140
|
+
terminal.clear();
|
141
|
+
try {
|
142
|
+
const response = await fetch(`/command_status/${command_id}`);
|
143
|
+
if (!response.ok) {
|
144
|
+
return;
|
145
|
+
}
|
146
|
+
const data = await response.json();
|
147
|
+
if (data.status === 'running') {
|
148
|
+
fetchOutput(nextOutputLink);
|
149
|
+
outputInterval = setInterval(() => fetchOutput(nextOutputLink), 1000);
|
150
|
+
} else {
|
151
|
+
fetchOutput(nextOutputLink);
|
152
|
+
}
|
153
|
+
fetchCommands(); // Refresh the command list to highlight the current command
|
154
|
+
} catch (error) {
|
155
|
+
console.log('Error viewing output:', error);
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
async function relaunchCommand(command_id) {
|
160
|
+
try {
|
161
|
+
const response = await fetch(`/command_status/${command_id}`);
|
162
|
+
if (!response.ok) {
|
163
|
+
throw new Error('Failed to fetch command status');
|
164
|
+
}
|
165
|
+
const data = await response.json();
|
166
|
+
if (data.error) {
|
167
|
+
alert(data.error);
|
168
|
+
return;
|
169
|
+
}
|
170
|
+
const relaunchResponse = await fetch('/run_command', {
|
171
|
+
method: 'POST',
|
172
|
+
headers: {
|
173
|
+
'Content-Type': 'application/json'
|
174
|
+
},
|
175
|
+
body: JSON.stringify({
|
176
|
+
command: data.command,
|
177
|
+
params: data.params
|
178
|
+
})
|
179
|
+
});
|
180
|
+
if (!relaunchResponse.ok) {
|
181
|
+
throw new Error('Failed to relaunch command');
|
182
|
+
}
|
183
|
+
const relaunchData = await relaunchResponse.json();
|
184
|
+
fetchCommands();
|
185
|
+
viewOutput(relaunchData.command_id);
|
186
|
+
} catch (error) {
|
187
|
+
console.log('Error relaunching command:', error);
|
188
|
+
alert('Failed to relaunch command. Please try again.');
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
async function stopCommand(command_id) {
|
193
|
+
try {
|
194
|
+
const response = await fetch(`/stop_command/${command_id}`, {
|
195
|
+
method: 'POST'
|
196
|
+
});
|
197
|
+
if (!response.ok) {
|
198
|
+
throw new Error('Failed to stop command');
|
199
|
+
}
|
200
|
+
const data = await response.json();
|
201
|
+
if (data.error) {
|
202
|
+
alert(data.error);
|
203
|
+
} else {
|
204
|
+
fetchCommands();
|
205
|
+
}
|
206
|
+
} catch (error) {
|
207
|
+
console.log('Error stopping command:', error);
|
208
|
+
alert('Failed to stop command. Please try again.');
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
function formatTime(time) {
|
213
|
+
if (!time || time === 'N/A') return 'N/A';
|
214
|
+
const date = new Date(time);
|
215
|
+
return date.toISOString().slice(0, 16).replace('T', ' ');
|
216
|
+
}
|
217
|
+
|
218
|
+
function formatDuration(startTime, endTime) {
|
219
|
+
if (!startTime || !endTime) return 'N/A';
|
220
|
+
const start = new Date(startTime);
|
221
|
+
const end = new Date(endTime);
|
222
|
+
const duration = (end - start) / 1000;
|
223
|
+
const hours = Math.floor(duration / 3600);
|
224
|
+
const minutes = Math.floor((duration % 3600) / 60);
|
225
|
+
const seconds = Math.floor(duration % 60);
|
226
|
+
return `${hours}h ${minutes}m ${seconds}s`;
|
227
|
+
}
|
228
|
+
|
229
|
+
function copyToClipboard(text, element, event) {
|
230
|
+
event.stopPropagation();
|
231
|
+
event.stopImmediatePropagation();
|
232
|
+
navigator.clipboard.writeText(text).then(() => {
|
233
|
+
element.classList.add('copy_clip_ok');
|
234
|
+
setTimeout(() => {
|
235
|
+
element.classList.remove('copy_clip_ok');
|
236
|
+
}, 1000);
|
237
|
+
});
|
238
|
+
}
|
239
|
+
|
240
|
+
function adjustOutputHeight() {
|
241
|
+
const outputDiv = document.getElementById('output');
|
242
|
+
const windowHeight = window.innerHeight;
|
243
|
+
const outputTop = outputDiv.getBoundingClientRect().top;
|
244
|
+
const maxHeight = windowHeight - outputTop - 30; // 20px for padding/margin
|
245
|
+
outputDiv.style.height = `${maxHeight}px`;
|
246
|
+
fitAddon.fit();
|
247
|
+
}
|
248
|
+
|
249
|
+
function initResizer() {
|
250
|
+
const resizer = document.getElementById('resizer');
|
251
|
+
const tableContainer = document.getElementById('tableContainer');
|
252
|
+
let startY, startHeight;
|
253
|
+
|
254
|
+
resizer.addEventListener('mousedown', (e) => {
|
255
|
+
startY = e.clientY;
|
256
|
+
startHeight = parseInt(document.defaultView.getComputedStyle(tableContainer).height, 10);
|
257
|
+
document.documentElement.addEventListener('mousemove', doDrag, false);
|
258
|
+
document.documentElement.addEventListener('mouseup', stopDrag, false);
|
259
|
+
});
|
260
|
+
|
261
|
+
function doDrag(e) {
|
262
|
+
tableContainer.style.height = `${startHeight + e.clientY - startY}px`;
|
263
|
+
adjustOutputHeight();
|
264
|
+
}
|
265
|
+
|
266
|
+
function stopDrag() {
|
267
|
+
document.documentElement.removeEventListener('mousemove', doDrag, false);
|
268
|
+
document.documentElement.removeEventListener('mouseup', stopDrag, false);
|
269
|
+
}
|
270
|
+
}
|
271
|
+
|
272
|
+
window.addEventListener('resize', adjustOutputHeight);
|
273
|
+
window.addEventListener('load', () => {
|
274
|
+
adjustOutputHeight();
|
275
|
+
initResizer();
|
276
|
+
});
|
277
|
+
|
278
|
+
fetchExecutables();
|
279
|
+
fetchCommands();
|
280
|
+
setInterval(fetchCommands, 5000);
|