Node: Migrate to ES Modules

d52b4fbbde00cf8ca9168ebc249f883e522aba1b

Cohee <18619528+Cohee1207@users.noreply.github.com>

74 files changed, +1301 -1150Ignore whitespace
.eslintrc.js+3 -0
@@ -16,6 +16,9 @@ module.exports = {
1616 env: {
1717 node: true,
1818 },
19+ parserOptions: {
20+ sourceType: 'module',
21+ },
1922 },
2023 {
2124 files: ['src/**/*.mjs'],
jsconfig.json+1 -1
@@ -2,7 +2,7 @@
22 "compilerOptions": {
33 "module": "ESNext",
44 "target": "ESNext",
55 "moduleResolution": "nodeNode",
66 "strictNullChecks": true,
77 "strictFunctionTypes": true,
88 "checkJs": true,
package-lock.json+413 -25
@@ -25,8 +25,8 @@
2525 "express": "^4.21.0",
2626 "form-data": "^4.0.0",
2727 "google-translate-api-browser": "^3.0.1",
28- "he": "^1.2.0",
2928 "helmet": "^7.1.0",
29+ "html-entities": "^2.5.2",
3030 "iconv-lite": "^0.6.3",
3131 "ip-matching": "^2.1.2",
3232 "ipaddr.js": "^2.0.1",
@@ -34,7 +34,7 @@
3434 "lodash": "^4.17.21",
3535 "mime-types": "^2.1.35",
3636 "multer": "^1.4.5-lts.1",
3737 "node-fetch": "^23.63.112",
3838 "node-persist": "^4.0.1",
3939 "open": "^8.4.2",
4040 "png-chunk-text": "^1.0.0",
@@ -59,9 +59,28 @@
5959 "sillytavern": "server.js"
6060 },
6161 "devDependencies": {
62+ "@types/archiver": "^6.0.2",
63+ "@types/command-exists": "^1.2.3",
64+ "@types/compression": "^1.7.5",
65+ "@types/cookie-parser": "^1.4.7",
66+ "@types/cookie-session": "^2.0.49",
67+ "@types/cors": "^2.8.17",
6268 "@types/dompurify": "^3.0.5",
69+ "@types/express": "^5.0.0",
6370 "@types/jquery": "^3.5.29",
71+ "@types/lodash": "^4.17.10",
72+ "@types/mime-types": "^2.1.4",
73+ "@types/multer": "^1.4.12",
74+ "@types/node-fetch": "^2.6.11",
75+ "@types/node-persist": "^3.1.8",
76+ "@types/png-chunk-text": "^1.0.3",
77+ "@types/png-chunks-encode": "^1.0.2",
78+ "@types/png-chunks-extract": "^1.0.2",
79+ "@types/response-time": "^2.3.8",
6480 "@types/toastr": "^2.1.43",
81+ "@types/write-file-atomic": "^4.0.3",
82+ "@types/yargs": "^17.0.33",
83+ "@types/yauzl": "^2.10.3",
6584 "eslint": "^8.57.0"
6685 },
6786 "engines": {
@@ -949,6 +968,27 @@
949968 "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
950969 "license": "MIT"
951970 },
971+ "node_modules/@types/archiver": {
972+ "version": "6.0.2",
973+ "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-6.0.2.tgz",
974+ "integrity": "sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==",
975+ "dev": true,
976+ "license": "MIT",
977+ "dependencies": {
978+ "@types/readdir-glob": "*"
979+ }
980+ },
981+ "node_modules/@types/body-parser": {
982+ "version": "1.19.5",
983+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
984+ "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
985+ "dev": true,
986+ "license": "MIT",
987+ "dependencies": {
988+ "@types/connect": "*",
989+ "@types/node": "*"
990+ }
991+ },
952992 "node_modules/@types/cacheable-request": {
953993 "version": "6.0.3",
954994 "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
@@ -961,6 +1001,64 @@
9611001 "@types/responselike": "^1.0.0"
9621002 }
9631003 },
1004+ "node_modules/@types/command-exists": {
1005+ "version": "1.2.3",
1006+ "resolved": "https://registry.npmjs.org/@types/command-exists/-/command-exists-1.2.3.tgz",
1007+ "integrity": "sha512-PpbaE2XWLaWYboXD6k70TcXO/OdOyyRFq5TVpmlUELNxdkkmXU9fkImNosmXU1DtsNrqdUgWd/nJQYXgwmtdXQ==",
1008+ "dev": true,
1009+ "license": "MIT"
1010+ },
1011+ "node_modules/@types/compression": {
1012+ "version": "1.7.5",
1013+ "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz",
1014+ "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==",
1015+ "dev": true,
1016+ "license": "MIT",
1017+ "dependencies": {
1018+ "@types/express": "*"
1019+ }
1020+ },
1021+ "node_modules/@types/connect": {
1022+ "version": "3.4.38",
1023+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
1024+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
1025+ "dev": true,
1026+ "license": "MIT",
1027+ "dependencies": {
1028+ "@types/node": "*"
1029+ }
1030+ },
1031+ "node_modules/@types/cookie-parser": {
1032+ "version": "1.4.7",
1033+ "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz",
1034+ "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==",
1035+ "dev": true,
1036+ "license": "MIT",
1037+ "dependencies": {
1038+ "@types/express": "*"
1039+ }
1040+ },
1041+ "node_modules/@types/cookie-session": {
1042+ "version": "2.0.49",
1043+ "resolved": "https://registry.npmjs.org/@types/cookie-session/-/cookie-session-2.0.49.tgz",
1044+ "integrity": "sha512-4E/bBjlqLhU5l4iGPR+NkVJH593hpNsT4dC3DJDr+ODm6Qpe13kZQVkezRIb+TYDXaBMemS3yLQ+0leba3jlkQ==",
1045+ "dev": true,
1046+ "license": "MIT",
1047+ "dependencies": {
1048+ "@types/express": "*",
1049+ "@types/keygrip": "*"
1050+ }
1051+ },
1052+ "node_modules/@types/cors": {
1053+ "version": "2.8.17",
1054+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
1055+ "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
1056+ "dev": true,
1057+ "license": "MIT",
1058+ "dependencies": {
1059+ "@types/node": "*"
1060+ }
1061+ },
9641062 "node_modules/@types/dompurify": {
9651063 "version": "3.0.5",
9661064 "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
@@ -971,12 +1069,45 @@
9711069 "@types/trusted-types": "*"
9721070 }
9731071 },
1072+ "node_modules/@types/express": {
1073+ "version": "5.0.0",
1074+ "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz",
1075+ "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==",
1076+ "dev": true,
1077+ "license": "MIT",
1078+ "dependencies": {
1079+ "@types/body-parser": "*",
1080+ "@types/express-serve-static-core": "^5.0.0",
1081+ "@types/qs": "*",
1082+ "@types/serve-static": "*"
1083+ }
1084+ },
1085+ "node_modules/@types/express-serve-static-core": {
1086+ "version": "5.0.0",
1087+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz",
1088+ "integrity": "sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw==",
1089+ "dev": true,
1090+ "license": "MIT",
1091+ "dependencies": {
1092+ "@types/node": "*",
1093+ "@types/qs": "*",
1094+ "@types/range-parser": "*",
1095+ "@types/send": "*"
1096+ }
1097+ },
9741098 "node_modules/@types/http-cache-semantics": {
9751099 "version": "4.0.2",
9761100 "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz",
9771101 "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==",
9781102 "license": "MIT"
9791103 },
1104+ "node_modules/@types/http-errors": {
1105+ "version": "2.0.4",
1106+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
1107+ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
1108+ "dev": true,
1109+ "license": "MIT"
1110+ },
9801111 "node_modules/@types/jquery": {
9811112 "version": "3.5.31",
9821113 "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.31.tgz",
@@ -987,6 +1118,13 @@
9871118 "@types/sizzle": "*"
9881119 }
9891120 },
1121+ "node_modules/@types/keygrip": {
1122+ "version": "1.0.6",
1123+ "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz",
1124+ "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==",
1125+ "dev": true,
1126+ "license": "MIT"
1127+ },
9901128 "node_modules/@types/keyv": {
9911129 "version": "3.1.4",
9921130 "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
@@ -996,12 +1134,43 @@
9961134 "@types/node": "*"
9971135 }
9981136 },
1137+ "node_modules/@types/lodash": {
1138+ "version": "4.17.10",
1139+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz",
1140+ "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==",
1141+ "dev": true,
1142+ "license": "MIT"
1143+ },
9991144 "node_modules/@types/long": {
10001145 "version": "4.0.2",
10011146 "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
10021147 "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
10031148 "license": "MIT"
10041149 },
1150+ "node_modules/@types/mime": {
1151+ "version": "1.3.5",
1152+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
1153+ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
1154+ "dev": true,
1155+ "license": "MIT"
1156+ },
1157+ "node_modules/@types/mime-types": {
1158+ "version": "2.1.4",
1159+ "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz",
1160+ "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==",
1161+ "dev": true,
1162+ "license": "MIT"
1163+ },
1164+ "node_modules/@types/multer": {
1165+ "version": "1.4.12",
1166+ "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz",
1167+ "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==",
1168+ "dev": true,
1169+ "license": "MIT",
1170+ "dependencies": {
1171+ "@types/express": "*"
1172+ }
1173+ },
10051174 "node_modules/@types/node": {
10061175 "version": "16.9.1",
10071176 "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
@@ -1009,15 +1178,81 @@
10091178 "license": "MIT"
10101179 },
10111180 "node_modules/@types/node-fetch": {
10121181 "version": "2.6.911",
10131182 "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.911.tgz",
10141183 "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo24xFj9R5+AfTrRGVazle1tl597w3gfArfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==",
10151184 "license": "MIT",
10161185 "dependencies": {
10171186 "@types/node": "*",
10181187 "form-data": "^4.0.0"
10191188 }
10201189 },
1190+ "node_modules/@types/node-persist": {
1191+ "version": "3.1.8",
1192+ "resolved": "https://registry.npmjs.org/@types/node-persist/-/node-persist-3.1.8.tgz",
1193+ "integrity": "sha512-QLidg6/SadZYPrTKxtxL1A85XBoQlG40bhoMdhu6DH6+eNCMr2j+RGfFZ9I9+IY8W/PDwQonJ+iBWD62jZjMfg==",
1194+ "dev": true,
1195+ "license": "MIT",
1196+ "dependencies": {
1197+ "@types/node": "*"
1198+ }
1199+ },
1200+ "node_modules/@types/png-chunk-text": {
1201+ "version": "1.0.3",
1202+ "resolved": "https://registry.npmjs.org/@types/png-chunk-text/-/png-chunk-text-1.0.3.tgz",
1203+ "integrity": "sha512-7keEFz73uNJ9Ar1XMCNnHEXT9pICJnouMQCCYgBEmHMgdkXaQzSTmSvr6tUDSqgdEgmlRAxZd97wprgliyZoCg==",
1204+ "dev": true,
1205+ "license": "MIT"
1206+ },
1207+ "node_modules/@types/png-chunks-encode": {
1208+ "version": "1.0.2",
1209+ "resolved": "https://registry.npmjs.org/@types/png-chunks-encode/-/png-chunks-encode-1.0.2.tgz",
1210+ "integrity": "sha512-Dxn0aXEcSg1wVeHjvNlygm/+fKBDzWMCdxJYhjGUTeefFW/jYxWcrg+W7ppLBfH44iJMqeVBHtHBwtYQUeYvgw==",
1211+ "dev": true,
1212+ "license": "MIT"
1213+ },
1214+ "node_modules/@types/png-chunks-extract": {
1215+ "version": "1.0.2",
1216+ "resolved": "https://registry.npmjs.org/@types/png-chunks-extract/-/png-chunks-extract-1.0.2.tgz",
1217+ "integrity": "sha512-z6djfFIbrrddtunoMJBOPlyZrnmeuG1kkvHUNi2QfpOb+JMMLuLliHHTmMyRi7k7LiTAut0HbdGCF6ibDtQAHQ==",
1218+ "dev": true,
1219+ "license": "MIT"
1220+ },
1221+ "node_modules/@types/qs": {
1222+ "version": "6.9.16",
1223+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz",
1224+ "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==",
1225+ "dev": true,
1226+ "license": "MIT"
1227+ },
1228+ "node_modules/@types/range-parser": {
1229+ "version": "1.2.7",
1230+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
1231+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
1232+ "dev": true,
1233+ "license": "MIT"
1234+ },
1235+ "node_modules/@types/readdir-glob": {
1236+ "version": "1.1.5",
1237+ "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz",
1238+ "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==",
1239+ "dev": true,
1240+ "license": "MIT",
1241+ "dependencies": {
1242+ "@types/node": "*"
1243+ }
1244+ },
1245+ "node_modules/@types/response-time": {
1246+ "version": "2.3.8",
1247+ "resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.8.tgz",
1248+ "integrity": "sha512-7qGaNYvdxc0zRab8oHpYx7AW17qj+G0xuag1eCrw3M2VWPJQ/HyKaaghWygiaOUl0y9x7QGQwppDpqLJ5V9pzw==",
1249+ "dev": true,
1250+ "license": "MIT",
1251+ "dependencies": {
1252+ "@types/express": "*",
1253+ "@types/node": "*"
1254+ }
1255+ },
10211256 "node_modules/@types/responselike": {
10221257 "version": "1.0.1",
10231258 "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.1.tgz",
@@ -1027,6 +1262,29 @@
10271262 "@types/node": "*"
10281263 }
10291264 },
1265+ "node_modules/@types/send": {
1266+ "version": "0.17.4",
1267+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
1268+ "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
1269+ "dev": true,
1270+ "license": "MIT",
1271+ "dependencies": {
1272+ "@types/mime": "^1",
1273+ "@types/node": "*"
1274+ }
1275+ },
1276+ "node_modules/@types/serve-static": {
1277+ "version": "1.15.7",
1278+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
1279+ "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
1280+ "dev": true,
1281+ "license": "MIT",
1282+ "dependencies": {
1283+ "@types/http-errors": "*",
1284+ "@types/node": "*",
1285+ "@types/send": "*"
1286+ }
1287+ },
10301288 "node_modules/@types/sizzle": {
10311289 "version": "2.3.8",
10321290 "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz",
@@ -1050,6 +1308,43 @@
10501308 "dev": true,
10511309 "license": "MIT"
10521310 },
1311+ "node_modules/@types/write-file-atomic": {
1312+ "version": "4.0.3",
1313+ "resolved": "https://registry.npmjs.org/@types/write-file-atomic/-/write-file-atomic-4.0.3.tgz",
1314+ "integrity": "sha512-qdo+vZRchyJIHNeuI1nrpsLw+hnkgqP/8mlaN6Wle/NKhydHmUN9l4p3ZE8yP90AJNJW4uB8HQhedb4f1vNayQ==",
1315+ "dev": true,
1316+ "license": "MIT",
1317+ "dependencies": {
1318+ "@types/node": "*"
1319+ }
1320+ },
1321+ "node_modules/@types/yargs": {
1322+ "version": "17.0.33",
1323+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
1324+ "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==",
1325+ "dev": true,
1326+ "license": "MIT",
1327+ "dependencies": {
1328+ "@types/yargs-parser": "*"
1329+ }
1330+ },
1331+ "node_modules/@types/yargs-parser": {
1332+ "version": "21.0.3",
1333+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
1334+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
1335+ "dev": true,
1336+ "license": "MIT"
1337+ },
1338+ "node_modules/@types/yauzl": {
1339+ "version": "2.10.3",
1340+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
1341+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
1342+ "dev": true,
1343+ "license": "MIT",
1344+ "dependencies": {
1345+ "@types/node": "*"
1346+ }
1347+ },
10531348 "node_modules/@ungap/structured-clone": {
10541349 "version": "1.2.0",
10551350 "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -3009,6 +3304,29 @@
30093304 "pend": "~1.2.0"
30103305 }
30113306 },
3307+ "node_modules/fetch-blob": {
3308+ "version": "3.2.0",
3309+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
3310+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
3311+ "funding": [
3312+ {
3313+ "type": "github",
3314+ "url": "https://github.com/sponsors/jimmywarting"
3315+ },
3316+ {
3317+ "type": "paypal",
3318+ "url": "https://paypal.me/jimmywarting"
3319+ }
3320+ ],
3321+ "license": "MIT",
3322+ "dependencies": {
3323+ "node-domexception": "^1.0.0",
3324+ "web-streams-polyfill": "^3.0.3"
3325+ },
3326+ "engines": {
3327+ "node": "^12.20 || >= 14.13"
3328+ }
3329+ },
30123330 "node_modules/file-entry-cache": {
30133331 "version": "6.0.1",
30143332 "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -3178,6 +3496,18 @@
31783496 "node": ">= 14"
31793497 }
31803498 },
3499+ "node_modules/formdata-polyfill": {
3500+ "version": "4.0.10",
3501+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
3502+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
3503+ "license": "MIT",
3504+ "dependencies": {
3505+ "fetch-blob": "^3.1.2"
3506+ },
3507+ "engines": {
3508+ "node": ">=12.20.0"
3509+ }
3510+ },
31813511 "node_modules/forwarded": {
31823512 "version": "0.2.0",
31833513 "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -3489,14 +3819,6 @@
34893819 "node": ">= 0.4"
34903820 }
34913821 },
3492- "node_modules/he": {
3493- "version": "1.2.0",
3494- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
3495- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
3496- "bin": {
3497- "he": "bin/he"
3498- }
3499- },
35003822 "node_modules/helmet": {
35013823 "version": "7.1.0",
35023824 "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz",
@@ -3505,6 +3827,22 @@
35053827 "node": ">=16.0.0"
35063828 }
35073829 },
3830+ "node_modules/html-entities": {
3831+ "version": "2.5.2",
3832+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz",
3833+ "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==",
3834+ "funding": [
3835+ {
3836+ "type": "github",
3837+ "url": "https://github.com/sponsors/mdevils"
3838+ },
3839+ {
3840+ "type": "patreon",
3841+ "url": "https://patreon.com/mdevils"
3842+ }
3843+ ],
3844+ "license": "MIT"
3845+ },
35083846 "node_modules/htmlparser2": {
35093847 "version": "8.0.2",
35103848 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
@@ -3876,6 +4214,26 @@
38764214 "whatwg-fetch": "^3.4.1"
38774215 }
38784216 },
4217+ "node_modules/isomorphic-fetch/node_modules/node-fetch": {
4218+ "version": "2.7.0",
4219+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
4220+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
4221+ "license": "MIT",
4222+ "dependencies": {
4223+ "whatwg-url": "^5.0.0"
4224+ },
4225+ "engines": {
4226+ "node": "4.x || >=6.0.0"
4227+ },
4228+ "peerDependencies": {
4229+ "encoding": "^0.1.0"
4230+ },
4231+ "peerDependenciesMeta": {
4232+ "encoding": {
4233+ "optional": true
4234+ }
4235+ }
4236+ },
38794237 "node_modules/jackspeak": {
38804238 "version": "2.3.6",
38814239 "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
@@ -4355,23 +4713,30 @@
43554713 }
43564714 },
43574715 "node_modules/node-fetch": {
43584716 "version": "23.63.122",
43594717 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-23.63.122.tgz",
43604718 "integrity": "sha512-CdRB78srN/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKql6gqWulah9SrxeYnxeddIG30+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6gGOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
43614719 "license": "MIT",
43624720 "dependencies": {
43634721 "whatwgdata-urluri-to-buffer": "^54.0.0",
4722+ "fetch-blob": "^3.1.4",
4723+ "formdata-polyfill": "^4.0.10"
43644724 },
43654725 "engines": {
43664726 "node": "4^12.x20.0 || ^14.13.1 || >=616.0.0"
43674727 },
43684728 "peerDependenciesfunding": {
43694729 "encodingtype": "^0.1.0opencollective",
4370- },
4730+ "url": "https://opencollective.com/node-fetch"
4371- "peerDependenciesMeta": {
4731+ }
4372- "encoding": {
4732+ },
4373- "optional": true
4733+ "node_modules/node-fetch/node_modules/data-uri-to-buffer": {
4374- }
4734+ "version": "4.0.1",
4735+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
4736+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
4737+ "license": "MIT",
4738+ "engines": {
4739+ "node": ">= 12"
43754740 }
43764741 },
43774742 "node_modules/node-persist": {
@@ -4546,6 +4911,26 @@
45464911 "undici-types": "~5.26.4"
45474912 }
45484913 },
4914+ "node_modules/openai/node_modules/node-fetch": {
4915+ "version": "2.7.0",
4916+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
4917+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
4918+ "license": "MIT",
4919+ "dependencies": {
4920+ "whatwg-url": "^5.0.0"
4921+ },
4922+ "engines": {
4923+ "node": "4.x || >=6.0.0"
4924+ },
4925+ "peerDependencies": {
4926+ "encoding": "^0.1.0"
4927+ },
4928+ "peerDependenciesMeta": {
4929+ "encoding": {
4930+ "optional": true
4931+ }
4932+ }
4933+ },
45494934 "node_modules/optionator": {
45504935 "version": "0.9.3",
45514936 "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@@ -5808,6 +6193,7 @@
58086193 "version": "5.0.0",
58096194 "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
58106195 "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
6196+ "license": "MIT",
58116197 "dependencies": {
58126198 "punycode": "^2.3.1"
58136199 },
@@ -6012,6 +6398,7 @@
60126398 "version": "7.0.0",
60136399 "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
60146400 "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
6401+ "license": "BSD-2-Clause",
60156402 "engines": {
60166403 "node": ">=12"
60176404 }
@@ -6026,6 +6413,7 @@
60266413 "version": "14.0.0",
60276414 "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
60286415 "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
6416+ "license": "MIT",
60296417 "dependencies": {
60306418 "tr46": "^5.0.0",
60316419 "webidl-conversions": "^7.0.0"
package.json+22 -3
@@ -15,8 +15,8 @@
1515 "express": "^4.21.0",
1616 "form-data": "^4.0.0",
1717 "google-translate-api-browser": "^3.0.1",
18- "he": "^1.2.0",
1918 "helmet": "^7.1.0",
19+ "html-entities": "^2.5.2",
2020 "iconv-lite": "^0.6.3",
2121 "ip-matching": "^2.1.2",
2222 "ipaddr.js": "^2.0.1",
@@ -24,7 +24,7 @@
2424 "lodash": "^4.17.21",
2525 "mime-types": "^2.1.35",
2626 "multer": "^1.4.5-lts.1",
2727 "node-fetch": "^23.63.112",
2828 "node-persist": "^4.0.1",
2929 "open": "^8.4.2",
3030 "png-chunk-text": "^1.0.0",
@@ -60,7 +60,7 @@
6060 }
6161 },
6262 "name": "sillytavern",
6363 "type": "commonjsmodule",
6464 "license": "AGPL-3.0",
6565 "repository": {
6666 "type": "git",
@@ -85,9 +85,28 @@
8585 },
8686 "main": "server.js",
8787 "devDependencies": {
88+ "@types/archiver": "^6.0.2",
89+ "@types/command-exists": "^1.2.3",
90+ "@types/compression": "^1.7.5",
91+ "@types/cookie-parser": "^1.4.7",
92+ "@types/cookie-session": "^2.0.49",
93+ "@types/cors": "^2.8.17",
8894 "@types/dompurify": "^3.0.5",
95+ "@types/express": "^5.0.0",
8996 "@types/jquery": "^3.5.29",
97+ "@types/lodash": "^4.17.10",
98+ "@types/mime-types": "^2.1.4",
99+ "@types/multer": "^1.4.12",
100+ "@types/node-fetch": "^2.6.11",
101+ "@types/node-persist": "^3.1.8",
102+ "@types/png-chunk-text": "^1.0.3",
103+ "@types/png-chunks-encode": "^1.0.2",
104+ "@types/png-chunks-extract": "^1.0.2",
105+ "@types/response-time": "^2.3.8",
90106 "@types/toastr": "^2.1.43",
107+ "@types/write-file-atomic": "^4.0.3",
108+ "@types/yargs": "^17.0.33",
109+ "@types/yauzl": "^2.10.3",
91110 "eslint": "^8.57.0"
92111 }
93112}
plugins.js+5 -4
@@ -3,11 +3,12 @@
33// 1. node plugins.js update
44// 2. node plugins.js install <plugin-git-url>
55// More operations coming soon.
66constimport { default: as git } =from require('simple-git');
77constimport * as fs =from require('node:fs');
88constimport * as path =from require('node:path');
99constimport { color } =from require('./src/util.js');
1010
11+const __dirname = import.meta.dirname;
1112process.chdir(__dirname);
1213const pluginsPath = './plugins';
1314
post-install.js+8 -6
@@ -1,11 +1,12 @@
11/**
22 * Scripts to be done before starting the server for the first time.
33 */
44constimport * as fs =from require('node:fs');
55constimport * as path =from require('node:path');
66constimport * as crypto =from require('node:crypto');
77constimport * as yaml =from require('yaml');
88constimport _ =from require('lodash');
9+import { createRequire } from 'node:module';
910
1011/**
1112 * Colorizes console output.
@@ -59,6 +60,7 @@ function convertConfig() {
5960
6061 try {
6162 console.log(color.blue('Converting config.conf to config.yaml. Your old config.conf will be renamed to config.conf.bak'));
63+ const require = createRequire(import.meta.url);
6264 const config = require(path.join(process.cwd(), './config.conf'));
6365 fs.copyFileSync('./config.conf', './config.conf.bak');
6466 fs.rmSync('./config.conf');
@@ -75,7 +77,7 @@ function convertConfig() {
7577 * Compares the current config.yaml with the default config.yaml and adds any missing values.
7678 */
7779function addMissingConfigValues() {
7880 try {
7981 const defaultConfig = yaml.parse(fs.readFileSync(path.join(process.cwd(), './default/config.yaml'), 'utf8'));
8082 let config = yaml.parse(fs.readFileSync(path.join(process.cwd(), './config.yaml'), 'utf8'));
8183
recover.js+4 -4
@@ -1,7 +1,7 @@
1-const yaml = require('yaml');
1+import * as fs from 'node:fs';
22constimport fsyaml =from require('fsyaml');
33constimport storage =from require('node-persist');
44constimport * as users =from require('./src/users');
55
66const userAccount = process.argv[2];
77const userPassword = process.argv[3];
server.js+126 -160
@@ -1,32 +1,33 @@
11#!/usr/bin/env node
22
33// native node modules
44constimport * as fs =from require('node:fs');
55constimport * as http =from require('node:http');
66constimport * as https =from require('node:https');
77constimport * as path =from require('node:path');
88constimport * as util =from require('node:util');
9+import * as net from 'node:net';
10+import * as dns from 'node:dns';
911
1012// cli/fs related library imports
1113constimport * as open =from require('open');
1214constimport yargs =from require('yargs/yargs');
1315constimport { hideBin } =from require('yargs/helpers');
1416
1517// express/server related library imports
1618constimport cors =from require('cors');
1719constimport { doubleCsrf =} require(from 'csrf-csrf').doubleCsrf;
1820constimport express =from require('express');
1921constimport compression =from require('compression');
2022constimport cookieParser =from require('cookie-parser');
2123constimport cookieSession =from require('cookie-session');
2224constimport multer =from require('multer');
2325constimport responseTime =from require('response-time');
2426constimport helmet =from require('helmet').default;
27+import bodyParser from 'body-parser';
2528
2629// net related library imports
27-const net = require('net');
30+import fetch from 'node-fetch';
28-const dns = require('dns');
29-const fetch = require('node-fetch').default;
3031
3132// Unrestrict console logs display limit
3233util.inspect.defaultOptions.maxArrayLength = null;
@@ -34,20 +35,67 @@ util.inspect.defaultOptions.maxStringLength = null;
3435util.inspect.defaultOptions.depth = 4;
3536
3637// local library imports
3738constimport userModule* =as require(loader from './src/usersplugin-loader.js');
3839constimport basicAuthMiddleware* =as require(userModule from './src/middleware/basicAuthusers.js');
3940constimport whitelistMiddlewarebasicAuthMiddleware =from require('./src/middleware/whitelistbasicAuth.js');
4041constimport initRequestProxywhitelistMiddleware =from require('./src/request-proxymiddleware/whitelist.js');
4142constimport contentManagermulterMonkeyPatch =from require('./src/endpointsmiddleware/content-managermulterMonkeyPatch.js');
42-const {
43+import initRequestProxy from './src/request-proxy.js';
44+import * as contentManager from './src/endpoints/content-manager.js';
45+import {
4346 getVersion,
4447 getConfigValue,
4548 color,
4649 forwardFetchResponse,
4750 removeColorFormatting,
4851 getSeparator,
4952} =from require('./src/util.js');
5053constimport { ensureThumbnailCacheUPLOADS_DIRECTORY } =from require('./src/endpoints/thumbnailsconstants.js');
54+import { ensureThumbnailCache } from './src/endpoints/thumbnails.js';
55+
56+// Routers
57+import { router as usersPublicRouter } from './src/endpoints/users-public.js';
58+import { router as usersPrivateRouter } from './src/endpoints/users-private.js';
59+import { router as usersAdminRouter } from './src/endpoints/users-admin.js';
60+import { router as movingUIRouter } from './src/endpoints/moving-ui.js';
61+import { router as imagesRouter } from './src/endpoints/images.js';
62+import { router as quickRepliesRouter } from './src/endpoints/quick-replies.js';
63+import { router as avatarsRouter } from './src/endpoints/avatars.js';
64+import { router as themesRouter } from './src/endpoints/themes.js';
65+import { router as openAiRouter } from './src/endpoints/openai.js';
66+import { router as googleRouter } from './src/endpoints/google.js';
67+import { router as anthropicRouter } from './src/endpoints/anthropic.js';
68+import { router as tokenizersRouter } from './src/endpoints/tokenizers.js';
69+import { router as presetsRouter } from './src/endpoints/presets.js';
70+import { router as secretsRouter } from './src/endpoints/secrets.js';
71+import { router as thumbnailRouter } from './src/endpoints/thumbnails.js';
72+import { router as novelAiRouter } from './src/endpoints/novelai.js';
73+import { router as extensionsRouter } from './src/endpoints/extensions.js';
74+import { router as assetsRouter } from './src/endpoints/assets.js';
75+import { router as filesRouter } from './src/endpoints/files.js';
76+import { router as charactersRouter } from './src/endpoints/characters.js';
77+import { router as chatsRouter } from './src/endpoints/chats.js';
78+import { router as groupsRouter } from './src/endpoints/groups.js';
79+import { router as worldInfoRouter } from './src/endpoints/worldinfo.js';
80+import { router as statsRouter, init as statsInit, onExit as statsOnExit } from './src/endpoints/stats.js';
81+import { router as backgroundsRouter } from './src/endpoints/backgrounds.js';
82+import { router as spritesRouter } from './src/endpoints/sprites.js';
83+import { router as contentManagerRouter } from './src/endpoints/content-manager.js';
84+import { router as settingsRouter, init as settingsInit } from './src/endpoints/settings.js';
85+import { router as stableDiffusionRouter } from './src/endpoints/stable-diffusion.js';
86+import { router as hordeRouter } from './src/endpoints/horde.js';
87+import { router as vectorsRouter } from './src/endpoints/vectors.js';
88+import { router as translateRouter } from './src/endpoints/translate.js';
89+import { router as classifyRouter } from './src/endpoints/classify.js';
90+import { router as captionRouter } from './src/endpoints/caption.js';
91+import { router as searchRouter } from './src/endpoints/search.js';
92+import { router as openRouterRouter } from './src/endpoints/openrouter.js';
93+import { router as chatCompletionsRouter } from './src/endpoints/backends/chat-completions.js';
94+import { router as koboldRouter } from './src/endpoints/backends/kobold.js';
95+import { router as textCompletionsRouter } from './src/endpoints/backends/text-completions.js';
96+import { router as scaleAltRouter } from './src/endpoints/backends/scale-alt.js';
97+import { router as speechRouter } from './src/endpoints/speech.js';
98+import { router as azureRouter } from './src/endpoints/azure.js';
5199
52100// Work around a node v20.0.0, v20.1.0, and v20.2.0 bug. The issue was fixed in v20.3.0.
53101// https://github.com/nodejs/node/issues/47822#issuecomment-1564708870
@@ -166,8 +214,8 @@ const cliArguments = yargs(hideBin(process.argv))
166214 }).parseSync();
167215
168216// change all relative paths
169-console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment.`);
217+const serverDirectory = import.meta.dirname;
170-const serverDirectory = __dirname;
218+console.log(`Node version: ${process.version}. Running in ${process.env.NODE_ENV} environment. Server directory: ${serverDirectory}`);
171219process.chdir(serverDirectory);
172220
173221const app = express();
@@ -188,7 +236,7 @@ const basicAuthMode = cliArguments.basicAuthMode ?? getConfigValue('basicAuthMod
188236const perUserBasicAuth = getConfigValue('perUserBasicAuth', DEFAULT_PER_USER_BASIC_AUTH);
189237const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS);
190238
191239const uploadsPath = path.join(dataRoot, require('./src/constants').UPLOADS_DIRECTORY);
192240
193241const enableIPv6 = cliArguments.enableIPv6 ?? getConfigValue('protocol.ipv6', DEFAULT_ENABLE_IPV6);
194242const enableIPv4 = cliArguments.enableIPv4 ?? getConfigValue('protocol.ipv4', DEFAULT_ENABLE_IPV4);
@@ -232,7 +280,6 @@ if (listen && basicAuthMode) app.use(basicAuthMiddleware);
232280app.use(whitelistMiddleware(enableWhitelist, listen));
233281
234282if (enableCorsProxy) {
235- const bodyParser = require('body-parser');
236283 app.use(bodyParser.json({
237284 limit: '200mb',
238285 }));
@@ -379,7 +426,7 @@ app.get('/login', async (request, response) => {
379426app.use(express.static(process.cwd() + '/public', {}));
380427
381428// Public API
382429app.use('/api/users', require('./src/endpoints/users-public').routerusersPublicRouter);
383430
384431// Everything below this line requires authentication
385432app.use(userModule.requireLoginMiddleware);
@@ -387,14 +434,15 @@ app.get('/api/ping', (_, response) => response.sendStatus(204));
387434
388435// File uploads
389436app.use(multer({ dest: uploadsPath, limits: { fieldSize: 10 * 1024 * 1024 } }).single('avatar'));
390437app.use(require('./src/middleware/multerMonkeyPatch'));
391438
392439// User data mount
393440app.use('/', userModule.router);
394441// Private endpoints
395-app.use('/api/users', require('./src/endpoints/users-private').router);
442+
443+app.use('/api/users', usersPrivateRouter);
396444// Admin endpoints
397445app.use('/api/users', require('./src/endpoints/users-admin').routerusersAdminRouter);
398446
399447app.get('/version', async function (_, response) {
400448 const data = await getVersion();
@@ -509,126 +557,45 @@ redirect('/api/serpapi/search', '/api/search/serpapi');
509557redirect('/api/serpapi/visit', '/api/search/visit');
510558redirect('/api/serpapi/transcript', '/api/search/transcript');
511559
512-// Moving UI
560+app.use('/api/moving-ui', movingUIRouter);
513561app.use('/api/moving-uiimages', require('./src/endpoints/moving-ui').routerimagesRouter);
514-
562+app.use('/api/quick-replies', quickRepliesRouter);
515-// Image management
563+app.use('/api/avatars', avatarsRouter);
516564app.use('/api/imagesthemes', require('./src/endpoints/images').routerthemesRouter);
517-
565+app.use('/api/openai', openAiRouter);
518-// Quick reply management
566+app.use('/api/google', googleRouter);
519567app.use('/api/quick-repliesanthropic', require('./src/endpoints/quick-replies').routeranthropicRouter);
520-
568+app.use('/api/tokenizers', tokenizersRouter);
521-// Avatar management
569+app.use('/api/presets', presetsRouter);
522570app.use('/api/avatarssecrets', require('./src/endpoints/avatars').routersecretsRouter);
523-
571+app.use('/thumbnail', thumbnailRouter);
524-// Theme management
572+app.use('/api/novelai', novelAiRouter);
525573app.use('/api/themesextensions', require('./src/endpoints/themes').routerextensionsRouter);
526-
574+app.use('/api/assets', assetsRouter);
527-// OpenAI API
575+app.use('/api/files', filesRouter);
528576app.use('/api/openaicharacters', require('./src/endpoints/openai').routercharactersRouter);
529-
577+app.use('/api/chats', chatsRouter);
530-//Google API
578+app.use('/api/groups', groupsRouter);
531579app.use('/api/googleworldinfo', require('./src/endpoints/google').routerworldInfoRouter);
532-
580+app.use('/api/stats', statsRouter);
533-//Anthropic API
581+app.use('/api/backgrounds', backgroundsRouter);
534582app.use('/api/anthropicsprites', require('./src/endpoints/anthropic').routerspritesRouter);
535-
583+app.use('/api/content', contentManagerRouter);
536-// Tokenizers
584+app.use('/api/settings', settingsRouter);
537585app.use('/api/tokenizerssd', require('./src/endpoints/tokenizers').routerstableDiffusionRouter);
538-
586+app.use('/api/horde', hordeRouter);
539-// Preset management
587+app.use('/api/vector', vectorsRouter);
540588app.use('/api/presetstranslate', require('./src/endpoints/presets').routertranslateRouter);
541-
589+app.use('/api/extra/classify', classifyRouter);
542-// Secrets managemenet
590+app.use('/api/extra/caption', captionRouter);
543591app.use('/api/secretssearch', require('./src/endpoints/secrets').routersearchRouter);
544-
592+app.use('/api/backends/text-completions', textCompletionsRouter);
545-// Thumbnail generation. These URLs are saved in chat, so this route cannot be renamed!
593+app.use('/api/openrouter', openRouterRouter);
546594app.use('/thumbnail', require('./srcapi/endpointsbackends/thumbnailskobold').router, koboldRouter);
547-
595+app.use('/api/backends/chat-completions', chatCompletionsRouter);
548-// NovelAI generation
596+app.use('/api/backends/scale-alt', scaleAltRouter);
549597app.use('/api/novelaispeech', require('./src/endpoints/novelai').routerspeechRouter);
550-
598+app.use('/api/azure', azureRouter);
551-// Third-party extensions
552-app.use('/api/extensions', require('./src/endpoints/extensions').router);
553-
554-// Asset management
555-app.use('/api/assets', require('./src/endpoints/assets').router);
556-
557-// File management
558-app.use('/api/files', require('./src/endpoints/files').router);
559-
560-// Character management
561-app.use('/api/characters', require('./src/endpoints/characters').router);
562-
563-// Chat management
564-app.use('/api/chats', require('./src/endpoints/chats').router);
565-
566-// Group management
567-app.use('/api/groups', require('./src/endpoints/groups').router);
568-
569-// World info management
570-app.use('/api/worldinfo', require('./src/endpoints/worldinfo').router);
571-
572-// Stats calculation
573-const statsEndpoint = require('./src/endpoints/stats');
574-app.use('/api/stats', statsEndpoint.router);
575-
576-// Background management
577-app.use('/api/backgrounds', require('./src/endpoints/backgrounds').router);
578-
579-// Character sprite management
580-app.use('/api/sprites', require('./src/endpoints/sprites').router);
581-
582-// Custom content management
583-app.use('/api/content', require('./src/endpoints/content-manager').router);
584-
585-// Settings load/store
586-const settingsEndpoint = require('./src/endpoints/settings');
587-app.use('/api/settings', settingsEndpoint.router);
588-
589-// Stable Diffusion generation
590-app.use('/api/sd', require('./src/endpoints/stable-diffusion').router);
591-
592-// LLM and SD Horde generation
593-app.use('/api/horde', require('./src/endpoints/horde').router);
594-
595-// Vector storage DB
596-app.use('/api/vector', require('./src/endpoints/vectors').router);
597-
598-// Chat translation
599-app.use('/api/translate', require('./src/endpoints/translate').router);
600-
601-// Emotion classification
602-app.use('/api/extra/classify', require('./src/endpoints/classify').router);
603-
604-// Image captioning
605-app.use('/api/extra/caption', require('./src/endpoints/caption').router);
606-
607-// Web search and scraping
608-app.use('/api/search', require('./src/endpoints/search').router);
609-
610-// The different text generation APIs
611-
612-// Ooba/OpenAI text completions
613-app.use('/api/backends/text-completions', require('./src/endpoints/backends/text-completions').router);
614-
615-// OpenRouter
616-app.use('/api/openrouter', require('./src/endpoints/openrouter').router);
617-
618-// KoboldAI
619-app.use('/api/backends/kobold', require('./src/endpoints/backends/kobold').router);
620-
621-// OpenAI chat completions
622-app.use('/api/backends/chat-completions', require('./src/endpoints/backends/chat-completions').router);
623-
624-// Scale (alt method)
625-app.use('/api/backends/scale-alt', require('./src/endpoints/backends/scale-alt').router);
626-
627-// Speech (text-to-speech and speech-to-text)
628-app.use('/api/speech', require('./src/endpoints/speech').router);
629-
630-// Azure TTS
631-app.use('/api/azure', require('./src/endpoints/azure').router);
632599
633600const tavernUrlV6 = new URL(
634601 (cliArguments.ssl ? 'https://' : 'http://') +
@@ -663,8 +630,8 @@ const preSetupTasks = async function () {
663630 await ensureThumbnailCache();
664631 cleanUploads();
665632
666633 await settingsEndpoint.initsettingsInit();
667634 await statsEndpoint.initstatsInit();
668635
669636 const cleanupPlugins = await loadPlugins();
670637 const consoleTitle = process.title;
@@ -673,7 +640,7 @@ const preSetupTasks = async function () {
673640 const exitProcess = async () => {
674641 if (isExiting) return;
675642 isExiting = true;
676643 statsEndpoint.onExitawait statsOnExit();
677644 if (typeof cleanupPlugins === 'function') {
678645 await cleanupPlugins();
679646 }
@@ -776,7 +743,6 @@ const postSetupTasks = async function (v6Failed, v4Failed) {
776743async function loadPlugins() {
777744 try {
778745 const pluginDirectory = path.join(serverDirectory, 'plugins');
779- const loader = require('./src/plugin-loader');
780746 const cleanupPlugins = await loader.loadPlugins(app, pluginDirectory);
781747 return cleanupPlugins;
782748 } catch {
src/additional-headers.js+6 -12
@@ -1,6 +1,6 @@
11constimport { TEXTGEN_TYPES, OPENROUTER_HEADERS, FEATHERLESS_HEADERS } =from require('./constants.js');
22constimport { SECRET_KEYS, readSecret } =from require('./endpoints/secrets.js');
33constimport { getConfigValue } =from require('./util.js');
44
55/**
66 * Gets the headers for the Mancer API.
@@ -172,7 +172,7 @@ function getHuggingFaceHeaders(directories) {
172172 }) : {};
173173}
174174
175175export function getOverrideHeaders(urlHost) {
176176 const requestOverrides = getConfigValue('requestOverrides', []);
177177 const overrideHeaders = requestOverrides?.find((e) => e.hosts?.includes(urlHost))?.headers;
178178 if (overrideHeaders && urlHost) {
@@ -188,7 +188,7 @@ function getOverrideHeaders(urlHost) {
188188 * @param {object} args New request arguments
189189 * @param {string|null} server API server for new request
190190 */
191191export function setAdditionalHeaders(request, args, server) {
192192 setAdditionalHeadersByType(args.headers, request.body.api_type, server, request.user.directories);
193193}
194194
@@ -199,7 +199,7 @@ function setAdditionalHeaders(request, args, server) {
199199 * @param {string|null} server API server for new request
200200 * @param {import('./users').UserDirectoryList} directories User directories
201201 */
202202export function setAdditionalHeadersByType(requestHeaders, type, server, directories) {
203203 const headerGetters = {
204204 [TEXTGEN_TYPES.MANCER]: getMancerHeaders,
205205 [TEXTGEN_TYPES.VLLM]: getVllmHeaders,
@@ -234,9 +234,3 @@ function setAdditionalHeadersByType(requestHeaders, type, server, directories) {
234234
235235 Object.assign(requestHeaders, headers);
236236}
237-
238-module.exports = {
239- getOverrideHeaders,
240- setAdditionalHeaders,
241- setAdditionalHeadersByType,
242-};
src/character-card-parser.js+10 -13
@@ -1,8 +1,8 @@
11constimport fs =from require('node:fs');
22
33constimport encode =from require('png-chunks-encode');
44constimport extract =from require('png-chunks-extract');
55constimport PNGtext =from require('png-chunk-text');
66
77/**
88 * Writes Character metadata to a PNG image buffer.
@@ -11,7 +11,7 @@ const PNGtext = require('png-chunk-text');
1111 * @param {string} data Character data to write
1212 * @returns {Buffer} PNG image buffer with metadata
1313 */
1414export const write = (image, data) => {
1515 const chunks = extract(image);
1616 const tEXtChunks = chunks.filter(chunk => chunk.name === 'tEXt');
1717
@@ -36,7 +36,9 @@ const write = (image, data) => {
3636
3737 const base64EncodedData = Buffer.from(JSON.stringify(v3Data), 'utf8').toString('base64');
3838 chunks.splice(-1, 0, PNGtext.encode('ccv3', base64EncodedData));
3939 } catch (error) { }
40+ // Ignore errors when adding v3 chunk
41+ }
4042
4143 const newBuffer = Buffer.from(encode(chunks));
4244 return newBuffer;
@@ -48,7 +50,7 @@ const write = (image, data) => {
4850 * @param {Buffer} image PNG image buffer
4951 * @returns {string} Character data
5052 */
5153export const read = (image) => {
5254 const chunks = extract(image);
5355
5456 const textChunks = chunks.filter((chunk) => chunk.name === 'tEXt').map((chunk) => PNGtext.decode(chunk.data));
@@ -80,7 +82,7 @@ const read = (image) => {
8082 * @param {string} format File format
8183 * @returns {string} Character data
8284 */
8385export const parse = (cardUrl, format) => {
8486 let fileFormat = format === undefined ? 'png' : format;
8587
8688 switch (fileFormat) {
@@ -93,8 +95,3 @@ const parse = (cardUrl, format) => {
9395 throw new Error('Unsupported format');
9496};
9597
96-module.exports = {
97- parse,
98- write,
99- read,
100-};
src/constants.js+21 -46
@@ -1,18 +1,18 @@
11export const PUBLIC_DIRECTORIES = {
22 images: 'public/img/',
33 backups: 'backups/',
44 sounds: 'public/sounds',
55 extensions: 'public/scripts/extensions',
66};
77
88export const SETTINGS_FILE = 'settings.json';
99
1010/**
1111 * @type {import('./users').UserDirectoryList}
1212 * @readonly
1313 * @enum {string}
1414 */
1515export const USER_DIRECTORY_TEMPLATE = Object.freeze({
1616 root: '',
1717 thumbnails: 'thumbnails',
1818 thumbnailsBg: 'thumbnails/bg',
@@ -48,7 +48,7 @@ const USER_DIRECTORY_TEMPLATE = Object.freeze({
4848 * @type {import('./users').User}
4949 * @readonly
5050 */
5151export const DEFAULT_USER = Object.freeze({
5252 handle: 'default-user',
5353 name: 'User',
5454 created: Date.now(),
@@ -58,7 +58,7 @@ const DEFAULT_USER = Object.freeze({
5858 salt: '',
5959});
6060
6161export const UNSAFE_EXTENSIONS = [
6262 '.php',
6363 '.exe',
6464 '.com',
@@ -135,7 +135,7 @@ const UNSAFE_EXTENSIONS = [
135135 '.ws',
136136];
137137
138138export const GEMINI_SAFETY = [
139139 {
140140 category: 'HARM_CATEGORY_HARASSMENT',
141141 threshold: 'BLOCK_NONE',
@@ -158,7 +158,7 @@ const GEMINI_SAFETY = [
158158 },
159159];
160160
161161export const BISON_SAFETY = [
162162 {
163163 category: 'HARM_CATEGORY_DEROGATORY',
164164 threshold: 'BLOCK_NONE',
@@ -185,7 +185,7 @@ const BISON_SAFETY = [
185185 },
186186];
187187
188188export const CHAT_COMPLETION_SOURCES = {
189189 OPENAI: 'openai',
190190 WINDOWAI: 'windowai',
191191 CLAUDE: 'claude',
@@ -205,10 +205,10 @@ const CHAT_COMPLETION_SOURCES = {
205205/**
206206 * Path to multer file uploads under the data root.
207207 */
208208export const UPLOADS_DIRECTORY = '_uploads';
209209
210210// TODO: this is copied from the client code; there should be a way to de-duplicate it eventually
211211export const TEXTGEN_TYPES = {
212212 OOBA: 'ooba',
213213 MANCER: 'mancer',
214214 VLLM: 'vllm',
@@ -225,7 +225,7 @@ const TEXTGEN_TYPES = {
225225 HUGGINGFACE: 'huggingface',
226226};
227227
228228export const INFERMATICAI_KEYS = [
229229 'model',
230230 'prompt',
231231 'max_tokens',
@@ -248,7 +248,7 @@ const INFERMATICAI_KEYS = [
248248 'logprobs',
249249];
250250
251251export const FEATHERLESS_KEYS = [
252252 'model',
253253 'prompt',
254254 'best_of',
@@ -290,9 +290,8 @@ const FEATHERLESS_KEYS = [
290290 'guided_whitespace_pattern',
291291];
292292
293-
294293// https://dreamgen.com/docs/api#openai-text
295294export const DREAMGEN_KEYS = [
296295 'model',
297296 'prompt',
298297 'max_tokens',
@@ -309,7 +308,7 @@ const DREAMGEN_KEYS = [
309308];
310309
311310// https://docs.together.ai/reference/completions
312311export const TOGETHERAI_KEYS = [
313312 'model',
314313 'prompt',
315314 'max_tokens',
@@ -325,7 +324,7 @@ const TOGETHERAI_KEYS = [
325324];
326325
327326// https://github.com/jmorganca/ollama/blob/main/docs/api.md#request-with-options
328327export const OLLAMA_KEYS = [
329328 'num_predict',
330329 'num_ctx',
331330 'stop',
@@ -345,20 +344,20 @@ const OLLAMA_KEYS = [
345344 'min_p',
346345];
347346
348347export const AVATAR_WIDTH = 512;
349348export const AVATAR_HEIGHT = 768;
350349
351350export const OPENROUTER_HEADERS = {
352351 'HTTP-Referer': 'https://sillytavern.app',
353352 'X-Title': 'SillyTavern',
354353};
355354
356355export const FEATHERLESS_HEADERS = {
357356 'HTTP-Referer': 'https://sillytavern.app',
358357 'X-Title': 'SillyTavern',
359358};
360359
361360export const OPENROUTER_KEYS = [
362361 'max_tokens',
363362 'temperature',
364363 'top_k',
@@ -378,7 +377,7 @@ const OPENROUTER_KEYS = [
378377];
379378
380379// https://github.com/vllm-project/vllm/blob/0f8a91401c89ac0a8018def3756829611b57727f/vllm/entrypoints/openai/protocol.py#L220
381380export const VLLM_KEYS = [
382381 'model',
383382 'prompt',
384383 'best_of',
@@ -419,27 +418,3 @@ const VLLM_KEYS = [
419418 'guided_decoding_backend',
420419 'guided_whitespace_pattern',
421420];
422-
423-module.exports = {
424- DEFAULT_USER,
425- SETTINGS_FILE,
426- PUBLIC_DIRECTORIES,
427- USER_DIRECTORY_TEMPLATE,
428- UNSAFE_EXTENSIONS,
429- UPLOADS_DIRECTORY,
430- GEMINI_SAFETY,
431- BISON_SAFETY,
432- TEXTGEN_TYPES,
433- CHAT_COMPLETION_SOURCES,
434- AVATAR_WIDTH,
435- AVATAR_HEIGHT,
436- TOGETHERAI_KEYS,
437- OLLAMA_KEYS,
438- INFERMATICAI_KEYS,
439- DREAMGEN_KEYS,
440- OPENROUTER_HEADERS,
441- OPENROUTER_KEYS,
442- VLLM_KEYS,
443- FEATHERLESS_KEYS,
444- FEATHERLESS_HEADERS,
445-};
src/endpoints/anthropic.js+6 -7
@@ -1,9 +1,10 @@
1-const { readSecret, SECRET_KEYS } = require('./secrets');
1+import fetch from 'node-fetch';
2-const fetch = require('node-fetch').default;
2+import express from 'express';
3-const express = require('express');
4-const { jsonParser } = require('../express-common');
53
6-const router = express.Router();
4+import { readSecret, SECRET_KEYS } from './secrets.js';
5+import { jsonParser } from '../express-common.js';
6+
7+export const router = express.Router();
78
89router.post('/caption-image', jsonParser, async (request, response) => {
910 try {
@@ -64,5 +65,3 @@ router.post('/caption-image', jsonParser, async (request, response) => {
6465 response.status(500).send('Internal server error');
6566 }
6667});
67-
68-module.exports = { router };
src/endpoints/assets.js+14 -14
@@ -1,13 +1,15 @@
11constimport * as path =from require('node:path');
22constimport * as fs =from require('node:fs');
3-const mime = require('mime-types');
3+import { finished } from 'node:stream/promises';
4-const express = require('express');
4+
55constimport sanitizemime =from require('sanitizemime-filenametypes');
6-const fetch = require('node-fetch').default;
6+import express from 'express';
7-const { finished } = require('stream/promises');
7+import sanitize from 'sanitize-filename';
8-const { UNSAFE_EXTENSIONS } = require('../constants');
8+import fetch from 'node-fetch';
9-const { jsonParser } = require('../express-common');
9+
1010constimport { clientRelativePathUNSAFE_EXTENSIONS } =from require('../utilconstants.js');
11+import { jsonParser } from '../express-common.js';
12+import { clientRelativePath } from '../util.js';
1113
1214const VALID_CATEGORIES = ['bgm', 'ambient', 'blip', 'live2d', 'vrm', 'character', 'temp'];
1315
@@ -16,7 +18,7 @@ const VALID_CATEGORIES = ['bgm', 'ambient', 'blip', 'live2d', 'vrm', 'character'
1618 * @param {string} inputFilename Input filename
1719 * @returns {{error: boolean, message?: string}} Whether validation failed, and why if so
1820 */
1921export function validateAssetFileName(inputFilename) {
2022 if (!/^[a-zA-Z0-9_\-.]+$/.test(inputFilename)) {
2123 return {
2224 error: true,
@@ -93,7 +95,7 @@ function ensureFoldersExist(directories) {
9395 }
9496}
9597
9698export const router = express.Router();
9799
98100/**
99101 * HTTP POST handler function to retrieve name of all files of a given folder path.
@@ -366,5 +368,3 @@ router.post('/character', jsonParser, async (request, response) => {
366368 return response.sendStatus(500);
367369 }
368370});
369-
370-module.exports = { router, validateAssetFileName };
src/endpoints/avatars.js+11 -13
@@ -1,16 +1,16 @@
1-const express = require('express');
1+import * as path from 'node:path';
2-const path = require('path');
2+import * as fs from 'node:fs';
3-const fs = require('fs');
4-const sanitize = require('sanitize-filename');
5-const writeFileAtomicSync = require('write-file-atomic').sync;
6-const { jsonParser, urlencodedParser } = require('../express-common');
7-const { AVATAR_WIDTH, AVATAR_HEIGHT } = require('../constants');
8-const { getImages, tryParse } = require('../util');
93
10-// image processing related library imports
4+import express from 'express';
11-const jimp = require('jimp');
5+import sanitize from 'sanitize-filename';
6+import jimp from 'jimp';
7+import { sync as writeFileAtomicSync } from 'write-file-atomic';
128
13-const router = express.Router();
9+import { jsonParser, urlencodedParser } from '../express-common.js';
10+import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js';
11+import { getImages, tryParse } from '../util.js';
12+
13+export const router = express.Router();
1414
1515router.post('/get', jsonParser, function (request, response) {
1616 var images = getImages(request.user.directories.avatars);
@@ -58,5 +58,3 @@ router.post('/upload', urlencodedParser, async (request, response) => {
5858 return response.status(400).send('Is not a valid image');
5959 }
6060});
61-
62-module.exports = { router };
src/endpoints/azure.js+6 -9
@@ -1,9 +1,10 @@
1-const { readSecret, SECRET_KEYS } = require('./secrets');
1+import fetch from 'node-fetch';
2-const fetch = require('node-fetch').default;
2+import { Router } from 'express';
3-const express = require('express');
4-const { jsonParser } = require('../express-common');
53
6-const router = express.Router();
4+import { readSecret, SECRET_KEYS } from './secrets.js';
5+import { jsonParser } from '../express-common.js';
6+
7+export const router = Router();
78
89router.post('/list', jsonParser, async (req, res) => {
910 try {
@@ -86,7 +87,3 @@ router.post('/generate', jsonParser, async (req, res) => {
8687 return res.sendStatus(500);
8788 }
8889});
89-
90-module.exports = {
91- router,
92-};
src/endpoints/backends/chat-completions.js+38 -51
@@ -1,13 +1,40 @@
11constimport express =from require('express');
22constimport fetch =from require('node-fetch').default;
33
44constimport { jsonParser } =from require('../../express-common.js');
5-const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');
5+import {
6-const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');
6+ CHAT_COMPLETION_SOURCES,
7-const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertAI21Messages, mergeMessages } = require('../../prompt-converters');
7+ GEMINI_SAFETY,
8-
8+ BISON_SAFETY,
9-const { readSecret, SECRET_KEYS } = require('../secrets');
9+ OPENROUTER_HEADERS,
10-const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');
10+} from '../../constants.js';
11+import {
12+ forwardFetchResponse,
13+ getConfigValue,
14+ tryParse,
15+ uuidv4,
16+ mergeObjectWithYaml,
17+ excludeKeysByYaml,
18+ color,
19+} from '../../util.js';
20+import {
21+ convertClaudeMessages,
22+ convertGooglePrompt,
23+ convertTextCompletionPrompt,
24+ convertCohereMessages,
25+ convertMistralMessages,
26+ convertAI21Messages,
27+ mergeMessages,
28+} from '../../prompt-converters.js';
29+
30+import { readSecret, SECRET_KEYS } from '../secrets.js';
31+import {
32+ getTokenizerModel,
33+ getSentencepiceTokenizer,
34+ getTiktokenTokenizer,
35+ sentencepieceTokenizers,
36+ TEXT_COMPLETION_MODELS,
37+} from '../tokenizers.js';
1138
1239const API_OPENAI = 'https://api.openai.com/v1';
1340const API_CLAUDE = 'https://api.anthropic.com/v1';
@@ -42,43 +69,6 @@ function postProcessPrompt(messages, type, charName, userName) {
4269}
4370
4471/**
45- * Ollama strikes back. Special boy #2's steaming routine.
46- * Wrap this abomination into proper SSE stream, again.
47- * @param {Response} jsonStream JSON stream
48- * @param {import('express').Request} request Express request
49- * @param {import('express').Response} response Express response
50- * @returns {Promise<any>} Nothing valuable
51- */
52-async function parseCohereStream(jsonStream, request, response) {
53- try {
54- const stream = new CohereStream({ stream: jsonStream.body, eventShape: { type: 'json', messageTerminator: '\n' } });
55-
56- for await (const json of stream.iterMessages()) {
57- if (json.message) {
58- const message = json.message || 'Unknown error';
59- const chunk = { error: { message: message } };
60- response.write(`data: ${JSON.stringify(chunk)}\n\n`);
61- } else if (json.event_type === 'text-generation') {
62- const text = json.text || '';
63- const chunk = { choices: [{ text }] };
64- response.write(`data: ${JSON.stringify(chunk)}\n\n`);
65- }
66- }
67-
68- console.log('Streaming request finished');
69- response.write('data: [DONE]\n\n');
70- response.end();
71- } catch (error) {
72- console.log('Error forwarding streaming response:', error);
73- if (!response.headersSent) {
74- return response.status(500).send({ error: true });
75- } else {
76- return response.end();
77- }
78- }
79-}
80-
81-/**
8272 * Sends a request to Claude API.
8373 * @param {express.Request} request Express request
8474 * @param {express.Response} response Express response
@@ -626,7 +616,7 @@ async function sendCohereRequest(request, response) {
626616 }
627617}
628618
629619export const router = express.Router();
630620
631621router.post('/status', jsonParser, async function (request, response_getstatus_openai) {
632622 if (!request.body) return response_getstatus_openai.sendStatus(400);
@@ -1069,6 +1059,3 @@ router.post('/generate', jsonParser, function (request, response) {
10691059 }
10701060});
10711061
1072-module.exports = {
1073- router,
1074-};
src/endpoints/backends/kobold.js+8 -10
@@ -1,13 +1,13 @@
1-const express = require('express');
1+import * as fs from 'node:fs';
2-const fetch = require('node-fetch').default;
2+import express from 'express';
3-const fs = require('fs');
3+import fetch from 'node-fetch';
44
55constimport { jsonParser, urlencodedParser } =from require('../../express-common.js');
66constimport { forwardFetchResponse, delay } =from require('../../util.js');
77constimport { getOverrideHeaders, setAdditionalHeaders, setAdditionalHeadersByType } =from require('../../additional-headers.js');
88constimport { TEXTGEN_TYPES } =from require('../../constants.js');
99
1010export const router = express.Router();
1111
1212router.post('/generate', jsonParser, async function (request, response_generate) {
1313 if (!request.body) return response_generate.sendStatus(400);
@@ -237,5 +237,3 @@ router.post('/transcribe-audio', urlencodedParser, async function (request, resp
237237 response.status(500).send('Internal server error');
238238 }
239239});
240-
241-module.exports = { router };
src/endpoints/backends/scale-alt.js+5 -8
@@ -1,11 +1,10 @@
11constimport express =from require('express');
22constimport fetch =from require('node-fetch').default;
33
44constimport { jsonParser } =from require('../../express-common.js');
5+import { readSecret, SECRET_KEYS } from '../secrets.js';
56
6-const { readSecret, SECRET_KEYS } = require('../secrets');
7+export const router = express.Router();
7-
8-const router = express.Router();
98
109router.post('/generate', jsonParser, async function (request, response) {
1110 if (!request.body) return response.sendStatus(400);
@@ -97,5 +96,3 @@ router.post('/generate', jsonParser, async function (request, response) {
9796 return response.sendStatus(500);
9897 }
9998});
100-
101-module.exports = { router };
src/endpoints/backends/text-completions.js+20 -13
@@ -1,14 +1,23 @@
1-const express = require('express');
1+import { Readable } from 'node:stream';
22constimport fetch =from require('node-fetch').default;
33constimport _express =from require('lodashexpress');
4-const Readable = require('stream').Readable;
4+import _ from 'lodash';
55
66constimport { jsonParser } =from require('../../express-common.js');
7-const { TEXTGEN_TYPES, TOGETHERAI_KEYS, OLLAMA_KEYS, INFERMATICAI_KEYS, OPENROUTER_KEYS, VLLM_KEYS, DREAMGEN_KEYS, FEATHERLESS_KEYS } = require('../../constants');
7+import {
8-const { forwardFetchResponse, trimV1, getConfigValue } = require('../../util');
8+ TEXTGEN_TYPES,
9-const { setAdditionalHeaders } = require('../../additional-headers');
9+ TOGETHERAI_KEYS,
10-
10+ OLLAMA_KEYS,
11-const router = express.Router();
11+ INFERMATICAI_KEYS,
12+ OPENROUTER_KEYS,
13+ VLLM_KEYS,
14+ DREAMGEN_KEYS,
15+ FEATHERLESS_KEYS,
16+} from '../../constants.js';
17+import { forwardFetchResponse, trimV1, getConfigValue } from '../../util.js';
18+import { setAdditionalHeaders } from '../../additional-headers.js';
19+
20+export const router = express.Router();
1221
1322/**
1423 * Special boy's steaming routine. Wrap this abomination into proper SSE stream.
@@ -641,5 +650,3 @@ tabby.post('/download', jsonParser, async function (request, response) {
641650router.use('/ollama', ollama);
642651router.use('/llamacpp', llamacpp);
643652router.use('/tabby', tabby);
644-
645-module.exports = { router };
src/endpoints/backgrounds.js+9 -10
@@ -1,13 +1,14 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
4-const sanitize = require('sanitize-filename');
53
6-const { jsonParser, urlencodedParser } = require('../express-common');
4+import express from 'express';
7-const { invalidateThumbnail } = require('./thumbnails');
5+import sanitize from 'sanitize-filename';
8-const { getImages } = require('../util');
96
10-const router = express.Router();
7+import { jsonParser, urlencodedParser } from '../express-common.js';
8+import { invalidateThumbnail } from './thumbnails.js';
9+import { getImages } from '../util.js';
10+
11+export const router = express.Router();
1112
1213router.post('/all', jsonParser, function (request, response) {
1314 var images = getImages(request.user.directories.backgrounds);
@@ -72,5 +73,3 @@ router.post('/upload', urlencodedParser, function (request, response) {
7273 response.sendStatus(500);
7374 }
7475});
75-
76-module.exports = { router };
src/endpoints/caption.js+6 -8
@@ -1,23 +1,23 @@
11constimport express =from require('express');
22constimport { jsonParser } =from require('../express-common.js');
3+import { getPipeline, getRawImage } from '../transformers.mjs';
34
45const TASK = 'image-to-text';
56
67export const router = express.Router();
78
89router.post('/', jsonParser, async (req, res) => {
910 try {
1011 const { image } = req.body;
1112
1213 const modulerawImage = await importgetRawImage('../transformers.mjs'image);
13- const rawImage = await module.default.getRawImage(image);
1414
1515 if (!rawImage) {
1616 console.log('Failed to parse captioned image');
1717 return res.sendStatus(400);
1818 }
1919
2020 const pipe = await module.default.getPipeline(TASK);
2121 const result = await pipe(rawImage);
2222 const text = result[0].generated_text;
2323 console.log('Image caption:', text);
@@ -28,5 +28,3 @@ router.post('/', jsonParser, async (req, res) => {
2828 return res.sendStatus(500);
2929 }
3030});
31-
32-module.exports = { router };
src/endpoints/characters.js+22 -24
@@ -1,24 +1,24 @@
11constimport * as path =from require('node:path');
22constimport * as fs =from require('node:fs');
3-const fsPromises = require('fs').promises;
3+import { promises as fsPromises } from 'node:fs';
44constimport * as readline =from require('node:readline');
5-const express = require('express');
5+
6-const sanitize = require('sanitize-filename');
6+import express from 'express';
7-const writeFileAtomicSync = require('write-file-atomic').sync;
7+import sanitize from 'sanitize-filename';
8-const yaml = require('yaml');
8+import { sync as writeFileAtomicSync } from 'write-file-atomic';
99constimport _yaml =from require('lodashyaml');
10-const mime = require('mime-types');
10+import _ from 'lodash';
11-
11+import mime from 'mime-types';
1212constimport jimp =from require('jimp');
1313
1414constimport { AVATAR_WIDTH, AVATAR_HEIGHT } =from require('../constants.js');
1515constimport { jsonParser, urlencodedParser } =from require('../express-common.js');
1616constimport { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer } =from require('../util.js');
1717constimport { TavernCardValidator } =from require('../validator/TavernCardValidator.js');
1818constimport * as characterCardParser =from require('../character-card-parser.js');
1919constimport { readWorldInfoFile } =from require('./worldinfo.js');
2020constimport { invalidateThumbnail } =from require('./thumbnails.js');
2121constimport { importRisuSprites } =from require('./sprites.js');
2222const defaultAvatarPath = './public/img/ai4.png';
2323
2424// KV-store for parsed character data
@@ -715,7 +715,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) {
715715 return '';
716716}
717717
718718export const router = express.Router();
719719
720720router.post('/create', urlencodedParser, async function (request, response) {
721721 try {
@@ -1231,5 +1231,3 @@ router.post('/export', jsonParser, async function (request, response) {
12311231 response.sendStatus(500);
12321232 }
12331233});
1234-
1235-module.exports = { router };
src/endpoints/chats.js+11 -12
@@ -1,13 +1,14 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
33constimport * as readline =from require('node:readline');
4-const express = require('express');
5-const sanitize = require('sanitize-filename');
6-const writeFileAtomicSync = require('write-file-atomic').sync;
7-const _ = require('lodash');
84
9-const { jsonParser, urlencodedParser } = require('../express-common');
5+import express from 'express';
10-const { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } = require('../util');
6+import sanitize from 'sanitize-filename';
7+import { sync as writeFileAtomicSync } from 'write-file-atomic';
8+import _ from 'lodash';
9+
10+import { jsonParser, urlencodedParser } from '../express-common.js';
11+import { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } from '../util.js';
1112
1213/**
1314 * Saves a chat to the backups directory.
@@ -188,7 +189,7 @@ function flattenChubChat(userName, characterName, lines) {
188189 return (lines ?? []).map(convert).join('\n');
189190}
190191
191192export const router = express.Router();
192193
193194router.post('/save', jsonParser, function (request, response) {
194195 try {
@@ -514,5 +515,3 @@ router.post('/group/save', jsonParser, (request, response) => {
514515 getBackupFunction(request.user.profile.handle)(request.user.directories.backups, String(id), jsonlData);
515516 return response.send({ ok: true });
516517});
517-
518-module.exports = { router };
src/endpoints/classify.js+7 -9
@@ -1,9 +1,11 @@
11constimport express =from require('express');
2-const { jsonParser } = require('../express-common');
2+
3+import { getPipeline } from '../transformers.mjs';
4+import { jsonParser } from '../express-common.js';
35
46const TASK = 'text-classification';
57
68export const router = express.Router();
79
810/**
911 * @type {Map<string, object>} Cache for classification results
@@ -12,8 +14,7 @@ const cacheObject = new Map();
1214
1315router.post('/labels', jsonParser, async (req, res) => {
1416 try {
1517 const modulepipe = await importgetPipeline('../transformers.mjs'TASK);
16- const pipe = await module.default.getPipeline(TASK);
1718 const result = Object.keys(pipe.model.config.label2id);
1819 return res.json({ labels: result });
1920 } catch (error) {
@@ -35,8 +36,7 @@ router.post('/', jsonParser, async (req, res) => {
3536 if (cacheObject.has(text)) {
3637 return cacheObject.get(text);
3738 } else {
3839 const modulepipe = await importgetPipeline('../transformers.mjs'TASK);
39- const pipe = await module.default.getPipeline(TASK);
4040 const result = await pipe(text, { topk: 5 });
4141 result.sort((a, b) => b.score - a.score);
4242 cacheObject.set(text, result);
@@ -54,5 +54,3 @@ router.post('/', jsonParser, async (req, res) => {
5454 return res.sendStatus(500);
5555 }
5656});
57-
58-module.exports = { router };
src/endpoints/content-manager.js+17 -24
@@ -1,16 +1,18 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
3+
4-const fetch = require('node-fetch').default;
4+import express from 'express';
55constimport sanitizefetch =from require('sanitizenode-filenamefetch');
6-const { getConfigValue, color } = require('../util');
6+import sanitize from 'sanitize-filename';
7-const { jsonParser } = require('../express-common');
7+
8-const writeFileAtomicSync = require('write-file-atomic').sync;
8+import { getConfigValue, color } from '../util.js';
9+import { jsonParser } from '../express-common.js';
10+import { sync as writeFileAtomicSync } from 'write-file-atomic';
911const contentDirectory = path.join(process.cwd(), 'default/content');
1012const scaffoldDirectory = path.join(process.cwd(), 'default/scaffold');
1113const contentIndexPath = path.join(contentDirectory, 'index.json');
1214const scaffoldIndexPath = path.join(scaffoldDirectory, 'index.json');
1315constimport * as characterCardParser =from require('../character-card-parser.js');
1416
1517const WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES = getConfigValue('whitelistImportDomains', []);
1618
@@ -26,7 +28,7 @@ const WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES = getConfigValue('whitelistImportDo
2628 * @typedef {string} ContentType
2729 * @enum {string}
2830 */
2931export const CONTENT_TYPES = {
3032 SETTINGS: 'settings',
3133 CHARACTER: 'character',
3234 SPRITES: 'sprites',
@@ -51,7 +53,7 @@ const CONTENT_TYPES = {
5153 * @param {import('../users').UserDirectoryList} directories User directories
5254 * @returns {object[]} Array of default presets
5355 */
5456export function getDefaultPresets(directories) {
5557 try {
5658 const contentIndex = getContentIndex();
5759 const presets = [];
@@ -76,7 +78,7 @@ function getDefaultPresets(directories) {
7678 * @param {string} filename Name of the file to get
7779 * @returns {object | null} JSON object or null if the file doesn't exist
7880 */
7981export function getDefaultPresetFile(filename) {
8082 try {
8183 const contentPath = path.join(contentDirectory, filename);
8284
@@ -158,7 +160,7 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
158160 * @param {string[]} forceCategories List of categories to force check (even if content check is skipped)
159161 * @returns {Promise<void>}
160162 */
161163export async function checkForNewContent(directoriesList, forceCategories = []) {
162164 try {
163165 const contentCheckSkip = getConfigValue('skipContentCheck', false);
164166 if (contentCheckSkip && forceCategories?.length === 0) {
@@ -224,7 +226,7 @@ function getContentIndex() {
224226 * @param {'json'|'string'|'raw'} format Format of content
225227 * @returns {string[]|Buffer[]} Array of content
226228 */
227229export function getContentOfType(type, format) {
228230 const contentIndex = getContentIndex();
229231 const indexItems = contentIndex.filter((item) => item.type === type && item.folder);
230232 const files = [];
@@ -618,7 +620,7 @@ function isHostWhitelisted(host) {
618620 return WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES.includes(host);
619621}
620622
621623export const router = express.Router();
622624
623625router.post('/importURL', jsonParser, async (request, response) => {
624626 if (!request.body.url) {
@@ -753,12 +755,3 @@ router.post('/importUUID', jsonParser, async (request, response) => {
753755 return response.sendStatus(500);
754756 }
755757});
756-
757-module.exports = {
758- CONTENT_TYPES,
759- checkForNewContent,
760- getDefaultPresets,
761- getDefaultPresetFile,
762- getContentOfType,
763- router,
764-};
src/endpoints/extensions.js+10 -10
@@ -1,10 +1,12 @@
11constimport * as path =from require('node:path');
22constimport * as fs =from require('node:fs');
3-const express = require('express');
3+
4-const { default: simpleGit } = require('simple-git');
4+import express from 'express';
55constimport sanitize =from require('sanitize-filename');
66constimport { PUBLIC_DIRECTORIESdefault as simpleGit } =from require('../constantssimple-git');
7-const { jsonParser } = require('../express-common');
7+
8+import { PUBLIC_DIRECTORIES } from '../constants.js';
9+import { jsonParser } from '../express-common.js';
810
911/**
1012 * This function extracts the extension information from the manifest file.
@@ -47,7 +49,7 @@ async function checkIfRepoIsUpToDate(extensionPath) {
4749 };
4850}
4951
5052export const router = express.Router();
5153
5254/**
5355 * HTTP POST handler function to clone a git repository from a provided URL, read the extension manifest,
@@ -240,5 +242,3 @@ router.get('/discover', jsonParser, function (request, response) {
240242
241243 return response.send(extensions);
242244});
243-
244-module.exports = { router };
src/endpoints/files.js+12 -11
@@ -1,12 +1,15 @@
11constimport * as path =from require('node:path');
22constimport * as fs =from require('node:fs');
3-const writeFileSyncAtomic = require('write-file-atomic').sync;
3+
44constimport express =from require('express');
55constimport sanitize =from require('sanitize-filename');
6-const router = express.Router();
6+import { sync as writeFileSyncAtomic } from 'write-file-atomic';
7-const { validateAssetFileName } = require('./assets');
7+
88constimport { jsonParservalidateAssetFileName } =from require('../express-commonassets.js');
99constimport { clientRelativePathjsonParser } =from require('../utilexpress-common.js');
10+import { clientRelativePath } from '../util.js';
11+
12+export const router = express.Router();
1013
1114router.post('/sanitize-filename', jsonParser, async (request, response) => {
1215 try {
@@ -97,5 +100,3 @@ router.post('/verify', jsonParser, async (request, response) => {
97100 return response.sendStatus(500);
98101 }
99102});
100-
101-module.exports = { router };
src/endpoints/google.js+7 -8
@@ -1,12 +1,13 @@
1-const { readSecret, SECRET_KEYS } = require('./secrets');
1+import fetch from 'node-fetch';
2-const fetch = require('node-fetch').default;
2+import express from 'express';
3-const express = require('express');
3+
44constimport { jsonParserreadSecret, SECRET_KEYS } =from require('../express-commonsecrets.js');
55constimport { GEMINI_SAFETYjsonParser } = from require('../constantsexpress-common.js');
6+import { GEMINI_SAFETY } from '../constants.js';
67
78const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
89
910export const router = express.Router();
1011
1112router.post('/caption-image', jsonParser, async (request, response) => {
1213 try {
@@ -67,5 +68,3 @@ router.post('/caption-image', jsonParser, async (request, response) => {
6768 response.status(500).send('Internal server error');
6869 }
6970});
70-
71-module.exports = { router };
src/endpoints/groups.js+9 -10
@@ -1,13 +1,14 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
4-const sanitize = require('sanitize-filename');
5-const writeFileAtomicSync = require('write-file-atomic').sync;
63
7-const { jsonParser } = require('../express-common');
4+import express from 'express';
8-const { humanizedISO8601DateTime } = require('../util');
5+import sanitize from 'sanitize-filename';
6+import { sync as writeFileAtomicSync } from 'write-file-atomic';
97
10-const router = express.Router();
8+import { jsonParser } from '../express-common.js';
9+import { humanizedISO8601DateTime } from '../util.js';
10+
11+export const router = express.Router();
1112
1213router.post('/all', jsonParser, (request, response) => {
1314 const groups = [];
@@ -131,5 +132,3 @@ router.post('/delete', jsonParser, async (request, response) => {
131132
132133 return response.send({ ok: true });
133134});
134-
135-module.exports = { router };
src/endpoints/horde.js+7 -9
@@ -1,14 +1,14 @@
11constimport fetch =from require('node-fetch').default;
22constimport express =from require('express');
33constimport { AIHorde, ModelGenerationInputStableSamplers, ModelInterrogationFormTypes, HordeAsyncRequestStates } =from require('@zeldafan0225/ai_horde');
44constimport { getVersion, delay, Cache } =from require('../util.js');
55constimport { readSecret, SECRET_KEYS } =from require('./secrets.js');
66constimport { jsonParser } =from require('../express-common.js');
77
88const ANONYMOUS_KEY = '0000000000';
99const HORDE_TEXT_MODEL_METADATA_URL = 'https://raw.githubusercontent.com/db0/AI-Horde-text-model-reference/main/db.json';
1010const cache = new Cache(60 * 1000);
1111export const router = express.Router();
1212
1313/**
1414 * Returns the AIHorde client agent.
@@ -403,5 +403,3 @@ router.post('/generate-image', jsonParser, async (request, response) => {
403403 return response.sendStatus(500);
404404 }
405405});
406-
407-module.exports = { router };
src/endpoints/images.js+8 -9
@@ -1,10 +1,11 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
4-const sanitize = require('sanitize-filename');
53
6-const { jsonParser } = require('../express-common');
4+import express from 'express';
7-const { clientRelativePath, removeFileExtension, getImages } = require('../util');
5+import sanitize from 'sanitize-filename';
6+
7+import { jsonParser } from '../express-common.js';
8+import { clientRelativePath, removeFileExtension, getImages } from '../util.js';
89
910/**
1011 * Ensure the directory for the provided file path exists.
@@ -21,7 +22,7 @@ function ensureDirectoryExistence(filePath) {
2122 fs.mkdirSync(dirname);
2223}
2324
2425export const router = express.Router();
2526
2627/**
2728 * Endpoint to handle image uploads.
@@ -89,5 +90,3 @@ router.post('/list/:folder', (request, response) => {
8990 return response.status(500).send({ error: 'Unable to retrieve files' });
9091 }
9192});
92-
93-module.exports = { router };
src/endpoints/moving-ui.js+6 -8
@@ -1,11 +1,11 @@
11constimport * as path =from require('node:path');
22constimport express =from require('express');
33constimport sanitize =from require('sanitize-filename');
44constimport { sync as writeFileAtomicSync =} require(from 'write-file-atomic').sync;
55
66constimport { jsonParser } =from require('../express-common.js');
77
88export const router = express.Router();
99
1010router.post('/save', jsonParser, (request, response) => {
1111 if (!request.body || !request.body.name) {
@@ -17,5 +17,3 @@ router.post('/save', jsonParser, (request, response) => {
1717
1818 return response.sendStatus(200);
1919});
20-
21-module.exports = { router };
src/endpoints/novelai.js+9 -9
@@ -1,9 +1,11 @@
1-const fetch = require('node-fetch').default;
1+import * as util from 'node:util';
2-const express = require('express');
2+
3-const util = require('util');
3+import fetch from 'node-fetch';
4-const { readSecret, SECRET_KEYS } = require('./secrets');
4+import express from 'express';
5-const { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } = require('../util');
5+
66constimport { jsonParserreadSecret, SECRET_KEYS } =from require('../express-commonsecrets.js');
7+import { readAllChunks, extractFileFromZipBuffer, forwardFetchResponse } from '../util.js';
8+import { jsonParser } from '../express-common.js';
79
810const API_NOVELAI = 'https://api.novelai.net';
911const TEXT_NOVELAI = 'https://text.novelai.net';
@@ -110,7 +112,7 @@ function getRepPenaltyWhitelist(model) {
110112 return null;
111113}
112114
113115export const router = express.Router();
114116
115117router.post('/status', jsonParser, async function (req, res) {
116118 if (!req.body) return res.sendStatus(400);
@@ -432,5 +434,3 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
432434 return response.sendStatus(500);
433435 }
434436});
435-
436-module.exports = { router };
src/endpoints/openai.js+13 -13
@@ -1,14 +1,16 @@
1-const { readSecret, SECRET_KEYS } = require('./secrets');
1+import * as fs from 'node:fs';
2-const fetch = require('node-fetch').default;
2+
3-const express = require('express');
3+import fetch from 'node-fetch';
44constimport FormData =from require('form-data');
55constimport fsexpress =from require('fsexpress');
6-const { jsonParser, urlencodedParser } = require('../express-common');
6+
77constimport { getConfigValue, mergeObjectWithYaml, excludeKeysByYamljsonParser, trimV1urlencodedParser } =from require('../utilexpress-common.js');
88constimport { setAdditionalHeadersgetConfigValue, mergeObjectWithYaml, excludeKeysByYaml, trimV1 } =from require('../additional-headersutil.js');
99constimport { OPENROUTER_HEADERSsetAdditionalHeaders } =from require('../constantsadditional-headers.js');
10-
10+import { readSecret, SECRET_KEYS } from './secrets.js';
11-const router = express.Router();
11+import { OPENROUTER_HEADERS } from '../constants.js';
12+
13+export const router = express.Router();
1214
1315router.post('/caption-image', jsonParser, async (request, response) => {
1416 try {
@@ -341,5 +343,3 @@ custom.post('/generate-voice', jsonParser, async (request, response) => {
341343});
342344
343345router.use('/custom', custom);
344-
345-module.exports = { router };
src/endpoints/openrouter.js+3 -5
@@ -1,7 +1,7 @@
11constimport express =from require('express');
22constimport { jsonParser } =from require('../express-common.js');
33
44export const router = express.Router();
55const API_OPENROUTER = 'https://openrouter.ai/api/v1';
66
77router.post('/models/multimodal', jsonParser, async (_req, res) => {
@@ -28,5 +28,3 @@ router.post('/models/multimodal', jsonParser, async (_req, res) => {
2828 return res.sendStatus(500);
2929 }
3030});
31-
32-module.exports = { router };
src/endpoints/presets.js+10 -10
@@ -1,10 +1,12 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
3+
4-const sanitize = require('sanitize-filename');
4+import express from 'express';
5-const writeFileAtomicSync = require('write-file-atomic').sync;
5+import sanitize from 'sanitize-filename';
66constimport { getDefaultPresetFile,sync getDefaultPresetsas writeFileAtomicSync } =from require('./contentwrite-managerfile-atomic');
7-const { jsonParser } = require('../express-common');
7+
8+import { getDefaultPresetFile, getDefaultPresets } from './content-manager.js';
9+import { jsonParser } from '../express-common.js';
810
911/**
1012 * Gets the folder and extension for the preset settings based on the API source ID.
@@ -34,7 +36,7 @@ function getPresetSettingsByAPI(apiId, directories) {
3436 }
3537}
3638
3739export const router = express.Router();
3840
3941router.post('/save', jsonParser, function (request, response) {
4042 const name = sanitize(request.body.name);
@@ -127,5 +129,3 @@ router.post('/delete-openai', jsonParser, function (request, response) {
127129
128130 return response.send({ error: true });
129131});
130-
131-module.exports = { router };
src/endpoints/quick-replies.js+8 -9
@@ -1,12 +1,13 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
4-const sanitize = require('sanitize-filename');
5-const writeFileAtomicSync = require('write-file-atomic').sync;
63
7-const { jsonParser } = require('../express-common');
4+import express from 'express';
5+import sanitize from 'sanitize-filename';
6+import { sync as writeFileAtomicSync } from 'write-file-atomic';
87
9-const router = express.Router();
8+import { jsonParser } from '../express-common.js';
9+
10+export const router = express.Router();
1011
1112router.post('/save', jsonParser, (request, response) => {
1213 if (!request.body || !request.body.name) {
@@ -31,5 +32,3 @@ router.post('/delete', jsonParser, (request, response) => {
3132
3233 return response.sendStatus(200);
3334});
34-
35-module.exports = { router };
src/endpoints/search.js+8 -9
@@ -1,9 +1,11 @@
11constimport fetch =from require('node-fetch').default;
22constimport express =from require('express');
3-const { readSecret, SECRET_KEYS } = require('./secrets');
4-const { jsonParser } = require('../express-common');
53
6-const router = express.Router();
4+import { decode } from 'html-entities';
5+import { readSecret, SECRET_KEYS } from './secrets.js';
6+import { jsonParser } from '../express-common.js';
7+
8+export const router = express.Router();
79
810// Cosplay as Chrome
911const visitHeaders = {
@@ -29,7 +31,6 @@ const visitHeaders = {
2931 * @returns {Promise<string>} Transcript text
3032 */
3133async function extractTranscript(videoPageBody, lang) {
32- const he = require('he');
3334 const RE_XML_TRANSCRIPT = /<text start="([^"]*)" dur="([^"]*)">([^<]*)<\/text>/g;
3435 const splittedHTML = videoPageBody.split('"captions":');
3536
@@ -84,7 +85,7 @@ async function extractTranscript(videoPageBody, lang) {
8485 lang: lang ?? captions.captionTracks[0].languageCode,
8586 }));
8687 // The text is double-encoded
8788 const transcriptText = transcript.map((line) => he.decode(he.decode(line.text))).join(' ');
8889 return transcriptText;
8990}
9091
@@ -263,5 +264,3 @@ router.post('/visit', jsonParser, async (request, response) => {
263264 return response.sendStatus(500);
264265 }
265266});
266-
267-module.exports = { router };
src/endpoints/secrets.js+16 -25
@@ -1,12 +1,13 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
3+
4-const { getConfigValue } = require('../util');
4+import express from 'express';
55constimport { sync as writeFileAtomicSync =} require(from 'write-file-atomic').sync;
66constimport { jsonParsergetConfigValue } =from require('../express-commonutil.js');
7-
7+import { jsonParser } from '../express-common.js';
8-const SECRETS_FILE = 'secrets.json';
8+
9-const SECRET_KEYS = {
9+export const SECRETS_FILE = 'secrets.json';
10+export const SECRET_KEYS = {
1011 HORDE: 'api_key_horde',
1112 MANCER: 'api_key_mancer',
1213 VLLM: 'api_key_vllm',
@@ -62,7 +63,7 @@ const EXPORTABLE_KEYS = [
6263 * @param {string} key Secret key
6364 * @param {string} value Secret value
6465 */
6566export function writeSecret(directories, key, value) {
6667 const filePath = path.join(directories.root, SECRETS_FILE);
6768
6869 if (!fs.existsSync(filePath)) {
@@ -82,7 +83,7 @@ function writeSecret(directories, key, value) {
8283 * @param {string} key Secret key
8384 * @returns
8485 */
8586export function deleteSecret(directories, key) {
8687 const filePath = path.join(directories.root, SECRETS_FILE);
8788
8889 if (!fs.existsSync(filePath)) {
@@ -101,7 +102,7 @@ function deleteSecret(directories, key) {
101102 * @param {string} key Secret key
102103 * @returns {string} Secret value
103104 */
104105export function readSecret(directories, key) {
105106 const filePath = path.join(directories.root, SECRETS_FILE);
106107
107108 if (!fs.existsSync(filePath)) {
@@ -118,7 +119,7 @@ function readSecret(directories, key) {
118119 * @param {import('../users').UserDirectoryList} directories User directories
119120 * @returns {object} Secret state
120121 */
121122export function readSecretState(directories) {
122123 const filePath = path.join(directories.root, SECRETS_FILE);
123124
124125 if (!fs.existsSync(filePath)) {
@@ -141,7 +142,7 @@ function readSecretState(directories) {
141142 * @param {import('../users').UserDirectoryList} directories User directories
142143 * @returns {Record<string, string> | undefined} Secrets
143144 */
144145export function getAllSecrets(directories) {
145146 const filePath = path.join(directories.root, SECRETS_FILE);
146147
147148 if (!fs.existsSync(filePath)) {
@@ -154,7 +155,7 @@ function getAllSecrets(directories) {
154155 return secrets;
155156}
156157
157158export const router = express.Router();
158159
159160router.post('/write', jsonParser, (request, response) => {
160161 const key = request.body.key;
@@ -218,13 +219,3 @@ router.post('/find', jsonParser, (request, response) => {
218219 return response.sendStatus(500);
219220 }
220221});
221-
222-module.exports = {
223- writeSecret,
224- readSecret,
225- deleteSecret,
226- readSecretState,
227- getAllSecrets,
228- SECRET_KEYS,
229- router,
230-};
src/endpoints/settings.js+13 -13
@@ -1,12 +1,14 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
3+
44constimport _express =from require('lodashexpress');
5-const writeFileAtomicSync = require('write-file-atomic').sync;
5+import _ from 'lodash';
6-const { SETTINGS_FILE } = require('../constants');
6+import { sync as writeFileAtomicSync } from 'write-file-atomic';
7-const { getConfigValue, generateTimestamp, removeOldBackups } = require('../util');
7+
88constimport { jsonParserSETTINGS_FILE } =from require('../express-commonconstants.js');
99constimport { getAllUserHandlesgetConfigValue, getUserDirectoriesgenerateTimestamp, removeOldBackups } =from require('../usersutil.js');
10+import { jsonParser } from '../express-common.js';
11+import { getAllUserHandles, getUserDirectories } from '../users.js';
1012
1113const ENABLE_EXTENSIONS = getConfigValue('enableExtensions', true);
1214const ENABLE_EXTENSIONS_AUTO_UPDATE = getConfigValue('enableExtensionsAutoUpdate', true);
@@ -190,7 +192,7 @@ function getLatestBackup(handle) {
190192 return path.join(userDirectories.backups, latestBackup);
191193}
192194
193195export const router = express.Router();
194196
195197router.post('/save', jsonParser, function (request, response) {
196198 try {
@@ -357,8 +359,6 @@ router.post('/restore-snapshot', jsonParser, async (request, response) => {
357359/**
358360 * Initializes the settings endpoint
359361 */
360362export async function init() {
361363 await backupSettings();
362364}
363-
364-module.exports = { router, init };
src/endpoints/speech.js+7 -11
@@ -1,7 +1,9 @@
11constimport express =from require('express');
2-const { jsonParser } = require('../express-common');
2+import wavefile from 'wavefile';
3+import { jsonParser } from '../express-common.js';
4+import { getPipeline } from '../transformers.mjs';
35
46export const router = express.Router();
57
68/**
79 * Gets the audio data from a base64-encoded audio file.
@@ -9,7 +11,6 @@ const router = express.Router();
911 * @returns {Float64Array} Audio data
1012 */
1113function getWaveFile(audio) {
12- const wavefile = require('wavefile');
1314 const wav = new wavefile.WaveFile();
1415 wav.fromDataURI(audio);
1516 wav.toBitDepth('32f');
@@ -36,8 +37,7 @@ router.post('/recognize', jsonParser, async (req, res) => {
3637 try {
3738 const TASK = 'automatic-speech-recognition';
3839 const { model, audio, lang } = req.body;
3940 const modulepipe = await importgetPipeline('../transformers.mjs'TASK, model);
40- const pipe = await module.default.getPipeline(TASK, model);
4141 const wav = getWaveFile(audio);
4242 const start = performance.now();
4343 const result = await pipe(wav, { language: lang || null, task: 'transcribe' });
@@ -54,11 +54,9 @@ router.post('/recognize', jsonParser, async (req, res) => {
5454
5555router.post('/synthesize', jsonParser, async (req, res) => {
5656 try {
57- const wavefile = require('wavefile');
5857 const TASK = 'text-to-speech';
5958 const { text, model, speaker } = req.body;
6059 const modulepipe = await importgetPipeline('../transformers.mjs'TASK, model);
61- const pipe = await module.default.getPipeline(TASK, model);
6260 const speaker_embeddings = speaker
6361 ? new Float32Array(new Uint8Array(Buffer.from(speaker.startsWith('data:') ? speaker.split(',')[1] : speaker, 'base64')).buffer)
6462 : null;
@@ -78,5 +76,3 @@ router.post('/synthesize', jsonParser, async (req, res) => {
7876 return res.sendStatus(500);
7977 }
8078});
81-
82-module.exports = { router };
src/endpoints/sprites.js+11 -15
@@ -1,12 +1,13 @@
1+import * as fs from 'node:fs';
2+import * as path from 'node:path';
13
24constimport fsexpress =from require('fsexpress');
3-const path = require('path');
5+import mime from 'mime-types';
4-const express = require('express');
6+import sanitize from 'sanitize-filename';
5-const mime = require('mime-types');
7+import { sync as writeFileAtomicSync } from 'write-file-atomic';
6-const sanitize = require('sanitize-filename');
8+
7-const writeFileAtomicSync = require('write-file-atomic').sync;
9+import { getImageBuffers } from '../util.js';
810constimport { getImageBuffersjsonParser, urlencodedParser } =from require('../utilexpress-common.js');
9-const { jsonParser, urlencodedParser } = require('../express-common');
1011
1112/**
1213 * Gets the path to the sprites folder for the provided character name
@@ -45,7 +46,7 @@ function getSpritesPath(directories, name, isSubfolder) {
4546 * @param {object} data RisuAI character data
4647 * @returns {void}
4748 */
4849export function importRisuSprites(directories, data) {
4950 try {
5051 const name = data?.data?.name;
5152 const risuData = data?.data?.extensions?.risuai;
@@ -106,7 +107,7 @@ function importRisuSprites(directories, data) {
106107 }
107108}
108109
109110export const router = express.Router();
110111
111112router.get('/get', jsonParser, function (request, response) {
112113 const name = String(request.query.name);
@@ -259,8 +260,3 @@ router.post('/upload', urlencodedParser, async (request, response) => {
259260 return response.sendStatus(500);
260261 }
261262});
262-
263-module.exports = {
264- router,
265- importRisuSprites,
266-};
src/endpoints/stable-diffusion.js+13 -13
@@ -1,13 +1,15 @@
1-const express = require('express');
1+import * as fs from 'node:fs';
2-const fetch = require('node-fetch').default;
2+import * as path from 'node:path';
3-const sanitize = require('sanitize-filename');
3+
4-const { getBasicAuthHeader, delay, getHexString } = require('../util.js');
4+import express from 'express';
5-const fs = require('fs');
5+import fetch from 'node-fetch';
6-const path = require('path');
6+import sanitize from 'sanitize-filename';
77constimport { sync as writeFileAtomicSync =} require(from 'write-file-atomic').sync;
8-const { jsonParser } = require('../express-common');
8+import FormData from 'form-data';
9-const { readSecret, SECRET_KEYS } = require('./secrets.js');
9+
10-const FormData = require('form-data');
10+import { getBasicAuthHeader, delay, getHexString } from '../util.js';
11+import { jsonParser } from '../express-common.js';
12+import { readSecret, SECRET_KEYS } from './secrets.js';
1113
1214/**
1315 * Gets the comfy workflows.
@@ -21,7 +23,7 @@ function getComfyWorkflows(directories) {
2123 .sort(Intl.Collator().compare);
2224}
2325
2426export const router = express.Router();
2527
2628router.post('/ping', jsonParser, async (request, response) => {
2729 try {
@@ -993,5 +995,3 @@ router.use('/pollinations', pollinations);
993995router.use('/stability', stability);
994996router.use('/blockentropy', blockentropy);
995997router.use('/huggingface', huggingface);
996-
997-module.exports = { router };
src/endpoints/stats.js+12 -18
@@ -1,14 +1,15 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
3+import * as crypto from 'node:crypto';
4-const writeFileAtomic = require('write-file-atomic');
4+
55constimport cryptoexpress =from require('cryptoexpress');
6+import writeFileAtomic from 'write-file-atomic';
67
78const readFile = fs.promises.readFile;
89const readdir = fs.promises.readdir;
910
1011constimport { jsonParser } =from require('../express-common.js');
1112constimport { getAllUserHandles, getUserDirectories } =from require('../users.js');
1213
1314const STATS_FILE = 'stats.json';
1415
@@ -146,7 +147,7 @@ async function collectAndCreateStats(chatsPath, charactersPath) {
146147 * @param {string} chatsPath Path to the directory containing the chat files.
147148 * @param {string} charactersPath Path to the directory containing the character files.
148149 */
149150export async function recreateStats(handle, chatsPath, charactersPath) {
150151 console.log('Collecting and creating stats for user:', handle);
151152 const stats = await collectAndCreateStats(chatsPath, charactersPath);
152153 STATS.set(handle, stats);
@@ -157,7 +158,7 @@ async function recreateStats(handle, chatsPath, charactersPath) {
157158 * Loads the stats file into memory. If the file doesn't exist or is invalid,
158159 * initializes stats by collecting and creating them for each character.
159160 */
160161export async function init() {
161162 try {
162163 const userHandles = await getAllUserHandles();
163164 for (const handle of userHandles) {
@@ -209,7 +210,7 @@ async function saveStatsToFile() {
209210 * Attempts to save charStats to a file and then terminates the process.
210211 * If an error occurs during the file write, it logs the error before exiting.
211212 */
212213export async function onExit() {
213214 try {
214215 await saveStatsToFile();
215216 } catch (err) {
@@ -434,7 +435,7 @@ function calculateTotalGenTimeAndWordCount(
434435 };
435436}
436437
437438export const router = express.Router();
438439
439440/**
440441 * Handle a POST request to get the stats object
@@ -465,10 +466,3 @@ router.post('/update', jsonParser, function (request, response) {
465466 setCharStats(request.user.profile.handle, request.body);
466467 return response.sendStatus(200);
467468});
468-
469-module.exports = {
470- router,
471- recreateStats,
472- init,
473- onExit,
474-};
src/endpoints/themes.js+9 -9
@@ -1,11 +1,13 @@
1-const express = require('express');
1+import * as path from 'node:path';
2-const path = require('path');
2+import * as fs from 'node:fs';
3-const fs = require('fs');
4-const sanitize = require('sanitize-filename');
5-const writeFileAtomicSync = require('write-file-atomic').sync;
6-const { jsonParser } = require('../express-common');
73
8-const router = express.Router();
4+import express from 'express';
5+import sanitize from 'sanitize-filename';
6+import { sync as writeFileAtomicSync } from 'write-file-atomic';
7+
8+import { jsonParser } from '../express-common.js';
9+
10+export const router = express.Router();
911
1012router.post('/save', jsonParser, (request, response) => {
1113 if (!request.body || !request.body.name) {
@@ -36,5 +38,3 @@ router.post('/delete', jsonParser, function (request, response) {
3638 return response.sendStatus(500);
3739 }
3840});
39-
40-module.exports = { router };
src/endpoints/thumbnails.js+16 -20
@@ -1,14 +1,16 @@
11constimport * as fs =from require('node:fs');
2-const fsPromises = require('fs').promises;
2+import * as fsPromises from 'node:fs/promises';
33constimport * as path =from require('node:path');
4-const mime = require('mime-types');
4+
5-const express = require('express');
5+import mime from 'mime-types';
6-const sanitize = require('sanitize-filename');
6+import express from 'express';
7-const jimp = require('jimp');
7+import sanitize from 'sanitize-filename';
8-const writeFileAtomicSync = require('write-file-atomic').sync;
8+import jimp from 'jimp';
9-const { getAllUserHandles, getUserDirectories } = require('../users');
9+import { sync as writeFileAtomicSync } from 'write-file-atomic';
10-const { getConfigValue } = require('../util');
10+
1111constimport { jsonParsergetAllUserHandles, getUserDirectories } =from require('../express-commonusers.js');
12+import { getConfigValue } from '../util.js';
13+import { jsonParser } from '../express-common.js';
1214
1315const thumbnailsDisabled = getConfigValue('disableThumbnails', false);
1416const quality = getConfigValue('thumbnailsQuality', 95);
@@ -62,7 +64,7 @@ function getOriginalFolder(directories, type) {
6264 * @param {'bg' | 'avatar'} type Type of the thumbnail
6365 * @param {string} file Name of the file
6466 */
6567export function invalidateThumbnail(directories, type, file) {
6668 const folder = getThumbnailFolder(directories, type);
6769 if (folder === undefined) throw new Error('Invalid thumbnail type');
6870
@@ -141,7 +143,7 @@ async function generateThumbnail(directories, type, file) {
141143 * Ensures that the thumbnail cache for backgrounds is valid.
142144 * @returns {Promise<void>} Promise that resolves when the cache is validated
143145 */
144146export async function ensureThumbnailCache() {
145147 const userHandles = await getAllUserHandles();
146148 for (const handle of userHandles) {
147149 const directories = getUserDirectories(handle);
@@ -166,7 +168,7 @@ async function ensureThumbnailCache() {
166168 }
167169}
168170
169171export const router = express.Router();
170172
171173// Important: This route must be mounted as '/thumbnail'. It is used in the client code and saved to chat files.
172174router.get('/', jsonParser, async function (request, response) {
@@ -227,9 +229,3 @@ router.get('/', jsonParser, async function (request, response) {
227229 return response.sendStatus(500);
228230 }
229231});
230-
231-module.exports = {
232- invalidateThumbnail,
233- ensureThumbnailCache,
234- router,
235-};
src/endpoints/tokenizers.js+22 -29
@@ -1,15 +1,18 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
3+
4-const { SentencePieceProcessor } = require('@agnai/sentencepiece-js');
4+import express from 'express';
5-const tiktoken = require('tiktoken');
5+import { sync as writeFileAtomicSync } from 'write-file-atomic';
6-const { Tokenizer } = require('@agnai/web-tokenizers');
6+
77constimport { convertClaudePromptTokenizer } =from require('..@agnai/promptweb-converterstokenizers');
88constimport { TEXTGEN_TYPESSentencePieceProcessor } =from require('..@agnai/constantssentencepiece-js');
9-const { jsonParser } = require('../express-common');
9+import tiktoken from 'tiktoken';
10-const { setAdditionalHeaders } = require('../additional-headers');
10+
1111constimport { getConfigValue, isValidUrlconvertClaudePrompt } =from require('../utilprompt-converters.js');
12-const writeFileAtomicSync = require('write-file-atomic').sync;
12+import { TEXTGEN_TYPES } from '../constants.js';
13+import { jsonParser } from '../express-common.js';
14+import { setAdditionalHeaders } from '../additional-headers.js';
15+import { getConfigValue, isValidUrl } from '../util.js';
1316
1417/**
1518 * @typedef { (req: import('express').Request, res: import('express').Response) => Promise<any> } TokenizationHandler
@@ -23,7 +26,7 @@ const tokenizersCache = {};
2326/**
2427 * @type {string[]}
2528 */
2629export const TEXT_COMPLETION_MODELS = [
2730 'gpt-3.5-turbo-instruct',
2831 'gpt-3.5-turbo-instruct-0914',
2932 'text-davinci-003',
@@ -223,7 +226,7 @@ const commandTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyT
223226const qwen2Tokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/qwen2.json', 'src/tokenizers/llama3.json');
224227const nemoTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/nemo.json', 'src/tokenizers/llama3.json');
225228
226229export const sentencepieceTokenizers = [
227230 'llama',
228231 'nerdstash',
229232 'nerdstash_v2',
@@ -238,7 +241,7 @@ const sentencepieceTokenizers = [
238241 * @param {string} model Sentencepiece model name
239242 * @returns {SentencePieceTokenizer|null} Sentencepiece tokenizer
240243 */
241244export function getSentencepiceTokenizer(model) {
242245 if (model.includes('llama')) {
243246 return spp_llama;
244247 }
@@ -350,7 +353,7 @@ function getWebTokenizersChunks(tokenizer, ids) {
350353 * @param {string} requestModel Models to use for tokenization
351354 * @returns {string} Tokenizer model to use
352355 */
353356export function getTokenizerModel(requestModel) {
354357 if (requestModel.includes('o1-preview') || requestModel.includes('o1-mini')) {
355358 return 'gpt-4o';
356359 }
@@ -427,7 +430,7 @@ function getTokenizerModel(requestModel) {
427430 return 'gpt-3.5-turbo';
428431}
429432
430433export function getTiktokenTokenizer(model) {
431434 if (tokenizersCache[model]) {
432435 return tokenizersCache[model];
433436 }
@@ -444,7 +447,7 @@ function getTiktokenTokenizer(model) {
444447 * @param {object[]} messages Array of messages
445448 * @returns {number} Number of tokens
446449 */
447450export function countWebTokenizerTokens(tokenizer, messages) {
448451 // Should be fine if we use the old conversion method instead of the messages API one i think?
449452 const convertedPrompt = convertClaudePrompt(messages, false, '', false, false, '', false);
450453
@@ -636,7 +639,7 @@ function createWebTokenizerDecodingHandler(tokenizer) {
636639 };
637640}
638641
639642export const router = express.Router();
640643
641644router.post('/llama/encode', jsonParser, createSentencepieceEncodingHandler(spp_llama));
642645router.post('/nerdstash/encode', jsonParser, createSentencepieceEncodingHandler(spp_nerd));
@@ -1002,13 +1005,3 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
10021005 return response.send({ error: true });
10031006 }
10041007});
1005-
1006-module.exports = {
1007- TEXT_COMPLETION_MODELS,
1008- getTokenizerModel,
1009- getTiktokenTokenizer,
1010- countWebTokenizerTokens,
1011- getSentencepiceTokenizer,
1012- sentencepieceTokenizers,
1013- router,
1014-};
src/endpoints/translate.js+14 -12
@@ -1,10 +1,16 @@
1-const fetch = require('node-fetch').default;
1+import * as https from 'node:https';
2-const https = require('https');
2+import { createRequire } from 'node:module';
3-const express = require('express');
3+import fetch from 'node-fetch';
4-const iconv = require('iconv-lite');
4+import express from 'express';
5-const { readSecret, SECRET_KEYS } = require('./secrets');
5+import iconv from 'iconv-lite';
6-const { getConfigValue, uuidv4 } = require('../util');
6+import bingTranslateApi from 'bing-translate-api';
7-const { jsonParser } = require('../express-common');
7+
8+const require = createRequire(import.meta.url);
9+const { generateRequestUrl, normaliseResponse } = require('google-translate-api-browser');
10+
11+import { readSecret, SECRET_KEYS } from './secrets.js';
12+import { getConfigValue, uuidv4 } from '../util.js';
13+import { jsonParser } from '../express-common.js';
814
915const DEEPLX_URL_DEFAULT = 'http://127.0.0.1:1188/translate';
1016const ONERING_URL_DEFAULT = 'http://127.0.0.1:4990/translate';
@@ -23,7 +29,7 @@ function decodeBuffer(buffer) {
2329 }
2430}
2531
2632export const router = express.Router();
2733
2834router.post('/libre', jsonParser, async (request, response) => {
2935 const key = readSecret(request.user.directories, SECRET_KEYS.LIBRE);
@@ -82,7 +88,6 @@ router.post('/libre', jsonParser, async (request, response) => {
8288
8389router.post('/google', jsonParser, async (request, response) => {
8490 try {
85- const { generateRequestUrl, normaliseResponse } = require('google-translate-api-browser');
8691 const text = request.body.text;
8792 const lang = request.body.lang;
8893
@@ -386,7 +391,6 @@ router.post('/deeplx', jsonParser, async (request, response) => {
386391});
387392
388393router.post('/bing', jsonParser, async (request, response) => {
389- const bingTranslateApi = require('bing-translate-api');
390394 const text = request.body.text;
391395 let lang = request.body.lang;
392396
@@ -408,5 +412,3 @@ router.post('/bing', jsonParser, async (request, response) => {
408412 return response.sendStatus(500);
409413 });
410414});
411-
412-module.exports = { router };
src/endpoints/users-admin.js+11 -14
@@ -1,10 +1,11 @@
1-const fsPromises = require('fs').promises;
1+import { promises as fsPromises } from 'node:fs';
2-const storage = require('node-persist');
2+
3-const express = require('express');
3+import storage from 'node-persist';
44constimport lodashexpress =from require('lodashexpress');
5-const { jsonParser } = require('../express-common');
5+import lodash from 'lodash';
66constimport { checkForNewContentjsonParser } =from require('../contentexpress-managercommon.js');
7-const {
7+import { checkForNewContent } from './content-manager.js';
8+import {
89 KEY_PREFIX,
910 toKey,
1011 requireAdminMiddleware,
@@ -14,10 +15,10 @@ const {
1415 getPasswordHash,
1516 getUserDirectories,
1617 ensurePublicDirectoriesExist,
1718} =from require('../users.js');
1819constimport { DEFAULT_USER } =from require('../constants.js');
1920
2021export const router = express.Router();
2122
2223router.post('/get', requireAdminMiddleware, jsonParser, async (_request, response) => {
2324 try {
@@ -249,7 +250,3 @@ router.post('/slugify', requireAdminMiddleware, jsonParser, async (request, resp
249250 return response.sendStatus(500);
250251 }
251252});
252-
253-module.exports = {
254- router,
255-};
src/endpoints/users-private.js+14 -16
@@ -1,18 +1,20 @@
11constimport * as path =from require('node:path');
2-const fsPromises = require('fs').promises;
2+import { promises as fsPromises } from 'node:fs';
3-const storage = require('node-persist');
3+import * as crypto from 'node:crypto';
4-const express = require('express');
4+
5-const crypto = require('crypto');
5+import storage from 'node-persist';
6-const { jsonParser } = require('../express-common');
6+import express from 'express';
7-const { getUserAvatar, toKey, getPasswordHash, getPasswordSalt, createBackupArchive, ensurePublicDirectoriesExist, toAvatarKey } = require('../users');
7+
88constimport { SETTINGS_FILEjsonParser } =from require('../constantsexpress-common.js');
9-const contentManager = require('./content-manager');
9+import { getUserAvatar, toKey, getPasswordHash, getPasswordSalt, createBackupArchive, ensurePublicDirectoriesExist, toAvatarKey } from '../users.js';
1010constimport { color, CacheSETTINGS_FILE } =from require('../utilconstants.js');
1111constimport {* checkForNewContentas }contentManager =from require('./content-manager.js');
12+import { color, Cache } from '../util.js';
13+import { checkForNewContent } from './content-manager.js';
1214
1315const RESET_CACHE = new Cache(5 * 60 * 1000);
1416
1517export const router = express.Router();
1618
1719router.post('/logout', async (request, response) => {
1820 try {
@@ -251,7 +253,3 @@ router.post('/reset-step2', jsonParser, async (request, response) => {
251253 return response.sendStatus(500);
252254 }
253255});
254-
255-module.exports = {
256- router,
257-};
src/endpoints/users-public.js+9 -12
@@ -1,15 +1,16 @@
11constimport * as crypto =from require('node:crypto');
2-const storage = require('node-persist');
2+
3-const express = require('express');
3+import storage from 'node-persist';
4-const { RateLimiterMemory, RateLimiterRes } = require('rate-limiter-flexible');
4+import express from 'express';
55constimport { jsonParserRateLimiterMemory, getIpFromRequestRateLimiterRes } =from require('../expressrate-commonlimiter-flexible');
66constimport { color, CachejsonParser, getConfigValuegetIpFromRequest } =from require('../utilexpress-common.js');
77constimport { KEY_PREFIX, getUserAvatar, toKeycolor, getPasswordHashCache, getPasswordSaltgetConfigValue } =from require('../usersutil.js');
8+import { KEY_PREFIX, getUserAvatar, toKey, getPasswordHash, getPasswordSalt } from '../users.js';
89
910const DISCREET_LOGIN = getConfigValue('enableDiscreetLogin', false);
1011const MFA_CACHE = new Cache(5 * 60 * 1000);
1112
1213export const router = express.Router();
1314const loginLimiter = new RateLimiterMemory({
1415 points: 5,
1516 duration: 60,
@@ -193,7 +194,3 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
193194 return response.sendStatus(500);
194195 }
195196});
196-
197-module.exports = {
198- router,
199-};
src/endpoints/vectors.js+38 -28
@@ -1,10 +1,22 @@
1-const vectra = require('vectra');
1+import * as path from 'node:path';
2-const path = require('path');
2+import * as fs from 'node:fs';
3-const fs = require('fs');
3+
44constimport expressvectra =from require('expressvectra');
5-const sanitize = require('sanitize-filename');
5+import express from 'express';
6-const { jsonParser } = require('../express-common');
6+import sanitize from 'sanitize-filename';
7-const { getConfigValue } = require('../util');
7+
8+import { jsonParser } from '../express-common.js';
9+import { getConfigValue } from '../util.js';
10+
11+import { getNomicAIBatchVector, getNomicAIVector } from '../vectors/nomicai-vectors.js';
12+import { getOpenAIVector, getOpenAIBatchVector } from '../vectors/openai-vectors.js';
13+import { getTransformersVector, getTransformersBatchVector } from '../vectors/embedding.js';
14+import { getExtrasVector, getExtrasBatchVector } from '../vectors/extras-vectors.js';
15+import { getMakerSuiteVector, getMakerSuiteBatchVector } from '../vectors/makersuite-vectors.js';
16+import { getCohereVector, getCohereBatchVector } from '../vectors/cohere-vectors.js';
17+import { getLlamaCppVector, getLlamaCppBatchVector } from '../vectors/llamacpp-vectors.js';
18+import { getVllmVector, getVllmBatchVector } from '../vectors/vllm-vectors.js';
19+import { getOllamaVector, getOllamaBatchVector } from '../vectors/ollama-vectors.js';
820
921// Don't forget to add new sources to the SOURCES array
1022const SOURCES = [
@@ -33,25 +45,25 @@ const SOURCES = [
3345async function getVector(source, sourceSettings, text, isQuery, directories) {
3446 switch (source) {
3547 case 'nomicai':
3648 return require('../vectors/nomicai-vectors').getNomicAIVector(text, source, directories);
3749 case 'togetherai':
3850 case 'mistral':
3951 case 'openai':
4052 return require('../vectors/openai-vectors').getOpenAIVector(text, source, directories, sourceSettings.model);
4153 case 'transformers':
4254 return require('../vectors/embedding').getTransformersVector(text);
4355 case 'extras':
4456 return require('../vectors/extras-vectors').getExtrasVector(text, sourceSettings.extrasUrl, sourceSettings.extrasKey);
4557 case 'palm':
4658 return require('../vectors/makersuite-vectors').getMakerSuiteVector(text, directories);
4759 case 'cohere':
4860 return require('../vectors/cohere-vectors').getCohereVector(text, isQuery, directories, sourceSettings.model);
4961 case 'llamacpp':
5062 return require('../vectors/llamacpp-vectors').getLlamaCppVector(text, sourceSettings.apiUrl, directories);
5163 case 'vllm':
5264 return require('../vectors/vllm-vectors').getVllmVector(text, sourceSettings.apiUrl, sourceSettings.model, directories);
5365 case 'ollama':
5466 return require('../vectors/ollama-vectors').getOllamaVector(text, sourceSettings.apiUrl, sourceSettings.model, sourceSettings.keep, directories);
5567 }
5668
5769 throw new Error(`Unknown vector source ${source}`);
@@ -74,33 +86,33 @@ async function getBatchVector(source, sourceSettings, texts, isQuery, directorie
7486 for (let batch of batches) {
7587 switch (source) {
7688 case 'nomicai':
7789 results.push(...await require('../vectors/nomicai-vectors').getNomicAIBatchVector(batch, source, directories));
7890 break;
7991 case 'togetherai':
8092 case 'mistral':
8193 case 'openai':
8294 results.push(...await require('../vectors/openai-vectors').getOpenAIBatchVector(batch, source, directories, sourceSettings.model));
8395 break;
8496 case 'transformers':
8597 results.push(...await require('../vectors/embedding').getTransformersBatchVector(batch));
8698 break;
8799 case 'extras':
88100 results.push(...await require('../vectors/extras-vectors').getExtrasBatchVector(batch, sourceSettings.extrasUrl, sourceSettings.extrasKey));
89101 break;
90102 case 'palm':
91103 results.push(...await require('../vectors/makersuite-vectors').getMakerSuiteBatchVector(batch, directories));
92104 break;
93105 case 'cohere':
94106 results.push(...await require('../vectors/cohere-vectors').getCohereBatchVector(batch, isQuery, directories, sourceSettings.model));
95107 break;
96108 case 'llamacpp':
97109 results.push(...await require('../vectors/llamacpp-vectors').getLlamaCppBatchVector(batch, sourceSettings.apiUrl, directories));
98110 break;
99111 case 'vllm':
100112 results.push(...await require('../vectors/vllm-vectors').getVllmBatchVector(batch, sourceSettings.apiUrl, sourceSettings.model, directories));
101113 break;
102114 case 'ollama':
103115 results.push(...await require('../vectors/ollama-vectors').getOllamaBatchVector(batch, sourceSettings.apiUrl, sourceSettings.model, sourceSettings.keep, directories));
104116 break;
105117 default:
106118 throw new Error(`Unknown vector source ${source}`);
@@ -359,7 +371,7 @@ async function regenerateCorruptedIndexErrorHandler(req, res, error) {
359371 return res.sendStatus(500);
360372}
361373
362374export const router = express.Router();
363375
364376router.post('/query', jsonParser, async (req, res) => {
365377 try {
@@ -495,5 +507,3 @@ router.post('/purge', jsonParser, async (req, res) => {
495507 return res.sendStatus(500);
496508 }
497509});
498-
499-module.exports = { router };
src/endpoints/worldinfo.js+9 -10
@@ -1,10 +1,11 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
3-const express = require('express');
4-const sanitize = require('sanitize-filename');
5-const writeFileAtomicSync = require('write-file-atomic').sync;
63
7-const { jsonParser, urlencodedParser } = require('../express-common');
4+import express from 'express';
5+import sanitize from 'sanitize-filename';
6+import { sync as writeFileAtomicSync } from 'write-file-atomic';
7+
8+import { jsonParser, urlencodedParser } from '../express-common.js';
89
910/**
1011 * Reads a World Info file and returns its contents
@@ -13,7 +14,7 @@ const { jsonParser, urlencodedParser } = require('../express-common');
1314 * @param {boolean} allowDummy If true, returns an empty object if the file doesn't exist
1415 * @returns {object} World Info file contents
1516 */
1617export function readWorldInfoFile(directories, worldInfoName, allowDummy) {
1718 const dummyObject = allowDummy ? { entries: {} } : null;
1819
1920 if (!worldInfoName) {
@@ -33,7 +34,7 @@ function readWorldInfoFile(directories, worldInfoName, allowDummy) {
3334 return worldInfo;
3435}
3536
3637export const router = express.Router();
3738
3839router.post('/get', jsonParser, (request, response) => {
3940 if (!request.body?.name) {
@@ -122,5 +123,3 @@ router.post('/edit', jsonParser, (request, response) => {
122123
123124 return response.send({ ok: true });
124125});
125-
126-module.exports = { router, readWorldInfoFile };
src/express-common.js+11 -12
@@ -1,28 +1,27 @@
11constimport express =from require('express');
22constimport ipaddr =from require('ipaddr.js');
33
44// Instantiate parser middleware here with application-level size limits
55export const jsonParser = express.json({ limit: '200mb' });
66export const urlencodedParser = express.urlencoded({ extended: true, limit: '200mb' });
77
88/**
99 * Gets the IP address of the client from the request object.
1010 * @param {import('express').Request)} req Request object
1111 * @returns {string} IP address of the client
1212 */
1313export function getIpFromRequest(req) {
1414 let clientIp = req.connectionsocket.remoteAddress;
15+ if (!clientIp) {
16+ return 'unknown';
17+ }
1518 let ip = ipaddr.parse(clientIp);
1619 // Check if the IP address is IPv4-mapped IPv6 address
1720 if (ip.kind() === 'ipv6' && ip instanceof ipaddr.IPv6 && ip.isIPv4MappedAddress()) {
1821 const ipv4 = ip.toIPv4Address().toString();
1922 clientIp = ipv4;
2023 } else {
2124 clientIp = ip.toString();
22- clientIp = clientIp.toString();
2325 }
2426 return clientIp;
2527}
26-
27-
28-module.exports = { jsonParser, urlencodedParser, getIpFromRequest };
src/middleware/basicAuth.js+4 -4
@@ -2,9 +2,9 @@
22 * When applied, this middleware will ensure the request contains the required header for basic authentication and only
33 * allow access to the endpoint after successful authentication.
44 */
5-const { getAllUserHandles, toKey, getPasswordHash } = require('../users.js');
5+import storage from 'node-persist';
66constimport { getConfiggetAllUserHandles, getConfigValuetoKey, getPasswordHash } =from require('../utilusers.js');
7-const storage = require('node-persist');
7+import { getConfig, getConfigValue } from '../util.js';
88
99const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false);
1010const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);
@@ -49,4 +49,4 @@ const basicAuthMiddleware = async function (request, response, callback) {
4949 return unauthorizedResponse(response);
5050};
5151
5252module.exportsexport =default basicAuthMiddleware;
src/middleware/multerMonkeyPatch.js+1 -3
@@ -14,7 +14,7 @@ function decodeFileName(str) {
1414 * @param {import('express').Response} _res Response
1515 * @param {import('express').NextFunction} next Next middleware
1616 */
1717export default function multerMonkeyPatch(req, _res, next) {
1818 try {
1919 if (req.file) {
2020 req.file.originalname = decodeFileName(req.file.originalname);
@@ -26,5 +26,3 @@ function multerMonkeyPatch(req, _res, next) {
2626 next();
2727 }
2828}
29-
30-module.exports = multerMonkeyPatch;
src/middleware/whitelist.js+6 -8
@@ -1,9 +1,9 @@
11constimport * as path =from require('node:path');
22constimport * as fs =from require('node:fs');
33constimport ipMatching =from require('ip-matching');
44
55constimport { getIpFromRequest } =from require('../express-common.js');
66constimport { color, getConfigValue } =from require('../util.js');
77
88const whitelistPath = path.join(process.cwd(), './whitelist.txt');
99const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false);
@@ -50,7 +50,7 @@ function getForwardedIp(req) {
5050 * @param {boolean} listen If listen mode is enabled via config or command line
5151 * @returns {import('express').RequestHandler} The middleware function
5252 */
5353export default function whitelistMiddleware(whitelistMode, listen) {
5454 return function (req, res, next) {
5555 const clientIp = getIpFromRequest(req);
5656 const forwardedIp = getForwardedIp(req);
@@ -82,5 +82,3 @@ function whitelistMiddleware(whitelistMode, listen) {
8282 next();
8383 };
8484}
85-
86-module.exports = whitelistMiddleware;
src/plugin-loader.js+7 -10
@@ -1,8 +1,9 @@
11constimport * as fs =from require('node:fs');
22constimport * as path =from require('node:path');
33constimport * as url =from require('node:url');
4-const express = require('express');
4+
5-const { getConfigValue } = require('./util');
5+import express from 'express';
6+import { getConfigValue } from './util.js';
67const enableServerPlugins = getConfigValue('enableServerPlugins', false);
78
89/**
@@ -32,7 +33,7 @@ const isESModule = (file) => path.extname(file) === '.mjs';
3233 * @returns {Promise<Function>} Promise that resolves when all plugins are loaded. Resolves to a "cleanup" function to
3334 * be called before the server shuts down.
3435 */
3536export async function loadPlugins(app, pluginsPath) {
3637 const exitHooks = [];
3738 const emptyFn = () => {};
3839
@@ -217,7 +218,3 @@ async function initPlugin(app, plugin, exitHooks) {
217218
218219 return true;
219220}
220-
221-module.exports = {
222- loadPlugins,
223-};
src/polyfill.js+1 -1
@@ -7,4 +7,4 @@ if (!Array.prototype.findLastIndex) {
77 };
88}
99
1010module.exportsexport default () => {};
src/prompt-converters.js+13 -22
@@ -1,6 +1,8 @@
1-require('./polyfill.js');
1+import * as crypto from 'node:crypto';
22const { getConfigValueimport }polyfill =from require('./utilpolyfill.js');
3-const crypto = require('crypto');
3+import { getConfigValue } from './util.js';
4+
5+polyfill();
46
57const PROMPT_PLACEHOLDER = getConfigValue('promptPlaceholder', 'Let\'s get started.');
68
@@ -17,7 +19,7 @@ const PROMPT_PLACEHOLDER = getConfigValue('promptPlaceholder', 'Let\'s get start
1719 * @returns {string} Prompt for Claude
1820 * @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3).
1921 */
2022export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg, excludePrefixes) {
2123
2224 //Prepare messages for claude.
2325 //When 'Exclude Human/Assistant prefixes' checked, setting messages role to the 'system'(last message is exception).
@@ -96,7 +98,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill,
9698 * @param {string} charName Character name
9799 * @param {string} userName User name
98100 */
99101export function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools, humanMsgFix, charName = '', userName = '') {
100102 let systemPrompt = [];
101103 if (useSysPrompt) {
102104 // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.
@@ -279,7 +281,7 @@ function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools,
279281 * @param {string} userName User name
280282 * @returns {{chatHistory: object[]}} Prompt for Cohere
281283 */
282284export function convertCohereMessages(messages, charName = '', userName = '') {
283285 if (messages.length === 0) {
284286 messages.unshift({
285287 role: 'user',
@@ -333,7 +335,7 @@ function convertCohereMessages(messages, charName = '', userName = '') {
333335 * @param {string} userName User name
334336 * @returns {{contents: *[], system_instruction: {parts: {text: string}}}} Prompt for Google MakerSuite models
335337 */
336338export function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '', userName = '') {
337339 // This is a 1x1 transparent PNG
338340 const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
339341
@@ -456,7 +458,7 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
456458 * @param {string} charName Character name
457459 * @param {string} userName User name
458460 */
459461export function convertAI21Messages(messages, charName = '', userName = '') {
460462 if (!Array.isArray(messages)) {
461463 return [];
462464 }
@@ -528,7 +530,7 @@ function convertAI21Messages(messages, charName = '', userName = '') {
528530 * @param {string} charName Character name
529531 * @param {string} userName User name
530532 */
531533export function convertMistralMessages(messages, charName = '', userName = '') {
532534 if (!Array.isArray(messages)) {
533535 return [];
534536 }
@@ -612,7 +614,7 @@ function convertMistralMessages(messages, charName = '', userName = '') {
612614 * @param {boolean} strict Enable strict mode: only allow one system message at the start, force user first message
613615 * @returns {any[]} Merged messages
614616 */
615617export function mergeMessages(messages, charName, userName, strict) {
616618 let mergedMessages = [];
617619
618620 // Remove names from the messages
@@ -686,7 +688,7 @@ function mergeMessages(messages, charName, userName, strict) {
686688 * @param {object[]} messages Array of messages
687689 * @returns {string} Prompt for Text Completion API
688690 */
689691export function convertTextCompletionPrompt(messages) {
690692 if (typeof messages === 'string') {
691693 return messages;
692694 }
@@ -705,14 +707,3 @@ function convertTextCompletionPrompt(messages) {
705707 });
706708 return messageStrings.join('\n') + '\nassistant:';
707709}
708-
709-module.exports = {
710- convertClaudePrompt,
711- convertClaudeMessages,
712- convertGooglePrompt,
713- convertTextCompletionPrompt,
714- convertCohereMessages,
715- convertMistralMessages,
716- convertAI21Messages,
717- mergeMessages,
718-};
src/request-proxy.js+5 -8
@@ -1,7 +1,8 @@
11constimport * as http =from require('node:http');
22constimport * as https =from require('node:https');
33
44constimport { isValidUrl, colorProxyAgent } =from require('./util.jsproxy-agent');
5+import { isValidUrl, color } from './util.js';
56
67const LOG_HEADER = '[Request Proxy]';
78
@@ -13,10 +14,8 @@ const LOG_HEADER = '[Request Proxy]';
1314 * @property {string} url Proxy URL.
1415 * @property {string[]} bypass List of URLs to bypass proxy.
1516 */
1617export default function initRequestProxy({ enabled, url, bypass }) {
1718 try {
18- const { ProxyAgent } = require('proxy-agent');
19-
2019 // No proxy is enabled, so return
2120 if (!enabled) {
2221 return;
@@ -52,5 +51,3 @@ function initRequestProxy({ enabled, url, bypass }) {
5251 console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error);
5352 }
5453}
55-
56-module.exports = initRequestProxy;
src/transformers.mjs+6 -5
@@ -1,7 +1,8 @@
1+import path from 'node:path';
2+import fs from 'node:fs';
3+
14import { pipeline, env, RawImage, Pipeline } from 'sillytavern-transformers';
25import { getConfigValue } from './util.js';
3-import path from 'path';
4-import fs from 'fs';
56
67configureTransformers();
78
@@ -50,7 +51,7 @@ const tasks = {
5051 * @param {string} image Base64-encoded image
5152 * @returns {Promise<RawImage|null>} Object representing the image
5253 */
5354export async function getRawImage(image) {
5455 try {
5556 const buffer = Buffer.from(image, 'base64');
5657 const byteArray = new Uint8Array(buffer);
@@ -116,7 +117,7 @@ async function migrateCacheToDataDir() {
116117 * @param {string} forceModel The model to use for the pipeline, if any
117118 * @returns {Promise<Pipeline>} Pipeline for the task
118119 */
119120export async function getPipeline(task, forceModel = '') {
120121 await migrateCacheToDataDir();
121122
122123 if (tasks[task].pipeline) {
@@ -138,6 +139,6 @@ async function getPipeline(task, forceModel = '') {
138139}
139140
140141export default {
141- getPipeline,
142142 getRawImage,
143+ getPipeline,
143144};
src/users.js+40 -68
@@ -1,22 +1,23 @@
11// Native Node Modules
22constimport * as path =from require('node:path');
33constimport * as fs =from require('node:fs');
44constimport * as crypto =from require('node:crypto');
55constimport * as os =from require('node:os');
66
77// Express and other dependencies
88constimport storage =from require('node-persist');
99constimport express =from require('express');
1010constimport mime =from require('mime-types');
1111constimport archiver =from require('archiver');
12-const writeFileAtomicSync = require('write-file-atomic').sync;
12+import _ from 'lodash';
13-const _ = require('lodash');
13+import { sync as writeFileAtomicSync } from 'write-file-atomic';
1414
1515constimport { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE } =from require('./constants.js');
1616constimport { getConfigValue, color, delay, setConfigValue, generateTimestamp } =from require('./util.js');
1717constimport { readSecret, writeSecret } =from require('./endpoints/secrets.js');
18-
18+import { getContentOfType } from './endpoints/content-manager.js';
19-const KEY_PREFIX = 'user:';
19+
20+export const KEY_PREFIX = 'user:';
2021const AVATAR_PREFIX = 'avatar:';
2122const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);
2223const AUTHELIA_AUTH = getConfigValue('autheliaAuth', false);
@@ -94,7 +95,7 @@ const STORAGE_KEYS = {
9495 * Ensures that the content directories exist.
9596 * @returns {Promise<import('./users').UserDirectoryList[]>} - The list of user directories
9697 */
9798export async function ensurePublicDirectoriesExist() {
9899 for (const dir of Object.values(PUBLIC_DIRECTORIES)) {
99100 if (!fs.existsSync(dir)) {
100101 fs.mkdirSync(dir, { recursive: true });
@@ -117,7 +118,7 @@ async function ensurePublicDirectoriesExist() {
117118 * Gets a list of all user directories.
118119 * @returns {Promise<import('./users').UserDirectoryList[]>} - The list of user directories
119120 */
120121export async function getUserDirectoriesList() {
121122 const userHandles = await getAllUserHandles();
122123 const directoriesList = userHandles.map(handle => getUserDirectories(handle));
123124 return directoriesList;
@@ -126,7 +127,7 @@ async function getUserDirectoriesList() {
126127/**
127128 * Perform migration from the old user data format to the new one.
128129 */
129130export async function migrateUserData() {
130131 const publicDirectory = path.join(process.cwd(), 'public');
131132
132133 // No need to migrate if the characters directory doesn't exists
@@ -328,14 +329,13 @@ async function migrateUserData() {
328329 console.log(color.green('Migration completed!'));
329330}
330331
331332export async function migrateSystemPrompts() {
332333 /**
333334 * Gets the default system prompts.
334335 * @returns {Promise<any[]>} - The list of default system prompts
335336 */
336337 async function getDefaultSystemPrompts() {
337338 try {
338- const { getContentOfType } = await import('./endpoints/content-manager.js');
339339 return getContentOfType('sysprompt', 'json');
340340 } catch {
341341 return [];
@@ -391,7 +391,7 @@ async function migrateSystemPrompts() {
391391 * @param {string} handle User handle
392392 * @returns {string} The key for the user storage
393393 */
394394export function toKey(handle) {
395395 return `${KEY_PREFIX}${handle}`;
396396}
397397
@@ -400,7 +400,7 @@ function toKey(handle) {
400400 * @param {string} handle User handle
401401 * @returns {string} The key for the avatar storage
402402 */
403403export function toAvatarKey(handle) {
404404 return `${AVATAR_PREFIX}${handle}`;
405405}
406406
@@ -409,7 +409,7 @@ function toAvatarKey(handle) {
409409 * @param {string} dataRoot The root directory for user data
410410 * @returns {Promise<void>}
411411 */
412412export async function initUserStorage(dataRoot) {
413413 global.DATA_ROOT = dataRoot;
414414 console.log('Using data root:', color.green(global.DATA_ROOT));
415415 console.log();
@@ -430,7 +430,7 @@ async function initUserStorage(dataRoot) {
430430 * Get the cookie secret from the config. If it doesn't exist, generate a new one.
431431 * @returns {string} The cookie secret
432432 */
433433export function getCookieSecret() {
434434 let secret = getConfigValue(STORAGE_KEYS.cookieSecret);
435435
436436 if (!secret) {
@@ -446,7 +446,7 @@ function getCookieSecret() {
446446 * Generates a random password salt.
447447 * @returns {string} The password salt
448448 */
449449export function getPasswordSalt() {
450450 return crypto.randomBytes(16).toString('base64');
451451}
452452
@@ -454,7 +454,7 @@ function getPasswordSalt() {
454454 * Get the session name for the current server.
455455 * @returns {string} The session name
456456 */
457457export function getCookieSessionName() {
458458 // Get server hostname and hash it to generate a session suffix
459459 const suffix = crypto.createHash('sha256').update(os.hostname()).digest('hex').slice(0, 8);
460460 return `session-${suffix}`;
@@ -466,7 +466,7 @@ function getCookieSessionName() {
466466 * @param {string} salt Salt to use for hashing
467467 * @returns {string} Hashed password
468468 */
469469export function getPasswordHash(password, salt) {
470470 return crypto.scryptSync(password.normalize(), salt, 64).toString('base64');
471471}
472472
@@ -475,7 +475,7 @@ function getPasswordHash(password, salt) {
475475 * @param {import('express').Request} [request] HTTP request object
476476 * @returns {string} The CSRF secret
477477 */
478478export function getCsrfSecret(request) {
479479 if (!request || !request.user) {
480480 return ANON_CSRF_SECRET;
481481 }
@@ -494,7 +494,7 @@ function getCsrfSecret(request) {
494494 * Gets a list of all user handles.
495495 * @returns {Promise<string[]>} - The list of user handles
496496 */
497497export async function getAllUserHandles() {
498498 const keys = await storage.keys(x => x.key.startsWith(KEY_PREFIX));
499499 const handles = keys.map(x => x.replace(KEY_PREFIX, ''));
500500 return handles;
@@ -505,7 +505,7 @@ async function getAllUserHandles() {
505505 * @param {string} handle User handle
506506 * @returns {UserDirectoryList} User directories
507507 */
508508export function getUserDirectories(handle) {
509509 if (DIRECTORIES_CACHE.has(handle)) {
510510 const cache = DIRECTORIES_CACHE.get(handle);
511511 if (cache) {
@@ -526,7 +526,7 @@ function getUserDirectories(handle) {
526526 * @param {string} handle User handle
527527 * @returns {Promise<string>} User avatar URL
528528 */
529529export async function getUserAvatar(handle) {
530530 try {
531531 // Check if the user has a custom avatar
532532 const avatarKey = toAvatarKey(handle);
@@ -563,7 +563,7 @@ async function getUserAvatar(handle) {
563563 * @param {import('express').Request} request Request object
564564 * @returns {boolean} Whether the user should be redirected to the login page
565565 */
566566export function shouldRedirectToLogin(request) {
567567 return ENABLE_ACCOUNTS && !request.user;
568568}
569569
@@ -574,7 +574,7 @@ function shouldRedirectToLogin(request) {
574574 * @param {boolean} basicAuthMode If Basic auth mode is enabled
575575 * @returns {Promise<boolean>} Whether auto-login was performed
576576 */
577577export async function tryAutoLogin(request, basicAuthMode) {
578578 if (!ENABLE_ACCOUNTS || request.user || !request.session) {
579579 return false;
580580 }
@@ -693,7 +693,7 @@ async function basicUserLogin(request) {
693693 * @param {import('express').Response} response Response object
694694 * @param {import('express').NextFunction} next Next function
695695 */
696696export async function setUserDataMiddleware(request, response, next) {
697697 // If user accounts are disabled, use the default user
698698 if (!ENABLE_ACCOUNTS) {
699699 const handle = DEFAULT_USER.handle;
@@ -751,7 +751,7 @@ async function setUserDataMiddleware(request, response, next) {
751751 * @param {import('express').Response} response Response object
752752 * @param {import('express').NextFunction} next Next function
753753 */
754754export function requireLoginMiddleware(request, response, next) {
755755 if (!request.user) {
756756 return response.sendStatus(403);
757757 }
@@ -787,7 +787,7 @@ function createRouteHandler(directoryFn) {
787787 * @param {import('express').NextFunction} next Next function
788788 * @returns {any}
789789 */
790790export function requireAdminMiddleware(request, response, next) {
791791 if (!request.user) {
792792 return response.sendStatus(403);
793793 }
@@ -806,7 +806,7 @@ function requireAdminMiddleware(request, response, next) {
806806 * @param {import('express').Response} response Express response object to write to
807807 * @returns {Promise<void>} Promise that resolves when the archive is created
808808 */
809809export async function createBackupArchive(handle, response) {
810810 const directories = getUserDirectories(handle);
811811
812812 console.log('Backup requested for', handle);
@@ -855,7 +855,7 @@ async function getAllUsers() {
855855 * Gets all of the enabled users.
856856 * @returns {Promise<User[]>}
857857 */
858858export async function getAllEnabledUsers() {
859859 const users = await getAllUsers();
860860 return users.filter(x => x.enabled);
861861}
@@ -863,7 +863,7 @@ async function getAllEnabledUsers() {
863863/**
864864 * Express router for serving files from the user's directories.
865865 */
866866export const router = express.Router();
867867router.use('/backgrounds/*', createRouteHandler(req => req.user.directories.backgrounds));
868868router.use('/characters/*', createRouteHandler(req => req.user.directories.characters));
869869router.use('/User%20Avatars/*', createRouteHandler(req => req.user.directories.avatars));
@@ -871,31 +871,3 @@ router.use('/assets/*', createRouteHandler(req => req.user.directories.assets));
871871router.use('/user/images/*', createRouteHandler(req => req.user.directories.userImages));
872872router.use('/user/files/*', createRouteHandler(req => req.user.directories.files));
873873router.use('/scripts/extensions/third-party/*', createRouteHandler(req => req.user.directories.extensions));
874-
875-module.exports = {
876- KEY_PREFIX,
877- toKey,
878- toAvatarKey,
879- initUserStorage,
880- ensurePublicDirectoriesExist,
881- getUserDirectoriesList,
882- getAllUserHandles,
883- getUserDirectories,
884- setUserDataMiddleware,
885- requireLoginMiddleware,
886- requireAdminMiddleware,
887- migrateUserData,
888- migrateSystemPrompts,
889- getPasswordSalt,
890- getPasswordHash,
891- getCsrfSecret,
892- getCookieSecret,
893- getCookieSessionName,
894- getUserAvatar,
895- shouldRedirectToLogin,
896- createBackupArchive,
897- tryAutoLogin,
898- getAllUsers,
899- getAllEnabledUsers,
900- router,
901-};
src/util.js+45 -73
@@ -1,13 +1,16 @@
11constimport * as path =from require('node:path');
22constimport * as fs =from require('node:fs');
3-const commandExistsSync = require('command-exists').sync;
3+import * as http2 from 'node:http2';
4-const writeFileAtomicSync = require('write-file-atomic').sync;
4+import { Readable } from 'node:stream';
5-const _ = require('lodash');
5+import { createRequire } from 'node:module';
6-const yauzl = require('yauzl');
6+
7-const mime = require('mime-types');
7+import yaml from 'yaml';
8-const yaml = require('yaml');
8+import { sync as commandExistsSync } from 'command-exists';
99constimport { default:sync simpleGitas writeFileAtomicSync } =from require('simplewrite-gitfile-atomic');
10-const { Readable } = require('stream');
10+import _ from 'lodash';
11+import yauzl from 'yauzl';
12+import mime from 'mime-types';
13+import { default as simpleGit } from 'simple-git';
1114
1215/**
1316 * Parsed config object.
@@ -18,7 +21,7 @@ let CACHED_CONFIG = null;
1821 * Returns the config object from the config.yaml file.
1922 * @returns {object} Config object
2023 */
2124export function getConfig() {
2225 if (CACHED_CONFIG) {
2326 return CACHED_CONFIG;
2427 }
@@ -46,7 +49,7 @@ function getConfig() {
4649 * @param {any} defaultValue - Default value to return if the key is not found
4750 * @returns {any} Value for the given key
4851 */
4952export function getConfigValue(key, defaultValue = null) {
5053 const config = getConfig();
5154 return _.get(config, key, defaultValue);
5255}
@@ -56,7 +59,7 @@ function getConfigValue(key, defaultValue = null) {
5659 * @param {string} key Key to set
5760 * @param {any} value Value to set
5861 */
5962export function setConfigValue(key, value) {
6063 // Reset cache so that the next getConfig call will read the updated config file
6164 CACHED_CONFIG = null;
6265 const config = getConfig();
@@ -69,7 +72,7 @@ function setConfigValue(key, value) {
6972 * @param {string} auth username:password
7073 * @returns {string} Basic Auth header value
7174 */
7275export function getBasicAuthHeader(auth) {
7376 const encoded = Buffer.from(`${auth}`).toString('base64');
7477 return `Basic ${encoded}`;
7578}
@@ -79,7 +82,7 @@ function getBasicAuthHeader(auth) {
7982 * Also returns the agent string for the Horde API.
8083 * @returns {Promise<{agent: string, pkgVersion: string, gitRevision: string | null, gitBranch: string | null, commitDate: string | null, isLatest: boolean}>} Version info object
8184 */
8285export async function getVersion() {
8386 let pkgVersion = 'UNKNOWN';
8487 let gitRevision = null;
8588 let gitBranch = null;
@@ -87,9 +90,10 @@ async function getVersion() {
8790 let isLatest = true;
8891
8992 try {
93+ const require = createRequire(import.meta.url);
9094 const pkgJson = require(path.join(process.cwd(), './package.json'));
9195 pkgVersion = pkgJson.version;
9296 if (!process['pkg'] && commandExistsSync('git')) {
9397 const git = simpleGit();
9498 const cwd = process.cwd();
9599 gitRevision = await git.cwd(cwd).revparse(['--short', 'HEAD']);
@@ -117,7 +121,7 @@ async function getVersion() {
117121 * @param {number} ms Milliseconds to wait
118122 * @returns {Promise<void>} Promise that resolves after the given amount of milliseconds
119123 */
120124export function delay(ms) {
121125 return new Promise(resolve => setTimeout(resolve, ms));
122126}
123127
@@ -127,7 +131,7 @@ function delay(ms) {
127131 * @returns {string} Random hex string
128132 * @example getHexString(8) // 'a1b2c3d4'
129133 */
130134export function getHexString(length) {
131135 const chars = '0123456789abcdef';
132136 let result = '';
133137 for (let i = 0; i < length; i++) {
@@ -142,7 +146,7 @@ function getHexString(length) {
142146 * @param {string} fileExtension File extension to look for
143147 * @returns {Promise<Buffer|null>} Buffer containing the extracted file. Null if the file was not found.
144148 */
145149export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
146150 return await new Promise((resolve, reject) => yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => {
147151 if (err) {
148152 reject(err);
@@ -181,7 +185,7 @@ async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
181185 * @param {string} zipFilePath Path to the ZIP archive
182186 * @returns {Promise<[string, Buffer][]>} Array of image buffers
183187 */
184188export async function getImageBuffers(zipFilePath) {
185189 return new Promise((resolve, reject) => {
186190 // Check if the zip file exists
187191 if (!fs.existsSync(zipFilePath)) {
@@ -237,7 +241,7 @@ async function getImageBuffers(zipFilePath) {
237241 * @param {any} readableStream Readable stream to read from
238242 * @returns {Promise<Buffer[]>} Array of chunks
239243 */
240244export async function readAllChunks(readableStream) {
241245 return new Promise((resolve, reject) => {
242246 // Consume the readable stream
243247 const chunks = [];
@@ -261,7 +265,7 @@ function isObject(item) {
261265 return (item && typeof item === 'object' && !Array.isArray(item));
262266}
263267
264268export function deepMerge(target, source) {
265269 let output = Object.assign({}, target);
266270 if (isObject(target) && isObject(source)) {
267271 Object.keys(source).forEach(key => {
@@ -278,7 +282,7 @@ function deepMerge(target, source) {
278282 return output;
279283}
280284
281285export const color = {
282286 byNum: (mess, fgNum) => {
283287 mess = mess || '';
284288 fgNum = fgNum === undefined ? 31 : fgNum;
@@ -298,7 +302,7 @@ const color = {
298302 * Gets a random UUIDv4 string.
299303 * @returns {string} A UUIDv4 string
300304 */
301305export function uuidv4() {
302306 if ('crypto' in global && 'randomUUID' in global.crypto) {
303307 return global.crypto.randomUUID();
304308 }
@@ -309,7 +313,7 @@ function uuidv4() {
309313 });
310314}
311315
312316export function humanizedISO8601DateTime(date) {
313317 let baseDate = typeof date === 'number' ? new Date(date) : new Date();
314318 let humanYear = baseDate.getFullYear();
315319 let humanMonth = (baseDate.getMonth() + 1);
@@ -322,7 +326,7 @@ function humanizedISO8601DateTime(date) {
322326 return HumanizedDateTime;
323327}
324328
325329export function tryParse(str) {
326330 try {
327331 return JSON.parse(str);
328332 } catch {
@@ -337,7 +341,7 @@ function tryParse(str) {
337341 * @param {string} inputPath The path to be converted.
338342 * @returns The relative URL path from which the client can access the file.
339343 */
340344export function clientRelativePath(root, inputPath) {
341345 if (!inputPath.startsWith(root)) {
342346 throw new Error('Input path does not start with the root directory');
343347 }
@@ -350,11 +354,11 @@ function clientRelativePath(root, inputPath) {
350354 * @param {string} filename The file name to remove the extension from.
351355 * @returns The file name, sans extension
352356 */
353357export function removeFileExtension(filename) {
354358 return filename.replace(/\.[^.]+$/, '');
355359}
356360
357361export function generateTimestamp() {
358362 const now = new Date();
359363 const year = now.getFullYear();
360364 const month = String(now.getMonth() + 1).padStart(2, '0');
@@ -371,7 +375,7 @@ function generateTimestamp() {
371375 * @param {string} directory The root directory to remove backups from.
372376 * @param {string} prefix File prefix to filter backups by.
373377 */
374378export function removeOldBackups(directory, prefix) {
375379 const MAX_BACKUPS = Number(getConfigValue('numberOfBackups', 50));
376380
377381 let files = fs.readdirSync(directory).filter(f => f.startsWith(prefix));
@@ -389,7 +393,7 @@ function removeOldBackups(directory, prefix) {
389393 * @param {'name' | 'date'} sortBy Sort images by name or date
390394 * @returns {string[]} List of image file names
391395 */
392396export function getImages(directoryPath, sortBy = 'name') {
393397 function getSortFunction() {
394398 switch (sortBy) {
395399 case 'name':
@@ -415,7 +419,7 @@ function getImages(directoryPath, sortBy = 'name') {
415419 * @param {import('node-fetch').Response} from The Fetch API response to pipe from.
416420 * @param {import('express').Response} to The Express response to pipe to.
417421 */
418422export function forwardFetchResponse(from, to) {
419423 let statusCode = from.status;
420424 let statusText = from.statusText;
421425
@@ -434,6 +438,7 @@ function forwardFetchResponse(from, to) {
434438
435439 to.statusCode = statusCode;
436440 to.statusMessage = statusText;
441+
437442 from.body.pipe(to);
438443
439444 to.socket.on('close', function () {
@@ -457,10 +462,9 @@ function forwardFetchResponse(from, to) {
457462 * @param {object} headers Request headers
458463 * @returns {Promise<string>} Response body
459464 */
460465export function makeHttp2Request(endpoint, method, body, headers) {
461466 return new Promise((resolve, reject) => {
462467 try {
463- const http2 = require('http2');
464468 const url = new URL(endpoint);
465469 const client = http2.connect(url.origin);
466470
@@ -511,7 +515,7 @@ function makeHttp2Request(endpoint, method, body, headers) {
511515 * @param {string} yamlString YAML-serialized object
512516 * @returns
513517 */
514518export function mergeObjectWithYaml(obj, yamlString) {
515519 if (!yamlString) {
516520 return;
517521 }
@@ -540,7 +544,7 @@ function mergeObjectWithYaml(obj, yamlString) {
540544 * @param {string} yamlString YAML-serialized array
541545 * @returns {void} Nothing
542546 */
543547export function excludeKeysByYaml(obj, yamlString) {
544548 if (!yamlString) {
545549 return;
546550 }
@@ -569,14 +573,14 @@ function excludeKeysByYaml(obj, yamlString) {
569573 * @param {string} str Input string
570574 * @returns {string} Trimmed string
571575 */
572576export function trimV1(str) {
573577 return String(str ?? '').replace(/\/$/, '').replace(/\/v1$/, '');
574578}
575579
576580/**
577581 * Simple TTL memory cache.
578582 */
579583export class Cache {
580584 /**
581585 * @param {number} ttl Time to live in milliseconds
582586 */
@@ -633,7 +637,7 @@ class Cache {
633637 * @param {string} text Text with color formatting
634638 * @returns {string} Text without color formatting
635639 */
636640export function removeColorFormatting(text) {
637641 // ANSI escape codes for colors are usually in the format \x1b[<codes>m
638642 return text.replace(/\x1b\[\d{1,2}(;\d{1,2})*m/g, '');
639643}
@@ -643,7 +647,7 @@ function removeColorFormatting(text) {
643647 * @param {number} n Number of times to repeat the separator
644648 * @returns {string} Separator string
645649 */
646650export function getSeparator(n) {
647651 return '='.repeat(n);
648652}
649653
@@ -652,7 +656,7 @@ function getSeparator(n) {
652656 * @param {string} url String to check
653657 * @returns {boolean} If the URL is valid
654658 */
655659export function isValidUrl(url) {
656660 try {
657661 new URL(url);
658662 return true;
@@ -660,35 +664,3 @@ function isValidUrl(url) {
660664 return false;
661665 }
662666}
663-
664-module.exports = {
665- getConfig,
666- getConfigValue,
667- setConfigValue,
668- getVersion,
669- getBasicAuthHeader,
670- extractFileFromZipBuffer,
671- getImageBuffers,
672- readAllChunks,
673- delay,
674- deepMerge,
675- color,
676- uuidv4,
677- humanizedISO8601DateTime,
678- tryParse,
679- clientRelativePath,
680- removeFileExtension,
681- generateTimestamp,
682- removeOldBackups,
683- getImages,
684- forwardFetchResponse,
685- getHexString,
686- mergeObjectWithYaml,
687- excludeKeysByYaml,
688- trimV1,
689- Cache,
690- makeHttp2Request,
691- removeColorFormatting,
692- getSeparator,
693- isValidUrl,
694-};
src/validator/TavernCardValidator.js+1 -3
@@ -5,7 +5,7 @@
55 *
66 * @link https://github.com/malfoyslastname/character-card-spec-v2
77 */
88export class TavernCardValidator {
99 /**
1010 * @type {string|null}
1111 */
@@ -167,5 +167,3 @@ class TavernCardValidator {
167167 return true;
168168 }
169169}
170-
171-module.exports = { TavernCardValidator };
src/vectors/cohere-vectors.js+4 -8
@@ -1,5 +1,5 @@
11constimport fetch =from require('node-fetch').default;
22constimport { SECRET_KEYS, readSecret } =from require('../endpoints/secrets.js');
33
44/**
55 * Gets the vector for the given text batch from an OpenAI compatible endpoint.
@@ -9,7 +9,7 @@ const { SECRET_KEYS, readSecret } = require('../endpoints/secrets');
99 * @param {string} model - The model to use for the embedding
1010 * @returns {Promise<number[][]>} - The array of vectors for the texts
1111 */
1212export async function getCohereBatchVector(texts, isQuery, directories, model) {
1313 const key = readSecret(directories, SECRET_KEYS.COHERE);
1414
1515 if (!key) {
@@ -55,12 +55,8 @@ async function getCohereBatchVector(texts, isQuery, directories, model) {
5555 * @param {string} model - The model to use for the embedding
5656 * @returns {Promise<number[]>} - The vector for the text
5757 */
5858export async function getCohereVector(text, isQuery, directories, model) {
5959 const vectors = await getCohereBatchVector([text], isQuery, directories, model);
6060 return vectors[0];
6161}
6262
63-module.exports = {
64- getCohereBatchVector,
65- getCohereVector,
66-};
src/vectors/embedding.js+4 -9
@@ -1,3 +1,4 @@
1+import { getPipeline } from '../transformers.mjs';
12const TASK = 'feature-extraction';
23
34/**
@@ -5,9 +6,8 @@ const TASK = 'feature-extraction';
56 * @param {string} text - The text to vectorize
67 * @returns {Promise<number[]>} - The vectorized text in form of an array of numbers
78 */
89export async function getTransformersVector(text) {
910 const modulepipe = await importgetPipeline('../transformers.mjs'TASK);
10- const pipe = await module.default.getPipeline(TASK);
1111 const result = await pipe(text, { pooling: 'mean', normalize: true });
1212 const vector = Array.from(result.data);
1313 return vector;
@@ -18,15 +18,10 @@ async function getTransformersVector(text) {
1818 * @param {string[]} texts - The texts to vectorize
1919 * @returns {Promise<number[][]>} - The vectorized texts in form of an array of arrays of numbers
2020 */
2121export async function getTransformersBatchVector(texts) {
2222 const result = [];
2323 for (const text of texts) {
2424 result.push(await getTransformersVector(text));
2525 }
2626 return result;
2727}
28-
29-module.exports = {
30- getTransformersVector,
31- getTransformersBatchVector,
32-};
src/vectors/extras-vectors.js+3 -8
@@ -1,4 +1,4 @@
11constimport fetch =from require('node-fetch').default;
22
33/**
44 * Gets the vector for the given text from SillyTavern-extras
@@ -7,7 +7,7 @@ const fetch = require('node-fetch').default;
77 * @param {string} apiKey - The Extras API key, or empty string if API key not enabled
88 * @returns {Promise<number[][]>} - The array of vectors for the texts
99 */
1010export async function getExtrasBatchVector(texts, apiUrl, apiKey) {
1111 return getExtrasVectorImpl(texts, apiUrl, apiKey);
1212}
1313
@@ -18,7 +18,7 @@ async function getExtrasBatchVector(texts, apiUrl, apiKey) {
1818 * @param {string} apiKey - The Extras API key, or empty string if API key not enabled
1919 * @returns {Promise<number[]>} - The vector for the text
2020 */
2121export async function getExtrasVector(text, apiUrl, apiKey) {
2222 return getExtrasVectorImpl(text, apiUrl, apiKey);
2323}
2424
@@ -71,8 +71,3 @@ async function getExtrasVectorImpl(text, apiUrl, apiKey) {
7171
7272 return vector;
7373}
74-
75-module.exports = {
76- getExtrasVector,
77- getExtrasBatchVector,
78-};
src/vectors/llamacpp-vectors.js+5 -10
@@ -1,6 +1,6 @@
11constimport fetch =from require('node-fetch').default;
22constimport { setAdditionalHeadersByType } =from require('../additional-headers.js');
33constimport { TEXTGEN_TYPES } =from require('../constants.js');
44
55/**
66 * Gets the vector for the given text from LlamaCpp
@@ -9,7 +9,7 @@ const { TEXTGEN_TYPES } = require('../constants');
99 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
1010 * @returns {Promise<number[][]>} - The array of vectors for the texts
1111 */
1212export async function getLlamaCppBatchVector(texts, apiUrl, directories) {
1313 const url = new URL(apiUrl);
1414 url.pathname = '/v1/embeddings';
1515
@@ -50,12 +50,7 @@ async function getLlamaCppBatchVector(texts, apiUrl, directories) {
5050 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
5151 * @returns {Promise<number[]>} - The vector for the text
5252 */
5353export async function getLlamaCppVector(text, apiUrl, directories) {
5454 const vectors = await getLlamaCppBatchVector([text], apiUrl, directories);
5555 return vectors[0];
5656}
57-
58-module.exports = {
59- getLlamaCppBatchVector,
60- getLlamaCppVector,
61-};
src/vectors/makersuite-vectors.js+4 -9
@@ -1,5 +1,5 @@
11constimport fetch =from require('node-fetch').default;
22constimport { SECRET_KEYS, readSecret } =from require('../endpoints/secrets.js');
33const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
44
55/**
@@ -8,7 +8,7 @@ const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
88 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
99 * @returns {Promise<number[][]>} - The array of vectors for the texts
1010 */
1111export async function getMakerSuiteBatchVector(texts, directories) {
1212 const promises = texts.map(text => getMakerSuiteVector(text, directories));
1313 return await Promise.all(promises);
1414}
@@ -19,7 +19,7 @@ async function getMakerSuiteBatchVector(texts, directories) {
1919 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
2020 * @returns {Promise<number[]>} - The vector for the text
2121 */
2222export async function getMakerSuiteVector(text, directories) {
2323 const key = readSecret(directories, SECRET_KEYS.MAKERSUITE);
2424
2525 if (!key) {
@@ -56,8 +56,3 @@ async function getMakerSuiteVector(text, directories) {
5656 // noinspection JSValidateTypes
5757 return data['embedding']['values'];
5858}
59-
60-module.exports = {
61- getMakerSuiteVector,
62- getMakerSuiteBatchVector,
63-};
src/vectors/nomicai-vectors.js+4 -9
@@ -1,5 +1,5 @@
11constimport fetch =from require('node-fetch').default;
22constimport { SECRET_KEYS, readSecret } =from require('../endpoints/secrets.js');
33
44const SOURCES = {
55 'nomicai': {
@@ -16,7 +16,7 @@ const SOURCES = {
1616 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
1717 * @returns {Promise<number[][]>} - The array of vectors for the texts
1818 */
1919export async function getNomicAIBatchVector(texts, source, directories) {
2020 const config = SOURCES[source];
2121
2222 if (!config) {
@@ -67,12 +67,7 @@ async function getNomicAIBatchVector(texts, source, directories) {
6767 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
6868 * @returns {Promise<number[]>} - The vector for the text
6969 */
7070export async function getNomicAIVector(text, source, directories) {
7171 const vectors = await getNomicAIBatchVector([text], source, directories);
7272 return vectors[0];
7373}
74-
75-module.exports = {
76- getNomicAIVector,
77- getNomicAIBatchVector,
78-};
src/vectors/ollama-vectors.js+5 -10
@@ -1,6 +1,6 @@
11constimport fetch =from require('node-fetch').default;
22constimport { setAdditionalHeadersByType } =from require('../additional-headers.js');
33constimport { TEXTGEN_TYPES } =from require('../constants.js');
44
55/**
66 * Gets the vector for the given text from Ollama
@@ -11,7 +11,7 @@ const { TEXTGEN_TYPES } = require('../constants');
1111 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
1212 * @returns {Promise<number[][]>} - The array of vectors for the texts
1313 */
1414export async function getOllamaBatchVector(texts, apiUrl, model, keep, directories) {
1515 const result = [];
1616 for (const text of texts) {
1717 const vector = await getOllamaVector(text, apiUrl, model, keep, directories);
@@ -29,7 +29,7 @@ async function getOllamaBatchVector(texts, apiUrl, model, keep, directories) {
2929 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
3030 * @returns {Promise<number[]>} - The vector for the text
3131 */
3232export async function getOllamaVector(text, apiUrl, model, keep, directories) {
3333 const url = new URL(apiUrl);
3434 url.pathname = '/api/embeddings';
3535
@@ -62,8 +62,3 @@ async function getOllamaVector(text, apiUrl, model, keep, directories) {
6262
6363 return data.embedding;
6464}
65-
66-module.exports = {
67- getOllamaBatchVector,
68- getOllamaVector,
69-};
src/vectors/openai-vectors.js+4 -9
@@ -1,5 +1,5 @@
11constimport fetch =from require('node-fetch').default;
22constimport { SECRET_KEYS, readSecret } =from require('../endpoints/secrets.js');
33
44const SOURCES = {
55 'togetherai': {
@@ -27,7 +27,7 @@ const SOURCES = {
2727 * @param {string} model - The model to use for the embedding
2828 * @returns {Promise<number[][]>} - The array of vectors for the texts
2929 */
3030export async function getOpenAIBatchVector(texts, source, directories, model = '') {
3131 const config = SOURCES[source];
3232
3333 if (!config) {
@@ -83,12 +83,7 @@ async function getOpenAIBatchVector(texts, source, directories, model = '') {
8383 * @param {string} model - The model to use for the embedding
8484 * @returns {Promise<number[]>} - The vector for the text
8585 */
8686export async function getOpenAIVector(text, source, directories, model = '') {
8787 const vectors = await getOpenAIBatchVector([text], source, directories, model);
8888 return vectors[0];
8989}
90-
91-module.exports = {
92- getOpenAIVector,
93- getOpenAIBatchVector,
94-};
src/vectors/vllm-vectors.js+5 -10
@@ -1,6 +1,6 @@
11constimport fetch =from require('node-fetch').default;
22constimport { setAdditionalHeadersByType } =from require('../additional-headers.js');
33constimport { TEXTGEN_TYPES } =from require('../constants.js');
44
55/**
66 * Gets the vector for the given text from VLLM
@@ -10,7 +10,7 @@ const { TEXTGEN_TYPES } = require('../constants');
1010 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
1111 * @returns {Promise<number[][]>} - The array of vectors for the texts
1212 */
1313export async function getVllmBatchVector(texts, apiUrl, model, directories) {
1414 const url = new URL(apiUrl);
1515 url.pathname = '/v1/embeddings';
1616
@@ -52,12 +52,7 @@ async function getVllmBatchVector(texts, apiUrl, model, directories) {
5252 * @param {import('../users').UserDirectoryList} directories - The directories object for the user
5353 * @returns {Promise<number[]>} - The vector for the text
5454 */
5555export async function getVllmVector(text, apiUrl, model, directories) {
5656 const vectors = await getVllmBatchVector([text], apiUrl, model, directories);
5757 return vectors[0];
5858}
59-
60-module.exports = {
61- getVllmBatchVector,
62- getVllmVector,
63-};