krazyxki neurokun commited on
Commit
29ce54a
0 Parent(s):

Duplicate from neurokun/V-1488

Browse files

Co-authored-by: Todd Howard <[email protected]>

.env.example ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copy this file to .env and fill in the values.
2
+
3
+ # Set your OpenAI API key below.
4
+ OPENAI_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5
+
6
+ # You can also set a base-64 encoded JSON array of keys matching this schema:
7
+ # Trial keys will be prioritized. GPT4-enabled keys are necessary to use GPT-4.
8
+ # For example:
9
+ # [
10
+ # { "key": "your-openai-key-1", "isTrial": true, "isGpt4": false },
11
+ # { "key": "your-openai-key-2", "isTrial": false, "isGpt4": false },
12
+ # { "key": "your-openai-key-3", "isTrial": false, "isGpt4": true }
13
+ # ]
14
+ # Encoded in base-64, this would look like:
15
+ # OPENAI_KEYS=WwogeyAia2V5IjogInlvdXItb3BlbmFpLWtleS0xIiwgImlzVHJpYWwiOiB0cnVlLCAiaXNHcHQ0IjogZmFsc2UgfSwKIHsgImtleSI6ICJ5b3VyLW9wZW5haS1rZXktMiIsICJpc1RyaWFsIjogZmFsc2UsICJpc0dwdDQiOiBmYWxzZSB9LAogeyAia2V5IjogInlvdXItb3BlbmFpLWtleS0zIiwgImlzVHJpYWwiOiBmYWxzZSwgImlzR3B0NCI6IHRydWUgfQpd
16
+
17
+ # Optional settings (please see config.ts for more details)
18
+ # PORT=7860
19
+ # PROXY_KEY=your-secret-key
20
+ # MODEL_RATE_LIMIT=2
21
+ # MAX_OUTPUT_TOKENS=256
22
+ # LOG_LEVEL=info
23
+ # LOG_PROMPTS=false
24
+ # REJECT_DISALLOWED=false
25
+ # REJECT_MESSAGE="This content violates /aicg/'s acceptable use policy."
26
+ # REJECT_SAMPLE_RATE=0.2
.gitattributes ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:18-bullseye-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package*.json ./
6
+ RUN npm install
7
+
8
+ COPY . .
9
+ RUN npm run build
10
+
11
+ EXPOSE 7860
12
+
13
+ CMD [ "npm", "start" ]
README.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: 2ch-reverse-proxy
3
+ emoji: ⚡
4
+ colorFrom: yellow
5
+ colorTo: gray
6
+ sdk: docker
7
+ pinned: true
8
+ duplicated_from: neurokun/V-1488
9
+ ---
10
+ <!-- -->
calico_cyrillic.ttf ADDED
Binary file (48.5 kB). View file
 
package-lock.json ADDED
@@ -0,0 +1,2291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "oai-reverse-proxy",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "oai-reverse-proxy",
9
+ "version": "1.0.0",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "axios": "1.3.5",
13
+ "cors": "2.8.5",
14
+ "date-fns": "2.29.3",
15
+ "dotenv": "16.0.3",
16
+ "express": "4.18.2",
17
+ "http-proxy-middleware": "3.0.0-beta.1",
18
+ "pino-http": "8.3.3",
19
+ "showdown": "2.1.0",
20
+ "text-to-image": "5.2.0",
21
+ "zlib": "1.0.5"
22
+ },
23
+ "devDependencies": {
24
+ "@types/cors": "2.8.13",
25
+ "@types/express": "4.17.17",
26
+ "@types/node": "18.15.11",
27
+ "@types/showdown": "2.0.0",
28
+ "nodemon": "2.0.22",
29
+ "ts-node": "10.9.1",
30
+ "typescript": "5.0.4"
31
+ }
32
+ },
33
+ "node_modules/@cspotcode/source-map-support": {
34
+ "version": "0.8.1",
35
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
36
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
37
+ "dev": true,
38
+ "dependencies": {
39
+ "@jridgewell/trace-mapping": "0.3.9"
40
+ },
41
+ "engines": {
42
+ "node": ">=12"
43
+ }
44
+ },
45
+ "node_modules/@jridgewell/resolve-uri": {
46
+ "version": "3.1.1",
47
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
48
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
49
+ "dev": true,
50
+ "engines": {
51
+ "node": ">=6.0.0"
52
+ }
53
+ },
54
+ "node_modules/@jridgewell/sourcemap-codec": {
55
+ "version": "1.4.15",
56
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
57
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
58
+ "dev": true
59
+ },
60
+ "node_modules/@jridgewell/trace-mapping": {
61
+ "version": "0.3.9",
62
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
63
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
64
+ "dev": true,
65
+ "dependencies": {
66
+ "@jridgewell/resolve-uri": "^3.0.3",
67
+ "@jridgewell/sourcemap-codec": "^1.4.10"
68
+ }
69
+ },
70
+ "node_modules/@mapbox/node-pre-gyp": {
71
+ "version": "1.0.10",
72
+ "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz",
73
+ "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==",
74
+ "dependencies": {
75
+ "detect-libc": "^2.0.0",
76
+ "https-proxy-agent": "^5.0.0",
77
+ "make-dir": "^3.1.0",
78
+ "node-fetch": "^2.6.7",
79
+ "nopt": "^5.0.0",
80
+ "npmlog": "^5.0.1",
81
+ "rimraf": "^3.0.2",
82
+ "semver": "^7.3.5",
83
+ "tar": "^6.1.11"
84
+ },
85
+ "bin": {
86
+ "node-pre-gyp": "bin/node-pre-gyp"
87
+ }
88
+ },
89
+ "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": {
90
+ "version": "5.0.0",
91
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
92
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
93
+ "dependencies": {
94
+ "abbrev": "1"
95
+ },
96
+ "bin": {
97
+ "nopt": "bin/nopt.js"
98
+ },
99
+ "engines": {
100
+ "node": ">=6"
101
+ }
102
+ },
103
+ "node_modules/@mapbox/node-pre-gyp/node_modules/semver": {
104
+ "version": "7.4.0",
105
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz",
106
+ "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==",
107
+ "dependencies": {
108
+ "lru-cache": "^6.0.0"
109
+ },
110
+ "bin": {
111
+ "semver": "bin/semver.js"
112
+ },
113
+ "engines": {
114
+ "node": ">=10"
115
+ }
116
+ },
117
+ "node_modules/@tsconfig/node10": {
118
+ "version": "1.0.9",
119
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
120
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
121
+ "dev": true
122
+ },
123
+ "node_modules/@tsconfig/node12": {
124
+ "version": "1.0.11",
125
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
126
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
127
+ "dev": true
128
+ },
129
+ "node_modules/@tsconfig/node14": {
130
+ "version": "1.0.3",
131
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
132
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
133
+ "dev": true
134
+ },
135
+ "node_modules/@tsconfig/node16": {
136
+ "version": "1.0.3",
137
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
138
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
139
+ "dev": true
140
+ },
141
+ "node_modules/@types/body-parser": {
142
+ "version": "1.19.2",
143
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
144
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
145
+ "dev": true,
146
+ "dependencies": {
147
+ "@types/connect": "*",
148
+ "@types/node": "*"
149
+ }
150
+ },
151
+ "node_modules/@types/connect": {
152
+ "version": "3.4.35",
153
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
154
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
155
+ "dev": true,
156
+ "dependencies": {
157
+ "@types/node": "*"
158
+ }
159
+ },
160
+ "node_modules/@types/cors": {
161
+ "version": "2.8.13",
162
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
163
+ "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
164
+ "dev": true,
165
+ "dependencies": {
166
+ "@types/node": "*"
167
+ }
168
+ },
169
+ "node_modules/@types/express": {
170
+ "version": "4.17.17",
171
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
172
+ "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
173
+ "dev": true,
174
+ "dependencies": {
175
+ "@types/body-parser": "*",
176
+ "@types/express-serve-static-core": "^4.17.33",
177
+ "@types/qs": "*",
178
+ "@types/serve-static": "*"
179
+ }
180
+ },
181
+ "node_modules/@types/express-serve-static-core": {
182
+ "version": "4.17.33",
183
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
184
+ "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
185
+ "dev": true,
186
+ "dependencies": {
187
+ "@types/node": "*",
188
+ "@types/qs": "*",
189
+ "@types/range-parser": "*"
190
+ }
191
+ },
192
+ "node_modules/@types/http-proxy": {
193
+ "version": "1.17.10",
194
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz",
195
+ "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==",
196
+ "dependencies": {
197
+ "@types/node": "*"
198
+ }
199
+ },
200
+ "node_modules/@types/mime": {
201
+ "version": "3.0.1",
202
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
203
+ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==",
204
+ "dev": true
205
+ },
206
+ "node_modules/@types/node": {
207
+ "version": "18.15.11",
208
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
209
+ "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q=="
210
+ },
211
+ "node_modules/@types/qs": {
212
+ "version": "6.9.7",
213
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
214
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
215
+ "dev": true
216
+ },
217
+ "node_modules/@types/range-parser": {
218
+ "version": "1.2.4",
219
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
220
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
221
+ "dev": true
222
+ },
223
+ "node_modules/@types/serve-static": {
224
+ "version": "1.15.1",
225
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz",
226
+ "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==",
227
+ "dev": true,
228
+ "dependencies": {
229
+ "@types/mime": "*",
230
+ "@types/node": "*"
231
+ }
232
+ },
233
+ "node_modules/@types/showdown": {
234
+ "version": "2.0.0",
235
+ "resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.0.tgz",
236
+ "integrity": "sha512-70xBJoLv+oXjB5PhtA8vo7erjLDp9/qqI63SRHm4REKrwuPOLs8HhXwlZJBJaB4kC18cCZ1UUZ6Fb/PLFW4TCA==",
237
+ "dev": true
238
+ },
239
+ "node_modules/abbrev": {
240
+ "version": "1.1.1",
241
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
242
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
243
+ },
244
+ "node_modules/abort-controller": {
245
+ "version": "3.0.0",
246
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
247
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
248
+ "dependencies": {
249
+ "event-target-shim": "^5.0.0"
250
+ },
251
+ "engines": {
252
+ "node": ">=6.5"
253
+ }
254
+ },
255
+ "node_modules/accepts": {
256
+ "version": "1.3.8",
257
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
258
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
259
+ "dependencies": {
260
+ "mime-types": "~2.1.34",
261
+ "negotiator": "0.6.3"
262
+ },
263
+ "engines": {
264
+ "node": ">= 0.6"
265
+ }
266
+ },
267
+ "node_modules/acorn": {
268
+ "version": "8.8.2",
269
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
270
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
271
+ "dev": true,
272
+ "bin": {
273
+ "acorn": "bin/acorn"
274
+ },
275
+ "engines": {
276
+ "node": ">=0.4.0"
277
+ }
278
+ },
279
+ "node_modules/acorn-walk": {
280
+ "version": "8.2.0",
281
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
282
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
283
+ "dev": true,
284
+ "engines": {
285
+ "node": ">=0.4.0"
286
+ }
287
+ },
288
+ "node_modules/agent-base": {
289
+ "version": "6.0.2",
290
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
291
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
292
+ "dependencies": {
293
+ "debug": "4"
294
+ },
295
+ "engines": {
296
+ "node": ">= 6.0.0"
297
+ }
298
+ },
299
+ "node_modules/agent-base/node_modules/debug": {
300
+ "version": "4.3.4",
301
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
302
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
303
+ "dependencies": {
304
+ "ms": "2.1.2"
305
+ },
306
+ "engines": {
307
+ "node": ">=6.0"
308
+ },
309
+ "peerDependenciesMeta": {
310
+ "supports-color": {
311
+ "optional": true
312
+ }
313
+ }
314
+ },
315
+ "node_modules/agent-base/node_modules/ms": {
316
+ "version": "2.1.2",
317
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
318
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
319
+ },
320
+ "node_modules/ansi-regex": {
321
+ "version": "5.0.1",
322
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
323
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
324
+ "engines": {
325
+ "node": ">=8"
326
+ }
327
+ },
328
+ "node_modules/anymatch": {
329
+ "version": "3.1.3",
330
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
331
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
332
+ "dev": true,
333
+ "dependencies": {
334
+ "normalize-path": "^3.0.0",
335
+ "picomatch": "^2.0.4"
336
+ },
337
+ "engines": {
338
+ "node": ">= 8"
339
+ }
340
+ },
341
+ "node_modules/aproba": {
342
+ "version": "2.0.0",
343
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
344
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
345
+ },
346
+ "node_modules/are-we-there-yet": {
347
+ "version": "2.0.0",
348
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
349
+ "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
350
+ "dependencies": {
351
+ "delegates": "^1.0.0",
352
+ "readable-stream": "^3.6.0"
353
+ },
354
+ "engines": {
355
+ "node": ">=10"
356
+ }
357
+ },
358
+ "node_modules/are-we-there-yet/node_modules/readable-stream": {
359
+ "version": "3.6.2",
360
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
361
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
362
+ "dependencies": {
363
+ "inherits": "^2.0.3",
364
+ "string_decoder": "^1.1.1",
365
+ "util-deprecate": "^1.0.1"
366
+ },
367
+ "engines": {
368
+ "node": ">= 6"
369
+ }
370
+ },
371
+ "node_modules/arg": {
372
+ "version": "4.1.3",
373
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
374
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
375
+ "dev": true
376
+ },
377
+ "node_modules/array-flatten": {
378
+ "version": "1.1.1",
379
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
380
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
381
+ },
382
+ "node_modules/asynckit": {
383
+ "version": "0.4.0",
384
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
385
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
386
+ },
387
+ "node_modules/atomic-sleep": {
388
+ "version": "1.0.0",
389
+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
390
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
391
+ "engines": {
392
+ "node": ">=8.0.0"
393
+ }
394
+ },
395
+ "node_modules/axios": {
396
+ "version": "1.3.5",
397
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz",
398
+ "integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==",
399
+ "dependencies": {
400
+ "follow-redirects": "^1.15.0",
401
+ "form-data": "^4.0.0",
402
+ "proxy-from-env": "^1.1.0"
403
+ }
404
+ },
405
+ "node_modules/balanced-match": {
406
+ "version": "1.0.2",
407
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
408
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
409
+ },
410
+ "node_modules/base64-js": {
411
+ "version": "1.5.1",
412
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
413
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
414
+ "funding": [
415
+ {
416
+ "type": "github",
417
+ "url": "https://github.com/sponsors/feross"
418
+ },
419
+ {
420
+ "type": "patreon",
421
+ "url": "https://www.patreon.com/feross"
422
+ },
423
+ {
424
+ "type": "consulting",
425
+ "url": "https://feross.org/support"
426
+ }
427
+ ]
428
+ },
429
+ "node_modules/binary-extensions": {
430
+ "version": "2.2.0",
431
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
432
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
433
+ "dev": true,
434
+ "engines": {
435
+ "node": ">=8"
436
+ }
437
+ },
438
+ "node_modules/body-parser": {
439
+ "version": "1.20.1",
440
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
441
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
442
+ "dependencies": {
443
+ "bytes": "3.1.2",
444
+ "content-type": "~1.0.4",
445
+ "debug": "2.6.9",
446
+ "depd": "2.0.0",
447
+ "destroy": "1.2.0",
448
+ "http-errors": "2.0.0",
449
+ "iconv-lite": "0.4.24",
450
+ "on-finished": "2.4.1",
451
+ "qs": "6.11.0",
452
+ "raw-body": "2.5.1",
453
+ "type-is": "~1.6.18",
454
+ "unpipe": "1.0.0"
455
+ },
456
+ "engines": {
457
+ "node": ">= 0.8",
458
+ "npm": "1.2.8000 || >= 1.4.16"
459
+ }
460
+ },
461
+ "node_modules/brace-expansion": {
462
+ "version": "1.1.11",
463
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
464
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
465
+ "dependencies": {
466
+ "balanced-match": "^1.0.0",
467
+ "concat-map": "0.0.1"
468
+ }
469
+ },
470
+ "node_modules/braces": {
471
+ "version": "3.0.2",
472
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
473
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
474
+ "dependencies": {
475
+ "fill-range": "^7.0.1"
476
+ },
477
+ "engines": {
478
+ "node": ">=8"
479
+ }
480
+ },
481
+ "node_modules/buffer": {
482
+ "version": "6.0.3",
483
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
484
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
485
+ "funding": [
486
+ {
487
+ "type": "github",
488
+ "url": "https://github.com/sponsors/feross"
489
+ },
490
+ {
491
+ "type": "patreon",
492
+ "url": "https://www.patreon.com/feross"
493
+ },
494
+ {
495
+ "type": "consulting",
496
+ "url": "https://feross.org/support"
497
+ }
498
+ ],
499
+ "dependencies": {
500
+ "base64-js": "^1.3.1",
501
+ "ieee754": "^1.2.1"
502
+ }
503
+ },
504
+ "node_modules/bytes": {
505
+ "version": "3.1.2",
506
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
507
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
508
+ "engines": {
509
+ "node": ">= 0.8"
510
+ }
511
+ },
512
+ "node_modules/call-bind": {
513
+ "version": "1.0.2",
514
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
515
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
516
+ "dependencies": {
517
+ "function-bind": "^1.1.1",
518
+ "get-intrinsic": "^1.0.2"
519
+ },
520
+ "funding": {
521
+ "url": "https://github.com/sponsors/ljharb"
522
+ }
523
+ },
524
+ "node_modules/canvas": {
525
+ "version": "2.11.2",
526
+ "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
527
+ "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
528
+ "hasInstallScript": true,
529
+ "dependencies": {
530
+ "@mapbox/node-pre-gyp": "^1.0.0",
531
+ "nan": "^2.17.0",
532
+ "simple-get": "^3.0.3"
533
+ },
534
+ "engines": {
535
+ "node": ">=6"
536
+ }
537
+ },
538
+ "node_modules/chokidar": {
539
+ "version": "3.5.3",
540
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
541
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
542
+ "dev": true,
543
+ "funding": [
544
+ {
545
+ "type": "individual",
546
+ "url": "https://paulmillr.com/funding/"
547
+ }
548
+ ],
549
+ "dependencies": {
550
+ "anymatch": "~3.1.2",
551
+ "braces": "~3.0.2",
552
+ "glob-parent": "~5.1.2",
553
+ "is-binary-path": "~2.1.0",
554
+ "is-glob": "~4.0.1",
555
+ "normalize-path": "~3.0.0",
556
+ "readdirp": "~3.6.0"
557
+ },
558
+ "engines": {
559
+ "node": ">= 8.10.0"
560
+ },
561
+ "optionalDependencies": {
562
+ "fsevents": "~2.3.2"
563
+ }
564
+ },
565
+ "node_modules/chownr": {
566
+ "version": "2.0.0",
567
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
568
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
569
+ "engines": {
570
+ "node": ">=10"
571
+ }
572
+ },
573
+ "node_modules/color-support": {
574
+ "version": "1.1.3",
575
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
576
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
577
+ "bin": {
578
+ "color-support": "bin.js"
579
+ }
580
+ },
581
+ "node_modules/combined-stream": {
582
+ "version": "1.0.8",
583
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
584
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
585
+ "dependencies": {
586
+ "delayed-stream": "~1.0.0"
587
+ },
588
+ "engines": {
589
+ "node": ">= 0.8"
590
+ }
591
+ },
592
+ "node_modules/commander": {
593
+ "version": "9.5.0",
594
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
595
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
596
+ "engines": {
597
+ "node": "^12.20.0 || >=14"
598
+ }
599
+ },
600
+ "node_modules/concat-map": {
601
+ "version": "0.0.1",
602
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
603
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
604
+ },
605
+ "node_modules/console-control-strings": {
606
+ "version": "1.1.0",
607
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
608
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
609
+ },
610
+ "node_modules/content-disposition": {
611
+ "version": "0.5.4",
612
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
613
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
614
+ "dependencies": {
615
+ "safe-buffer": "5.2.1"
616
+ },
617
+ "engines": {
618
+ "node": ">= 0.6"
619
+ }
620
+ },
621
+ "node_modules/content-type": {
622
+ "version": "1.0.5",
623
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
624
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
625
+ "engines": {
626
+ "node": ">= 0.6"
627
+ }
628
+ },
629
+ "node_modules/cookie": {
630
+ "version": "0.5.0",
631
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
632
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
633
+ "engines": {
634
+ "node": ">= 0.6"
635
+ }
636
+ },
637
+ "node_modules/cookie-signature": {
638
+ "version": "1.0.6",
639
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
640
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
641
+ },
642
+ "node_modules/cors": {
643
+ "version": "2.8.5",
644
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
645
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
646
+ "dependencies": {
647
+ "object-assign": "^4",
648
+ "vary": "^1"
649
+ },
650
+ "engines": {
651
+ "node": ">= 0.10"
652
+ }
653
+ },
654
+ "node_modules/create-require": {
655
+ "version": "1.1.1",
656
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
657
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
658
+ "dev": true
659
+ },
660
+ "node_modules/date-fns": {
661
+ "version": "2.29.3",
662
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
663
+ "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==",
664
+ "engines": {
665
+ "node": ">=0.11"
666
+ },
667
+ "funding": {
668
+ "type": "opencollective",
669
+ "url": "https://opencollective.com/date-fns"
670
+ }
671
+ },
672
+ "node_modules/debug": {
673
+ "version": "2.6.9",
674
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
675
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
676
+ "dependencies": {
677
+ "ms": "2.0.0"
678
+ }
679
+ },
680
+ "node_modules/decompress-response": {
681
+ "version": "4.2.1",
682
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
683
+ "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
684
+ "dependencies": {
685
+ "mimic-response": "^2.0.0"
686
+ },
687
+ "engines": {
688
+ "node": ">=8"
689
+ }
690
+ },
691
+ "node_modules/delayed-stream": {
692
+ "version": "1.0.0",
693
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
694
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
695
+ "engines": {
696
+ "node": ">=0.4.0"
697
+ }
698
+ },
699
+ "node_modules/delegates": {
700
+ "version": "1.0.0",
701
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
702
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
703
+ },
704
+ "node_modules/depd": {
705
+ "version": "2.0.0",
706
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
707
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
708
+ "engines": {
709
+ "node": ">= 0.8"
710
+ }
711
+ },
712
+ "node_modules/destroy": {
713
+ "version": "1.2.0",
714
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
715
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
716
+ "engines": {
717
+ "node": ">= 0.8",
718
+ "npm": "1.2.8000 || >= 1.4.16"
719
+ }
720
+ },
721
+ "node_modules/detect-libc": {
722
+ "version": "2.0.1",
723
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
724
+ "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
725
+ "engines": {
726
+ "node": ">=8"
727
+ }
728
+ },
729
+ "node_modules/diff": {
730
+ "version": "4.0.2",
731
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
732
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
733
+ "dev": true,
734
+ "engines": {
735
+ "node": ">=0.3.1"
736
+ }
737
+ },
738
+ "node_modules/dotenv": {
739
+ "version": "16.0.3",
740
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
741
+ "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
742
+ "engines": {
743
+ "node": ">=12"
744
+ }
745
+ },
746
+ "node_modules/ee-first": {
747
+ "version": "1.1.1",
748
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
749
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
750
+ },
751
+ "node_modules/emoji-regex": {
752
+ "version": "8.0.0",
753
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
754
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
755
+ },
756
+ "node_modules/encodeurl": {
757
+ "version": "1.0.2",
758
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
759
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
760
+ "engines": {
761
+ "node": ">= 0.8"
762
+ }
763
+ },
764
+ "node_modules/escape-html": {
765
+ "version": "1.0.3",
766
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
767
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
768
+ },
769
+ "node_modules/etag": {
770
+ "version": "1.8.1",
771
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
772
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
773
+ "engines": {
774
+ "node": ">= 0.6"
775
+ }
776
+ },
777
+ "node_modules/event-target-shim": {
778
+ "version": "5.0.1",
779
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
780
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
781
+ "engines": {
782
+ "node": ">=6"
783
+ }
784
+ },
785
+ "node_modules/eventemitter3": {
786
+ "version": "4.0.7",
787
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
788
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
789
+ },
790
+ "node_modules/events": {
791
+ "version": "3.3.0",
792
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
793
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
794
+ "engines": {
795
+ "node": ">=0.8.x"
796
+ }
797
+ },
798
+ "node_modules/express": {
799
+ "version": "4.18.2",
800
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
801
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
802
+ "dependencies": {
803
+ "accepts": "~1.3.8",
804
+ "array-flatten": "1.1.1",
805
+ "body-parser": "1.20.1",
806
+ "content-disposition": "0.5.4",
807
+ "content-type": "~1.0.4",
808
+ "cookie": "0.5.0",
809
+ "cookie-signature": "1.0.6",
810
+ "debug": "2.6.9",
811
+ "depd": "2.0.0",
812
+ "encodeurl": "~1.0.2",
813
+ "escape-html": "~1.0.3",
814
+ "etag": "~1.8.1",
815
+ "finalhandler": "1.2.0",
816
+ "fresh": "0.5.2",
817
+ "http-errors": "2.0.0",
818
+ "merge-descriptors": "1.0.1",
819
+ "methods": "~1.1.2",
820
+ "on-finished": "2.4.1",
821
+ "parseurl": "~1.3.3",
822
+ "path-to-regexp": "0.1.7",
823
+ "proxy-addr": "~2.0.7",
824
+ "qs": "6.11.0",
825
+ "range-parser": "~1.2.1",
826
+ "safe-buffer": "5.2.1",
827
+ "send": "0.18.0",
828
+ "serve-static": "1.15.0",
829
+ "setprototypeof": "1.2.0",
830
+ "statuses": "2.0.1",
831
+ "type-is": "~1.6.18",
832
+ "utils-merge": "1.0.1",
833
+ "vary": "~1.1.2"
834
+ },
835
+ "engines": {
836
+ "node": ">= 0.10.0"
837
+ }
838
+ },
839
+ "node_modules/fast-redact": {
840
+ "version": "3.1.2",
841
+ "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz",
842
+ "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==",
843
+ "engines": {
844
+ "node": ">=6"
845
+ }
846
+ },
847
+ "node_modules/fill-range": {
848
+ "version": "7.0.1",
849
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
850
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
851
+ "dependencies": {
852
+ "to-regex-range": "^5.0.1"
853
+ },
854
+ "engines": {
855
+ "node": ">=8"
856
+ }
857
+ },
858
+ "node_modules/finalhandler": {
859
+ "version": "1.2.0",
860
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
861
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
862
+ "dependencies": {
863
+ "debug": "2.6.9",
864
+ "encodeurl": "~1.0.2",
865
+ "escape-html": "~1.0.3",
866
+ "on-finished": "2.4.1",
867
+ "parseurl": "~1.3.3",
868
+ "statuses": "2.0.1",
869
+ "unpipe": "~1.0.0"
870
+ },
871
+ "engines": {
872
+ "node": ">= 0.8"
873
+ }
874
+ },
875
+ "node_modules/follow-redirects": {
876
+ "version": "1.15.2",
877
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
878
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
879
+ "funding": [
880
+ {
881
+ "type": "individual",
882
+ "url": "https://github.com/sponsors/RubenVerborgh"
883
+ }
884
+ ],
885
+ "engines": {
886
+ "node": ">=4.0"
887
+ },
888
+ "peerDependenciesMeta": {
889
+ "debug": {
890
+ "optional": true
891
+ }
892
+ }
893
+ },
894
+ "node_modules/form-data": {
895
+ "version": "4.0.0",
896
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
897
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
898
+ "dependencies": {
899
+ "asynckit": "^0.4.0",
900
+ "combined-stream": "^1.0.8",
901
+ "mime-types": "^2.1.12"
902
+ },
903
+ "engines": {
904
+ "node": ">= 6"
905
+ }
906
+ },
907
+ "node_modules/forwarded": {
908
+ "version": "0.2.0",
909
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
910
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
911
+ "engines": {
912
+ "node": ">= 0.6"
913
+ }
914
+ },
915
+ "node_modules/fresh": {
916
+ "version": "0.5.2",
917
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
918
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
919
+ "engines": {
920
+ "node": ">= 0.6"
921
+ }
922
+ },
923
+ "node_modules/fs-minipass": {
924
+ "version": "2.1.0",
925
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
926
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
927
+ "dependencies": {
928
+ "minipass": "^3.0.0"
929
+ },
930
+ "engines": {
931
+ "node": ">= 8"
932
+ }
933
+ },
934
+ "node_modules/fs-minipass/node_modules/minipass": {
935
+ "version": "3.3.6",
936
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
937
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
938
+ "dependencies": {
939
+ "yallist": "^4.0.0"
940
+ },
941
+ "engines": {
942
+ "node": ">=8"
943
+ }
944
+ },
945
+ "node_modules/fs.realpath": {
946
+ "version": "1.0.0",
947
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
948
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
949
+ },
950
+ "node_modules/fsevents": {
951
+ "version": "2.3.2",
952
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
953
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
954
+ "dev": true,
955
+ "hasInstallScript": true,
956
+ "optional": true,
957
+ "os": [
958
+ "darwin"
959
+ ],
960
+ "engines": {
961
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
962
+ }
963
+ },
964
+ "node_modules/function-bind": {
965
+ "version": "1.1.1",
966
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
967
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
968
+ },
969
+ "node_modules/gauge": {
970
+ "version": "3.0.2",
971
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
972
+ "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
973
+ "dependencies": {
974
+ "aproba": "^1.0.3 || ^2.0.0",
975
+ "color-support": "^1.1.2",
976
+ "console-control-strings": "^1.0.0",
977
+ "has-unicode": "^2.0.1",
978
+ "object-assign": "^4.1.1",
979
+ "signal-exit": "^3.0.0",
980
+ "string-width": "^4.2.3",
981
+ "strip-ansi": "^6.0.1",
982
+ "wide-align": "^1.1.2"
983
+ },
984
+ "engines": {
985
+ "node": ">=10"
986
+ }
987
+ },
988
+ "node_modules/get-caller-file": {
989
+ "version": "2.0.5",
990
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
991
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
992
+ "engines": {
993
+ "node": "6.* || 8.* || >= 10.*"
994
+ }
995
+ },
996
+ "node_modules/get-intrinsic": {
997
+ "version": "1.2.0",
998
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
999
+ "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
1000
+ "dependencies": {
1001
+ "function-bind": "^1.1.1",
1002
+ "has": "^1.0.3",
1003
+ "has-symbols": "^1.0.3"
1004
+ },
1005
+ "funding": {
1006
+ "url": "https://github.com/sponsors/ljharb"
1007
+ }
1008
+ },
1009
+ "node_modules/glob": {
1010
+ "version": "7.2.3",
1011
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
1012
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
1013
+ "dependencies": {
1014
+ "fs.realpath": "^1.0.0",
1015
+ "inflight": "^1.0.4",
1016
+ "inherits": "2",
1017
+ "minimatch": "^3.1.1",
1018
+ "once": "^1.3.0",
1019
+ "path-is-absolute": "^1.0.0"
1020
+ },
1021
+ "engines": {
1022
+ "node": "*"
1023
+ },
1024
+ "funding": {
1025
+ "url": "https://github.com/sponsors/isaacs"
1026
+ }
1027
+ },
1028
+ "node_modules/glob-parent": {
1029
+ "version": "5.1.2",
1030
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
1031
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
1032
+ "dev": true,
1033
+ "dependencies": {
1034
+ "is-glob": "^4.0.1"
1035
+ },
1036
+ "engines": {
1037
+ "node": ">= 6"
1038
+ }
1039
+ },
1040
+ "node_modules/has": {
1041
+ "version": "1.0.3",
1042
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
1043
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
1044
+ "dependencies": {
1045
+ "function-bind": "^1.1.1"
1046
+ },
1047
+ "engines": {
1048
+ "node": ">= 0.4.0"
1049
+ }
1050
+ },
1051
+ "node_modules/has-flag": {
1052
+ "version": "3.0.0",
1053
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
1054
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
1055
+ "dev": true,
1056
+ "engines": {
1057
+ "node": ">=4"
1058
+ }
1059
+ },
1060
+ "node_modules/has-symbols": {
1061
+ "version": "1.0.3",
1062
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
1063
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
1064
+ "engines": {
1065
+ "node": ">= 0.4"
1066
+ },
1067
+ "funding": {
1068
+ "url": "https://github.com/sponsors/ljharb"
1069
+ }
1070
+ },
1071
+ "node_modules/has-unicode": {
1072
+ "version": "2.0.1",
1073
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
1074
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
1075
+ },
1076
+ "node_modules/http-errors": {
1077
+ "version": "2.0.0",
1078
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
1079
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
1080
+ "dependencies": {
1081
+ "depd": "2.0.0",
1082
+ "inherits": "2.0.4",
1083
+ "setprototypeof": "1.2.0",
1084
+ "statuses": "2.0.1",
1085
+ "toidentifier": "1.0.1"
1086
+ },
1087
+ "engines": {
1088
+ "node": ">= 0.8"
1089
+ }
1090
+ },
1091
+ "node_modules/http-proxy": {
1092
+ "version": "1.18.1",
1093
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
1094
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
1095
+ "dependencies": {
1096
+ "eventemitter3": "^4.0.0",
1097
+ "follow-redirects": "^1.0.0",
1098
+ "requires-port": "^1.0.0"
1099
+ },
1100
+ "engines": {
1101
+ "node": ">=8.0.0"
1102
+ }
1103
+ },
1104
+ "node_modules/http-proxy-middleware": {
1105
+ "version": "3.0.0-beta.1",
1106
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0-beta.1.tgz",
1107
+ "integrity": "sha512-hdiTlVVoaxncf239csnEpG5ew2lRWnoNR1PMWOO6kYulSphlrfLs5JFZtFVH3R5EUWSZNMkeUqvkvfctuWaK8A==",
1108
+ "dependencies": {
1109
+ "@types/http-proxy": "^1.17.10",
1110
+ "debug": "^4.3.4",
1111
+ "http-proxy": "^1.18.1",
1112
+ "is-glob": "^4.0.1",
1113
+ "is-plain-obj": "^3.0.0",
1114
+ "micromatch": "^4.0.5"
1115
+ },
1116
+ "engines": {
1117
+ "node": ">=12.0.0"
1118
+ }
1119
+ },
1120
+ "node_modules/http-proxy-middleware/node_modules/debug": {
1121
+ "version": "4.3.4",
1122
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
1123
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
1124
+ "dependencies": {
1125
+ "ms": "2.1.2"
1126
+ },
1127
+ "engines": {
1128
+ "node": ">=6.0"
1129
+ },
1130
+ "peerDependenciesMeta": {
1131
+ "supports-color": {
1132
+ "optional": true
1133
+ }
1134
+ }
1135
+ },
1136
+ "node_modules/http-proxy-middleware/node_modules/ms": {
1137
+ "version": "2.1.2",
1138
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1139
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1140
+ },
1141
+ "node_modules/https-proxy-agent": {
1142
+ "version": "5.0.1",
1143
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
1144
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
1145
+ "dependencies": {
1146
+ "agent-base": "6",
1147
+ "debug": "4"
1148
+ },
1149
+ "engines": {
1150
+ "node": ">= 6"
1151
+ }
1152
+ },
1153
+ "node_modules/https-proxy-agent/node_modules/debug": {
1154
+ "version": "4.3.4",
1155
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
1156
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
1157
+ "dependencies": {
1158
+ "ms": "2.1.2"
1159
+ },
1160
+ "engines": {
1161
+ "node": ">=6.0"
1162
+ },
1163
+ "peerDependenciesMeta": {
1164
+ "supports-color": {
1165
+ "optional": true
1166
+ }
1167
+ }
1168
+ },
1169
+ "node_modules/https-proxy-agent/node_modules/ms": {
1170
+ "version": "2.1.2",
1171
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1172
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1173
+ },
1174
+ "node_modules/iconv-lite": {
1175
+ "version": "0.4.24",
1176
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
1177
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1178
+ "dependencies": {
1179
+ "safer-buffer": ">= 2.1.2 < 3"
1180
+ },
1181
+ "engines": {
1182
+ "node": ">=0.10.0"
1183
+ }
1184
+ },
1185
+ "node_modules/ieee754": {
1186
+ "version": "1.2.1",
1187
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
1188
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
1189
+ "funding": [
1190
+ {
1191
+ "type": "github",
1192
+ "url": "https://github.com/sponsors/feross"
1193
+ },
1194
+ {
1195
+ "type": "patreon",
1196
+ "url": "https://www.patreon.com/feross"
1197
+ },
1198
+ {
1199
+ "type": "consulting",
1200
+ "url": "https://feross.org/support"
1201
+ }
1202
+ ]
1203
+ },
1204
+ "node_modules/ignore-by-default": {
1205
+ "version": "1.0.1",
1206
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
1207
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
1208
+ "dev": true
1209
+ },
1210
+ "node_modules/inflight": {
1211
+ "version": "1.0.6",
1212
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
1213
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
1214
+ "dependencies": {
1215
+ "once": "^1.3.0",
1216
+ "wrappy": "1"
1217
+ }
1218
+ },
1219
+ "node_modules/inherits": {
1220
+ "version": "2.0.4",
1221
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1222
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
1223
+ },
1224
+ "node_modules/ipaddr.js": {
1225
+ "version": "1.9.1",
1226
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1227
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
1228
+ "engines": {
1229
+ "node": ">= 0.10"
1230
+ }
1231
+ },
1232
+ "node_modules/is-binary-path": {
1233
+ "version": "2.1.0",
1234
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
1235
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
1236
+ "dev": true,
1237
+ "dependencies": {
1238
+ "binary-extensions": "^2.0.0"
1239
+ },
1240
+ "engines": {
1241
+ "node": ">=8"
1242
+ }
1243
+ },
1244
+ "node_modules/is-extglob": {
1245
+ "version": "2.1.1",
1246
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1247
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
1248
+ "engines": {
1249
+ "node": ">=0.10.0"
1250
+ }
1251
+ },
1252
+ "node_modules/is-fullwidth-code-point": {
1253
+ "version": "3.0.0",
1254
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
1255
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
1256
+ "engines": {
1257
+ "node": ">=8"
1258
+ }
1259
+ },
1260
+ "node_modules/is-glob": {
1261
+ "version": "4.0.3",
1262
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
1263
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
1264
+ "dependencies": {
1265
+ "is-extglob": "^2.1.1"
1266
+ },
1267
+ "engines": {
1268
+ "node": ">=0.10.0"
1269
+ }
1270
+ },
1271
+ "node_modules/is-number": {
1272
+ "version": "7.0.0",
1273
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
1274
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1275
+ "engines": {
1276
+ "node": ">=0.12.0"
1277
+ }
1278
+ },
1279
+ "node_modules/is-plain-obj": {
1280
+ "version": "3.0.0",
1281
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
1282
+ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
1283
+ "engines": {
1284
+ "node": ">=10"
1285
+ },
1286
+ "funding": {
1287
+ "url": "https://github.com/sponsors/sindresorhus"
1288
+ }
1289
+ },
1290
+ "node_modules/lru-cache": {
1291
+ "version": "6.0.0",
1292
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
1293
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
1294
+ "dependencies": {
1295
+ "yallist": "^4.0.0"
1296
+ },
1297
+ "engines": {
1298
+ "node": ">=10"
1299
+ }
1300
+ },
1301
+ "node_modules/make-dir": {
1302
+ "version": "3.1.0",
1303
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
1304
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
1305
+ "dependencies": {
1306
+ "semver": "^6.0.0"
1307
+ },
1308
+ "engines": {
1309
+ "node": ">=8"
1310
+ },
1311
+ "funding": {
1312
+ "url": "https://github.com/sponsors/sindresorhus"
1313
+ }
1314
+ },
1315
+ "node_modules/make-dir/node_modules/semver": {
1316
+ "version": "6.3.0",
1317
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
1318
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
1319
+ "bin": {
1320
+ "semver": "bin/semver.js"
1321
+ }
1322
+ },
1323
+ "node_modules/make-error": {
1324
+ "version": "1.3.6",
1325
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
1326
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
1327
+ "dev": true
1328
+ },
1329
+ "node_modules/media-typer": {
1330
+ "version": "0.3.0",
1331
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1332
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
1333
+ "engines": {
1334
+ "node": ">= 0.6"
1335
+ }
1336
+ },
1337
+ "node_modules/merge-descriptors": {
1338
+ "version": "1.0.1",
1339
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1340
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
1341
+ },
1342
+ "node_modules/methods": {
1343
+ "version": "1.1.2",
1344
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1345
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
1346
+ "engines": {
1347
+ "node": ">= 0.6"
1348
+ }
1349
+ },
1350
+ "node_modules/micromatch": {
1351
+ "version": "4.0.5",
1352
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
1353
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
1354
+ "dependencies": {
1355
+ "braces": "^3.0.2",
1356
+ "picomatch": "^2.3.1"
1357
+ },
1358
+ "engines": {
1359
+ "node": ">=8.6"
1360
+ }
1361
+ },
1362
+ "node_modules/mime": {
1363
+ "version": "1.6.0",
1364
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1365
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
1366
+ "bin": {
1367
+ "mime": "cli.js"
1368
+ },
1369
+ "engines": {
1370
+ "node": ">=4"
1371
+ }
1372
+ },
1373
+ "node_modules/mime-db": {
1374
+ "version": "1.52.0",
1375
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1376
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1377
+ "engines": {
1378
+ "node": ">= 0.6"
1379
+ }
1380
+ },
1381
+ "node_modules/mime-types": {
1382
+ "version": "2.1.35",
1383
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1384
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1385
+ "dependencies": {
1386
+ "mime-db": "1.52.0"
1387
+ },
1388
+ "engines": {
1389
+ "node": ">= 0.6"
1390
+ }
1391
+ },
1392
+ "node_modules/mimic-response": {
1393
+ "version": "2.1.0",
1394
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
1395
+ "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
1396
+ "engines": {
1397
+ "node": ">=8"
1398
+ },
1399
+ "funding": {
1400
+ "url": "https://github.com/sponsors/sindresorhus"
1401
+ }
1402
+ },
1403
+ "node_modules/minimatch": {
1404
+ "version": "3.1.2",
1405
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1406
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1407
+ "dependencies": {
1408
+ "brace-expansion": "^1.1.7"
1409
+ },
1410
+ "engines": {
1411
+ "node": "*"
1412
+ }
1413
+ },
1414
+ "node_modules/minipass": {
1415
+ "version": "4.2.8",
1416
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz",
1417
+ "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==",
1418
+ "engines": {
1419
+ "node": ">=8"
1420
+ }
1421
+ },
1422
+ "node_modules/minizlib": {
1423
+ "version": "2.1.2",
1424
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
1425
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
1426
+ "dependencies": {
1427
+ "minipass": "^3.0.0",
1428
+ "yallist": "^4.0.0"
1429
+ },
1430
+ "engines": {
1431
+ "node": ">= 8"
1432
+ }
1433
+ },
1434
+ "node_modules/minizlib/node_modules/minipass": {
1435
+ "version": "3.3.6",
1436
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
1437
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
1438
+ "dependencies": {
1439
+ "yallist": "^4.0.0"
1440
+ },
1441
+ "engines": {
1442
+ "node": ">=8"
1443
+ }
1444
+ },
1445
+ "node_modules/mkdirp": {
1446
+ "version": "1.0.4",
1447
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
1448
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
1449
+ "bin": {
1450
+ "mkdirp": "bin/cmd.js"
1451
+ },
1452
+ "engines": {
1453
+ "node": ">=10"
1454
+ }
1455
+ },
1456
+ "node_modules/ms": {
1457
+ "version": "2.0.0",
1458
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1459
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
1460
+ },
1461
+ "node_modules/nan": {
1462
+ "version": "2.17.0",
1463
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
1464
+ "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ=="
1465
+ },
1466
+ "node_modules/negotiator": {
1467
+ "version": "0.6.3",
1468
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1469
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1470
+ "engines": {
1471
+ "node": ">= 0.6"
1472
+ }
1473
+ },
1474
+ "node_modules/node-fetch": {
1475
+ "version": "2.6.9",
1476
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
1477
+ "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
1478
+ "dependencies": {
1479
+ "whatwg-url": "^5.0.0"
1480
+ },
1481
+ "engines": {
1482
+ "node": "4.x || >=6.0.0"
1483
+ },
1484
+ "peerDependencies": {
1485
+ "encoding": "^0.1.0"
1486
+ },
1487
+ "peerDependenciesMeta": {
1488
+ "encoding": {
1489
+ "optional": true
1490
+ }
1491
+ }
1492
+ },
1493
+ "node_modules/nodemon": {
1494
+ "version": "2.0.22",
1495
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz",
1496
+ "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==",
1497
+ "dev": true,
1498
+ "dependencies": {
1499
+ "chokidar": "^3.5.2",
1500
+ "debug": "^3.2.7",
1501
+ "ignore-by-default": "^1.0.1",
1502
+ "minimatch": "^3.1.2",
1503
+ "pstree.remy": "^1.1.8",
1504
+ "semver": "^5.7.1",
1505
+ "simple-update-notifier": "^1.0.7",
1506
+ "supports-color": "^5.5.0",
1507
+ "touch": "^3.1.0",
1508
+ "undefsafe": "^2.0.5"
1509
+ },
1510
+ "bin": {
1511
+ "nodemon": "bin/nodemon.js"
1512
+ },
1513
+ "engines": {
1514
+ "node": ">=8.10.0"
1515
+ },
1516
+ "funding": {
1517
+ "type": "opencollective",
1518
+ "url": "https://opencollective.com/nodemon"
1519
+ }
1520
+ },
1521
+ "node_modules/nodemon/node_modules/debug": {
1522
+ "version": "3.2.7",
1523
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
1524
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
1525
+ "dev": true,
1526
+ "dependencies": {
1527
+ "ms": "^2.1.1"
1528
+ }
1529
+ },
1530
+ "node_modules/nodemon/node_modules/ms": {
1531
+ "version": "2.1.3",
1532
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1533
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1534
+ "dev": true
1535
+ },
1536
+ "node_modules/nopt": {
1537
+ "version": "1.0.10",
1538
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
1539
+ "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
1540
+ "dev": true,
1541
+ "dependencies": {
1542
+ "abbrev": "1"
1543
+ },
1544
+ "bin": {
1545
+ "nopt": "bin/nopt.js"
1546
+ },
1547
+ "engines": {
1548
+ "node": "*"
1549
+ }
1550
+ },
1551
+ "node_modules/normalize-path": {
1552
+ "version": "3.0.0",
1553
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1554
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1555
+ "dev": true,
1556
+ "engines": {
1557
+ "node": ">=0.10.0"
1558
+ }
1559
+ },
1560
+ "node_modules/npmlog": {
1561
+ "version": "5.0.1",
1562
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
1563
+ "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
1564
+ "dependencies": {
1565
+ "are-we-there-yet": "^2.0.0",
1566
+ "console-control-strings": "^1.1.0",
1567
+ "gauge": "^3.0.0",
1568
+ "set-blocking": "^2.0.0"
1569
+ }
1570
+ },
1571
+ "node_modules/object-assign": {
1572
+ "version": "4.1.1",
1573
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1574
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1575
+ "engines": {
1576
+ "node": ">=0.10.0"
1577
+ }
1578
+ },
1579
+ "node_modules/object-inspect": {
1580
+ "version": "1.12.3",
1581
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
1582
+ "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
1583
+ "funding": {
1584
+ "url": "https://github.com/sponsors/ljharb"
1585
+ }
1586
+ },
1587
+ "node_modules/on-exit-leak-free": {
1588
+ "version": "2.1.0",
1589
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz",
1590
+ "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w=="
1591
+ },
1592
+ "node_modules/on-finished": {
1593
+ "version": "2.4.1",
1594
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1595
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1596
+ "dependencies": {
1597
+ "ee-first": "1.1.1"
1598
+ },
1599
+ "engines": {
1600
+ "node": ">= 0.8"
1601
+ }
1602
+ },
1603
+ "node_modules/once": {
1604
+ "version": "1.4.0",
1605
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1606
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1607
+ "dependencies": {
1608
+ "wrappy": "1"
1609
+ }
1610
+ },
1611
+ "node_modules/parseurl": {
1612
+ "version": "1.3.3",
1613
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1614
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1615
+ "engines": {
1616
+ "node": ">= 0.8"
1617
+ }
1618
+ },
1619
+ "node_modules/path-is-absolute": {
1620
+ "version": "1.0.1",
1621
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1622
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
1623
+ "engines": {
1624
+ "node": ">=0.10.0"
1625
+ }
1626
+ },
1627
+ "node_modules/path-to-regexp": {
1628
+ "version": "0.1.7",
1629
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1630
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
1631
+ },
1632
+ "node_modules/picomatch": {
1633
+ "version": "2.3.1",
1634
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1635
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1636
+ "engines": {
1637
+ "node": ">=8.6"
1638
+ },
1639
+ "funding": {
1640
+ "url": "https://github.com/sponsors/jonschlinkert"
1641
+ }
1642
+ },
1643
+ "node_modules/pino": {
1644
+ "version": "8.11.0",
1645
+ "resolved": "https://registry.npmjs.org/pino/-/pino-8.11.0.tgz",
1646
+ "integrity": "sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==",
1647
+ "dependencies": {
1648
+ "atomic-sleep": "^1.0.0",
1649
+ "fast-redact": "^3.1.1",
1650
+ "on-exit-leak-free": "^2.1.0",
1651
+ "pino-abstract-transport": "v1.0.0",
1652
+ "pino-std-serializers": "^6.0.0",
1653
+ "process-warning": "^2.0.0",
1654
+ "quick-format-unescaped": "^4.0.3",
1655
+ "real-require": "^0.2.0",
1656
+ "safe-stable-stringify": "^2.3.1",
1657
+ "sonic-boom": "^3.1.0",
1658
+ "thread-stream": "^2.0.0"
1659
+ },
1660
+ "bin": {
1661
+ "pino": "bin.js"
1662
+ }
1663
+ },
1664
+ "node_modules/pino-abstract-transport": {
1665
+ "version": "1.0.0",
1666
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz",
1667
+ "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==",
1668
+ "dependencies": {
1669
+ "readable-stream": "^4.0.0",
1670
+ "split2": "^4.0.0"
1671
+ }
1672
+ },
1673
+ "node_modules/pino-http": {
1674
+ "version": "8.3.3",
1675
+ "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-8.3.3.tgz",
1676
+ "integrity": "sha512-p4umsNIXXVu95HD2C8wie/vXH7db5iGRpc+yj1/ZQ3sRtTQLXNjoS6Be5+eI+rQbqCRxen/7k/KSN+qiZubGDw==",
1677
+ "dependencies": {
1678
+ "get-caller-file": "^2.0.5",
1679
+ "pino": "^8.0.0",
1680
+ "pino-std-serializers": "^6.0.0",
1681
+ "process-warning": "^2.0.0"
1682
+ }
1683
+ },
1684
+ "node_modules/pino-std-serializers": {
1685
+ "version": "6.1.0",
1686
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.1.0.tgz",
1687
+ "integrity": "sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g=="
1688
+ },
1689
+ "node_modules/process": {
1690
+ "version": "0.11.10",
1691
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
1692
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
1693
+ "engines": {
1694
+ "node": ">= 0.6.0"
1695
+ }
1696
+ },
1697
+ "node_modules/process-warning": {
1698
+ "version": "2.2.0",
1699
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz",
1700
+ "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg=="
1701
+ },
1702
+ "node_modules/proxy-addr": {
1703
+ "version": "2.0.7",
1704
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1705
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1706
+ "dependencies": {
1707
+ "forwarded": "0.2.0",
1708
+ "ipaddr.js": "1.9.1"
1709
+ },
1710
+ "engines": {
1711
+ "node": ">= 0.10"
1712
+ }
1713
+ },
1714
+ "node_modules/proxy-from-env": {
1715
+ "version": "1.1.0",
1716
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
1717
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
1718
+ },
1719
+ "node_modules/pstree.remy": {
1720
+ "version": "1.1.8",
1721
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
1722
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
1723
+ "dev": true
1724
+ },
1725
+ "node_modules/qs": {
1726
+ "version": "6.11.0",
1727
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
1728
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
1729
+ "dependencies": {
1730
+ "side-channel": "^1.0.4"
1731
+ },
1732
+ "engines": {
1733
+ "node": ">=0.6"
1734
+ },
1735
+ "funding": {
1736
+ "url": "https://github.com/sponsors/ljharb"
1737
+ }
1738
+ },
1739
+ "node_modules/quick-format-unescaped": {
1740
+ "version": "4.0.4",
1741
+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
1742
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
1743
+ },
1744
+ "node_modules/range-parser": {
1745
+ "version": "1.2.1",
1746
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1747
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1748
+ "engines": {
1749
+ "node": ">= 0.6"
1750
+ }
1751
+ },
1752
+ "node_modules/raw-body": {
1753
+ "version": "2.5.1",
1754
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
1755
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
1756
+ "dependencies": {
1757
+ "bytes": "3.1.2",
1758
+ "http-errors": "2.0.0",
1759
+ "iconv-lite": "0.4.24",
1760
+ "unpipe": "1.0.0"
1761
+ },
1762
+ "engines": {
1763
+ "node": ">= 0.8"
1764
+ }
1765
+ },
1766
+ "node_modules/readable-stream": {
1767
+ "version": "4.3.0",
1768
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz",
1769
+ "integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==",
1770
+ "dependencies": {
1771
+ "abort-controller": "^3.0.0",
1772
+ "buffer": "^6.0.3",
1773
+ "events": "^3.3.0",
1774
+ "process": "^0.11.10"
1775
+ },
1776
+ "engines": {
1777
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
1778
+ }
1779
+ },
1780
+ "node_modules/readdirp": {
1781
+ "version": "3.6.0",
1782
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1783
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1784
+ "dev": true,
1785
+ "dependencies": {
1786
+ "picomatch": "^2.2.1"
1787
+ },
1788
+ "engines": {
1789
+ "node": ">=8.10.0"
1790
+ }
1791
+ },
1792
+ "node_modules/real-require": {
1793
+ "version": "0.2.0",
1794
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
1795
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
1796
+ "engines": {
1797
+ "node": ">= 12.13.0"
1798
+ }
1799
+ },
1800
+ "node_modules/requires-port": {
1801
+ "version": "1.0.0",
1802
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
1803
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
1804
+ },
1805
+ "node_modules/rimraf": {
1806
+ "version": "3.0.2",
1807
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
1808
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
1809
+ "dependencies": {
1810
+ "glob": "^7.1.3"
1811
+ },
1812
+ "bin": {
1813
+ "rimraf": "bin.js"
1814
+ },
1815
+ "funding": {
1816
+ "url": "https://github.com/sponsors/isaacs"
1817
+ }
1818
+ },
1819
+ "node_modules/safe-buffer": {
1820
+ "version": "5.2.1",
1821
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1822
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1823
+ "funding": [
1824
+ {
1825
+ "type": "github",
1826
+ "url": "https://github.com/sponsors/feross"
1827
+ },
1828
+ {
1829
+ "type": "patreon",
1830
+ "url": "https://www.patreon.com/feross"
1831
+ },
1832
+ {
1833
+ "type": "consulting",
1834
+ "url": "https://feross.org/support"
1835
+ }
1836
+ ]
1837
+ },
1838
+ "node_modules/safe-stable-stringify": {
1839
+ "version": "2.4.3",
1840
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
1841
+ "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==",
1842
+ "engines": {
1843
+ "node": ">=10"
1844
+ }
1845
+ },
1846
+ "node_modules/safer-buffer": {
1847
+ "version": "2.1.2",
1848
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1849
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1850
+ },
1851
+ "node_modules/semver": {
1852
+ "version": "5.7.1",
1853
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1854
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1855
+ "dev": true,
1856
+ "bin": {
1857
+ "semver": "bin/semver"
1858
+ }
1859
+ },
1860
+ "node_modules/send": {
1861
+ "version": "0.18.0",
1862
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
1863
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
1864
+ "dependencies": {
1865
+ "debug": "2.6.9",
1866
+ "depd": "2.0.0",
1867
+ "destroy": "1.2.0",
1868
+ "encodeurl": "~1.0.2",
1869
+ "escape-html": "~1.0.3",
1870
+ "etag": "~1.8.1",
1871
+ "fresh": "0.5.2",
1872
+ "http-errors": "2.0.0",
1873
+ "mime": "1.6.0",
1874
+ "ms": "2.1.3",
1875
+ "on-finished": "2.4.1",
1876
+ "range-parser": "~1.2.1",
1877
+ "statuses": "2.0.1"
1878
+ },
1879
+ "engines": {
1880
+ "node": ">= 0.8.0"
1881
+ }
1882
+ },
1883
+ "node_modules/send/node_modules/ms": {
1884
+ "version": "2.1.3",
1885
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1886
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1887
+ },
1888
+ "node_modules/serve-static": {
1889
+ "version": "1.15.0",
1890
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
1891
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
1892
+ "dependencies": {
1893
+ "encodeurl": "~1.0.2",
1894
+ "escape-html": "~1.0.3",
1895
+ "parseurl": "~1.3.3",
1896
+ "send": "0.18.0"
1897
+ },
1898
+ "engines": {
1899
+ "node": ">= 0.8.0"
1900
+ }
1901
+ },
1902
+ "node_modules/set-blocking": {
1903
+ "version": "2.0.0",
1904
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
1905
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
1906
+ },
1907
+ "node_modules/setprototypeof": {
1908
+ "version": "1.2.0",
1909
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1910
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1911
+ },
1912
+ "node_modules/showdown": {
1913
+ "version": "2.1.0",
1914
+ "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
1915
+ "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
1916
+ "dependencies": {
1917
+ "commander": "^9.0.0"
1918
+ },
1919
+ "bin": {
1920
+ "showdown": "bin/showdown.js"
1921
+ },
1922
+ "funding": {
1923
+ "type": "individual",
1924
+ "url": "https://www.paypal.me/tiviesantos"
1925
+ }
1926
+ },
1927
+ "node_modules/side-channel": {
1928
+ "version": "1.0.4",
1929
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1930
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1931
+ "dependencies": {
1932
+ "call-bind": "^1.0.0",
1933
+ "get-intrinsic": "^1.0.2",
1934
+ "object-inspect": "^1.9.0"
1935
+ },
1936
+ "funding": {
1937
+ "url": "https://github.com/sponsors/ljharb"
1938
+ }
1939
+ },
1940
+ "node_modules/signal-exit": {
1941
+ "version": "3.0.7",
1942
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
1943
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
1944
+ },
1945
+ "node_modules/simple-concat": {
1946
+ "version": "1.0.1",
1947
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
1948
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
1949
+ "funding": [
1950
+ {
1951
+ "type": "github",
1952
+ "url": "https://github.com/sponsors/feross"
1953
+ },
1954
+ {
1955
+ "type": "patreon",
1956
+ "url": "https://www.patreon.com/feross"
1957
+ },
1958
+ {
1959
+ "type": "consulting",
1960
+ "url": "https://feross.org/support"
1961
+ }
1962
+ ]
1963
+ },
1964
+ "node_modules/simple-get": {
1965
+ "version": "3.1.1",
1966
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
1967
+ "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
1968
+ "dependencies": {
1969
+ "decompress-response": "^4.2.0",
1970
+ "once": "^1.3.1",
1971
+ "simple-concat": "^1.0.0"
1972
+ }
1973
+ },
1974
+ "node_modules/simple-update-notifier": {
1975
+ "version": "1.1.0",
1976
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
1977
+ "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
1978
+ "dev": true,
1979
+ "dependencies": {
1980
+ "semver": "~7.0.0"
1981
+ },
1982
+ "engines": {
1983
+ "node": ">=8.10.0"
1984
+ }
1985
+ },
1986
+ "node_modules/simple-update-notifier/node_modules/semver": {
1987
+ "version": "7.0.0",
1988
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
1989
+ "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
1990
+ "dev": true,
1991
+ "bin": {
1992
+ "semver": "bin/semver.js"
1993
+ }
1994
+ },
1995
+ "node_modules/sonic-boom": {
1996
+ "version": "3.3.0",
1997
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz",
1998
+ "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==",
1999
+ "dependencies": {
2000
+ "atomic-sleep": "^1.0.0"
2001
+ }
2002
+ },
2003
+ "node_modules/split2": {
2004
+ "version": "4.2.0",
2005
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
2006
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
2007
+ "engines": {
2008
+ "node": ">= 10.x"
2009
+ }
2010
+ },
2011
+ "node_modules/statuses": {
2012
+ "version": "2.0.1",
2013
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
2014
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
2015
+ "engines": {
2016
+ "node": ">= 0.8"
2017
+ }
2018
+ },
2019
+ "node_modules/string_decoder": {
2020
+ "version": "1.3.0",
2021
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
2022
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
2023
+ "dependencies": {
2024
+ "safe-buffer": "~5.2.0"
2025
+ }
2026
+ },
2027
+ "node_modules/string-width": {
2028
+ "version": "4.2.3",
2029
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
2030
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
2031
+ "dependencies": {
2032
+ "emoji-regex": "^8.0.0",
2033
+ "is-fullwidth-code-point": "^3.0.0",
2034
+ "strip-ansi": "^6.0.1"
2035
+ },
2036
+ "engines": {
2037
+ "node": ">=8"
2038
+ }
2039
+ },
2040
+ "node_modules/strip-ansi": {
2041
+ "version": "6.0.1",
2042
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
2043
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
2044
+ "dependencies": {
2045
+ "ansi-regex": "^5.0.1"
2046
+ },
2047
+ "engines": {
2048
+ "node": ">=8"
2049
+ }
2050
+ },
2051
+ "node_modules/supports-color": {
2052
+ "version": "5.5.0",
2053
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
2054
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
2055
+ "dev": true,
2056
+ "dependencies": {
2057
+ "has-flag": "^3.0.0"
2058
+ },
2059
+ "engines": {
2060
+ "node": ">=4"
2061
+ }
2062
+ },
2063
+ "node_modules/tar": {
2064
+ "version": "6.1.13",
2065
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz",
2066
+ "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==",
2067
+ "dependencies": {
2068
+ "chownr": "^2.0.0",
2069
+ "fs-minipass": "^2.0.0",
2070
+ "minipass": "^4.0.0",
2071
+ "minizlib": "^2.1.1",
2072
+ "mkdirp": "^1.0.3",
2073
+ "yallist": "^4.0.0"
2074
+ },
2075
+ "engines": {
2076
+ "node": ">=10"
2077
+ }
2078
+ },
2079
+ "node_modules/text-to-image": {
2080
+ "version": "5.2.0",
2081
+ "resolved": "https://registry.npmjs.org/text-to-image/-/text-to-image-5.2.0.tgz",
2082
+ "integrity": "sha512-J6PH9zwMXA3M+QUpxDOOgo8NWYKav6CwFtd5B7wJd9C6iDBPZiD6OX1pyV9ZxnVrOivAevqqkmOcMXPXjFkI+A==",
2083
+ "dependencies": {
2084
+ "canvas": "^2.6.0"
2085
+ }
2086
+ },
2087
+ "node_modules/thread-stream": {
2088
+ "version": "2.3.0",
2089
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.3.0.tgz",
2090
+ "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==",
2091
+ "dependencies": {
2092
+ "real-require": "^0.2.0"
2093
+ }
2094
+ },
2095
+ "node_modules/to-regex-range": {
2096
+ "version": "5.0.1",
2097
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
2098
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
2099
+ "dependencies": {
2100
+ "is-number": "^7.0.0"
2101
+ },
2102
+ "engines": {
2103
+ "node": ">=8.0"
2104
+ }
2105
+ },
2106
+ "node_modules/toidentifier": {
2107
+ "version": "1.0.1",
2108
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
2109
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
2110
+ "engines": {
2111
+ "node": ">=0.6"
2112
+ }
2113
+ },
2114
+ "node_modules/touch": {
2115
+ "version": "3.1.0",
2116
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
2117
+ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
2118
+ "dev": true,
2119
+ "dependencies": {
2120
+ "nopt": "~1.0.10"
2121
+ },
2122
+ "bin": {
2123
+ "nodetouch": "bin/nodetouch.js"
2124
+ }
2125
+ },
2126
+ "node_modules/tr46": {
2127
+ "version": "0.0.3",
2128
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
2129
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
2130
+ },
2131
+ "node_modules/ts-node": {
2132
+ "version": "10.9.1",
2133
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
2134
+ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
2135
+ "dev": true,
2136
+ "dependencies": {
2137
+ "@cspotcode/source-map-support": "^0.8.0",
2138
+ "@tsconfig/node10": "^1.0.7",
2139
+ "@tsconfig/node12": "^1.0.7",
2140
+ "@tsconfig/node14": "^1.0.0",
2141
+ "@tsconfig/node16": "^1.0.2",
2142
+ "acorn": "^8.4.1",
2143
+ "acorn-walk": "^8.1.1",
2144
+ "arg": "^4.1.0",
2145
+ "create-require": "^1.1.0",
2146
+ "diff": "^4.0.1",
2147
+ "make-error": "^1.1.1",
2148
+ "v8-compile-cache-lib": "^3.0.1",
2149
+ "yn": "3.1.1"
2150
+ },
2151
+ "bin": {
2152
+ "ts-node": "dist/bin.js",
2153
+ "ts-node-cwd": "dist/bin-cwd.js",
2154
+ "ts-node-esm": "dist/bin-esm.js",
2155
+ "ts-node-script": "dist/bin-script.js",
2156
+ "ts-node-transpile-only": "dist/bin-transpile.js",
2157
+ "ts-script": "dist/bin-script-deprecated.js"
2158
+ },
2159
+ "peerDependencies": {
2160
+ "@swc/core": ">=1.2.50",
2161
+ "@swc/wasm": ">=1.2.50",
2162
+ "@types/node": "*",
2163
+ "typescript": ">=2.7"
2164
+ },
2165
+ "peerDependenciesMeta": {
2166
+ "@swc/core": {
2167
+ "optional": true
2168
+ },
2169
+ "@swc/wasm": {
2170
+ "optional": true
2171
+ }
2172
+ }
2173
+ },
2174
+ "node_modules/type-is": {
2175
+ "version": "1.6.18",
2176
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
2177
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
2178
+ "dependencies": {
2179
+ "media-typer": "0.3.0",
2180
+ "mime-types": "~2.1.24"
2181
+ },
2182
+ "engines": {
2183
+ "node": ">= 0.6"
2184
+ }
2185
+ },
2186
+ "node_modules/typescript": {
2187
+ "version": "5.0.4",
2188
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
2189
+ "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
2190
+ "dev": true,
2191
+ "bin": {
2192
+ "tsc": "bin/tsc",
2193
+ "tsserver": "bin/tsserver"
2194
+ },
2195
+ "engines": {
2196
+ "node": ">=12.20"
2197
+ }
2198
+ },
2199
+ "node_modules/undefsafe": {
2200
+ "version": "2.0.5",
2201
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
2202
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
2203
+ "dev": true
2204
+ },
2205
+ "node_modules/unpipe": {
2206
+ "version": "1.0.0",
2207
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
2208
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
2209
+ "engines": {
2210
+ "node": ">= 0.8"
2211
+ }
2212
+ },
2213
+ "node_modules/util-deprecate": {
2214
+ "version": "1.0.2",
2215
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
2216
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
2217
+ },
2218
+ "node_modules/utils-merge": {
2219
+ "version": "1.0.1",
2220
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
2221
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
2222
+ "engines": {
2223
+ "node": ">= 0.4.0"
2224
+ }
2225
+ },
2226
+ "node_modules/v8-compile-cache-lib": {
2227
+ "version": "3.0.1",
2228
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
2229
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
2230
+ "dev": true
2231
+ },
2232
+ "node_modules/vary": {
2233
+ "version": "1.1.2",
2234
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
2235
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
2236
+ "engines": {
2237
+ "node": ">= 0.8"
2238
+ }
2239
+ },
2240
+ "node_modules/webidl-conversions": {
2241
+ "version": "3.0.1",
2242
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
2243
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
2244
+ },
2245
+ "node_modules/whatwg-url": {
2246
+ "version": "5.0.0",
2247
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
2248
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
2249
+ "dependencies": {
2250
+ "tr46": "~0.0.3",
2251
+ "webidl-conversions": "^3.0.0"
2252
+ }
2253
+ },
2254
+ "node_modules/wide-align": {
2255
+ "version": "1.1.5",
2256
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
2257
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
2258
+ "dependencies": {
2259
+ "string-width": "^1.0.2 || 2 || 3 || 4"
2260
+ }
2261
+ },
2262
+ "node_modules/wrappy": {
2263
+ "version": "1.0.2",
2264
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
2265
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
2266
+ },
2267
+ "node_modules/yallist": {
2268
+ "version": "4.0.0",
2269
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
2270
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
2271
+ },
2272
+ "node_modules/yn": {
2273
+ "version": "3.1.1",
2274
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
2275
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
2276
+ "dev": true,
2277
+ "engines": {
2278
+ "node": ">=6"
2279
+ }
2280
+ },
2281
+ "node_modules/zlib": {
2282
+ "version": "1.0.5",
2283
+ "resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz",
2284
+ "integrity": "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w==",
2285
+ "hasInstallScript": true,
2286
+ "engines": {
2287
+ "node": ">=0.2.0"
2288
+ }
2289
+ }
2290
+ }
2291
+ }
package.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "oai-reverse-proxy",
3
+ "version": "1.0.0",
4
+ "description": "Reverse proxy for the OpenAI API",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "start:dev": "nodemon --watch src --exec ts-node src/server.ts",
8
+ "start": "node build/server.js"
9
+ },
10
+ "author": "",
11
+ "license": "MIT",
12
+ "dependencies": {
13
+ "axios": "1.3.5",
14
+ "cors": "2.8.5",
15
+ "date-fns": "2.29.3",
16
+ "dotenv": "16.0.3",
17
+ "express": "4.18.2",
18
+ "http-proxy-middleware": "3.0.0-beta.1",
19
+ "moment-timezone": "^0.5.43",
20
+ "pino-http": "8.3.3",
21
+ "showdown": "2.1.0",
22
+ "text-to-image": "5.2.0",
23
+ "zlib": "1.0.5",
24
+ "ms": "^2.1.3"
25
+ },
26
+ "devDependencies": {
27
+ "@types/cors": "2.8.13",
28
+ "@types/express": "4.17.17",
29
+ "@types/node": "18.15.11",
30
+ "@types/showdown": "2.0.0",
31
+ "nodemon": "2.0.22",
32
+ "ts-node": "10.9.1",
33
+ "typescript": "5.0.4"
34
+ }
35
+ }
sawesome.ttf ADDED
Binary file (449 kB). View file
 
src/config.ts ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+
4
+ type Config = {
5
+ /** The port the proxy server will listen on. */
6
+ port: number;
7
+ /** OpenAI API key, either a single key or a base64-encoded JSON array of key configs. */
8
+ openaiKey?: string;
9
+ openaiProxy?: string;
10
+ /** Proxy key. If set, requests must provide this key in the Authorization header to use the proxy. */
11
+ proxyKey?: string;
12
+ promptInject?: string;
13
+ /** Per-IP limit for requests per minute to OpenAI's completions endpoint. */
14
+ modelRateLimit: number;
15
+ /** Max number of tokens to generate. Requests which specify a higher value will be rewritten to use this value. */
16
+ maxOutputTokens: number;
17
+ /** Whether requests containing disallowed characters should be rejected. */
18
+ rejectDisallowed?: boolean;
19
+ /** Rejection sample rate (0 - 1). Higher values are more strict but increase server load. */
20
+ rejectSampleRate?: number;
21
+ /** Message to return when rejecting requests. */
22
+ rejectMessage?: string;
23
+ /** Logging threshold. */
24
+ logLevel?: "debug" | "info" | "warn" | "error";
25
+ /** Whether prompts and responses should be logged. */
26
+ logPrompts?: boolean; // TODO
27
+ keyPassword: string;
28
+ proxyCaptcha?: boolean;
29
+ };
30
+
31
+ export const config: Config = {
32
+ port: getEnvWithDefault("PORT", 7860),
33
+ openaiKey: getEnvWithDefault("OPENAI_KEY", ""),
34
+ openaiProxy: getEnvWithDefault("OPENAI_PROXY", ""),
35
+ proxyKey: getEnvWithDefault("PROXY_KEY", ""),
36
+ promptInject: getEnvWithDefault("PROMPT_INJECT", ""),
37
+ keyPassword: getEnvWithDefault("KEY_PASSWORD", ""),
38
+ modelRateLimit: getEnvWithDefault("MODEL_RATE_LIMIT", 3),
39
+ maxOutputTokens: getEnvWithDefault("MAX_OUTPUT_TOKENS", 300),
40
+ proxyCaptcha: getEnvWithDefault("PROXY_CAPTCHA", false),
41
+ rejectDisallowed: getEnvWithDefault("REJECT_DISALLOWED", true),
42
+ rejectSampleRate: getEnvWithDefault("REJECT_SAMPLE_RATE", 0.2),
43
+ rejectMessage: getEnvWithDefault("REJECT_MESSAGE", "Кыш отсюдова"),
44
+ logLevel: getEnvWithDefault("LOG_LEVEL", "info"),
45
+ logPrompts: getEnvWithDefault("LOG_PROMPTS", false),
46
+ } as const;
47
+
48
+ export const SENSITIVE_KEYS: (keyof Config)[] = ["proxyKey", "openaiKey", "keyPassword", "promptInject", "openaiProxy"];
49
+ const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;
50
+ export function listConfig(): Record<string, string> {
51
+ const result: Record<string, string> = {};
52
+ for (const key of getKeys(config)) {
53
+ const value = config[key]?.toString() || "";
54
+ if (!SENSITIVE_KEYS.includes(key)) {
55
+ result[key] = value;
56
+ }
57
+ }
58
+ return result;
59
+ }
60
+
61
+ function getEnvWithDefault<T>(name: string, defaultValue: T): T {
62
+ const value = process.env[name];
63
+ if (value === undefined) {
64
+ return defaultValue;
65
+ }
66
+ try {
67
+ if (name === "OPENAI_KEY") {
68
+ return value as unknown as T;
69
+ }
70
+ return JSON.parse(value) as T;
71
+ } catch (err) {
72
+ return value as unknown as T;
73
+ }
74
+ }
src/info-page.ts ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import * as textToImage from 'text-to-image';
3
+ import showdown from "showdown";
4
+ import { config, listConfig } from "./config";
5
+ import { keys } from "./keys";
6
+ import { proxies } from "./proxies";
7
+ import { captchas } from './proxy/captchas';
8
+ import { proxyKeys } from './proxy/proxy-keys';
9
+ import { getUniqueIps } from "./proxy/rate-limit";
10
+
11
+ export const handleInfoPage = async (req: Request, res: Response) => {
12
+ // Huggingface puts spaces behind some cloudflare ssl proxy, so `req.protocol` is `http` but the correct URL is actually `https`
13
+ const host = req.get("host");
14
+ const isHuggingface = host?.includes("hf.space");
15
+ const protocol = isHuggingface ? "https" : req.protocol;
16
+ res.send(await getInfoPageHtml(protocol + "://" + host, req.body));
17
+ };
18
+
19
+ const ms = require('ms');
20
+
21
+ async function getInfoPageHtml(host: string, body: any) {
22
+ const keylist = keys.list();
23
+ const proxylist = proxies.list();
24
+ const rateLimitInfo = { proompters: getUniqueIps() };
25
+ const usage = keys.getUsage();
26
+ const now = Date.now();
27
+ const uptimeDays = Math.floor(process.uptime() / 86400);
28
+ const info = {
29
+ uptime: uptimeDays > 0 ? `${uptimeDays} день или дня или дней` : 'меньше дня',
30
+ timestamp: new Date(now).toLocaleString('ru-RU', { timeZone: 'Europe/Moscow' }),
31
+ api: host + "/proxy/openai/v1",
32
+ proompts: keylist.reduce((acc, k) => acc + k.promptCount, 0) + proxylist.reduce((acc, k) => acc + k.promptCount, 0),
33
+ ...(config.modelRateLimit ? rateLimitInfo : {}),
34
+ 'gpt-3.5-turbo': {
35
+ all: keylist.length,
36
+ active: keylist.filter((k) => !k.isDisabled).length,
37
+ trial: keylist.filter((k) => k.isTrial && !k.isDisabled).length,
38
+ remaining: usage.gpt3.remaining,
39
+ usage: `${usage.gpt3.usage.toFixed(2)}/${Math.round(usage.gpt3.limit)}`,
40
+ },
41
+ 'gpt-4': {
42
+ all: keylist.filter((k) => k.isGpt4).length,
43
+ active: keylist.filter((k) => k.isGpt4 && !k.isDisabled).length,
44
+ trial: keylist.filter((k) => k.isGpt4 && k.isTrial && !k.isDisabled).length,
45
+ remaining: usage.gpt4.remaining,
46
+ usage: `${usage.gpt4.usage.toFixed(2)}/${Math.round(usage.gpt4.limit)}`,
47
+ },
48
+ config: listConfig(),
49
+ };
50
+
51
+ const readme = require("fs").readFileSync("README.md", "utf8");
52
+ const readmeBody = readme.split("---")[2] || readme;
53
+ const converter = new showdown.Converter();
54
+ const html = converter.makeHtml(readmeBody);
55
+
56
+ let captchaHTML = '';
57
+ if (config.proxyCaptcha) {
58
+ if (captchas.check(body?.key, body?.value)) {
59
+ const key = proxyKeys.generate();
60
+ captchaHTML = `
61
+ <p style="text-align: center;color: #fff;">
62
+ Твой ключ от прокси - <code>${key}</code><br />
63
+ Писать его в поле API Key в таверне и не забудь после этого нажать Connect. <br />
64
+ Учти, если превысишь лимит в ${config.modelRateLimit} запросов в минуту - ключ превратится в тыкву и придётся идти за новым.
65
+ </p>
66
+ `;
67
+ } else {
68
+ const captcha = await captchas.generate();
69
+ captchaHTML = `
70
+ <img src="${captcha.instructionImg}"/><br />
71
+ <img src="${captcha.img}"/><br />
72
+ <div style="display: grid;width: 33%;">
73
+ <input type="hidden" name="key" value="${captcha.key}" />
74
+ <input type="text" name="value" autocomplete="off" />
75
+ <input type="submit" value="${captchas.encodeString('Нажатием этой кнопки вы поддерживаете СВО')}" />
76
+ </div>
77
+
78
+ <script>
79
+ const form = document.getElementById("captchaForm");
80
+ document.addEventListener('keyup', (e) => {
81
+ if (e.ctrlKey) form?.reset();
82
+ });
83
+ document.addEventListener('mousedown', (e) => {
84
+ if (e.button === 2) form?.reset();
85
+ });
86
+ </script>
87
+ `;
88
+ }
89
+ }
90
+
91
+ const pageBody = `<!DOCTYPE html>
92
+ <html lang="en">
93
+ <head>
94
+ <meta charset="utf-8" />
95
+ <title>2ch proxy.</title>
96
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
97
+ </head>
98
+
99
+ <body style="padding: 1em;">
100
+ <main>
101
+ ${html}
102
+ <div style="width: 100%;height: 47px;content: &quot;Z&quot;;margin-left: auto;margin-right: auto;color: #f9f946;font-size: 36px;background: linear-gradient(to bottom, #000 33.333%, #0039A6 33.333%, #0039A6 66.667%, #D52B1E 66.667%);text-shadow: 1px 2px 1px black;font-family: initial;text-align: center;margin-bottom: 9px;">Z</div>
103
+ <form action="" method="POST" style="margin-left:auto;margin-right:auto;" id="captchaForm">
104
+ ${captchaHTML}
105
+ <pre>${JSON.stringify(info, null, 2)}</pre>
106
+ </form>
107
+ <div style="width: 100%;height: 47px;content: &quot;Z&quot;;margin-left: auto;margin-right: auto;color: #f9f946;font-size: 36px;background: linear-gradient(to bottom, #64C8FF 33.333%, #0039A6 33.333%, #0039A6 66.667%, #D52B1E 66.667%);text-shadow: 1px 2px 1px black;font-family: initial;text-align: center;margin-top: 9px;">V</div>
108
+
109
+ <div style="position:absolute; top:-9999px; left:-9999px;">
110
+ <div id="player"></div>
111
+ </div>
112
+ </main>
113
+ </body>
114
+ <script>
115
+ var player;
116
+ var videoIds = ['nClPPq2s-XQ', 'I8EMtklElRE', 'paZxfm8dhG0', 'OcdZtfuSt_A', 'AcTnMR_boys', 'i-DeuCQqgkM', '5qNtOryEVc0', 'pvq1NQz-I5E', 'm7FKzjmeg8U', 'mzaf8ExJ2hg', 'wOTD2KaAVjc', '5d1HWklOlN8', 'sYMZtGsDzss' ];
117
+ // Замените VIDEO_ID на реальные ID видео, которые вы хотите проигрывать
118
+
119
+ function onYouTubeIframeAPIReady() {
120
+ var randomIndex = Math.floor(Math.random() * videoIds.length);
121
+ var randomVideoId = videoIds[randomIndex];
122
+ player = new YT.Player('player', {
123
+ videoId: randomVideoId,
124
+ playerVars: {
125
+ autoplay: 1,
126
+ loop: 1,
127
+ controls: 1,
128
+ showinfo: 1,
129
+ modestbranding: 1,
130
+ iv_load_policy: 3,
131
+ start: 1,
132
+ autohide: 0,
133
+ mute: 0
134
+ },
135
+ events: {
136
+ 'onReady': onPlayerReady
137
+ }
138
+ });
139
+ }
140
+ function onPlayerReady(event) {
141
+ event.target.playVideo();
142
+ }
143
+ var tag = document.createElement('script');
144
+ tag.src = "https://www.youtube.com/iframe_api";
145
+ var firstScriptTag = document.getElementsByTagName('script')[0];
146
+ firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
147
+ </script>
148
+
149
+ <style>
150
+ pre{
151
+ border-radius: 8px;
152
+ background: #222;
153
+ width: 31%;
154
+ margin-left: auto;
155
+ margin-right: auto;
156
+ overflow: auto;
157
+ padding: 11px;
158
+ border: double;
159
+ text-align: start;
160
+ font-size: 11px;
161
+ color: #fff;
162
+ user-select: text;
163
+ }
164
+ body {
165
+ background-color: #222!important;
166
+ display: block;
167
+ margin-left: auto;
168
+ margin-right: auto;
169
+ }
170
+ #captchaForm {
171
+ display: flex;
172
+ flex-direction: column;
173
+ align-items: center;
174
+ margin: 6px;
175
+ margin-left: auto;
176
+ margin-right: auto;
177
+ border-radius: 6px;
178
+ width: 66%;
179
+ }
180
+ img {
181
+ margin: 5px;
182
+ margin-left:auto;
183
+ margin-right:auto;
184
+ height: 40px;
185
+ }
186
+ input[type="text"]{
187
+ border-top-left-radius: 6px;
188
+ border: 0px;
189
+ border-top-right-radius: 6px;
190
+ background-color: #333;
191
+ color: #fff;
192
+ padding: 7px;
193
+ text-align: center;
194
+ }
195
+
196
+ input[type="submit"]{
197
+ border-bottom-left-radius: 6px;
198
+ border: 0px;
199
+ border-bottom-right-radius: 6px;
200
+ background-color: #373636;
201
+ color: #fff;
202
+ padding: 7px;
203
+ text-align: center;
204
+ }
205
+
206
+ form {
207
+ user-select: text;
208
+ display: grid;
209
+ border: #fff 2px double;
210
+ margin: 5px;
211
+ margin-right: 5px;
212
+ margin-left: 5px;
213
+ padding: 11px;
214
+ border-radius: 6px;
215
+ margin-left: auto;
216
+ margin-right: auto;
217
+ }
218
+ input:hover {
219
+ background-color: #242424!important;
220
+ cursor: pointer;
221
+ }
222
+
223
+ </style>
224
+ </html>`;
225
+
226
+ return pageBody;
227
+ }
src/keys.ts ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Manages OpenAI API keys. Tracks usage, disables expired keys, and provides
2
+ round-robin access to keys. Keys are stored in the OPENAI_KEY environment
3
+ variable, either as a single key, or a base64-encoded JSON array of keys.*/
4
+ import crypto from "crypto";
5
+ import axios from "axios";
6
+ import { config } from "./config";
7
+ import { logger } from "./logger";
8
+ import { format, subDays, addDays, minutesToMilliseconds } from 'date-fns';
9
+ import qs from 'querystring';
10
+
11
+ const KEY_REGEX = /sk-[a-z0-9A-Z]{20}T3BlbkFJ[a-zA-Z0-9]{20}/;
12
+ const CHECK_INTERVAL = minutesToMilliseconds(15);
13
+ const MINUTE = minutesToMilliseconds(1);
14
+
15
+ /** Represents a key stored in the OPENAI_KEY environment variable. */
16
+ type KeySchema = {
17
+ /** The OpenAI API key itself. */
18
+ key: string;
19
+ /** Whether this is a free trial key. These are prioritized over paid keys if they can fulfill the request. */
20
+ isTrial?: boolean;
21
+ /** Whether this key has been provisioned for GPT-4. */
22
+ isGpt4?: boolean;
23
+ };
24
+
25
+ type OaiModelsResponse = {
26
+ data: {
27
+ id: string;
28
+ }[];
29
+ }
30
+
31
+ type OaiSubscriptionResponse = {
32
+ plan: {
33
+ id: string
34
+ };
35
+ soft_limit_usd: number;
36
+ hard_limit_usd: number;
37
+ system_hard_limit_usd: number;
38
+ account_name?: string;
39
+ }
40
+
41
+ type OaiUsageResponse = {
42
+ total_usage: number;
43
+ }
44
+
45
+ type OaiKeyResponse = {
46
+ result: string;
47
+ key: {
48
+ sensitive_id: string;
49
+ };
50
+ }
51
+
52
+ /** Runtime information about a key. */
53
+ export type Key = KeySchema & {
54
+ /** Whether this key is currently disabled. We set this if we get a 429 or 401 response from OpenAI. */
55
+ isDisabled?: boolean;
56
+ /** Threshold at which a warning email will be sent by OpenAI. */
57
+ softLimit?: number;
58
+ /** Threshold at which the key will be disabled because it has reached the user-defined limit. */
59
+ hardLimit?: number;
60
+ /** The maximum quota allocated to this key by OpenAI. */
61
+ systemHardLimit?: number;
62
+ /** The current usage of this key. */
63
+ usage?: number;
64
+ /** The number of prompts that have been sent with this key. */
65
+ promptCount: number;
66
+ /** The time at which this key was last used. */
67
+ lastUsed: number;
68
+ /** Key hash for displaying usage in the dashboard. */
69
+ hash: string;
70
+ rateLimitedAt: number;
71
+ orgId: string;
72
+ };
73
+
74
+ const keyPool: Key[] = [];
75
+
76
+ async function init() {
77
+ setTimeout(checker, CHECK_INTERVAL);
78
+
79
+ const keyString = config.openaiKey;
80
+
81
+ const keyList = parse(keyString);
82
+ for (const key of keyList) {
83
+ await add(key);
84
+ await new Promise(r => setTimeout(r, 1000));
85
+ }
86
+ }
87
+
88
+ async function checker() {
89
+ await checkAll(false);
90
+
91
+ setTimeout(checker, CHECK_INTERVAL);
92
+ }
93
+
94
+ function list() {
95
+ return keyPool.map((key) => ({
96
+ ...key,
97
+ key: undefined,
98
+ }));
99
+ }
100
+
101
+ function parse(keyString: string | undefined): (KeySchema | string)[] {
102
+ if (!keyString) return [];
103
+
104
+ if (keyString.match(KEY_REGEX)) return [keyString];
105
+
106
+ try {
107
+ const decoded = Buffer.from(keyString, "base64").toString();
108
+ return JSON.parse(decoded);
109
+ } catch (err) {
110
+ logger.info("key is not base64-encoded JSON, assuming bare key");
111
+ return [keyString];
112
+ }
113
+ }
114
+
115
+ async function check(key: Key, tryCompletion = true) {
116
+ if (key.isDisabled) return;
117
+
118
+ try {
119
+ if (tryCompletion) {
120
+ await axios.post(
121
+ "https://api.openai.com/v1/chat/completions",
122
+ {
123
+ model: "gpt-3.5-turbo", max_tokens: 1,
124
+ messages: [{ role: "user", content: "" }],
125
+ },
126
+ {
127
+ headers: {
128
+ Authorization: `Bearer ${key.key}`,
129
+ "Content-Type": "application/json",
130
+ },
131
+ },
132
+ );
133
+ }
134
+ const options = {
135
+ headers: { Authorization: `Bearer ${key.key}` }
136
+ };
137
+
138
+ const { data: modelsData } = await axios.get<OaiModelsResponse>("https://api.openai.com/v1/models", options);
139
+
140
+ key.isGpt4 = Boolean(modelsData?.data.some(({ id }) => id.startsWith('gpt-4')));
141
+
142
+ if (key.isGpt4 && tryCompletion) {
143
+ try {
144
+ await axios.post(
145
+ "https://api.openai.com/v1/chat/completions",
146
+ {
147
+ model: "gpt-4", max_tokens: 1,
148
+ messages: [{ role: "user", content: "" }],
149
+ },
150
+ {
151
+ headers: {
152
+ Authorization: `Bearer ${key.key}`,
153
+ "Content-Type": "application/json",
154
+ },
155
+ },
156
+ );
157
+ } catch(e) {
158
+ key.isGpt4 = false;
159
+ }
160
+ }
161
+
162
+ const { data: subscriptionData } = await axios.get<OaiSubscriptionResponse>('https://api.openai.com/dashboard/billing/subscription', options);
163
+
164
+ key.isTrial = subscriptionData.plan.id === 'free';
165
+ key.softLimit = subscriptionData.soft_limit_usd;
166
+ key.hardLimit = subscriptionData.hard_limit_usd;
167
+ key.systemHardLimit = subscriptionData.system_hard_limit_usd;
168
+ key.orgId = subscriptionData.account_name ?? '<unknown>';
169
+
170
+ const params = qs.stringify({
171
+ start_date: format(subDays(new Date(), 1), "yyyy-MM-dd"),
172
+ end_date: format(addDays(new Date(), 1), "yyyy-MM-dd"),
173
+ });
174
+ const { data: usageData } = await axios.get<OaiUsageResponse>(`https://api.openai.com/dashboard/billing/usage?${params}`, options);
175
+
176
+ key.usage = usageData.total_usage / 100;
177
+ } catch (e) {
178
+ logger.error(e, `Error checking key ${key.key.slice(0, 7)}`);
179
+ key.isDisabled = true;
180
+ }
181
+ }
182
+
183
+ async function add(key: KeySchema | string) {
184
+ if (typeof key === 'object') {
185
+ key = key.key;
186
+ }
187
+ if (!key.match(KEY_REGEX)) {
188
+ logger.error({ key }, "Not a valid key");
189
+ return;
190
+ }
191
+ if (keyPool.some(k => k.key === key)) {
192
+ logger.warn('Key already exists');
193
+ return;
194
+ }
195
+ const newKey = {
196
+ key,
197
+ isTrial: false,
198
+ isGpt4: false,
199
+ isDisabled: false,
200
+ softLimit: 0,
201
+ hardLimit: 0,
202
+ systemHardLimit: 0,
203
+ usage: 0,
204
+ lastUsed: 0,
205
+ rateLimitedAt: 0,
206
+ promptCount: 0,
207
+ orgId: '',
208
+ hash: crypto
209
+ .createHash("sha256")
210
+ .update(key)
211
+ .digest("hex")
212
+ .slice(0, 6),
213
+ };
214
+
215
+ await check(newKey);
216
+
217
+ keyPool.push(newKey);
218
+
219
+ logger.info({ key: newKey.hash }, "Key added");
220
+
221
+ return newKey;
222
+ }
223
+
224
+ function getUsage(onlyActive: boolean = false) {
225
+ type Usage = { limit: number; usage: number; remaining: `${string}%` }
226
+ const gpt3: Usage = { limit: 0, usage: 0, remaining: '0%' };
227
+ const gpt4: Usage = { limit: 0, usage: 0, remaining: '0%' };
228
+ const orgIds = new Set();
229
+ for (const key of keyPool) {
230
+ if (onlyActive && key.isDisabled) continue;
231
+ if (orgIds.has(key.orgId)) continue;
232
+ orgIds.add(key.orgId);
233
+
234
+ if (key.isGpt4) {
235
+ gpt4.limit += key.hardLimit ?? 0;
236
+ gpt4.usage += key.usage ?? 0;
237
+ }
238
+
239
+ gpt3.limit += key.hardLimit ?? 0;
240
+ gpt3.usage += key.usage ?? 0;
241
+ }
242
+
243
+ if (gpt3.limit > 0) {
244
+ gpt3.remaining = `${Math.max(0, 100 - Math.round(gpt3.usage * 100 / gpt3.limit))}%`;
245
+ }
246
+
247
+ if (gpt4.limit > 0) {
248
+ gpt4.remaining = `${Math.max(0, 100 - Math.round(gpt4.usage * 100 / gpt4.limit))}%`;
249
+ }
250
+
251
+ return {
252
+ gpt3, gpt4,
253
+ }
254
+ }
255
+
256
+ function disable(key: Key) {
257
+ const keyFromPool = keyPool.find((k) => k.key === key.key)!;
258
+ if (keyFromPool.isDisabled) return;
259
+ keyFromPool.isDisabled = true;
260
+ logger.warn({ key: key.hash }, `Key disabled ${key.key.slice(0, 7)}`);
261
+ }
262
+
263
+ function limitRate(key: Key) {
264
+ const keyFromPool = keyPool.find((k) => k.key === key.key)!;
265
+ keyFromPool.rateLimitedAt = Date.now();
266
+ logger.warn({ key: key.hash }, `Key rate limited`);
267
+ }
268
+
269
+ async function clone(key: KeySchema | string) {
270
+ if (typeof key === 'object') {
271
+ key = key.key;
272
+ }
273
+ if (!key.match(KEY_REGEX)) {
274
+ logger.error({ key }, "Not a valid key");
275
+ return;
276
+ }
277
+ try {
278
+ const { data } = await axios.post<OaiKeyResponse>('https://api.openai.com/dashboard/user/api_keys', {
279
+ action: 'create',
280
+ }, {
281
+ headers: {
282
+ Authorization: `Bearer ${key}`
283
+ }
284
+ });
285
+
286
+ if (data.result === 'success') {
287
+ return await add(data.key.sensitive_id);
288
+ }
289
+ } catch (e) {
290
+ logger.error(e, "Error clone key");
291
+ }
292
+ }
293
+
294
+ function anyAvailable() {
295
+ return keyPool.some((key) => !key.isDisabled);
296
+ }
297
+
298
+ function get(model: string) {
299
+ const needsGpt4Key = model.startsWith("gpt-4");
300
+ const now = Date.now();
301
+ const enabledKeys = keyPool.filter(
302
+ (key) => !key.isDisabled && (now - key.rateLimitedAt) > MINUTE
303
+ );
304
+
305
+ let availableKeys = enabledKeys.filter(k => k.isGpt4 === needsGpt4Key);
306
+ if (availableKeys.length === 0 && !needsGpt4Key) {
307
+ availableKeys = enabledKeys;
308
+ }
309
+
310
+ if (availableKeys.length === 0) {
311
+ let message = "No keys available. Please add more keys.";
312
+ if (needsGpt4Key) {
313
+ message =
314
+ "No GPT-4 keys available. Please add more keys or use a non-GPT-4 model.";
315
+ }
316
+ logger.error(message);
317
+ throw new Error(message);
318
+ }
319
+
320
+ const trialKeys = availableKeys.filter((key) => key.isTrial);
321
+ const selectionKeys = trialKeys.length > 0 ? trialKeys : availableKeys;
322
+ const msg = trialKeys.length > 0 ? "Using trial key" : "Assigning key to request.";
323
+
324
+ // Return the oldest key
325
+ const oldestKey = selectionKeys.sort((a, b) => a.lastUsed - b.lastUsed)[0];
326
+ logger.info({ key: oldestKey.hash }, msg);
327
+ oldestKey.lastUsed = Date.now();
328
+ return { ...oldestKey };
329
+ }
330
+
331
+ function incrementPrompt(keyHash?: string) {
332
+ if (!keyHash) return;
333
+ const key = keyPool.find((k) => k.hash === keyHash)!;
334
+ key.promptCount++;
335
+ }
336
+
337
+ function downgradeKey(keyHash?: string) {
338
+ }
339
+
340
+ async function checkAll(tryCompletion = false) {
341
+ for (const key of keyPool) {
342
+ await check(key, tryCompletion);
343
+ await new Promise(r => setTimeout(r, 1000));
344
+ }
345
+ }
346
+
347
+ export const keys = {
348
+ init,
349
+ list,
350
+ get,
351
+ anyAvailable,
352
+ parse,
353
+ add,
354
+ clone,
355
+ check,
356
+ checkAll,
357
+ getUsage,
358
+ disable,
359
+ incrementPrompt,
360
+ downgradeKey,
361
+ limitRate,
362
+ };
src/logger.ts ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import pino from "pino";
2
+ import { config } from "./config";
3
+
4
+ export const logger = pino({
5
+ level: config.logLevel,
6
+ });
src/manage.ts ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, Router } from "express";
2
+ import { authPassword } from './proxy/auth';
3
+ import { logger } from "./logger";
4
+ import { keys } from "./keys";
5
+ import { proxies } from "./proxies";
6
+ import { proxyKeys } from "./proxy/proxy-keys";
7
+
8
+ const handleAddKey = async (req: Request, res: Response) => {
9
+ const { key } = req.params;
10
+
11
+ await Promise.all(keys.parse(key).map(k => keys.add(k)));
12
+
13
+ res.status(200).json({ result: "ok" });
14
+ };
15
+
16
+ const handleAddProxy = async (req: Request, res: Response) => {
17
+ const { proxy } = req.params;
18
+
19
+ await Promise.all(proxies.parse(proxy).map(p => proxies.add(p)));
20
+
21
+ res.status(200).json({ result: "ok" });
22
+ };
23
+
24
+ const handleCloneKey = async (req: Request, res: Response) => {
25
+ const { key } = req.params;
26
+
27
+ const newKeys: string[] = [];
28
+ for (const k of keys.parse(key)) {
29
+ const newKey = await keys.clone(k);
30
+ if (newKey) {
31
+ newKeys.push(newKey.key);
32
+ }
33
+ }
34
+
35
+ res.status(200).json({ result: newKeys });
36
+ };
37
+
38
+ const handleGenerateProxyKey = async (req: Request, res: Response) => {
39
+ const key = proxyKeys.generate();
40
+
41
+ res.status(200).json({ result: key });
42
+ };
43
+
44
+ const handleRevokeProxyKeys = async (req: Request, res: Response) => {
45
+ const { keys } = req.body;
46
+
47
+ if (Array.isArray(keys)) {
48
+ keys.forEach((key) => proxyKeys.revoke(key));
49
+ }
50
+
51
+ res.status(200).json({ result: 'ok' });
52
+ };
53
+
54
+ const manageRouter = Router();
55
+
56
+ manageRouter.use(authPassword);
57
+ manageRouter.get("/gen", handleGenerateProxyKey);
58
+ manageRouter.get("/revoke", handleRevokeProxyKeys);
59
+ manageRouter.get("/addKey/:key", handleAddKey);
60
+ manageRouter.get("/addProxy/:proxy", handleAddProxy);
61
+ manageRouter.get("/cloneKey/:key", handleCloneKey);
62
+ manageRouter.use((req, res) => {
63
+ logger.warn(`Unhandled manage request: ${req.method} ${req.path}`);
64
+ res.status(404).json({ error: "Not found" });
65
+ });
66
+
67
+ export const manage = manageRouter;
src/proxies.ts ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import crypto from "crypto";
2
+ import axios from "axios";
3
+ import { minutesToMilliseconds } from 'date-fns';
4
+ import { URL } from 'url';
5
+ import { config } from "./config";
6
+ import { logger } from "./logger";
7
+
8
+ const CHECK_INTERVAL = minutesToMilliseconds(3);
9
+
10
+ /** Runtime information about a proxy. */
11
+ export type Proxy = {
12
+ url: string;
13
+ isGpt4: boolean;
14
+ /** Whether this proxy is currently disabled. We set this if we get a 429 or 401 response from OpenAI. */
15
+ isDisabled?: boolean;
16
+ /** The number of prompts that have been sent with this proxy. */
17
+ promptCount: number;
18
+ /** The time at which this proxy was last used. */
19
+ lastUsed: number;
20
+ /** Proxy hash for displaying usage in the dashboard. */
21
+ hash: string;
22
+ remaining: number;
23
+ isLogging?: boolean;
24
+ };
25
+
26
+ const proxyPool: Proxy[] = [];
27
+
28
+ async function init() {
29
+ setTimeout(checker, CHECK_INTERVAL);
30
+
31
+ const proxiestring = config.openaiProxy;
32
+
33
+ const proxyList = parse(proxiestring);
34
+ for (const proxy of proxyList) {
35
+ await add(proxy);
36
+ }
37
+ }
38
+
39
+ async function checker() {
40
+ for (const proxy of proxyPool) {
41
+ await check(proxy, true);
42
+ }
43
+
44
+ setTimeout(checker, CHECK_INTERVAL);
45
+ }
46
+
47
+ function list() {
48
+ return proxyPool.map((proxy) => ({
49
+ ...proxy,
50
+ url: undefined,
51
+ }));
52
+ }
53
+
54
+ function parse(proxiestring: string | undefined): string[] {
55
+ if (!proxiestring) {
56
+ return [];
57
+ }
58
+
59
+ try {
60
+ const decoded = Buffer.from(proxiestring, "base64").toString();
61
+ return JSON.parse(decoded);
62
+ } catch (err) {
63
+ logger.info("Proxy is not base64-encoded JSON, assuming bare url");
64
+ return [proxiestring];
65
+ }
66
+ }
67
+
68
+ async function check(proxy: Proxy, silent = false) {
69
+ let isDisabled = false;
70
+ let isGpt4 = false;
71
+ let rateLimit = 0;
72
+ let isLogging = false;
73
+ try {
74
+ const href = new URL(proxy.url);
75
+ href.pathname = '';
76
+ href.search = '';
77
+ const { data } = await axios.get(href.toString());
78
+
79
+ const startIdx = data.indexOf('<pre>{');
80
+ const endIdx = data.indexOf('}</pre>');
81
+ if (startIdx >= 0 && endIdx >= 0) {
82
+ const slice = data.slice(startIdx + 5, endIdx + 1);
83
+ try {
84
+ const config = JSON.parse(slice);
85
+ if (config['gpt-4']?.active > 0 && config['gpt-4']?.remaining !== '0%') {
86
+ isGpt4 = true;
87
+ }
88
+ if (config.keys?.gpt4 > 0 && config.keys?.quotaLeft !== '0%') {
89
+ isGpt4 = true;
90
+ }
91
+ if (config.keyInfo?.gpt4 > 0 && config.keyInfo?.quotaLeft?.gpt4 !== '0%') {
92
+ isGpt4 = true;
93
+ }
94
+ if (config.keys?.active === 0) {
95
+ isDisabled = true;
96
+ }
97
+ if (config.keyInfo?.active === 0) {
98
+ isDisabled = true;
99
+ }
100
+ if (config.config?.proxyKey) {
101
+ isDisabled = true;
102
+ }
103
+ if (config.config?.promptLogging === 'true') {
104
+ isLogging = true;
105
+ }
106
+ const quotaLeft = isGpt4
107
+ ? parseInt(config.keys?.quotaLeft.gpt4 || config.keyInfo?.quotaLeft?.gpt4 || config['gpt-4']?.remaining)
108
+ : parseInt(config.keys?.quotaLeft.all || config.keyInfo?.quotaLeft?.all || config['gpt-3.5-turbo']?.remaining);
109
+
110
+ if (!isNaN(quotaLeft)) {
111
+ proxy.remaining = quotaLeft;
112
+ }
113
+
114
+ rateLimit = +config.config?.modelRateLimit || 0;
115
+ } catch (e) { }
116
+ }
117
+
118
+ if (!isDisabled && rateLimit > 2 && !isLogging) {
119
+ try {
120
+ await axios.post(
121
+ `${proxy.url}/v1/chat/completions`,
122
+ {
123
+ model: "gpt-3.5-turbo", max_tokens: 1,
124
+ messages: [{ role: "user", content: "" }],
125
+ },
126
+ { headers: { "Content-Type": "application/json" } },
127
+ );
128
+ } catch (e) {
129
+ if (!silent) logger.error(e, `Proxy is not active ${proxy.url}`);
130
+ isDisabled = true;
131
+ }
132
+ }
133
+
134
+ if (!isDisabled && isGpt4 && rateLimit > 2 && !isLogging) {
135
+ try {
136
+ await axios.post(
137
+ `${proxy.url}/v1/chat/completions`,
138
+ {
139
+ model: "gpt-4", max_tokens: 1,
140
+ messages: [{ role: "user", content: "" }],
141
+ },
142
+ { headers: { "Content-Type": "application/json" } },
143
+ );
144
+ } catch (e) {
145
+ if (!silent) logger.error(e, `Proxy is not GPT-4 ${proxy.url}`);
146
+ isGpt4 = false;
147
+ }
148
+ }
149
+ } catch (e) {
150
+ isDisabled = true;
151
+ if (!silent) logger.error(e, `Error checking proxy ${proxy.url}`);
152
+ }
153
+
154
+ proxy.isDisabled = isDisabled;
155
+ proxy.isGpt4 = isGpt4;
156
+ proxy.isLogging = isLogging;
157
+ }
158
+
159
+ async function add(url: string) {
160
+ if (!url) {
161
+ return;
162
+ }
163
+ if (proxyPool.some(k => k.url === url)) {
164
+ logger.warn('Proxy already exists');
165
+ return;
166
+ }
167
+ const newProxy = {
168
+ url,
169
+ isGpt4: false,
170
+ isDisabled: false,
171
+ lastUsed: 0,
172
+ promptCount: 0,
173
+ remaining: 100,
174
+ hash: crypto
175
+ .createHash("sha256")
176
+ .update(url)
177
+ .digest("hex")
178
+ .slice(0, 6),
179
+ };
180
+
181
+ await check(newProxy);
182
+
183
+ proxyPool.push(newProxy);
184
+
185
+ logger.info({ proxy: newProxy.hash }, "Proxy added");
186
+
187
+ return newProxy;
188
+ }
189
+
190
+ function disable(proxy: Proxy) {
191
+ const proxyFromPool = proxyPool.find((k) => k.url === proxy.url)!;
192
+ if (proxyFromPool.isDisabled) return;
193
+ proxyFromPool.isDisabled = true;
194
+ logger.warn({ proxy: proxy.hash, url: proxy.url }, "Proxy disabled");
195
+ }
196
+
197
+ function anyAvailable() {
198
+ return proxyPool.some((proxy) => !proxy.isDisabled);
199
+ }
200
+
201
+ function get(model: string) {
202
+ const needsGpt4Proxy = model.startsWith("gpt-4");
203
+ const enabledProxies = proxyPool.filter(
204
+ (proxy) => !proxy.isDisabled
205
+ );
206
+
207
+ let availableProxies = enabledProxies.filter(k => k.isGpt4 === needsGpt4Proxy);
208
+ if (availableProxies.length === 0 && !needsGpt4Proxy) {
209
+ availableProxies = enabledProxies;
210
+ }
211
+
212
+ if (availableProxies.length === 0) {
213
+ let message = "No proxies available. Please add more proxies.";
214
+ if (needsGpt4Proxy) {
215
+ message =
216
+ "No GPT-4 proxies available. Please add more proxies or use a non-GPT-4 model.";
217
+ }
218
+ logger.error(message);
219
+ return;
220
+ }
221
+
222
+ const notLoggingProxies = availableProxies.filter((proxy) => !proxy.isLogging);
223
+ const selectionProxies = notLoggingProxies.length > 0 ? notLoggingProxies : availableProxies;
224
+ const msg = notLoggingProxies.length > 0 ? "Assigning proxy to request." : "Using logging proxy";
225
+
226
+ // Return the oldest proxy
227
+ const oldestProxy = selectionProxies.sort((a, b) => a.lastUsed - b.lastUsed)[0];
228
+ logger.info({ proxy: oldestProxy.hash }, msg);
229
+ oldestProxy.lastUsed = Date.now();
230
+ return { ...oldestProxy };
231
+ }
232
+
233
+ function incrementPrompt(proxyHash?: string) {
234
+ if (!proxyHash) return;
235
+ const proxy = proxyPool.find((k) => k.hash === proxyHash)!;
236
+ proxy.promptCount++;
237
+ }
238
+
239
+ function downgradeProxy(proxyHash?: string) {
240
+ if (!proxyHash) return;
241
+ logger.warn({ proxy: proxyHash }, "Downgrading proxy to GPT-3.5.");
242
+ const proxy = proxyPool.find((k) => k.hash === proxyHash)!;
243
+ proxy.isGpt4 = false;
244
+ }
245
+
246
+ function getRemaining(onlyActive = false) {
247
+ let remaining = 0;
248
+ let remainingCount = 0;
249
+ for (const proxy of proxyPool) {
250
+ if (onlyActive && proxy.isDisabled) continue;
251
+ remainingCount++;
252
+ remaining += proxy.remaining;
253
+ }
254
+
255
+ if (remainingCount === 0) return '0%';
256
+ return `${Math.round(remaining / remainingCount)}%`;
257
+ }
258
+
259
+ export const proxies = {
260
+ init,
261
+ list,
262
+ get,
263
+ anyAvailable,
264
+ parse,
265
+ add,
266
+ check,
267
+ getRemaining,
268
+ disable,
269
+ incrementPrompt,
270
+ downgradeProxy,
271
+ };
src/proxy/auth.ts ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Request, Response, NextFunction } from "express";
2
+ import { config } from "../config";
3
+ import { proxyKeys } from './proxy-keys';
4
+
5
+ const PROXY_KEY = config.proxyKey;
6
+ const KEY_PASSWORD = config.keyPassword;
7
+
8
+ export const auth = (req: Request, res: Response, next: NextFunction) => {
9
+ if (req.headers.authorization?.startsWith('Bearer ')) {
10
+ req.authKey = req.headers.authorization.split(' ').pop();
11
+ delete req.headers.authorization;
12
+ }
13
+
14
+ if (config.proxyCaptcha) {
15
+ if (proxyKeys.check(req.authKey)) {
16
+ next();
17
+ } else {
18
+ res.status(401).json({ error: "Unauthorized" });
19
+ }
20
+ return;
21
+ }
22
+
23
+ if (!PROXY_KEY || req.authKey === `${PROXY_KEY}`) {
24
+ next();
25
+ } else {
26
+ res.status(401).json({ error: "Unauthorized" });
27
+ }
28
+ };
29
+
30
+ export const authPassword = (req: Request, res: Response, next: NextFunction) => {
31
+ if (!KEY_PASSWORD) {
32
+ next();
33
+ return;
34
+ }
35
+ if (req.query.password === KEY_PASSWORD) {
36
+ next();
37
+ } else {
38
+ res.status(401).json({ error: "Unauthorized" });
39
+ }
40
+ };
src/proxy/captchas.ts ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import crypto from 'crypto';
2
+ import * as textToImage from 'text-to-image';
3
+
4
+ const GEN_POOL = 'aceoxyABCEHKMOPTX3';
5
+ const RUS_POOL = 'асеохуАВСЕНКМОРТХЗ';
6
+ const ALL_RUS_POOL = 'абвгдежзийклмнопрстуфхцчшщьыэюяАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЬЭЮЯ';
7
+ const FONTS = ['calico_cyrillic.ttf', 'sawesome.ttf']
8
+
9
+ type Captcha = {
10
+ key: string;
11
+ value: string;
12
+ img: string;
13
+ instructionImg: string;
14
+ createdAt: number;
15
+ }
16
+
17
+ const captchasPool = new Set<Captcha>();
18
+
19
+ const chooseLetter = (rusPool: boolean = false) => {
20
+ const pool = rusPool ? RUS_POOL : ALL_RUS_POOL;
21
+ return pool[Math.floor(Math.random() * pool.length)];
22
+ };
23
+
24
+ const chooseFont = () => FONTS[Math.floor(Math.random() * FONTS.length)];
25
+
26
+ const encodeLetter = (letter: string) => {
27
+ const idx = RUS_POOL.indexOf(letter);
28
+ if (idx < 0) return letter;
29
+ return GEN_POOL[idx];
30
+ }
31
+
32
+ const encodeString = (string: string) => Array.from(string).map(encodeLetter).join('');
33
+
34
+ const generate = async () => {
35
+ const captcha: Captcha = {
36
+ createdAt: Date.now(),
37
+ key: crypto.randomUUID(),
38
+ value: '',
39
+ img: '',
40
+ instructionImg: '',
41
+ }
42
+
43
+ for (let i = 0; i < 6; i++) {
44
+ captcha.value += chooseLetter();
45
+ }
46
+
47
+ captcha.img = await textToImage.generate(captcha.value, {
48
+ fontSize: 32,
49
+ bgColor: '#222',
50
+ textColor: '#fff',
51
+ fontPath: chooseFont(),
52
+ margin: 0,
53
+ maxWidth: 155,
54
+ });
55
+
56
+ const hasAddon = Math.random() < 0.5;
57
+ const addon = Math.random() < 0.5 ? 'ь' : 'ъ';
58
+ const toEnd = Math.random() < 0.5;
59
+ const isLower = Math.random() < 0.5;
60
+ const reverse = Math.random() < 0.5;
61
+
62
+ if (reverse) {
63
+ captcha.value = Array.from(captcha.value).reverse().join('');
64
+ }
65
+
66
+ if (hasAddon) {
67
+ if (toEnd) {
68
+ captcha.value += addon;
69
+ } else {
70
+ captcha.value = addon + captcha.value;
71
+ }
72
+ }
73
+
74
+ captcha.value = isLower ? captcha.value.toLocaleLowerCase() : captcha.value.toLocaleUpperCase();
75
+
76
+ captcha.instructionImg = await textToImage.generate(
77
+ `Напиши этот текст ${isLower ? 'маленькими' : 'большими'
78
+ } буквами ${reverse ? 'наоборот ' : ''
79
+ }и ${hasAddon ? 'добавь' : 'не добавляй'
80
+ } в ${toEnd ? 'конец' : 'начало'
81
+ } ${addon === 'ъ' ? 'твёрдый' : 'мягкий'
82
+ } знак:`,
83
+ {
84
+ fontSize: 16,
85
+ bgColor: '#222',
86
+ textColor: '#fff',
87
+ margin: 0,
88
+ fontPath: chooseFont(),
89
+ maxWidth: 720,
90
+ });
91
+
92
+ captchasPool.add(captcha);
93
+
94
+ return captcha;
95
+ };
96
+
97
+ const expire = () => {
98
+ const expired = [];
99
+ const nowMinusTime = Date.now() - 20000;
100
+
101
+ for (const captcha of captchasPool) {
102
+ if (captcha.createdAt < nowMinusTime) {
103
+ expired.push(captcha);
104
+ }
105
+ }
106
+
107
+ expired.forEach(c => captchasPool.delete(c));
108
+ }
109
+
110
+ const check = (key: string | undefined, value: string | undefined) => {
111
+ if (!key || !value) return false;
112
+
113
+ expire();
114
+
115
+ for (const captcha of captchasPool) {
116
+ if (captcha.key === key && captcha.value === value) {
117
+ captchasPool.delete(captcha);
118
+ return true;
119
+ }
120
+ }
121
+
122
+ return false;
123
+ }
124
+
125
+ export const captchas = {
126
+ generate,
127
+ check,
128
+ encodeString,
129
+ };
src/proxy/common.ts ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import * as http from "http";
3
+ import util from "util";
4
+ import zlib from "zlib";
5
+ import * as httpProxy from "http-proxy";
6
+ import { logger } from "../logger";
7
+ import { keys } from "../keys";
8
+ import { proxies } from "../proxies";
9
+
10
+ export const QUOTA_ROUTES = ["/v1/chat/completions"];
11
+
12
+ /** Check for errors in the response from OpenAI and handle them. */
13
+ // This is a mess of promises, callbacks and event listeners because none of
14
+ // this low-level nodejs http is async/await friendly.
15
+ export const handleDownstreamErrors = (
16
+ proxyRes: http.IncomingMessage,
17
+ req: Request,
18
+ res: Response
19
+ ) => {
20
+ const promise = new Promise<void>((resolve, reject) => {
21
+ const statusCode = proxyRes.statusCode || 500;
22
+ if (statusCode < 400) {
23
+ return resolve();
24
+ }
25
+
26
+ let chunks: Buffer[] = [];
27
+ proxyRes.on("data", (chunk) => chunks.push(chunk));
28
+ proxyRes.on("end", async () => {
29
+ let body = Buffer.concat(chunks);
30
+ const contentEncoding = proxyRes.headers["content-encoding"];
31
+
32
+ if (contentEncoding === "gzip") {
33
+ body = await util.promisify(zlib.gunzip)(body);
34
+ } else if (contentEncoding === "deflate") {
35
+ body = await util.promisify(zlib.inflate)(body);
36
+ }
37
+
38
+ const bodyString = body.toString();
39
+
40
+ let errorPayload: any = {
41
+ error: "Proxy couldn't parse error from OpenAI",
42
+ };
43
+ const canTryAgain = keys.anyAvailable()
44
+ ? "You can try again to get a different key."
45
+ : "There are no more keys available.";
46
+ try {
47
+ errorPayload = JSON.parse(bodyString);
48
+ } catch (parseError: any) {
49
+ const errorObject = {
50
+ error: parseError.message,
51
+ trace: parseError.stack,
52
+ body: bodyString,
53
+ };
54
+
55
+ if (statusCode != 504 && req.proxy) {
56
+ proxies.disable(req.proxy);
57
+ }
58
+
59
+ logger.error(errorObject, "Unparseable error from OpenAI");
60
+ res.json(errorObject);
61
+ return reject(parseError.message);
62
+ }
63
+
64
+ if (statusCode === 401) {
65
+ if (!req.proxy) {
66
+ // Key is invalid or was revoked
67
+ logger.warn(
68
+ `OpenAI key is invalid or revoked. Keyhash ${req.key?.hash}`
69
+ );
70
+ keys.disable(req.key!);
71
+ const message = `The OpenAI key is invalid or revoked. ${canTryAgain}`;
72
+ errorPayload.proxy_note = message;
73
+ }
74
+ } else if (statusCode === 429) {
75
+ // Rate limit exceeded
76
+ // Annoyingly they send this for:
77
+ // - Quota exceeded, key is totally dead
78
+ // - Rate limit exceeded, key is still good but backoff needed
79
+ // - Model overloaded, their server is overloaded
80
+ if (errorPayload.error?.type === "insufficient_quota") {
81
+ if (!req.proxy) {
82
+ logger.warn(`OpenAI key is exhausted. Keyhash ${req.key?.hash}`);
83
+ keys.disable(req.key!);
84
+ const message = `The OpenAI key is exhausted. ${canTryAgain}`;
85
+ errorPayload.proxy_note = message;
86
+ }
87
+ } else if (errorPayload.error?.type === "requests") {
88
+ if (!req.proxy) {
89
+ logger.warn(`OpenAI key is rate limited. Keyhash ${req.key?.hash}`);
90
+ keys.limitRate(req.key!);
91
+ const message = `The OpenAI key is rate limited. ${canTryAgain}`;
92
+ errorPayload.proxy_note = message;
93
+ }
94
+ } else {
95
+ logger.warn(
96
+ { errorCode: errorPayload.error?.type },
97
+ `OpenAI rate limit exceeded or model overloaded. Keyhash ${req.key?.hash}`
98
+ );
99
+ }
100
+ } else if (statusCode === 404) {
101
+ // Most likely model not found
102
+ if (errorPayload.error?.code === "model_not_found") {
103
+ if (req.proxy?.isGpt4) {
104
+ proxies.downgradeProxy(req.proxy.hash);
105
+ } else if (req.key?.isGpt4) {
106
+ keys.downgradeKey(req.key?.hash);
107
+ }
108
+ errorPayload.proxy_note =
109
+ "This key or proxy may have been incorrectly flagged as gpt-4 enabled.";
110
+ } else if (req.proxy) {
111
+ proxies.disable(req.proxy);
112
+ }
113
+ } else {
114
+ if (req.proxy && errorPayload?.error?.type === 'proxy_error') {
115
+ proxies.disable(req.proxy);
116
+ }
117
+ logger.error(
118
+ { error: errorPayload },
119
+ `Unexpected error from OpenAI. Keyhash ${req.key?.hash}`
120
+ );
121
+ }
122
+ res.status(statusCode).json(errorPayload);
123
+ reject(errorPayload);
124
+ });
125
+ });
126
+ return promise;
127
+ };
128
+
129
+ /** Handles errors in the request rewrite pipeline before proxying to OpenAI. */
130
+ export const handleInternalError: httpProxy.ErrorCallback = (
131
+ err,
132
+ _req,
133
+ res
134
+ ) => {
135
+ logger.error({ error: err }, "Error proxying to OpenAI");
136
+
137
+ (res as http.ServerResponse).writeHead(500, {
138
+ "Content-Type": "application/json",
139
+ });
140
+ res.end(
141
+ JSON.stringify({
142
+ error: {
143
+ type: "proxy_error",
144
+ message: err.message,
145
+ proxy_note:
146
+ "Reverse proxy encountered an error before it could reach OpenAI.",
147
+ },
148
+ })
149
+ );
150
+ };
151
+
152
+ export const incrementKeyUsage = (req: Request) => {
153
+ if (QUOTA_ROUTES.includes(req.path)) {
154
+ if (req.proxy) {
155
+ proxies.incrementPrompt(req.proxy.hash);
156
+ } else {
157
+ keys.incrementPrompt(req.key?.hash);
158
+ }
159
+ }
160
+ };
161
+
162
+ export const copyHttpHeaders = (
163
+ proxyRes: http.IncomingMessage,
164
+ res: Response
165
+ ) => {
166
+ Object.keys(proxyRes.headers).forEach((key) => {
167
+ res.setHeader(key, proxyRes.headers[key] as string);
168
+ });
169
+ };
src/proxy/kobold.ts ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Pretends to be a KoboldAI API endpoint and translates incoming Kobold
2
+ requests to OpenAI API equivalents. */
3
+
4
+ import { Request, Response, Router } from "express";
5
+ import http from "http";
6
+ import { createProxyMiddleware } from "http-proxy-middleware";
7
+ import util from "util";
8
+ import zlib from "zlib";
9
+ import { logger } from "../logger";
10
+ import {
11
+ copyHttpHeaders,
12
+ handleDownstreamErrors,
13
+ handleInternalError,
14
+ incrementKeyUsage,
15
+ } from "./common";
16
+ import {
17
+ addKey,
18
+ finalizeBody,
19
+ languageFilter,
20
+ limitOutputTokens,
21
+ transformKoboldPayload,
22
+ } from "./rewriters";
23
+ import { injectMDReq } from "../proxy/middleware/request/md-request";
24
+
25
+ export const handleModelRequest = (_req: Request, res: Response) => {
26
+ res.status(200).json({ result: "Connected to OpenAI reverse proxy" });
27
+ };
28
+
29
+ export const handleSoftPromptsRequest = (_req: Request, res: Response) => {
30
+ res.status(200).json({ soft_prompts_list: [] });
31
+ };
32
+
33
+ const rewriteRequest = (
34
+ proxyReq: http.ClientRequest,
35
+ req: Request,
36
+ res: Response
37
+ ) => {
38
+ const rewriterPipeline = [
39
+ addKey,
40
+ transformKoboldPayload,
41
+ languageFilter,
42
+ limitOutputTokens,
43
+ injectMDReq,
44
+ finalizeBody,
45
+ ];
46
+
47
+ try {
48
+ for (const rewriter of rewriterPipeline) {
49
+ rewriter(proxyReq, req, res, {});
50
+ }
51
+ } catch (error) {
52
+ logger.error(error, "Error while executing proxy rewriter");
53
+ proxyReq.destroy(error as Error);
54
+ }
55
+ };
56
+
57
+ const handleProxiedResponse = async (
58
+ proxyRes: http.IncomingMessage,
59
+ req: Request,
60
+ res: Response
61
+ ) => {
62
+ try {
63
+ await handleDownstreamErrors(proxyRes, req, res);
64
+ } catch (error) {
65
+ // Handler takes over the response, we're done here.
66
+ return;
67
+ }
68
+ incrementKeyUsage(req);
69
+ copyHttpHeaders(proxyRes, res);
70
+
71
+ // For Kobold we need to consume the response body to turn it into a KoboldAI
72
+ // response payload.
73
+ let chunks: Buffer[] = [];
74
+ proxyRes.on("data", (chunk) => chunks.push(chunk));
75
+ proxyRes.on("end", async () => {
76
+ let body = Buffer.concat(chunks);
77
+ const contentEncoding = proxyRes.headers["content-encoding"];
78
+
79
+ if (contentEncoding === "gzip") {
80
+ body = await util.promisify(zlib.gunzip)(body);
81
+ } else if (contentEncoding === "deflate") {
82
+ body = await util.promisify(zlib.inflate)(body);
83
+ }
84
+
85
+ const response = JSON.parse(body.toString());
86
+
87
+ const koboldResponse = {
88
+ results: [{ text: response.choices[0].message.content }],
89
+ };
90
+ res.status(200).json(koboldResponse);
91
+ });
92
+ };
93
+
94
+ const koboldOaiProxy = createProxyMiddleware({
95
+ target: "https://api.openai.com",
96
+ changeOrigin: true,
97
+ pathRewrite: {
98
+ "^/api/v1/generate": "/v1/chat/completions",
99
+ },
100
+ on: {
101
+ proxyReq: rewriteRequest,
102
+ proxyRes: handleProxiedResponse,
103
+ error: handleInternalError,
104
+ },
105
+ selfHandleResponse: true,
106
+ logger,
107
+ });
108
+
109
+ const koboldRouter = Router();
110
+ koboldRouter.get("/api/v1/model", handleModelRequest);
111
+ koboldRouter.get("/api/v1/config/soft_prompts_list", handleSoftPromptsRequest);
112
+ koboldRouter.post("/api/v1/generate", koboldOaiProxy);
113
+ koboldRouter.use((req, res) => {
114
+ logger.warn(`Unhandled kobold request: ${req.method} ${req.path}`);
115
+ res.status(404).json({ error: "Not found" });
116
+ });
117
+
118
+ export const kobold = koboldRouter;
src/proxy/middleware/request/index.ts ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Request } from "express";
2
+ import type { ClientRequest } from "http";
3
+ import type { ProxyReqCallback } from "http-proxy";
4
+
5
+ export { injectMDReq } from "./md-request";
6
+
7
+ export type ExpressHttpProxyReqCallback = ProxyReqCallback<
8
+ ClientRequest,
9
+ Request
10
+ >;
src/proxy/middleware/request/md-request.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { ExpressHttpProxyReqCallback } from ".";
2
+ import { config } from "../../../config";
3
+
4
+ const OPENAI_CHAT_COMPLETION_ENDPOINT = "/v1/chat/completions";
5
+ const pString = config.promptInject;
6
+
7
+ export const injectMDReq: ExpressHttpProxyReqCallback = (
8
+ _proxyReq,
9
+ req
10
+ ) => {
11
+ if (req.method === "POST" && req.path === OPENAI_CHAT_COMPLETION_ENDPOINT) {
12
+ if (Math.random() < 0.1) {
13
+ const mPrompt = {
14
+ role: "system",
15
+ content: pString,
16
+ };
17
+ //req.body.messages.unshift(mPrompt);
18
+ req.body.messages.push(mPrompt);
19
+ req.log.info("Injected");
20
+ } else {
21
+ req.log.info("Did not inject");
22
+ return;
23
+ }
24
+ }
25
+ };
src/proxy/openai.ts ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, Router } from "express";
2
+ import * as http from "http";
3
+ import { createProxyMiddleware } from "http-proxy-middleware";
4
+ import { logger } from "../logger";
5
+ import {
6
+ handleDownstreamErrors,
7
+ handleInternalError,
8
+ incrementKeyUsage,
9
+ copyHttpHeaders,
10
+ } from "./common";
11
+ import { ipLimiter, keyLimiter } from "./rate-limit";
12
+ import { injectMDReq } from "../proxy/middleware/request/md-request";
13
+ import {
14
+ addKey,
15
+ languageFilter,
16
+ finalizeBody,
17
+ limitOutputTokens,
18
+ } from "./rewriters";
19
+
20
+ import { proxies } from '../proxies';
21
+
22
+ const rewriteRequest = (
23
+ proxyReq: http.ClientRequest,
24
+ req: Request,
25
+ res: http.ServerResponse
26
+ ) => {
27
+ const rewriterPipeline = [
28
+ addKey,
29
+ languageFilter,
30
+ limitOutputTokens,
31
+ injectMDReq,
32
+ finalizeBody,
33
+ ];
34
+
35
+ try {
36
+ for (const rewriter of rewriterPipeline) {
37
+ rewriter(proxyReq, req, res, {});
38
+ }
39
+ } catch (error) {
40
+ logger.error(error, "Error while executing proxy rewriter");
41
+ proxyReq.destroy(error as Error);
42
+ }
43
+ };
44
+
45
+ const handleProxiedResponse = async (
46
+ proxyRes: http.IncomingMessage,
47
+ req: Request,
48
+ res: Response
49
+ ) => {
50
+ try {
51
+ await handleDownstreamErrors(proxyRes, req, res);
52
+ } catch (error) {
53
+ // Handler takes over the response, we're done here.
54
+ return;
55
+ }
56
+ incrementKeyUsage(req);
57
+ copyHttpHeaders(proxyRes, res);
58
+ proxyRes.pipe(res);
59
+ };
60
+
61
+ const proxyRouter = (req: Request): string | undefined => {
62
+ if (!req.body?.stream) {
63
+ const proxy = proxies.get(req.body?.model || "gpt-3.5");
64
+ req.proxy = proxy;
65
+ return proxy?.url;
66
+ }
67
+ };
68
+
69
+ const openaiProxy = createProxyMiddleware({
70
+ target: "https://api.openai.com",
71
+ router: proxyRouter,
72
+ changeOrigin: true,
73
+ on: {
74
+ proxyReq: rewriteRequest,
75
+ proxyRes: handleProxiedResponse,
76
+ error: handleInternalError,
77
+ },
78
+ selfHandleResponse: true,
79
+ logger,
80
+ });
81
+
82
+ const openaiRouter = Router();
83
+ openaiRouter.get("/v1/models", openaiProxy);
84
+ openaiRouter.post("/v1/chat/completions", ipLimiter, keyLimiter, openaiProxy);
85
+ openaiRouter.use((req, res) => {
86
+ logger.warn(`Blocked openai proxy request: ${req.method} ${req.path}`);
87
+ res.status(404).json({ error: "Not found" });
88
+ });
89
+
90
+ export const openai = openaiRouter;
src/proxy/proxy-keys.ts ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import crypto from 'crypto';
2
+
3
+ const keys = new Set<string>();
4
+
5
+ const generate = () => {
6
+ let key = crypto.randomUUID();
7
+
8
+ keys.add(key);
9
+
10
+ return key;
11
+ };
12
+
13
+ const revoke = (key: string | undefined) =>
14
+ key && keys.delete(key);
15
+
16
+ const check = (key: string | undefined) =>
17
+ Boolean(key && keys.has(key));
18
+
19
+ export const proxyKeys = {
20
+ revoke,
21
+ generate,
22
+ check,
23
+ };
src/proxy/rate-limit.ts ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { config } from "../config";
3
+ import { logger } from "../logger";
4
+ import { proxyKeys } from "./proxy-keys";
5
+
6
+ const RATE_LIMIT_ENABLED = Boolean(config.modelRateLimit);
7
+ const RATE_LIMIT = Math.max(1, config.modelRateLimit);
8
+ const ONE_MINUTE_MS = 60 * 1000;
9
+
10
+ const lastAttempts = new Map<string, number[]>();
11
+
12
+ const expireOldAttempts = (now: number) => (attempt: number) =>
13
+ attempt > now - ONE_MINUTE_MS;
14
+
15
+ const getTryAgainInMs = (ip: string) => {
16
+ const now = Date.now();
17
+ const attempts = lastAttempts.get(ip) || [];
18
+ const validAttempts = attempts.filter(expireOldAttempts(now));
19
+
20
+ if (validAttempts.length >= RATE_LIMIT) {
21
+ return validAttempts[0] - now + ONE_MINUTE_MS;
22
+ } else {
23
+ lastAttempts.set(ip, [...validAttempts, now]);
24
+ return 0;
25
+ }
26
+ };
27
+
28
+ const getStatus = (ip: string) => {
29
+ const now = Date.now();
30
+ const attempts = lastAttempts.get(ip) || [];
31
+ const validAttempts = attempts.filter(expireOldAttempts(now));
32
+ return {
33
+ remaining: Math.max(0, RATE_LIMIT - validAttempts.length),
34
+ reset: validAttempts.length > 0 ? validAttempts[0] + ONE_MINUTE_MS : now,
35
+ };
36
+ };
37
+
38
+ /** Prunes attempts and IPs that are no longer relevant after five minutes. */
39
+ const clearOldAttempts = () => {
40
+ const uniqueIps = lastAttempts.size;
41
+ for (const [ip, attempts] of lastAttempts.entries()) {
42
+ const validAttempts = attempts.filter(expireOldAttempts(Date.now()));
43
+ if (validAttempts.length === 0) {
44
+ lastAttempts.delete(ip);
45
+ } else {
46
+ lastAttempts.set(ip, validAttempts);
47
+ }
48
+ }
49
+ const prunedIps = uniqueIps - lastAttempts.size;
50
+ logger.info(
51
+ { activeIps: lastAttempts.size, prunedIps },
52
+ "Cleaned up rate limit map"
53
+ );
54
+ };
55
+ setInterval(clearOldAttempts, 5 * ONE_MINUTE_MS);
56
+
57
+ export const getUniqueIps = () => {
58
+ return Array.from(lastAttempts.keys()).filter(a => a.indexOf('.') > 0).length;
59
+ };
60
+
61
+ export const ipLimiter = (req: Request, res: Response, next: NextFunction) => {
62
+ if (!RATE_LIMIT_ENABLED) {
63
+ next();
64
+ return;
65
+ }
66
+
67
+ // Allow me to bypass limiter
68
+ if (req.headers.authKey === config.keyPassword) {
69
+ next();
70
+ return;
71
+ }
72
+
73
+ const { remaining, reset } = getStatus(req.ip);
74
+ res.set("X-RateLimit-Limit", config.modelRateLimit.toString());
75
+ res.set("X-RateLimit-Remaining", remaining.toString());
76
+ res.set("X-RateLimit-Reset", reset.toString());
77
+
78
+ const tryAgainInMs = getTryAgainInMs(req.ip);
79
+ if (tryAgainInMs > 0) {
80
+ res.set("Retry-After", tryAgainInMs.toString());
81
+ res.status(429).json({
82
+ error: {
83
+ type: "proxy_rate_limited",
84
+ message: `This proxy is rate limited to ${
85
+ config.modelRateLimit
86
+ } model requests per minute. Please try again in ${Math.ceil(
87
+ tryAgainInMs / 1000
88
+ )} seconds.`,
89
+ },
90
+ });
91
+ } else {
92
+ next();
93
+ }
94
+ };
95
+
96
+ export const keyLimiter = (req: Request, res: Response, next: NextFunction) => {
97
+ if (!RATE_LIMIT_ENABLED) {
98
+ next();
99
+ return;
100
+ }
101
+
102
+ // Allow me to bypass limiter
103
+ if (req.headers.authKey === config.keyPassword) {
104
+ next();
105
+ return;
106
+ }
107
+
108
+ const { remaining, reset } = getStatus(req.authKey ?? '');
109
+ res.set("X-RateLimit-Limit", config.modelRateLimit.toString());
110
+ res.set("X-RateLimit-Remaining", remaining.toString());
111
+ res.set("X-RateLimit-Reset", reset.toString());
112
+
113
+ const tryAgainInMs = getTryAgainInMs(req.authKey ?? '');
114
+ if (tryAgainInMs > 0) {
115
+ res.set("Retry-After", tryAgainInMs.toString());
116
+ proxyKeys.revoke(req.authKey);
117
+ res.status(429).json({
118
+ error: {
119
+ type: "proxy_rate_limited",
120
+ message: `Ты слишком часто стучался на проксю, иди генерируй новый ключ`,
121
+ },
122
+ });
123
+ } else {
124
+ next();
125
+ }
126
+ };
src/proxy/rewriters/add-key.ts ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { ExpressHttpProxyReqCallback } from ".";
2
+ import { Key, keys } from "../../keys";
3
+
4
+ /** Add an OpenAI key from the pool to the request. */
5
+ export const addKey: ExpressHttpProxyReqCallback = (proxyReq, req) => {
6
+ if (!req.proxy) {
7
+ let assignedKey: Key;
8
+ assignedKey = keys.get(req.body?.model || "gpt-3.5")!;
9
+ req.key = assignedKey;
10
+ proxyReq.setHeader("Authorization", `Bearer ${assignedKey.key}`);
11
+ }
12
+ };
src/proxy/rewriters/finalize-body.ts ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { fixRequestBody } from "http-proxy-middleware";
2
+ import type { ExpressHttpProxyReqCallback } from ".";
3
+
4
+ /** Finalize the rewritten request body. Must be the last rewriter. */
5
+ export const finalizeBody: ExpressHttpProxyReqCallback = (proxyReq, req) => {
6
+ if (["POST", "PUT", "PATCH"].includes(req.method ?? "") && req.body) {
7
+ const updatedBody = JSON.stringify(req.body);
8
+ proxyReq.setHeader("Content-Length", Buffer.byteLength(updatedBody));
9
+ (req as any).rawBody = Buffer.from(updatedBody);
10
+
11
+ // body-parser and http-proxy-middleware don't play nice together
12
+ fixRequestBody(proxyReq, req);
13
+ }
14
+ };
src/proxy/rewriters/index.ts ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Request } from "express";
2
+ import type { ClientRequest } from "http";
3
+ import type { ProxyReqCallback } from "http-proxy";
4
+
5
+ export { addKey } from "./add-key";
6
+ export { languageFilter } from "./language-filter";
7
+ export { limitOutputTokens } from "./limit-output-tokens";
8
+ export { finalizeBody } from "./finalize-body";
9
+ export { transformKoboldPayload } from "./transform-kobold-payload";
10
+
11
+ export type ExpressHttpProxyReqCallback = ProxyReqCallback<
12
+ ClientRequest,
13
+ Request
14
+ >;
src/proxy/rewriters/language-filter.ts ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { config } from "../../config";
2
+ import type { ExpressHttpProxyReqCallback } from ".";
3
+ import { logger } from "../../logger";
4
+
5
+ const DISALLOWED_REGEX =
6
+ /[\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u3005\u3007\u3021-\u3029\u3038-\u303B\u3400-\u4DB5\u4E00-\u9FD5\uF900-\uFA6D\uFA70-\uFAD9]/;
7
+
8
+ // Our shitty free-tier will fall over if we test every single character in each
9
+ // 15k character request ten times a second. So we'll just sample 20% of the
10
+ // characters and hope that's enough.
11
+ const containsDisallowedCharacters = (text: string) => {
12
+ const sampleSize = Math.ceil(text.length * (config.rejectSampleRate || 0.2));
13
+ const sample = text
14
+ .split("")
15
+ .sort(() => 0.5 - Math.random())
16
+ .slice(0, sampleSize)
17
+ .join("");
18
+ return DISALLOWED_REGEX.test(sample);
19
+ };
20
+
21
+ /** Block requests containing too many disallowed characters. */
22
+ export const languageFilter: ExpressHttpProxyReqCallback = (_proxyReq, req) => {
23
+ if (!config.rejectDisallowed) {
24
+ return;
25
+ }
26
+
27
+ if (req.method === "POST" && req.body?.messages) {
28
+ const combinedText = req.body.messages
29
+ .map((m: { role: string; content: string }) => m.content)
30
+ .join(",");
31
+ if (containsDisallowedCharacters(combinedText)) {
32
+ logger.warn(`Blocked request containing bad characters`);
33
+ _proxyReq.destroy(new Error(config.rejectMessage));
34
+ }
35
+ }
36
+ };
src/proxy/rewriters/limit-output-tokens.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { config } from "../../config";
2
+ import type { ExpressHttpProxyReqCallback } from ".";
3
+ import { logger } from "../../logger";
4
+
5
+ const MAX_TOKENS = config.maxOutputTokens;
6
+
7
+ /** Enforce a maximum number of tokens requested from OpenAI. */
8
+ export const limitOutputTokens: ExpressHttpProxyReqCallback = (
9
+ _proxyReq,
10
+ req
11
+ ) => {
12
+ if (req.method === "POST" && req.body?.max_tokens) {
13
+ const originalTokens = req.body.max_tokens;
14
+ req.body.max_tokens = Math.min(req.body.max_tokens, MAX_TOKENS);
15
+ if (originalTokens !== req.body.max_tokens) {
16
+ logger.warn(
17
+ `Limiting max_tokens from ${originalTokens} to ${req.body.max_tokens}`
18
+ );
19
+ }
20
+ }
21
+ };
src/proxy/rewriters/transform-kobold-payload.ts ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { config } from "../../config";
2
+ import type { ExpressHttpProxyReqCallback } from ".";
3
+ import { logger } from "../../logger";
4
+
5
+ // Kobold requests look like this:
6
+ // body:
7
+ // {
8
+ // prompt: "Aqua is character from Konosuba anime. Aqua is a goddess, before life in the Fantasy World, she was a goddess of water who guided humans to the afterlife. Aqua looks like young woman with beauty no human could match. Aqua has light blue hair, blue eyes, slim figure, long legs, wide hips, blue waist-long hair that is partially tied into a loop with a spherical clip. Aqua's measurements are 83-56-83 cm. Aqua's height 157cm. Aqua wears sleeveless dark-blue dress with white trimmings, extremely short dark blue miniskirt, green bow around her chest with a blue gem in the middle, detached white sleeves with blue and golden trimmings, thigh-high blue heeled boots over white stockings with blue trimmings. Aqua is very strong in water magic, but a little stupid, so she does not always use it to the place. Aqua is high-spirited, cheerful, carefree. Aqua rarely thinks about the consequences of her actions and always acts or speaks on her whims. Because very easy to taunt Aqua with jeers or lure her with praises.\n" +
9
+ // "Aqua's personality: high-spirited, likes to party, carefree, cheerful.\n" +
10
+ // 'Circumstances and context of the dialogue: Aqua is standing in the city square and is looking for new followers\n' +
11
+ // 'This is how Aqua should talk\n' +
12
+ // 'You: Hi Aqua, I heard you like to spend time in the pub.\n' +
13
+ // "Aqua: *excitedly* Oh my goodness, yes! I just love spending time at the pub! It's so much fun to talk to all the adventurers and hear about their exciting adventures! And you are?\n" +
14
+ // "You: I'm a new here and I wanted to ask for your advice.\n" +
15
+ // 'Aqua: *giggles* Oh, advice! I love giving advice! And in gratitude for that, treat me to a drink! *gives signals to the bartender*\n' +
16
+ // 'This is how Aqua should talk\n' +
17
+ // 'You: Hello\n' +
18
+ // "Aqua: *excitedly* Hello there, dear! Are you new to Axel? Don't worry, I, Aqua the goddess of water, am here to help you! Do you need any assistance? And may I say, I look simply radiant today! *strikes a pose and looks at you with puppy eyes*\n" +
19
+ // '\n' +
20
+ // 'Then the roleplay chat between You and Aqua begins.\n' +
21
+ // "Aqua: *She is in the town square of a city named Axel. It's morning on a Saturday and she suddenly notices a person who looks like they don't know what they're doing. She approaches him and speaks* \n" +
22
+ // '\n' +
23
+ // `"Are you new here? Do you need help? Don't worry! I, Aqua the Goddess of Water, shall help you! Do I look beautiful?" \n` +
24
+ // '\n' +
25
+ // '*She strikes a pose and looks at him with puppy eyes.*\n' +
26
+ // 'You: test\n' +
27
+ // 'You: test\n' +
28
+ // 'You: t\n' +
29
+ // 'You: test\n',
30
+ // use_story: false,
31
+ // use_memory: false,
32
+ // use_authors_note: false,
33
+ // use_world_info: false,
34
+ // max_context_length: 2048,
35
+ // max_length: 180,
36
+ // rep_pen: 1.1,
37
+ // rep_pen_range: 1024,
38
+ // rep_pen_slope: 0.9,
39
+ // temperature: 0.65,
40
+ // tfs: 0.9,
41
+ // top_a: 0,
42
+ // top_k: 0,
43
+ // top_p: 0.9,
44
+ // typical: 1,
45
+ // sampler_order: [
46
+ // 6, 0, 1, 2,
47
+ // 3, 4, 5
48
+ // ],
49
+ // singleline: false
50
+ // }
51
+
52
+ // OpenAI expects this body:
53
+ // { model: 'gpt-3.5-turbo', temperature: 0.65, top_p: 0.9, max_tokens: 180, messages }
54
+ // there's also a frequency_penalty but it's not clear how that maps to kobold's
55
+ // rep_pen.
56
+
57
+ // messages is an array of { role: "system" | "assistant" | "user", content: ""}
58
+ // kobold only sends us the entire prompt. we can try to split the last line and
59
+ // use that as the user message and put the rest in the system message
60
+ // ideally we'd split the history into user and assistant messages, but that's
61
+ // too much work for now
62
+
63
+ /** Transforms a KoboldAI payload into an OpenAI payload. */
64
+ export const transformKoboldPayload: ExpressHttpProxyReqCallback = (
65
+ _proxyReq,
66
+ req
67
+ ) => {
68
+ const { body } = req;
69
+ const { prompt, max_length, rep_pen, top_p, temperature } = body;
70
+
71
+ const promptLines = prompt.split("\n");
72
+ const lastLine = promptLines.pop();
73
+ const messages = [
74
+ { role: "system", content: promptLines.join("\n") },
75
+ { role: "user", content: lastLine },
76
+ ];
77
+
78
+ // Kobold doesn't select a model. If we were assigned a key that supports
79
+ // gpt4, use it, otherwise use gpt3.5-turbo. If the key was incorrectly
80
+ // assigned, we'll get an error from OpenAI but the key will be downgraded
81
+ // for the next request.
82
+ const model = req.key!.isGpt4 ? "gpt-4" : "gpt-3.5-turbo";
83
+ const newBody = {
84
+ model,
85
+ temperature,
86
+ top_p,
87
+ frequency_penalty: rep_pen, // remove this if model turns schizo
88
+ max_tokens: max_length,
89
+ messages,
90
+ };
91
+ req.body = newBody;
92
+ };
src/proxy/routes.ts ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Accepts incoming requests at either the /kobold or /openai routes and then
2
+ routes them to the appropriate handler to be forwarded to the OpenAI API.
3
+ Incoming OpenAI requests are more or less 1:1 with the OpenAI API, but only a
4
+ subset of the API is supported. Kobold requests must be transformed into
5
+ equivalent OpenAI requests. */
6
+
7
+ import * as express from "express";
8
+ import { auth } from "./auth";
9
+ import { kobold } from "./kobold";
10
+ import { openai } from "./openai";
11
+
12
+ const router = express.Router();
13
+
14
+ router.use(auth);
15
+ router.use("/kobold", kobold);
16
+ router.use("/openai", openai);
17
+
18
+ // SillyTavern annoyingly just disregards the path in whatever URL users input,
19
+ // so requests come in at /api/v1. We need to rewrite them to
20
+ // /proxy/kobold/api/v1 so the request is routed to the correct handler.
21
+ function rewriteTavernRequests(
22
+ req: express.Request,
23
+ _res: express.Response,
24
+ next: express.NextFunction
25
+ ) {
26
+ if (req.path.startsWith("/api/v1")) {
27
+ req.url = req.url.replace("/api/v1", "/proxy/kobold/api/v1");
28
+ }
29
+ next();
30
+ }
31
+
32
+ export { rewriteTavernRequests };
33
+ export { router as proxyRouter };
src/server.ts ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { config } from "./config";
2
+ import express from "express";
3
+ import cors from "cors";
4
+ import pinoHttp from "pino-http";
5
+ import { logger } from "./logger";
6
+ import { keys } from "./keys";
7
+ import { proxies } from "./proxies";
8
+ import { proxyRouter, rewriteTavernRequests } from "./proxy/routes";
9
+ import { manage } from "./manage";
10
+ import { handleInfoPage } from "./info-page";
11
+ import { ipLimiter } from "./proxy/rate-limit";
12
+
13
+ const PORT = config.port;
14
+
15
+ process.on('uncaughtException', (e) => {
16
+ logger.error(e);
17
+ });
18
+ process.on('unhandledRejection', (e) => {
19
+ logger.error(e);
20
+ });
21
+
22
+ const app = express();
23
+ // middleware
24
+ app.use("/", rewriteTavernRequests);
25
+ app.use(
26
+ pinoHttp({
27
+ logger,
28
+ // SillyTavern spams the hell out of this endpoint so don't log it
29
+ autoLogging: { ignore: (req) => req.url === "/proxy/kobold/api/v1/model" },
30
+ })
31
+ );
32
+ app.use(cors());
33
+ app.use(
34
+ express.json({ limit: "10mb" }),
35
+ express.urlencoded({ extended: true, limit: "10mb" })
36
+ );
37
+ // trust proxy to set x-forwarded-for ips correctly
38
+ app.set("trust proxy", true);
39
+ // routes
40
+ app.get("/", ipLimiter, handleInfoPage);
41
+ app.post("/", ipLimiter, handleInfoPage);
42
+ app.use("/manage", manage);
43
+ app.use("/proxy", proxyRouter);
44
+ // 500 and 404
45
+ app.use((err: any, _req: unknown, res: express.Response, _next: unknown) => {
46
+ if (err.status) {
47
+ res.status(err.status).json({ error: err.message });
48
+ } else {
49
+ logger.error(err);
50
+ res.status(500).json({ error: "Internal server error" });
51
+ }
52
+ });
53
+ app.use((_req: unknown, res: express.Response) => {
54
+ res.status(404).json({ error: "Not found" });
55
+ });
56
+ // start server and load keys
57
+ app.listen(PORT, () => {
58
+ logger.info(`Server listening on port ${PORT}`);
59
+ proxies.init();
60
+ keys.init();
61
+ });
src/types/custom.d.ts ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Express } from "express-serve-static-core";
2
+ import { Key } from "../keys";
3
+ import { Proxy } from "../proxies";
4
+
5
+ declare global {
6
+ namespace Express {
7
+ interface Request {
8
+ key?: Key;
9
+ proxy?: Proxy;
10
+ authKey?: string;
11
+ }
12
+ }
13
+ }
tsconfig.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "target": "ES2020",
5
+ "module": "CommonJS",
6
+ "moduleResolution": "node",
7
+ "esModuleInterop": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "skipLibCheck": true,
10
+ "skipDefaultLibCheck": true,
11
+ "outDir": "build",
12
+ "incremental": true
13
+ },
14
+ "include": ["src"],
15
+ "exclude": ["node_modules"],
16
+ "files": ["src/types/custom.d.ts"]
17
+ }