pywebexec 1.9.3__py3-none-any.whl → 1.9.5__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 +111 -11
- pywebexec/static/css/style.css +53 -0
- pywebexec/static/js/{commands.js → executables.js} +36 -5
- pywebexec/static/js/marked/LICENSE.md +44 -0
- pywebexec/static/js/marked/marked.min.js +6 -0
- pywebexec/static/js/script.js +3 -1
- pywebexec/swagger.yaml +127 -1
- pywebexec/templates/index.html +3 -1
- pywebexec/version.py +2 -2
- {pywebexec-1.9.3.dist-info → pywebexec-1.9.5.dist-info}/METADATA +29 -8
- {pywebexec-1.9.3.dist-info → pywebexec-1.9.5.dist-info}/RECORD +15 -13
- {pywebexec-1.9.3.dist-info → pywebexec-1.9.5.dist-info}/LICENSE +0 -0
- {pywebexec-1.9.3.dist-info → pywebexec-1.9.5.dist-info}/WHEEL +0 -0
- {pywebexec-1.9.3.dist-info → pywebexec-1.9.5.dist-info}/entry_points.txt +0 -0
- {pywebexec-1.9.3.dist-info → pywebexec-1.9.5.dist-info}/top_level.txt +0 -0
pywebexec/pywebexec.py
CHANGED
@@ -29,7 +29,7 @@ from pathlib import Path
|
|
29
29
|
import pyte
|
30
30
|
from . import host_ip
|
31
31
|
from flask_swagger_ui import get_swaggerui_blueprint # new import
|
32
|
-
import yaml
|
32
|
+
import yaml
|
33
33
|
|
34
34
|
if os.environ.get('PYWEBEXEC_LDAP_SERVER'):
|
35
35
|
from ldap3 import Server, Connection, ALL, SIMPLE, SUBTREE, Tls
|
@@ -167,12 +167,22 @@ def strip_ansi_control_chars(text):
|
|
167
167
|
def decode_line(line: bytes) -> str:
|
168
168
|
"""try decode line exception on binary"""
|
169
169
|
try:
|
170
|
-
return
|
170
|
+
return get_visible_line(line.decode())
|
171
|
+
except UnicodeDecodeError:
|
172
|
+
return ""
|
173
|
+
|
174
|
+
def get_visible_output(output, cols, rows):
|
175
|
+
"""pyte vt100 render to get last line"""
|
176
|
+
try:
|
177
|
+
screen = pyte.Screen(cols, rows)
|
178
|
+
stream = pyte.Stream(screen)
|
179
|
+
stream.feed(output)
|
180
|
+
return "\n".join(screen.display).strip()
|
171
181
|
except UnicodeDecodeError:
|
172
182
|
return ""
|
173
183
|
|
174
184
|
|
175
|
-
def
|
185
|
+
def get_visible_line(line, cols, rows):
|
176
186
|
"""pyte vt100 render to get last line"""
|
177
187
|
try:
|
178
188
|
screen = pyte.Screen(cols, rows)
|
@@ -197,7 +207,7 @@ def get_last_line(file_path, cols=None, rows=None, maxsize=2048):
|
|
197
207
|
fd.seek(-maxsize, os.SEEK_END)
|
198
208
|
except OSError:
|
199
209
|
fd.seek(0)
|
200
|
-
return
|
210
|
+
return get_visible_line(fd.read(), cols, rows)
|
201
211
|
|
202
212
|
|
203
213
|
def start_gunicorn(daemonized=False, baselog=None):
|
@@ -619,6 +629,19 @@ def log_error(fromip, user, message):
|
|
619
629
|
def log_request(message):
|
620
630
|
log_info(request.remote_addr, session.get('username', '-'), message)
|
621
631
|
|
632
|
+
def get_executables():
|
633
|
+
executables_list = []
|
634
|
+
for f in os.listdir('.'):
|
635
|
+
if os.path.isfile(f) and os.access(f, os.X_OK):
|
636
|
+
help_file = f"{f}.help"
|
637
|
+
help_text = ""
|
638
|
+
if os.path.exists(help_file) and os.path.isfile(help_file):
|
639
|
+
with open(help_file, 'r') as hf:
|
640
|
+
help_text = hf.read()
|
641
|
+
executables_list.append({"command": f, "help": help_text})
|
642
|
+
return executables_list
|
643
|
+
|
644
|
+
|
622
645
|
@app.route('/commands/<command_id>/stop', methods=['PATCH'])
|
623
646
|
def stop_command(command_id):
|
624
647
|
log_request(f"stop_command {command_id}")
|
@@ -744,6 +767,37 @@ def run_command_endpoint():
|
|
744
767
|
|
745
768
|
return jsonify({'message': 'Command is running', 'command_id': command_id})
|
746
769
|
|
770
|
+
@app.route('/commands/<cmd>', methods=['POST'])
|
771
|
+
def run_dynamic_command(cmd):
|
772
|
+
# Validate that 'cmd' is an executable in the current directory
|
773
|
+
cmd_path = os.path.join(".", os.path.basename(cmd))
|
774
|
+
if not os.path.isfile(cmd_path) or not os.access(cmd_path, os.X_OK):
|
775
|
+
return jsonify({'error': 'Command not found or not executable'}), 400
|
776
|
+
try:
|
777
|
+
data = request.json
|
778
|
+
except Exception as e:
|
779
|
+
data = {}
|
780
|
+
params = data.get('params', [])
|
781
|
+
rows = data.get('rows', tty_rows) or tty_rows
|
782
|
+
cols = data.get('cols', tty_cols) or tty_cols
|
783
|
+
try:
|
784
|
+
params = shlex.split(' '.join(params)) if isinstance(params, list) else []
|
785
|
+
except Exception as e:
|
786
|
+
return jsonify({'error': str(e)}), 400
|
787
|
+
user = session.get('username', '-')
|
788
|
+
command_id = str(uuid.uuid4())
|
789
|
+
update_command_status(command_id, {
|
790
|
+
'status': 'running',
|
791
|
+
'command': cmd,
|
792
|
+
'params': params,
|
793
|
+
'user': user,
|
794
|
+
'from': request.remote_addr,
|
795
|
+
})
|
796
|
+
Path(get_output_file_path(command_id)).touch()
|
797
|
+
thread = threading.Thread(target=run_command, args=(request.remote_addr, user, cmd_path, params, command_id, rows, cols))
|
798
|
+
thread.start()
|
799
|
+
return jsonify({'message': 'Command is running', 'command_id': command_id})
|
800
|
+
|
747
801
|
@app.route('/commands/<command_id>', methods=['GET'])
|
748
802
|
def get_command_status(command_id):
|
749
803
|
status = read_command_status(command_id)
|
@@ -757,15 +811,15 @@ def index():
|
|
757
811
|
|
758
812
|
@app.route('/commands', methods=['GET'])
|
759
813
|
def list_commands():
|
760
|
-
# Sort commands by start_time in descending order
|
761
814
|
commands = read_commands()
|
762
815
|
commands.sort(key=lambda x: x['start_time'], reverse=True)
|
763
|
-
return jsonify(commands)
|
816
|
+
return jsonify({"commands": commands})
|
764
817
|
|
765
818
|
@app.route('/commands/<command_id>/output', methods=['GET'])
|
766
819
|
def get_command_output(command_id):
|
767
820
|
offset = int(request.args.get('offset', 0))
|
768
821
|
maxsize = int(request.args.get('maxsize', 10485760))
|
822
|
+
maxlines = int(request.args.get('maxlines', 5000))
|
769
823
|
output_file_path = get_output_file_path(command_id)
|
770
824
|
if os.path.exists(output_file_path):
|
771
825
|
size = os.path.getsize(output_file_path)
|
@@ -790,7 +844,7 @@ def get_command_output(command_id):
|
|
790
844
|
}
|
791
845
|
}
|
792
846
|
if request.headers.get('Accept') == 'text/plain':
|
793
|
-
return f"{output}\nstatus: {status_data.get('status')}", 200, {'Content-Type': 'text/plain'}
|
847
|
+
return f"{get_visible_output(output, status_data.get('cols'), maxlines)}\nstatus: {status_data.get('status')}", 200, {'Content-Type': 'text/plain'}
|
794
848
|
return jsonify(response)
|
795
849
|
return jsonify({'error': 'Invalid command_id'}), 404
|
796
850
|
|
@@ -820,9 +874,8 @@ def get_command_output_raw(command_id):
|
|
820
874
|
|
821
875
|
@app.route('/executables', methods=['GET'])
|
822
876
|
def list_executables():
|
823
|
-
|
824
|
-
|
825
|
-
return jsonify(executables)
|
877
|
+
executables_list = get_executables()
|
878
|
+
return jsonify({"executables": executables_list})
|
826
879
|
|
827
880
|
@app.route('/commands/<command_id>/popup')
|
828
881
|
def popup(command_id):
|
@@ -854,7 +907,54 @@ def swagger_yaml():
|
|
854
907
|
with open(swagger_path, 'r') as f:
|
855
908
|
swagger_spec_str = f.read()
|
856
909
|
swagger_spec = yaml.safe_load(swagger_spec_str)
|
857
|
-
#
|
910
|
+
# Update existing POST /commands enum if present
|
911
|
+
executables = get_executables()
|
912
|
+
post_cmd = swagger_spec.get('paths', {}).get('/commands', {}).get('post')
|
913
|
+
if post_cmd:
|
914
|
+
params_list = post_cmd.get('parameters', [])
|
915
|
+
for param in params_list:
|
916
|
+
if param.get('in') == 'body' and 'schema' in param:
|
917
|
+
props = param['schema'].get('properties', {})
|
918
|
+
if 'command' in props:
|
919
|
+
props['command']['enum'] = [e['command'] for e in executables]
|
920
|
+
# Add dynamic paths for each
|
921
|
+
# Add dynamic paths for each executable:
|
922
|
+
for exe in executables:
|
923
|
+
dynamic_path = "/commands/" + exe["command"]
|
924
|
+
swagger_spec.setdefault("paths", {})[dynamic_path] = {
|
925
|
+
"post": {
|
926
|
+
"summary": f"Run command {exe["command"]}",
|
927
|
+
"description": f"{exe["help"]}",
|
928
|
+
"consumes": ["application/json"],
|
929
|
+
"produces": ["application/json"],
|
930
|
+
"parameters": [
|
931
|
+
{
|
932
|
+
"in": "body",
|
933
|
+
"name": "commandRequest",
|
934
|
+
"schema": {
|
935
|
+
"type": "object",
|
936
|
+
"properties": {
|
937
|
+
"params": {"type": "array", "items": {"type": "string"}, "default": []},
|
938
|
+
"rows": {"type": "integer", "default": tty_rows},
|
939
|
+
"cols": {"type": "integer", "default": tty_cols},
|
940
|
+
}
|
941
|
+
}
|
942
|
+
}
|
943
|
+
],
|
944
|
+
"responses": {
|
945
|
+
"200": {
|
946
|
+
"description": "Command started",
|
947
|
+
"schema": {
|
948
|
+
"type": "object",
|
949
|
+
"properties": {
|
950
|
+
"message": {"type": "string"},
|
951
|
+
"command_id": {"type": "string"}
|
952
|
+
}
|
953
|
+
}
|
954
|
+
}
|
955
|
+
}
|
956
|
+
}
|
957
|
+
}
|
858
958
|
swagger_spec['info']['title'] = app.config.get('TITLE', 'PyWebExec API')
|
859
959
|
swagger_spec_str = yaml.dump(swagger_spec)
|
860
960
|
return Response(swagger_spec_str, mimetype='application/yaml')
|
pywebexec/static/css/style.css
CHANGED
@@ -99,6 +99,59 @@ form {
|
|
99
99
|
vertical-align: bottom;
|
100
100
|
padding-left: 35px;
|
101
101
|
}
|
102
|
+
#paramsHelp {
|
103
|
+
position: absolute;
|
104
|
+
background-color: #e0e0ff;
|
105
|
+
border: 1px solid #ccc;
|
106
|
+
padding: 0px 8px 8px 8px;
|
107
|
+
font-size: 0.9em;
|
108
|
+
display: none;
|
109
|
+
z-index: 1000;
|
110
|
+
/* white-space: pre; */
|
111
|
+
border-radius: 5px;
|
112
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
113
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.40);
|
114
|
+
|
115
|
+
h1, h2, h3, h4, h5, h6 {
|
116
|
+
margin-top: 4px;
|
117
|
+
}
|
118
|
+
table {
|
119
|
+
border-spacing: 0px;
|
120
|
+
border-collapse: unset;
|
121
|
+
background-color: unset;
|
122
|
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.50);
|
123
|
+
border-radius: 10px;
|
124
|
+
overflow: hidden;
|
125
|
+
}
|
126
|
+
td, th{
|
127
|
+
margin: 0;
|
128
|
+
padding: 4px 6px;
|
129
|
+
border-bottom: 1px solid #aaa;
|
130
|
+
}
|
131
|
+
|
132
|
+
th:last-child {
|
133
|
+
border-top-right-radius: 10px;
|
134
|
+
}
|
135
|
+
th:first-child {
|
136
|
+
border-top-left-radius: 10px;
|
137
|
+
}
|
138
|
+
tr:last-child > td:last-child {
|
139
|
+
border-bottom-right-radius: 10px;
|
140
|
+
}
|
141
|
+
tr:last-child > td:first-child {
|
142
|
+
border-bottom-left-radius: 10px;
|
143
|
+
}
|
144
|
+
tr > td:first-child {
|
145
|
+
border-left: 1px solid #aaa;
|
146
|
+
}
|
147
|
+
tr > td:last-child {
|
148
|
+
border-right: 1px solid #aaa;
|
149
|
+
}
|
150
|
+
code {
|
151
|
+
font-family: "CommitMono Nerd Font Mono", Consolas, "Courier New", monospace;
|
152
|
+
font-size: 14px;
|
153
|
+
}
|
154
|
+
}
|
102
155
|
#thStatus {
|
103
156
|
cursor: pointer;
|
104
157
|
}
|
@@ -5,6 +5,8 @@ let commandListDiv = document.getElementById('commandList');
|
|
5
5
|
let showCommandListButton = document.getElementById('showCommandListButton');
|
6
6
|
let isHandlingKeydown = false;
|
7
7
|
let firstVisibleItem = 0;
|
8
|
+
let gExecutables = {};
|
9
|
+
let helpDiv = document.getElementById('paramsHelp');
|
8
10
|
|
9
11
|
function unfilterCommands() {
|
10
12
|
const items = commandListDiv.children;
|
@@ -43,6 +45,13 @@ function setCommandListPosition() {
|
|
43
45
|
commandListDiv.style.top = `${rect.bottom}px`;
|
44
46
|
}
|
45
47
|
|
48
|
+
// Update helpDiv position relative to paramsInput
|
49
|
+
function setHelpDivPosition() {
|
50
|
+
const rect = commandInput.getBoundingClientRect();
|
51
|
+
helpDiv.style.left = `${rect.left}px`;
|
52
|
+
helpDiv.style.top = `${rect.bottom + 2}px`;
|
53
|
+
}
|
54
|
+
|
46
55
|
function adjustInputWidth(input) {
|
47
56
|
input.style.width = 'auto';
|
48
57
|
input.style.width = `${input.scrollWidth}px`;
|
@@ -218,15 +227,18 @@ async function fetchExecutables() {
|
|
218
227
|
try {
|
219
228
|
const response = await fetch(`/executables`);
|
220
229
|
if (!response.ok) {
|
221
|
-
throw new Error('Failed to fetch
|
230
|
+
throw new Error('Failed to fetch executables');
|
222
231
|
}
|
223
|
-
const
|
232
|
+
const data = await response.json();
|
233
|
+
// Build mapping from executable name to its object
|
234
|
+
gExecutables = {};
|
224
235
|
commandListDiv.innerHTML = '';
|
225
|
-
executables.forEach(
|
236
|
+
data.executables.forEach(exeObj => {
|
237
|
+
gExecutables[exeObj.command] = exeObj;
|
226
238
|
const div = document.createElement('div');
|
227
239
|
div.className = 'command-item';
|
228
|
-
div.textContent =
|
229
|
-
div.tabIndex = 0;
|
240
|
+
div.textContent = exeObj.command;
|
241
|
+
div.tabIndex = 0;
|
230
242
|
commandListDiv.appendChild(div);
|
231
243
|
});
|
232
244
|
} catch (error) {
|
@@ -240,3 +252,22 @@ async function fetchExecutables() {
|
|
240
252
|
document.getElementById('launchForm').style.display = 'none';
|
241
253
|
|
242
254
|
}
|
255
|
+
|
256
|
+
paramsInput.addEventListener('focus', () => {
|
257
|
+
const currentCmd = commandInput.value;
|
258
|
+
if (gExecutables[currentCmd] && gExecutables[currentCmd].help) {
|
259
|
+
//helpDiv.innerHTML = gExecutables[currentCmd].help.replace(/\n/g, '<br>');
|
260
|
+
helpDiv.innerHTML = marked.parse(gExecutables[currentCmd].help);
|
261
|
+
setHelpDivPosition();
|
262
|
+
helpDiv.style.display = 'block';
|
263
|
+
} else {
|
264
|
+
helpDiv.style.display = 'none';
|
265
|
+
}
|
266
|
+
});
|
267
|
+
|
268
|
+
paramsInput.addEventListener('blur', () => {
|
269
|
+
helpDiv.style.display = 'none';
|
270
|
+
});
|
271
|
+
|
272
|
+
window.addEventListener('resize', setHelpDivPosition);
|
273
|
+
window.addEventListener('scroll', setHelpDivPosition);
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# License information
|
2
|
+
|
3
|
+
## Contribution License Agreement
|
4
|
+
|
5
|
+
If you contribute code to this project, you are implicitly allowing your code
|
6
|
+
to be distributed under the MIT license. You are also implicitly verifying that
|
7
|
+
all code is your original work. `</legalese>`
|
8
|
+
|
9
|
+
## Marked
|
10
|
+
|
11
|
+
Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
|
12
|
+
Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
|
13
|
+
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
16
|
+
in the Software without restriction, including without limitation the rights
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
19
|
+
furnished to do so, subject to the following conditions:
|
20
|
+
|
21
|
+
The above copyright notice and this permission notice shall be included in
|
22
|
+
all copies or substantial portions of the Software.
|
23
|
+
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
30
|
+
THE SOFTWARE.
|
31
|
+
|
32
|
+
## Markdown
|
33
|
+
|
34
|
+
Copyright © 2004, John Gruber
|
35
|
+
http://daringfireball.net/
|
36
|
+
All rights reserved.
|
37
|
+
|
38
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
39
|
+
|
40
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
41
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
42
|
+
* Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
43
|
+
|
44
|
+
This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.
|
@@ -0,0 +1,6 @@
|
|
1
|
+
/**
|
2
|
+
* marked v15.0.7 - a markdown parser
|
3
|
+
* Copyright (c) 2011-2025, Christopher Jeffrey. (MIT Licensed)
|
4
|
+
* https://github.com/markedjs/marked
|
5
|
+
*/
|
6
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s={exec:()=>null};function r(e,t=""){let n="string"==typeof e?e:e.source;const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(i.caret,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}const i={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^<a /i,endATag:/^<\/a>/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^</,endAngleBracket:/>$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[\t ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i")},l=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,o=/(?:[*+-]|\d{1,9}[.)])/,a=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,c=r(a).replace(/bull/g,o).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),h=r(a).replace(/bull/g,o).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),p=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,u=/(?!\s*\])(?:\\.|[^\[\]\\])+/,g=r(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",u).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),k=r(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,o).getRegex(),d="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",f=/<!--(?:-?>|[\s\S]*?(?:-->|$))/,x=r("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$))","i").replace("comment",f).replace("tag",d).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),b=r(p).replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex(),w={blockquote:r(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",b).getRegex(),code:/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,def:g,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:l,html:x,lheading:c,list:k,newline:/^(?:[ \t]*(?:\n|$))+/,paragraph:b,table:s,text:/^[^\n]+/},m=r("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3}\t)[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex(),y={...w,lheading:h,table:m,paragraph:r(p).replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",m).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex()},$={...w,html:r("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:\"[^\"]*\"|'[^']*'|\\s[^'\"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",f).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:s,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:r(p).replace("hr",l).replace("heading"," *#{1,6} *[^\n]").replace("lheading",c).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},R=/^( {2,}|\\)\n(?!\s*$)/,S=/[\p{P}\p{S}]/u,T=/[\s\p{P}\p{S}]/u,z=/[^\s\p{P}\p{S}]/u,A=r(/^((?![*_])punctSpace)/,"u").replace(/punctSpace/g,T).getRegex(),_=/(?!~)[\p{P}\p{S}]/u,P=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,I=r(P,"u").replace(/punct/g,S).getRegex(),L=r(P,"u").replace(/punct/g,_).getRegex(),B="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",C=r(B,"gu").replace(/notPunctSpace/g,z).replace(/punctSpace/g,T).replace(/punct/g,S).getRegex(),q=r(B,"gu").replace(/notPunctSpace/g,/(?:[^\s\p{P}\p{S}]|~)/u).replace(/punctSpace/g,/(?!~)[\s\p{P}\p{S}]/u).replace(/punct/g,_).getRegex(),E=r("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,z).replace(/punctSpace/g,T).replace(/punct/g,S).getRegex(),Z=r(/\\(punct)/,"gu").replace(/punct/g,S).getRegex(),v=r(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),D=r(f).replace("(?:--\x3e|$)","--\x3e").getRegex(),M=r("^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment",D).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),O=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Q=r(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",O).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),j=r(/^!?\[(label)\]\[(ref)\]/).replace("label",O).replace("ref",u).getRegex(),N=r(/^!?\[(ref)\](?:\[\])?/).replace("ref",u).getRegex(),G={_backpedal:s,anyPunctuation:Z,autolink:v,blockSkip:/\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g,br:R,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:s,emStrongLDelim:I,emStrongRDelimAst:C,emStrongRDelimUnd:E,escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,link:Q,nolink:N,punctuation:A,reflink:j,reflinkSearch:r("reflink|nolink(?!\\()","g").replace("reflink",j).replace("nolink",N).getRegex(),tag:M,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,url:s},H={...G,link:r(/^!?\[(label)\]\((.*?)\)/).replace("label",O).getRegex(),reflink:r(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",O).getRegex()},X={...G,emStrongRDelimAst:q,emStrongLDelim:L,url:r(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,"i").replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/},F={...X,br:r(R).replace("{2,}","*").getRegex(),text:r(X.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()},U={normal:w,gfm:y,pedantic:$},J={normal:G,gfm:X,breaks:F,pedantic:H},K={"&":"&","<":"<",">":">",'"':""","'":"'"},V=e=>K[e];function W(e,t){if(t){if(i.escapeTest.test(e))return e.replace(i.escapeReplace,V)}else if(i.escapeTestNoEncode.test(e))return e.replace(i.escapeReplaceNoEncode,V);return e}function Y(e){try{e=encodeURI(e).replace(i.percentDecode,"%")}catch{return null}return e}function ee(e,t){const n=e.replace(i.findPipe,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(i.splitPipe);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;s<n.length;s++)n[s]=n[s].trim().replace(i.slashPipe,"|");return n}function te(e,t,n){const s=e.length;if(0===s)return"";let r=0;for(;r<s;){if(e.charAt(s-r-1)!==t)break;r++}return e.slice(0,s-r)}function ne(e,t,n,s,r){const i=t.href,l=t.title||null,o=e[1].replace(r.other.outputLinkReplace,"$1");if("!"!==e[0].charAt(0)){s.state.inLink=!0;const e={type:"link",raw:n,href:i,title:l,text:o,tokens:s.inlineTokens(o)};return s.state.inLink=!1,e}return{type:"image",raw:n,href:i,title:l,text:o}}class se{options;rules;lexer;constructor(t){this.options=t||e.defaults}space(e){const t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:te(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t,n){const s=e.match(n.other.indentCodeCompensation);if(null===s)return t;const r=s[1];return t.split("\n").map((e=>{const t=e.match(n.other.beginningSpace);if(null===t)return e;const[s]=t;return s.length>=r.length?e.slice(r.length):e})).join("\n")}(e,t[3]||"",this.rules);return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(this.rules.other.endingHash.test(e)){const t=te(e,"#");this.options.pedantic?e=t.trim():t&&!this.rules.other.endingSpaceChar.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:te(t[0],"\n")}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let e=te(t[0],"\n").split("\n"),n="",s="";const r=[];for(;e.length>0;){let t=!1;const i=[];let l;for(l=0;l<e.length;l++)if(this.rules.other.blockquoteStart.test(e[l]))i.push(e[l]),t=!0;else{if(t)break;i.push(e[l])}e=e.slice(l);const o=i.join("\n"),a=o.replace(this.rules.other.blockquoteSetextReplace,"\n $1").replace(this.rules.other.blockquoteSetextReplace2,"");n=n?`${n}\n${o}`:o,s=s?`${s}\n${a}`:a;const c=this.lexer.state.top;if(this.lexer.state.top=!0,this.lexer.blockTokens(a,r,!0),this.lexer.state.top=c,0===e.length)break;const h=r.at(-1);if("code"===h?.type)break;if("blockquote"===h?.type){const t=h,i=t.raw+"\n"+e.join("\n"),l=this.blockquote(i);r[r.length-1]=l,n=n.substring(0,n.length-t.raw.length)+l.raw,s=s.substring(0,s.length-t.text.length)+l.text;break}if("list"!==h?.type);else{const t=h,i=t.raw+"\n"+e.join("\n"),l=this.list(i);r[r.length-1]=l,n=n.substring(0,n.length-h.raw.length)+l.raw,s=s.substring(0,s.length-t.raw.length)+l.raw,e=i.substring(r.at(-1).raw.length).split("\n")}}return{type:"blockquote",raw:n,tokens:r,text:s}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim();const s=n.length>1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=this.rules.other.listItemRegex(n);let l=!1;for(;e;){let n=!1,s="",o="";if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;s=t[0],e=e.substring(s.length);let a=t[2].split("\n",1)[0].replace(this.rules.other.listReplaceTabs,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=!a.trim(),p=0;if(this.options.pedantic?(p=2,o=a.trimStart()):h?p=t[1].length+1:(p=t[2].search(this.rules.other.nonSpaceChar),p=p>4?1:p,o=a.slice(p),p+=t[1].length),h&&this.rules.other.blankLine.test(c)&&(s+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=this.rules.other.nextBulletRegex(p),n=this.rules.other.hrRegex(p),r=this.rules.other.fencesBeginRegex(p),i=this.rules.other.headingBeginRegex(p),l=this.rules.other.htmlBeginRegex(p);for(;e;){const u=e.split("\n",1)[0];let g;if(c=u,this.options.pedantic?(c=c.replace(this.rules.other.listReplaceNesting," "),g=c):g=c.replace(this.rules.other.tabCharGlobal," "),r.test(c))break;if(i.test(c))break;if(l.test(c))break;if(t.test(c))break;if(n.test(c))break;if(g.search(this.rules.other.nonSpaceChar)>=p||!c.trim())o+="\n"+g.slice(p);else{if(h)break;if(a.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4)break;if(r.test(a))break;if(i.test(a))break;if(n.test(a))break;o+="\n"+c}h||c.trim()||(h=!0),s+=u+"\n",e=e.substring(u.length+1),a=g.slice(p)}}r.loose||(l?r.loose=!0:this.rules.other.doubleBlankLine.test(s)&&(l=!0));let u,g=null;this.options.gfm&&(g=this.rules.other.listIsTask.exec(o),g&&(u="[ ] "!==g[0],o=o.replace(this.rules.other.listReplaceTask,""))),r.items.push({type:"list_item",raw:s,task:!!g,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=s}const o=r.items.at(-1);if(!o)return;o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e<r.items.length;e++)if(this.lexer.state.top=!1,r.items[e].tokens=this.lexer.blockTokens(r.items[e].text,[]),!r.loose){const t=r.items[e].tokens.filter((e=>"space"===e.type)),n=t.length>0&&t.some((e=>this.rules.other.anyLine.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e<r.items.length;e++)r.items[e].loose=!0;return r}}html(e){const t=this.rules.block.html.exec(e);if(t){return{type:"html",block:!0,raw:t[0],pre:"pre"===t[1]||"script"===t[1]||"style"===t[1],text:t[0]}}}def(e){const t=this.rules.block.def.exec(e);if(t){const e=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),n=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",s=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:e,raw:t[0],href:n,title:s}}}table(e){const t=this.rules.block.table.exec(e);if(!t)return;if(!this.rules.other.tableDelimiter.test(t[2]))return;const n=ee(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),r=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split("\n"):[],i={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(const e of s)this.rules.other.tableAlignRight.test(e)?i.align.push("right"):this.rules.other.tableAlignCenter.test(e)?i.align.push("center"):this.rules.other.tableAlignLeft.test(e)?i.align.push("left"):i.align.push(null);for(let e=0;e<n.length;e++)i.header.push({text:n[e],tokens:this.lexer.inline(n[e]),header:!0,align:i.align[e]});for(const e of r)i.rows.push(ee(e,i.header.length).map(((e,t)=>({text:e,tokens:this.lexer.inline(e),header:!1,align:i.align[t]}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(e)){if(!this.rules.other.endAngleBracket.test(e))return;const t=te(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s<e.length;s++)if("\\"===e[s])s++;else if(e[s]===t[0])n++;else if(e[s]===t[1]&&(n--,n<0))return s;return-1}(t[2],"()");if(e>-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=this.rules.other.pedanticHrefTitle.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),this.rules.other.startAngleBracket.test(n)&&(n=this.options.pedantic&&!this.rules.other.endAngleBracket.test(e)?n.slice(1):n.slice(1,-1)),ne(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return ne(n,e,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(this.rules.other.newLineCharGlobal," ");const n=this.rules.other.nonSpaceChar.test(e),s=this.rules.other.startingSpaceChar.test(e)&&this.rules.other.endingSpaceChar.test(e);return n&&s&&(e=e.substring(1,e.length-1)),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=t[1],n="mailto:"+e):(e=t[1],n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=t[0],n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=t[0],n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){const e=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:e}}}}class re{tokens;options;state;tokenizer;inlineQueue;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||e.defaults,this.options.tokenizer=this.options.tokenizer||new se,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};const n={other:i,block:U.normal,inline:J.normal};this.options.pedantic?(n.block=U.pedantic,n.inline=J.pedantic):this.options.gfm&&(n.block=U.gfm,this.options.breaks?n.inline=J.breaks:n.inline=J.gfm),this.tokenizer.rules=n}static get rules(){return{block:U,inline:J}}static lex(e,t){return new re(t).lex(e)}static lexInline(e,t){return new re(t).inlineTokens(e)}lex(e){e=e.replace(i.carriageReturn,"\n"),this.blockTokens(e,this.tokens);for(let e=0;e<this.inlineQueue.length;e++){const t=this.inlineQueue[e];this.inlineTokens(t.src,t.tokens)}return this.inlineQueue=[],this.tokens}blockTokens(e,t=[],n=!1){for(this.options.pedantic&&(e=e.replace(i.tabCharGlobal," ").replace(i.spaceLine,""));e;){let s;if(this.options.extensions?.block?.some((n=>!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0))))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);const n=t.at(-1);1===s.raw.length&&void 0!==n?n.raw+="\n":t.push(s);continue}if(s=this.tokenizer.code(e)){e=e.substring(s.raw.length);const n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.text,this.inlineQueue.at(-1).src=n.text):t.push(s);continue}if(s=this.tokenizer.fences(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.heading(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.hr(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.blockquote(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.list(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.html(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.def(e)){e=e.substring(s.raw.length);const n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.raw,this.inlineQueue.at(-1).src=n.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title});continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),t.push(s);continue}let r=e;if(this.options.extensions?.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(this.state.top&&(s=this.tokenizer.paragraph(r))){const i=t.at(-1);n&&"paragraph"===i?.type?(i.raw+="\n"+s.raw,i.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=i.text):t.push(s),n=r.length!==e.length,e=e.substring(s.raw.length)}else if(s=this.tokenizer.text(e)){e=e.substring(s.raw.length);const n=t.at(-1);"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=n.text):t.push(s)}else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,s=null;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(s=this.tokenizer.rules.inline.reflinkSearch.exec(n));)e.includes(s[0].slice(s[0].lastIndexOf("[")+1,-1))&&(n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(s=this.tokenizer.rules.inline.blockSkip.exec(n));)n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(s=this.tokenizer.rules.inline.anyPunctuation.exec(n));)n=n.slice(0,s.index)+"++"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let r=!1,i="";for(;e;){let s;if(r||(i=""),r=!1,this.options.extensions?.inline?.some((n=>!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0))))continue;if(s=this.tokenizer.escape(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.tag(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.link(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(s.raw.length);const n=t.at(-1);"text"===s.type&&"text"===n?.type?(n.raw+=s.raw,n.text+=s.text):t.push(s);continue}if(s=this.tokenizer.emStrong(e,n,i)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.codespan(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.br(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.del(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.autolink(e)){e=e.substring(s.raw.length),t.push(s);continue}if(!this.state.inLink&&(s=this.tokenizer.url(e))){e=e.substring(s.raw.length),t.push(s);continue}let l=e;if(this.options.extensions?.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(l=e.substring(0,t+1))}if(s=this.tokenizer.inlineText(l)){e=e.substring(s.raw.length),"_"!==s.raw.slice(-1)&&(i=s.raw.slice(-1)),r=!0;const n=t.at(-1);"text"===n?.type?(n.raw+=s.raw,n.text+=s.text):t.push(s)}else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return t}}class ie{options;parser;constructor(t){this.options=t||e.defaults}space(e){return""}code({text:e,lang:t,escaped:n}){const s=(t||"").match(i.notSpaceStart)?.[0],r=e.replace(i.endingNewline,"")+"\n";return s?'<pre><code class="language-'+W(s)+'">'+(n?r:W(r,!0))+"</code></pre>\n":"<pre><code>"+(n?r:W(r,!0))+"</code></pre>\n"}blockquote({tokens:e}){return`<blockquote>\n${this.parser.parse(e)}</blockquote>\n`}html({text:e}){return e}heading({tokens:e,depth:t}){return`<h${t}>${this.parser.parseInline(e)}</h${t}>\n`}hr(e){return"<hr>\n"}list(e){const t=e.ordered,n=e.start;let s="";for(let t=0;t<e.items.length;t++){const n=e.items[t];s+=this.listitem(n)}const r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+s+"</"+r+">\n"}listitem(e){let t="";if(e.task){const n=this.checkbox({checked:!!e.checked});e.loose?"paragraph"===e.tokens[0]?.type?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&"text"===e.tokens[0].tokens[0].type&&(e.tokens[0].tokens[0].text=n+" "+W(e.tokens[0].tokens[0].text),e.tokens[0].tokens[0].escaped=!0)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" ",escaped:!0}):t+=n+" "}return t+=this.parser.parse(e.tokens,!!e.loose),`<li>${t}</li>\n`}checkbox({checked:e}){return"<input "+(e?'checked="" ':"")+'disabled="" type="checkbox">'}paragraph({tokens:e}){return`<p>${this.parser.parseInline(e)}</p>\n`}table(e){let t="",n="";for(let t=0;t<e.header.length;t++)n+=this.tablecell(e.header[t]);t+=this.tablerow({text:n});let s="";for(let t=0;t<e.rows.length;t++){const r=e.rows[t];n="";for(let e=0;e<r.length;e++)n+=this.tablecell(r[e]);s+=this.tablerow({text:n})}return s&&(s=`<tbody>${s}</tbody>`),"<table>\n<thead>\n"+t+"</thead>\n"+s+"</table>\n"}tablerow({text:e}){return`<tr>\n${e}</tr>\n`}tablecell(e){const t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`</${n}>\n`}strong({tokens:e}){return`<strong>${this.parser.parseInline(e)}</strong>`}em({tokens:e}){return`<em>${this.parser.parseInline(e)}</em>`}codespan({text:e}){return`<code>${W(e,!0)}</code>`}br(e){return"<br>"}del({tokens:e}){return`<del>${this.parser.parseInline(e)}</del>`}link({href:e,title:t,tokens:n}){const s=this.parser.parseInline(n),r=Y(e);if(null===r)return s;let i='<a href="'+(e=r)+'"';return t&&(i+=' title="'+W(t)+'"'),i+=">"+s+"</a>",i}image({href:e,title:t,text:n}){const s=Y(e);if(null===s)return W(n);let r=`<img src="${e=s}" alt="${n}"`;return t&&(r+=` title="${W(t)}"`),r+=">",r}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):"escaped"in e&&e.escaped?e.text:W(e.text)}}class le{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}}class oe{options;renderer;textRenderer;constructor(t){this.options=t||e.defaults,this.options.renderer=this.options.renderer||new ie,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new le}static parse(e,t){return new oe(t).parse(e)}static parseInline(e,t){return new oe(t).parseInline(e)}parse(e,t=!0){let n="";for(let s=0;s<e.length;s++){const r=e[s];if(this.options.extensions?.renderers?.[r.type]){const e=r,t=this.options.extensions.renderers[e.type].call({parser:this},e);if(!1!==t||!["space","hr","heading","code","table","blockquote","list","html","paragraph","text"].includes(e.type)){n+=t||"";continue}}const i=r;switch(i.type){case"space":n+=this.renderer.space(i);continue;case"hr":n+=this.renderer.hr(i);continue;case"heading":n+=this.renderer.heading(i);continue;case"code":n+=this.renderer.code(i);continue;case"table":n+=this.renderer.table(i);continue;case"blockquote":n+=this.renderer.blockquote(i);continue;case"list":n+=this.renderer.list(i);continue;case"html":n+=this.renderer.html(i);continue;case"paragraph":n+=this.renderer.paragraph(i);continue;case"text":{let r=i,l=this.renderer.text(r);for(;s+1<e.length&&"text"===e[s+1].type;)r=e[++s],l+="\n"+this.renderer.text(r);n+=t?this.renderer.paragraph({type:"paragraph",raw:l,text:l,tokens:[{type:"text",raw:l,text:l,escaped:!0}]}):l;continue}default:{const e='Token with "'+i.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}parseInline(e,t=this.renderer){let n="";for(let s=0;s<e.length;s++){const r=e[s];if(this.options.extensions?.renderers?.[r.type]){const e=this.options.extensions.renderers[r.type].call({parser:this},r);if(!1!==e||!["escape","html","link","image","strong","em","codespan","br","del","text"].includes(r.type)){n+=e||"";continue}}const i=r;switch(i.type){case"escape":case"text":n+=t.text(i);break;case"html":n+=t.html(i);break;case"link":n+=t.link(i);break;case"image":n+=t.image(i);break;case"strong":n+=t.strong(i);break;case"em":n+=t.em(i);break;case"codespan":n+=t.codespan(i);break;case"br":n+=t.br(i);break;case"del":n+=t.del(i);break;default:{const e='Token with "'+i.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}}class ae{options;block;constructor(t){this.options=t||e.defaults}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}provideLexer(){return this.block?re.lex:re.lexInline}provideParser(){return this.block?oe.parse:oe.parseInline}}class ce{defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=oe;Renderer=ie;TextRenderer=le;Lexer=re;Tokenizer=se;Hooks=ae;constructor(...e){this.use(...e)}walkTokens(e,t){let n=[];for(const s of e)switch(n=n.concat(t.call(this,s)),s.type){case"table":{const e=s;for(const s of e.header)n=n.concat(this.walkTokens(s.tokens,t));for(const s of e.rows)for(const e of s)n=n.concat(this.walkTokens(e.tokens,t));break}case"list":{const e=s;n=n.concat(this.walkTokens(e.items,t));break}default:{const e=s;this.defaults.extensions?.childTokens?.[e.type]?this.defaults.extensions.childTokens[e.type].forEach((s=>{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new ie(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new se(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new ae;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;const s=n,r=e.hooks[s],i=t[s];ae.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return re.lex(e,t??this.defaults)}parser(e,t){return oe.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{const s={...n},r={...this.defaults,...s},i=this.onError(!!r.silent,!!r.async);if(!0===this.defaults.async&&!1===s.async)return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(null==t)return i(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof t)return i(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));r.hooks&&(r.hooks.options=r,r.hooks.block=e);const l=r.hooks?r.hooks.provideLexer():e?re.lex:re.lexInline,o=r.hooks?r.hooks.provideParser():e?oe.parse:oe.parseInline;if(r.async)return Promise.resolve(r.hooks?r.hooks.preprocess(t):t).then((e=>l(e,r))).then((e=>r.hooks?r.hooks.processAllTokens(e):e)).then((e=>r.walkTokens?Promise.all(this.walkTokens(e,r.walkTokens)).then((()=>e)):e)).then((e=>o(e,r))).then((e=>r.hooks?r.hooks.postprocess(e):e)).catch(i);try{r.hooks&&(t=r.hooks.preprocess(t));let e=l(t,r);r.hooks&&(e=r.hooks.processAllTokens(e)),r.walkTokens&&this.walkTokens(e,r.walkTokens);let n=o(e,r);return r.hooks&&(n=r.hooks.postprocess(n)),n}catch(e){return i(e)}}}onError(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="<p>An error occurred:</p><pre>"+W(n.message+"",!0)+"</pre>";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const he=new ce;function pe(e,t){return he.parse(e,t)}pe.options=pe.setOptions=function(e){return he.setOptions(e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.getDefaults=t,pe.defaults=e.defaults,pe.use=function(...e){return he.use(...e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.walkTokens=function(e,t){return he.walkTokens(e,t)},pe.parseInline=he.parseInline,pe.Parser=oe,pe.parser=oe.parse,pe.Renderer=ie,pe.TextRenderer=le,pe.Lexer=re,pe.lexer=re.lex,pe.Tokenizer=se,pe.Hooks=ae,pe.parse=pe;const ue=pe.options,ge=pe.setOptions,ke=pe.use,de=pe.walkTokens,fe=pe.parseInline,xe=pe,be=oe.parse,we=re.lex;e.Hooks=ae,e.Lexer=re,e.Marked=ce,e.Parser=oe,e.Renderer=ie,e.TextRenderer=le,e.Tokenizer=se,e.getDefaults=t,e.lexer=we,e.marked=pe,e.options=ue,e.parse=xe,e.parseInline=fe,e.parser=be,e.setOptions=ge,e.use=ke,e.walkTokens=de}));
|
pywebexec/static/js/script.js
CHANGED
@@ -143,7 +143,9 @@ async function fetchCommands(hide=false) {
|
|
143
143
|
document.getElementById('dimmer').style.display = 'block';
|
144
144
|
return;
|
145
145
|
}
|
146
|
-
|
146
|
+
// Adapt to the new result structure:
|
147
|
+
const data = await response.json();
|
148
|
+
const commands = data.commands;
|
147
149
|
commands.sort((a, b) => new Date(b.start_time) - new Date(a.start_time));
|
148
150
|
const commandsTbody = document.getElementById('commands');
|
149
151
|
commandsTbody.innerHTML = '';
|
pywebexec/swagger.yaml
CHANGED
@@ -9,6 +9,32 @@ paths:
|
|
9
9
|
responses:
|
10
10
|
"200":
|
11
11
|
description: "List of all commands status"
|
12
|
+
schema:
|
13
|
+
type: object
|
14
|
+
properties:
|
15
|
+
commands:
|
16
|
+
type: array
|
17
|
+
items:
|
18
|
+
type: object
|
19
|
+
properties:
|
20
|
+
command_id:
|
21
|
+
type: string
|
22
|
+
command:
|
23
|
+
type: string
|
24
|
+
status:
|
25
|
+
type: string
|
26
|
+
start_time:
|
27
|
+
type: string
|
28
|
+
format: date-time
|
29
|
+
end_time:
|
30
|
+
type: string
|
31
|
+
format: date-time
|
32
|
+
exit_code:
|
33
|
+
type: integer
|
34
|
+
last_output_line:
|
35
|
+
type: string
|
36
|
+
user:
|
37
|
+
type: string
|
12
38
|
post:
|
13
39
|
summary: "Run a command"
|
14
40
|
consumes:
|
@@ -23,17 +49,31 @@ paths:
|
|
23
49
|
properties:
|
24
50
|
command:
|
25
51
|
type: string
|
52
|
+
# Enum will be added dynamically by the APIs
|
53
|
+
default: commandName
|
26
54
|
params:
|
27
55
|
type: array
|
28
56
|
items:
|
29
57
|
type: string
|
58
|
+
default: []
|
30
59
|
rows:
|
31
60
|
type: integer
|
61
|
+
default: 24
|
32
62
|
cols:
|
33
63
|
type: integer
|
64
|
+
default: 125
|
65
|
+
required:
|
66
|
+
- command
|
34
67
|
responses:
|
35
68
|
"200":
|
36
69
|
description: "Command started"
|
70
|
+
schema:
|
71
|
+
type: object
|
72
|
+
properties:
|
73
|
+
command_id:
|
74
|
+
type: string
|
75
|
+
message:
|
76
|
+
type: string
|
37
77
|
/commands/{command_id}:
|
38
78
|
get:
|
39
79
|
summary: "Get command status"
|
@@ -45,6 +85,40 @@ paths:
|
|
45
85
|
responses:
|
46
86
|
"200":
|
47
87
|
description: "Command status returned"
|
88
|
+
schema:
|
89
|
+
type: object
|
90
|
+
properties:
|
91
|
+
command_id:
|
92
|
+
type: string
|
93
|
+
command:
|
94
|
+
type: string
|
95
|
+
params:
|
96
|
+
type: array
|
97
|
+
items:
|
98
|
+
type: string
|
99
|
+
status:
|
100
|
+
type: string
|
101
|
+
start_time:
|
102
|
+
type: string
|
103
|
+
format: date-time
|
104
|
+
end_time:
|
105
|
+
type: string
|
106
|
+
format: date-time
|
107
|
+
exit_code:
|
108
|
+
type: integer
|
109
|
+
last_output_line:
|
110
|
+
type: string
|
111
|
+
cols:
|
112
|
+
type: integer
|
113
|
+
rows:
|
114
|
+
type: integer
|
115
|
+
user:
|
116
|
+
type: string
|
117
|
+
from:
|
118
|
+
type: string
|
119
|
+
pid:
|
120
|
+
type: integer
|
121
|
+
|
48
122
|
/commands/{command_id}/output:
|
49
123
|
get:
|
50
124
|
summary: "Get command output"
|
@@ -61,9 +135,35 @@ paths:
|
|
61
135
|
name: maxsize
|
62
136
|
type: integer
|
63
137
|
default: 10485760
|
138
|
+
- in: query
|
139
|
+
name: maxlines
|
140
|
+
type: integer
|
141
|
+
default: 5000
|
142
|
+
consumes:
|
143
|
+
- application/json
|
144
|
+
produces:
|
145
|
+
- application/json
|
146
|
+
- text/plain
|
64
147
|
responses:
|
65
148
|
"200":
|
66
149
|
description: "Command output returned"
|
150
|
+
schema:
|
151
|
+
type: object
|
152
|
+
properties:
|
153
|
+
output:
|
154
|
+
type: string
|
155
|
+
status:
|
156
|
+
type: string
|
157
|
+
rows:
|
158
|
+
type: integer
|
159
|
+
cols:
|
160
|
+
type: integer
|
161
|
+
links:
|
162
|
+
type: object
|
163
|
+
properties:
|
164
|
+
next:
|
165
|
+
type: string
|
166
|
+
format: uri
|
67
167
|
/commands/{command_id}/stop:
|
68
168
|
patch:
|
69
169
|
summary: "Stop a running command"
|
@@ -75,9 +175,35 @@ paths:
|
|
75
175
|
responses:
|
76
176
|
"200":
|
77
177
|
description: "Command stopped successfully"
|
178
|
+
schema:
|
179
|
+
type: object
|
180
|
+
properties:
|
181
|
+
message:
|
182
|
+
type: string
|
78
183
|
/executables:
|
79
184
|
get:
|
80
185
|
summary: "List available executable commands"
|
186
|
+
produces:
|
187
|
+
- application/json
|
81
188
|
responses:
|
82
189
|
"200":
|
83
|
-
description: "List of executables returned as an array of executable
|
190
|
+
description: "List of executables returned as an array of executable objects"
|
191
|
+
schema:
|
192
|
+
type: object
|
193
|
+
properties:
|
194
|
+
executables:
|
195
|
+
type: array
|
196
|
+
items:
|
197
|
+
type: object
|
198
|
+
properties:
|
199
|
+
command:
|
200
|
+
type: string
|
201
|
+
help:
|
202
|
+
type: string
|
203
|
+
examples:
|
204
|
+
application/json:
|
205
|
+
executables:
|
206
|
+
- command: ls
|
207
|
+
help: "List directory contents\nparams:\n <ls_params>"
|
208
|
+
- command: cat
|
209
|
+
help: "Concatenate and display files\nparams:\n <cat_params>"
|
pywebexec/templates/index.html
CHANGED
@@ -21,6 +21,7 @@
|
|
21
21
|
</div>
|
22
22
|
<div id="commandList" class="command-list"></div>
|
23
23
|
<input type="text" id="params" name="params" oninput="this.style.width = ((this.value.length + 1) * 8) + 'px';">
|
24
|
+
<div id="paramsHelp"></div>
|
24
25
|
</div>
|
25
26
|
<button type="submit">Run</button>
|
26
27
|
</form>
|
@@ -59,7 +60,8 @@
|
|
59
60
|
<script src="/static/js/xterm/addon-unicode11.js"></script>
|
60
61
|
<script src="/static/js/xterm/addon-unicode-graphemes.js"></script>
|
61
62
|
<script src="/static/js/xterm/addon-fit.js"></script>
|
63
|
+
<script src="/static/js/marked/marked.min.js"></script>
|
62
64
|
<script type="text/javascript" src="/static/js/script.js"></script>
|
63
|
-
<script type="text/javascript" src="/static/js/
|
65
|
+
<script type="text/javascript" src="/static/js/executables.js"></script>
|
64
66
|
</body>
|
65
67
|
</html>
|
pywebexec/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: pywebexec
|
3
|
-
Version: 1.9.
|
3
|
+
Version: 1.9.5
|
4
4
|
Summary: Simple Python HTTP Exec Server
|
5
5
|
Home-page: https://github.com/joknarf/pywebexec
|
6
6
|
Author: Franck Jouvanceau
|
@@ -63,13 +63,13 @@ Requires-Dist: ldap3>=2.9.1
|
|
63
63
|
Requires-Dist: pyte>=0.8.1
|
64
64
|
|
65
65
|
[](https://pypi.org/project/pywebexec/)
|
66
|
-

|
67
67
|
[](https://shields.io/)
|
68
68
|
[](https://pepy.tech/projects/pywebexec)
|
69
69
|
[](https://shields.io/)
|
70
70
|
|
71
71
|
# pywebexec
|
72
|
-
Simple Python HTTP(S) API/Web Command Launcher and Terminal sharing
|
72
|
+
Simple Python HTTP(S) API/Web Server Command Launcher and Terminal sharing
|
73
73
|
|
74
74
|
## Install
|
75
75
|
```
|
@@ -79,6 +79,8 @@ $ pip install pywebexec
|
|
79
79
|
## Quick start
|
80
80
|
|
81
81
|
* share terminal
|
82
|
+
* start http server and spawn a new terminal shared on 0.0.0.0 port 8080 (defaults)
|
83
|
+
* exiting terminal stops server/share
|
82
84
|
```shell
|
83
85
|
$ pywebexec shareterm
|
84
86
|
```
|
@@ -100,6 +102,7 @@ all commands output / statuses are available in the executables directory in sub
|
|
100
102
|
## features
|
101
103
|
|
102
104
|
* Serve executables in a directory
|
105
|
+
* full API driven with dynamic swagger UI
|
103
106
|
* Launch commands with params from web browser or API call
|
104
107
|
* multiple share terminal output
|
105
108
|
* Follow live output
|
@@ -114,6 +117,7 @@ all commands output / statuses are available in the executables directory in sub
|
|
114
117
|
* Can be started as a daemon (POSIX)
|
115
118
|
* Uses gunicorn to serve http/https
|
116
119
|
* Linux/MacOS compatible
|
120
|
+
* Markdown help for commands
|
117
121
|
|
118
122
|
## Customize server
|
119
123
|
```shell
|
@@ -190,20 +194,37 @@ $ pywebexec stop
|
|
190
194
|
## Launch command through API
|
191
195
|
|
192
196
|
```shell
|
193
|
-
$ curl http://myhost:8080/
|
194
|
-
$ curl http://myhost:8080/
|
195
|
-
$ curl http://myhost:8080/
|
197
|
+
$ curl http://myhost:8080/commands/myscript -H 'Content-Type: application/json' -X POST -d '{"params":["param1", ...]}'
|
198
|
+
$ curl http://myhost:8080/commands/<command_id>
|
199
|
+
$ curl http://myhost:8080/commands/<command_id>/output -H "Accept: text/plain"
|
196
200
|
```
|
197
201
|
|
202
|
+
## Add markdown help to commands
|
203
|
+
|
204
|
+
For each exposed command, you can add a help message by creating a file named `<command>.help` in the same directory as the command. The help message must be written in markdown.
|
205
|
+
The help message is displayed:
|
206
|
+
* in the web interface as tooltip when focused on param input field,
|
207
|
+
* in the response when calling the API `/executables`
|
208
|
+
* in the swagger-ui in the `/commands/<command>` route.
|
209
|
+
|
210
|
+
<img src="https://github.com/user-attachments/assets/b51b14f7-c349-42ca-98c5-29f0cb5dd656" width="300"/>
|
211
|
+
|
212
|
+
## Swagger UI
|
213
|
+
|
214
|
+
A swagger UI is available at `http[s]://<srv>/v0/documentation`
|
215
|
+
|
216
|
+
<img src="https://github.com/user-attachments/assets/759e889a-8fef-4f49-9319-d2d9dbaf7e69" width="400"/>
|
217
|
+
|
198
218
|
## API reference
|
199
219
|
|
200
220
|
|
201
221
|
| method | route | params/payload | returns
|
202
222
|
|-----------|-----------------------------|--------------------|---------------------|
|
203
|
-
| GET | /executables | |
|
204
|
-
| GET | /commands | |
|
223
|
+
| GET | /executables | | executables: [<br> {command: str,help: str},<br>] |
|
224
|
+
| GET | /commands | | commands: [<br> {<br> command_id: uuid<br> command: str<br> start_time: isotime<br> end_time: isotime<br> status: str<br> exit_code: int<br> last_output_line: str<br> },<br>] |
|
205
225
|
| GET | /commands/{id} | | command_id: uuid<br>command: str<br>params: array[str]<br>start_time: isotime<br>end_time: isotime<br>status: str<br>exit_code: int<br>last_output_line: str |
|
206
226
|
| GET | /commands/{id}/output | offset: int | output: str<br>status: str<br>links: { next: str } |
|
207
227
|
| GET | /commands/{id}/output_raw | offset: int | output: stream raw output until end of command<br>curl -Ns http://srv/commands/{id}/output_raw|
|
208
228
|
| POST | /commands | command: str<br>params: array[str]<br>rows: int<br>cols: int | command_id: uuid<br>message: str |
|
229
|
+
| POST | /commands/{cmd} | params: array[str]<br>rows: int<br>cols: int | command_id: uuid<br>message: str |
|
209
230
|
| PATCH | /commands/{id}/stop | | message: str |
|
@@ -1,9 +1,9 @@
|
|
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=
|
6
|
-
pywebexec/static/css/style.css,sha256=
|
3
|
+
pywebexec/pywebexec.py,sha256=zs2a6QKordcXd4Vb4_8ARjYbDeg9G5ig3z4d0f-8XBk,37898
|
4
|
+
pywebexec/swagger.yaml,sha256=w5Jl1S9yKhXZ5oaUunX8DJBToUGb9vsxvJE-K4KMNNA,5504
|
5
|
+
pywebexec/version.py,sha256=_pj6MJVKa2AM2Ch5jxcodyJv_OLiHc3jp2Di6QMGWJo,511
|
6
|
+
pywebexec/static/css/style.css,sha256=M2zqrDSolDUxeVVeJ0e6NMTMM8B4_LUEmG-lWQwtUoI,9530
|
7
7
|
pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
|
8
8
|
pywebexec/static/fonts/CommitMonoNerdFontMono-Regular.ttf,sha256=v6nZdSx5cs_TIic8Fujrjzg9u9glWjorDIr7RlwNceM,2370228
|
9
9
|
pywebexec/static/fonts/LICENSE,sha256=gsBdbFPfoMkCWYXBnjcYEAILdO0sYdUdNw8qirJQbVI,4395
|
@@ -23,9 +23,11 @@ pywebexec/static/images/popup.svg,sha256=0Bl9A_v5cBsMPn6FnOlVWlAQKgd2zqiWQbhjcL9
|
|
23
23
|
pywebexec/static/images/resume.svg,sha256=99LP1Ya2JXakRCO9kW8JMuT_4a_CannF65EiuwtvK4A,607
|
24
24
|
pywebexec/static/images/running.svg,sha256=fBCYwYb2O9K4N3waC2nURP25NRwZlqR4PbDZy6JQMww,610
|
25
25
|
pywebexec/static/images/success.svg,sha256=NVwezvVMplt46ElW798vqGfrL21Mw_DWHUp_qiD_FU8,489
|
26
|
-
pywebexec/static/js/
|
26
|
+
pywebexec/static/js/executables.js,sha256=Dalcybfllp1GdZT6WCfQ96U4GAwZ0Aqiv4_LfhOeP3Y,9471
|
27
27
|
pywebexec/static/js/popup.js,sha256=0fr3pp4j9D2fXEVnHyQrx2bPWFHfgbb336dbewgH1d8,9023
|
28
|
-
pywebexec/static/js/script.js,sha256=
|
28
|
+
pywebexec/static/js/script.js,sha256=EpDwM1CyvHgsfkMSAkYmS9nU0sKOEXpQ6L3xhDIabEY,17933
|
29
|
+
pywebexec/static/js/marked/LICENSE.md,sha256=jjo_gvWaYJWPVsoI9EVkfDKkcz3HymwsRvbriYRxq5w,2942
|
30
|
+
pywebexec/static/js/marked/marked.min.js,sha256=k04-Nuni2gr7Gm51B1uw8JrwUpOoROhKdHfvQJEcNJo,39589
|
29
31
|
pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
|
30
32
|
pywebexec/static/js/xterm/addon-canvas.js,sha256=ez6QTVvsmLVNJmdJlM-ZQ5bErwlxAQ_9DUmDIptl2TM,94607
|
31
33
|
pywebexec/static/js/xterm/addon-canvas.js.map,sha256=ECBA4B-BqUpdFeRzlsEWLSQnudnhLP-yPQJ8_hKquMo,379537
|
@@ -38,11 +40,11 @@ pywebexec/static/js/xterm/addon-unicode11.js.map,sha256=paDj5KKtTIUGedQn2x7CaUTD
|
|
38
40
|
pywebexec/static/js/xterm/xterm.js,sha256=H5kaw7Syg-v5bmCuI6AKUnZd06Lkb6b92p8aqwMvdJU,289441
|
39
41
|
pywebexec/static/js/xterm/xterm.js.map,sha256=Y7O2Pb-fIS7Z8AC1D5s04_aiW_Jf1f4mCfN0U_OI6Zw,1118392
|
40
42
|
pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
41
|
-
pywebexec/templates/index.html,sha256=
|
43
|
+
pywebexec/templates/index.html,sha256=66Oevi5umxCK81IoGoudx3vWp0ltdFMFsymXsnLQw-c,3209
|
42
44
|
pywebexec/templates/popup.html,sha256=3kpMccKD_OLLhJ4Y9KRw6Ny8wQWjVaRrUfV9y5-bDiQ,1580
|
43
|
-
pywebexec-1.9.
|
44
|
-
pywebexec-1.9.
|
45
|
-
pywebexec-1.9.
|
46
|
-
pywebexec-1.9.
|
47
|
-
pywebexec-1.9.
|
48
|
-
pywebexec-1.9.
|
45
|
+
pywebexec-1.9.5.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
|
46
|
+
pywebexec-1.9.5.dist-info/METADATA,sha256=s0MqHzrWNNLVIQr4x2raTgAXvtNGmHV5C6UH5qZ-Yvo,9450
|
47
|
+
pywebexec-1.9.5.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
48
|
+
pywebexec-1.9.5.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
|
49
|
+
pywebexec-1.9.5.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
|
50
|
+
pywebexec-1.9.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|