Merge branch 'staging' into small-bookmark-updates

d082c2f14dae9840f6e9fbeae9d6e4f4dbd5de26

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

13 files changed, +744 -127Showing whitespace changes
.github/readme.md+40 -0
@@ -276,6 +276,46 @@ In order to enable viewing your keys by clicking a button in the API block:
2762761. Set the value of `allowKeysExposure` to `true` in `config.yaml` file.
2772772. Restart the SillyTavern server.
278278
279+## Command-line arguments
280+
281+You can pass command-line arguments to SillyTavern server startup to override some settings in `config.yaml`.
282+
283+### Examples
284+
285+```shell
286+node server.js --port 8000 --listen false
287+# or
288+npm run start -- --port 8000 --listen false
289+# or (Windows only)
290+Start.bat --port 8000 --listen false
291+```
292+
293+### Supported arguments
294+
295+| Option | Description | Type | Default |
296+|-------------------------|------------------------------------------------------------------------------------------------------|----------|------------------------------|
297+| `--version` | Show version number | boolean | |
298+| `--enableIPv6` | Enables IPv6. | boolean | false |
299+| `--enableIPv4` | Enables IPv4. | boolean | true |
300+| `--port` | Sets the port under which SillyTavern will run. If not provided falls back to yaml config 'port'. | number | 8000 |
301+| `--dnsPreferIPv6` | Prefers IPv6 for dns. If not provided falls back to yaml config 'preferIPv6'. | boolean | false |
302+| `--autorun` | Automatically launch SillyTavern in the browser. If not provided falls back to yaml config 'autorun'.| boolean | false |
303+| `--autorunHostname` | The autorun hostname, probably best left on 'auto'. | string | null |
304+| `--autorunPortOverride` | Overrides the port for autorun. | string | null |
305+| `--listen` | SillyTavern is listening on all network interfaces. If not provided falls back to yaml config 'listen'.| boolean | false |
306+| `--corsProxy` | Enables CORS proxy. If not provided falls back to yaml config 'enableCorsProxy'. | boolean | false |
307+| `--disableCsrf` | Disables CSRF protection | boolean | null |
308+| `--ssl` | Enables SSL | boolean | false |
309+| `--certPath` | Path to your certificate file. | string | "certs/cert.pem" |
310+| `--keyPath` | Path to your private key file. | string | "certs/privkey.pem" |
311+| `--whitelist` | Enables whitelist mode | boolean | null |
312+| `--dataRoot` | Root directory for data storage | string | null |
313+| `--avoidLocalhost` | Avoids using 'localhost' for autorun in auto mode. | boolean | null |
314+| `--basicAuthMode` | Enables basic authentication | boolean | null |
315+| `--requestProxyEnabled` | Enables a use of proxy for outgoing requests | boolean | null |
316+| `--requestProxyUrl` | Request proxy URL (HTTP or SOCKS protocols) | string | null |
317+| `--requestProxyBypass` | Request proxy bypass list (space separated list of hosts) | array | null |
318+
279319## Remote connections
280320
281321Most often this is for people who want to use SillyTavern on their mobile phones while their PC runs the ST server on the same wifi network.
default/config.yaml+10 -0
@@ -37,6 +37,16 @@ basicAuthUser:
3737 password: "password"
3838# Enables CORS proxy middleware
3939enableCorsProxy: false
40+# -- REQUEST PROXY CONFIGURATION --
41+requestProxy:
42+ # If a proxy is enabled, all outgoing HTTP/HTTPS requests will be routed through it.
43+ enabled: false
44+ # Proxy URL. Possible protocols: http, https, socks, socks5, socks4, pac
45+ url: "socks5://username:password@example.com:1080"
46+ # Proxy bypass list. Requests to these hosts won't be routed through the proxy.
47+ bypass:
48+ - localhost
49+ - 127.0.0.1
4050# Enable multi-user mode
4151enableUserAccounts: false
4252# Enable discreet login mode: hides user list on the login screen
package-lock.json+483 -33
@@ -22,7 +22,7 @@
2222 "cookie-session": "^2.1.0",
2323 "cors": "^2.8.5",
2424 "csrf-csrf": "^2.2.3",
2525 "express": "^4.1920.20",
2626 "form-data": "^4.0.0",
2727 "google-translate-api-browser": "^3.0.1",
2828 "he": "^1.2.0",
@@ -40,6 +40,7 @@
4040 "png-chunk-text": "^1.0.0",
4141 "png-chunks-encode": "^1.0.0",
4242 "png-chunks-extract": "^1.0.0",
43+ "proxy-agent": "^6.4.0",
4344 "rate-limiter-flexible": "^5.0.0",
4445 "response-time": "^2.3.2",
4546 "sanitize-filename": "^1.6.3",
@@ -941,6 +942,12 @@
941942 "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
942943 "license": "MIT"
943944 },
945+ "node_modules/@tootallnate/quickjs-emscripten": {
946+ "version": "0.23.0",
947+ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
948+ "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
949+ "license": "MIT"
950+ },
944951 "node_modules/@types/cacheable-request": {
945952 "version": "6.0.3",
946953 "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
@@ -1080,6 +1087,41 @@
10801087 "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
10811088 }
10821089 },
1090+ "node_modules/agent-base": {
1091+ "version": "7.1.1",
1092+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
1093+ "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
1094+ "license": "MIT",
1095+ "dependencies": {
1096+ "debug": "^4.3.4"
1097+ },
1098+ "engines": {
1099+ "node": ">= 14"
1100+ }
1101+ },
1102+ "node_modules/agent-base/node_modules/debug": {
1103+ "version": "4.3.7",
1104+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
1105+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
1106+ "license": "MIT",
1107+ "dependencies": {
1108+ "ms": "^2.1.3"
1109+ },
1110+ "engines": {
1111+ "node": ">=6.0"
1112+ },
1113+ "peerDependenciesMeta": {
1114+ "supports-color": {
1115+ "optional": true
1116+ }
1117+ }
1118+ },
1119+ "node_modules/agent-base/node_modules/ms": {
1120+ "version": "2.1.3",
1121+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1122+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1123+ "license": "MIT"
1124+ },
10831125 "node_modules/agentkeepalive": {
10841126 "version": "4.5.0",
10851127 "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
@@ -1382,6 +1424,18 @@
13821424 "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
13831425 "license": "MIT"
13841426 },
1427+ "node_modules/ast-types": {
1428+ "version": "0.13.4",
1429+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
1430+ "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
1431+ "license": "MIT",
1432+ "dependencies": {
1433+ "tslib": "^2.0.1"
1434+ },
1435+ "engines": {
1436+ "node": ">=4"
1437+ }
1438+ },
13851439 "node_modules/async": {
13861440 "version": "3.2.5",
13871441 "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
@@ -1445,6 +1499,15 @@
14451499 ],
14461500 "license": "MIT"
14471501 },
1502+ "node_modules/basic-ftp": {
1503+ "version": "5.0.5",
1504+ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
1505+ "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
1506+ "license": "MIT",
1507+ "engines": {
1508+ "node": ">=10.0.0"
1509+ }
1510+ },
14481511 "node_modules/bing-translate-api": {
14491512 "version": "2.9.1",
14501513 "resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-2.9.1.tgz",
@@ -2250,6 +2313,15 @@
22502313 "url": "https://github.com/sponsors/fb55"
22512314 }
22522315 },
2316+ "node_modules/data-uri-to-buffer": {
2317+ "version": "6.0.2",
2318+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
2319+ "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
2320+ "license": "MIT",
2321+ "engines": {
2322+ "node": ">= 14"
2323+ }
2324+ },
22532325 "node_modules/debug": {
22542326 "version": "2.6.9",
22552327 "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -2316,6 +2388,20 @@
23162388 "node": ">=8"
23172389 }
23182390 },
2391+ "node_modules/degenerator": {
2392+ "version": "5.0.1",
2393+ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
2394+ "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
2395+ "license": "MIT",
2396+ "dependencies": {
2397+ "ast-types": "^0.13.4",
2398+ "escodegen": "^2.1.0",
2399+ "esprima": "^4.0.1"
2400+ },
2401+ "engines": {
2402+ "node": ">= 14"
2403+ }
2404+ },
23192405 "node_modules/delayed-stream": {
23202406 "version": "1.0.0",
23212407 "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -2528,6 +2614,27 @@
25282614 "node": ">=0.8.0"
25292615 }
25302616 },
2617+ "node_modules/escodegen": {
2618+ "version": "2.1.0",
2619+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
2620+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
2621+ "license": "BSD-2-Clause",
2622+ "dependencies": {
2623+ "esprima": "^4.0.1",
2624+ "estraverse": "^5.2.0",
2625+ "esutils": "^2.0.2"
2626+ },
2627+ "bin": {
2628+ "escodegen": "bin/escodegen.js",
2629+ "esgenerate": "bin/esgenerate.js"
2630+ },
2631+ "engines": {
2632+ "node": ">=6.0"
2633+ },
2634+ "optionalDependencies": {
2635+ "source-map": "~0.6.1"
2636+ }
2637+ },
25312638 "node_modules/eslint": {
25322639 "version": "8.57.0",
25332640 "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
@@ -2683,6 +2790,19 @@
26832790 "url": "https://opencollective.com/eslint"
26842791 }
26852792 },
2793+ "node_modules/esprima": {
2794+ "version": "4.0.1",
2795+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
2796+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
2797+ "license": "BSD-2-Clause",
2798+ "bin": {
2799+ "esparse": "bin/esparse.js",
2800+ "esvalidate": "bin/esvalidate.js"
2801+ },
2802+ "engines": {
2803+ "node": ">=4"
2804+ }
2805+ },
26862806 "node_modules/esquery": {
26872807 "version": "1.5.0",
26882808 "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
@@ -2713,7 +2833,6 @@
27132833 "version": "5.3.0",
27142834 "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
27152835 "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
2716- "dev": true,
27172836 "license": "BSD-2-Clause",
27182837 "engines": {
27192838 "node": ">=4.0"
@@ -2723,7 +2842,6 @@
27232842 "version": "2.0.3",
27242843 "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
27252844 "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
2726- "dev": true,
27272845 "license": "BSD-2-Clause",
27282846 "engines": {
27292847 "node": ">=0.10.0"
@@ -3071,6 +3189,20 @@
30713189 "node": ">= 0.6"
30723190 }
30733191 },
3192+ "node_modules/fs-extra": {
3193+ "version": "11.2.0",
3194+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
3195+ "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
3196+ "license": "MIT",
3197+ "dependencies": {
3198+ "graceful-fs": "^4.2.0",
3199+ "jsonfile": "^6.0.1",
3200+ "universalify": "^2.0.0"
3201+ },
3202+ "engines": {
3203+ "node": ">=14.14"
3204+ }
3205+ },
30743206 "node_modules/fs.realpath": {
30753207 "version": "1.0.0",
30763208 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3130,6 +3262,44 @@
31303262 "url": "https://github.com/sponsors/sindresorhus"
31313263 }
31323264 },
3265+ "node_modules/get-uri": {
3266+ "version": "6.0.3",
3267+ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz",
3268+ "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==",
3269+ "license": "MIT",
3270+ "dependencies": {
3271+ "basic-ftp": "^5.0.2",
3272+ "data-uri-to-buffer": "^6.0.2",
3273+ "debug": "^4.3.4",
3274+ "fs-extra": "^11.2.0"
3275+ },
3276+ "engines": {
3277+ "node": ">= 14"
3278+ }
3279+ },
3280+ "node_modules/get-uri/node_modules/debug": {
3281+ "version": "4.3.7",
3282+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
3283+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
3284+ "license": "MIT",
3285+ "dependencies": {
3286+ "ms": "^2.1.3"
3287+ },
3288+ "engines": {
3289+ "node": ">=6.0"
3290+ },
3291+ "peerDependenciesMeta": {
3292+ "supports-color": {
3293+ "optional": true
3294+ }
3295+ }
3296+ },
3297+ "node_modules/get-uri/node_modules/ms": {
3298+ "version": "2.1.3",
3299+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
3300+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
3301+ "license": "MIT"
3302+ },
31333303 "node_modules/gifwrap": {
31343304 "version": "0.10.1",
31353305 "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz",
@@ -3369,6 +3539,42 @@
33693539 "node": ">= 0.8"
33703540 }
33713541 },
3542+ "node_modules/http-proxy-agent": {
3543+ "version": "7.0.2",
3544+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
3545+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
3546+ "license": "MIT",
3547+ "dependencies": {
3548+ "agent-base": "^7.1.0",
3549+ "debug": "^4.3.4"
3550+ },
3551+ "engines": {
3552+ "node": ">= 14"
3553+ }
3554+ },
3555+ "node_modules/http-proxy-agent/node_modules/debug": {
3556+ "version": "4.3.7",
3557+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
3558+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
3559+ "license": "MIT",
3560+ "dependencies": {
3561+ "ms": "^2.1.3"
3562+ },
3563+ "engines": {
3564+ "node": ">=6.0"
3565+ },
3566+ "peerDependenciesMeta": {
3567+ "supports-color": {
3568+ "optional": true
3569+ }
3570+ }
3571+ },
3572+ "node_modules/http-proxy-agent/node_modules/ms": {
3573+ "version": "2.1.3",
3574+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
3575+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
3576+ "license": "MIT"
3577+ },
33723578 "node_modules/http2-wrapper": {
33733579 "version": "1.0.3",
33743580 "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
@@ -3382,6 +3588,42 @@
33823588 "node": ">=10.19.0"
33833589 }
33843590 },
3591+ "node_modules/https-proxy-agent": {
3592+ "version": "7.0.5",
3593+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
3594+ "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
3595+ "license": "MIT",
3596+ "dependencies": {
3597+ "agent-base": "^7.0.2",
3598+ "debug": "4"
3599+ },
3600+ "engines": {
3601+ "node": ">= 14"
3602+ }
3603+ },
3604+ "node_modules/https-proxy-agent/node_modules/debug": {
3605+ "version": "4.3.7",
3606+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
3607+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
3608+ "license": "MIT",
3609+ "dependencies": {
3610+ "ms": "^2.1.3"
3611+ },
3612+ "engines": {
3613+ "node": ">=6.0"
3614+ },
3615+ "peerDependenciesMeta": {
3616+ "supports-color": {
3617+ "optional": true
3618+ }
3619+ }
3620+ },
3621+ "node_modules/https-proxy-agent/node_modules/ms": {
3622+ "version": "2.1.3",
3623+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
3624+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
3625+ "license": "MIT"
3626+ },
33853627 "node_modules/humanize-ms": {
33863628 "version": "1.2.1",
33873629 "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
@@ -3485,6 +3727,19 @@
34853727 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
34863728 "license": "ISC"
34873729 },
3730+ "node_modules/ip-address": {
3731+ "version": "9.0.5",
3732+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
3733+ "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
3734+ "license": "MIT",
3735+ "dependencies": {
3736+ "jsbn": "1.1.0",
3737+ "sprintf-js": "^1.1.3"
3738+ },
3739+ "engines": {
3740+ "node": ">= 12"
3741+ }
3742+ },
34883743 "node_modules/ip-matching": {
34893744 "version": "2.1.2",
34903745 "resolved": "https://registry.npmjs.org/ip-matching/-/ip-matching-2.1.2.tgz",
@@ -3669,6 +3924,12 @@
36693924 "js-yaml": "bin/js-yaml.js"
36703925 }
36713926 },
3927+ "node_modules/jsbn": {
3928+ "version": "1.1.0",
3929+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
3930+ "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
3931+ "license": "MIT"
3932+ },
36723933 "node_modules/json-buffer": {
36733934 "version": "3.0.1",
36743935 "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -3761,6 +4022,18 @@
37614022 "dev": true,
37624023 "license": "MIT"
37634024 },
4025+ "node_modules/jsonfile": {
4026+ "version": "6.1.0",
4027+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
4028+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
4029+ "license": "MIT",
4030+ "dependencies": {
4031+ "universalify": "^2.0.0"
4032+ },
4033+ "optionalDependencies": {
4034+ "graceful-fs": "^4.1.6"
4035+ }
4036+ },
37644037 "node_modules/keygrip": {
37654038 "version": "1.1.0",
37664039 "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
@@ -4053,6 +4326,15 @@
40534326 "node": ">= 0.6"
40544327 }
40554328 },
4329+ "node_modules/netmask": {
4330+ "version": "2.0.2",
4331+ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
4332+ "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
4333+ "license": "MIT",
4334+ "engines": {
4335+ "node": ">= 0.4.0"
4336+ }
4337+ },
40564338 "node_modules/node-domexception": {
40574339 "version": "1.0.0",
40584340 "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@@ -4323,6 +4605,61 @@
43234605 "url": "https://github.com/sponsors/sindresorhus"
43244606 }
43254607 },
4608+ "node_modules/pac-proxy-agent": {
4609+ "version": "7.0.2",
4610+ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz",
4611+ "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==",
4612+ "license": "MIT",
4613+ "dependencies": {
4614+ "@tootallnate/quickjs-emscripten": "^0.23.0",
4615+ "agent-base": "^7.0.2",
4616+ "debug": "^4.3.4",
4617+ "get-uri": "^6.0.1",
4618+ "http-proxy-agent": "^7.0.0",
4619+ "https-proxy-agent": "^7.0.5",
4620+ "pac-resolver": "^7.0.1",
4621+ "socks-proxy-agent": "^8.0.4"
4622+ },
4623+ "engines": {
4624+ "node": ">= 14"
4625+ }
4626+ },
4627+ "node_modules/pac-proxy-agent/node_modules/debug": {
4628+ "version": "4.3.7",
4629+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
4630+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
4631+ "license": "MIT",
4632+ "dependencies": {
4633+ "ms": "^2.1.3"
4634+ },
4635+ "engines": {
4636+ "node": ">=6.0"
4637+ },
4638+ "peerDependenciesMeta": {
4639+ "supports-color": {
4640+ "optional": true
4641+ }
4642+ }
4643+ },
4644+ "node_modules/pac-proxy-agent/node_modules/ms": {
4645+ "version": "2.1.3",
4646+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
4647+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
4648+ "license": "MIT"
4649+ },
4650+ "node_modules/pac-resolver": {
4651+ "version": "7.0.1",
4652+ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
4653+ "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
4654+ "license": "MIT",
4655+ "dependencies": {
4656+ "degenerator": "^5.0.0",
4657+ "netmask": "^2.0.2"
4658+ },
4659+ "engines": {
4660+ "node": ">= 14"
4661+ }
4662+ },
43264663 "node_modules/pako": {
43274664 "version": "1.0.11",
43284665 "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
@@ -4618,6 +4955,57 @@
46184955 "node": ">= 0.10"
46194956 }
46204957 },
4958+ "node_modules/proxy-agent": {
4959+ "version": "6.4.0",
4960+ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz",
4961+ "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==",
4962+ "license": "MIT",
4963+ "dependencies": {
4964+ "agent-base": "^7.0.2",
4965+ "debug": "^4.3.4",
4966+ "http-proxy-agent": "^7.0.1",
4967+ "https-proxy-agent": "^7.0.3",
4968+ "lru-cache": "^7.14.1",
4969+ "pac-proxy-agent": "^7.0.1",
4970+ "proxy-from-env": "^1.1.0",
4971+ "socks-proxy-agent": "^8.0.2"
4972+ },
4973+ "engines": {
4974+ "node": ">= 14"
4975+ }
4976+ },
4977+ "node_modules/proxy-agent/node_modules/debug": {
4978+ "version": "4.3.7",
4979+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
4980+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
4981+ "license": "MIT",
4982+ "dependencies": {
4983+ "ms": "^2.1.3"
4984+ },
4985+ "engines": {
4986+ "node": ">=6.0"
4987+ },
4988+ "peerDependenciesMeta": {
4989+ "supports-color": {
4990+ "optional": true
4991+ }
4992+ }
4993+ },
4994+ "node_modules/proxy-agent/node_modules/lru-cache": {
4995+ "version": "7.18.3",
4996+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
4997+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
4998+ "license": "ISC",
4999+ "engines": {
5000+ "node": ">=12"
5001+ }
5002+ },
5003+ "node_modules/proxy-agent/node_modules/ms": {
5004+ "version": "2.1.3",
5005+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
5006+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
5007+ "license": "MIT"
5008+ },
46215009 "node_modules/proxy-from-env": {
46225010 "version": "1.1.0",
46235011 "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -5006,36 +5394,6 @@
50065394 "node": ">= 0.8.0"
50075395 }
50085396 },
5009- "node_modules/serve-static/node_modules/ms": {
5010- "version": "2.1.3",
5011- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
5012- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
5013- "license": "MIT"
5014- },
5015- "node_modules/serve-static/node_modules/send": {
5016- "version": "0.18.0",
5017- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
5018- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
5019- "license": "MIT",
5020- "dependencies": {
5021- "debug": "2.6.9",
5022- "depd": "2.0.0",
5023- "destroy": "1.2.0",
5024- "encodeurl": "~1.0.2",
5025- "escape-html": "~1.0.3",
5026- "etag": "~1.8.1",
5027- "fresh": "0.5.2",
5028- "http-errors": "2.0.0",
5029- "mime": "1.6.0",
5030- "ms": "2.1.3",
5031- "on-finished": "2.4.1",
5032- "range-parser": "~1.2.1",
5033- "statuses": "2.0.1"
5034- },
5035- "engines": {
5036- "node": ">= 0.8.0"
5037- }
5038- },
50395397 "node_modules/set-function-length": {
50405398 "version": "1.2.2",
50415399 "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -5165,6 +5523,83 @@
51655523 "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==",
51665524 "license": "MIT"
51675525 },
5526+ "node_modules/smart-buffer": {
5527+ "version": "4.2.0",
5528+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
5529+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
5530+ "license": "MIT",
5531+ "engines": {
5532+ "node": ">= 6.0.0",
5533+ "npm": ">= 3.0.0"
5534+ }
5535+ },
5536+ "node_modules/socks": {
5537+ "version": "2.8.3",
5538+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
5539+ "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
5540+ "license": "MIT",
5541+ "dependencies": {
5542+ "ip-address": "^9.0.5",
5543+ "smart-buffer": "^4.2.0"
5544+ },
5545+ "engines": {
5546+ "node": ">= 10.0.0",
5547+ "npm": ">= 3.0.0"
5548+ }
5549+ },
5550+ "node_modules/socks-proxy-agent": {
5551+ "version": "8.0.4",
5552+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
5553+ "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
5554+ "license": "MIT",
5555+ "dependencies": {
5556+ "agent-base": "^7.1.1",
5557+ "debug": "^4.3.4",
5558+ "socks": "^2.8.3"
5559+ },
5560+ "engines": {
5561+ "node": ">= 14"
5562+ }
5563+ },
5564+ "node_modules/socks-proxy-agent/node_modules/debug": {
5565+ "version": "4.3.7",
5566+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
5567+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
5568+ "license": "MIT",
5569+ "dependencies": {
5570+ "ms": "^2.1.3"
5571+ },
5572+ "engines": {
5573+ "node": ">=6.0"
5574+ },
5575+ "peerDependenciesMeta": {
5576+ "supports-color": {
5577+ "optional": true
5578+ }
5579+ }
5580+ },
5581+ "node_modules/socks-proxy-agent/node_modules/ms": {
5582+ "version": "2.1.3",
5583+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
5584+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
5585+ "license": "MIT"
5586+ },
5587+ "node_modules/source-map": {
5588+ "version": "0.6.1",
5589+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
5590+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
5591+ "license": "BSD-3-Clause",
5592+ "optional": true,
5593+ "engines": {
5594+ "node": ">=0.10.0"
5595+ }
5596+ },
5597+ "node_modules/sprintf-js": {
5598+ "version": "1.1.3",
5599+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
5600+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
5601+ "license": "BSD-3-Clause"
5602+ },
51685603 "node_modules/statuses": {
51695604 "version": "2.0.1",
51705605 "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -5378,6 +5813,12 @@
53785813 "utf8-byte-length": "^1.0.1"
53795814 }
53805815 },
5816+ "node_modules/tslib": {
5817+ "version": "2.7.0",
5818+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
5819+ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
5820+ "license": "0BSD"
5821+ },
53815822 "node_modules/tsscmp": {
53825823 "version": "1.0.6",
53835824 "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
@@ -5437,6 +5878,15 @@
54375878 "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
54385879 "license": "MIT"
54395880 },
5881+ "node_modules/universalify": {
5882+ "version": "2.0.1",
5883+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
5884+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
5885+ "license": "MIT",
5886+ "engines": {
5887+ "node": ">= 10.0.0"
5888+ }
5889+ },
54405890 "node_modules/unpipe": {
54415891 "version": "1.0.0",
54425892 "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
package.json+5 -1
@@ -12,7 +12,7 @@
1212 "cookie-session": "^2.1.0",
1313 "cors": "^2.8.5",
1414 "csrf-csrf": "^2.2.3",
1515 "express": "^4.1920.20",
1616 "form-data": "^4.0.0",
1717 "google-translate-api-browser": "^3.0.1",
1818 "he": "^1.2.0",
@@ -30,6 +30,7 @@
3030 "png-chunk-text": "^1.0.0",
3131 "png-chunks-encode": "^1.0.0",
3232 "png-chunks-extract": "^1.0.0",
33+ "proxy-agent": "^6.4.0",
3334 "rate-limiter-flexible": "^5.0.0",
3435 "response-time": "^2.3.2",
3536 "sanitize-filename": "^1.6.3",
@@ -62,6 +63,9 @@
6263 },
6364 "node-fetch": {
6465 "whatwg-url": "^14.0.0"
66+ },
67+ "express": {
68+ "send": "^0.19.0"
6569 }
6670 },
6771 "name": "sillytavern",
public/index.html+1 -1
@@ -6479,8 +6479,8 @@
64796479 </div>
64806480
64816481 <!-- Script includes -->
6482+ <script src="lib/polyfill.js"></script>
64826483 <script src="lib/diff_match_patch.js"></script>
6483- <script src="lib/object-hasown.js"></script>
64846484 <script src="lib/jquery-3.5.1.min.js"></script>
64856485 <script src="lib/jquery-ui.min.js"></script>
64866486 <script src="lib/jquery.transit.min.js"></script>
public/lib/object-hasown.js+0 -4
@@ -1,4 +0,0 @@
1-// Polyfill for old Safari versions
2-if (!Object.hasOwn) {
3- Object.hasOwn = function (obj, prop) { return obj.hasOwnProperty(prop); }
4-}
public/lib/polyfill.js+19 -0
@@ -0,0 +1,19 @@
1+// Polyfills for old Safari versions
2+if (!Object.hasOwn) {
3+ Object.hasOwn = function (obj, prop) { return obj.hasOwnProperty(prop); }
4+}
5+
6+if (!Array.prototype.findLastIndex) {
7+ Array.prototype.findLastIndex = function (callback, thisArg) {
8+ for (let i = this.length - 1; i >= 0; i--) {
9+ if (callback.call(thisArg, this[i], i, this)) return i;
10+ }
11+ return -1;
12+ };
13+}
14+
15+if (!Array.prototype.toSorted) {
16+ Array.prototype.toSorted = function (compareFunction) {
17+ return this.slice().sort(compareFunction);
18+ };
19+}
public/script.js+56 -51
@@ -7472,7 +7472,7 @@ export function showSwipeButtons() {
74727472
74737473 const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`);
74747474 const swipeId = chat[chat.length - 1].swipe_id;
74757475 varconst swipesCounterHTMLswipeCounterText = (`${(swipeId + 1)}\u200B/\u200b${(chat[chat.length - 1].swipes.length)}`);
74767476
74777477 if (swipeId !== undefined && (chat[chat.length - 1].swipes.length > 1 || swipeId > 0)) {
74787478 currentMessage.children('.swipe_left').css('display', 'flex');
@@ -7489,7 +7489,7 @@ export function showSwipeButtons() {
74897489 }
74907490 //console.log(swipesCounterHTML);
74917491
74927492 $('.swipes-counter').htmltext(swipesCounterHTMLswipeCounterText);
74937493
74947494 //console.log(swipeId);
74957495 //console.log(chat[chat.length - 1].swipes.length);
@@ -7878,13 +7878,19 @@ async function createOrEditCharacter(e) {
78787878 formData.set('avatar', convertedFile);
78797879 }
78807880
7881+ const headers = getRequestHeaders();
7882+ delete headers['Content-Type'];
7883+
78817884 if ($('#form_create').attr('actiontype') == 'createcharacter') {
78827885 if (String($('#character_name_pole').val()).length >=== 0) {
7886+ toastr.error('Name is required');
7887+ return;
7888+ }
78837889 if (is_group_generating || is_send_press) {
78847890 toastr.error('Cannot create characters while generating. Stop the request and try again.', 'Creation aborted');
7885- throw new Error('Cannot import character while generating');
7891+ return;
78867892 }
7887-
7893+ try {
78887894 //if the character name text area isn't empty (only posible when creating a new character)
78897895 let url = '/api/characters/create';
78907896
@@ -7899,18 +7905,19 @@ async function createOrEditCharacter(e) {
78997905
79007906 formData.append('extensions', JSON.stringify(create_save.extensions));
79017907
7902- await jQuery.ajax({
7908+ const fetchResult = await fetch(url, {
79037909 typemethod: 'POST',
79047910 urlheaders: urlheaders,
79057911 databody: formData,
7906- beforeSend: function () {
7912+ cache: 'no-cache',
7907- $('#create_button').attr('disabled', String(true));
7913+ });
7908- $('#create_button').attr('value', '⏳');
7914+
7909- },
7915+ if (!fetchResult.ok) {
7910- cache: false,
7916+ throw new Error('Fetch result is not ok');
7911- contentType: false,
7917+ }
7912- processData: false,
7918+
7913- success: async function (html) {
7919+ const avatarId = await fetchResult.text();
7920+
79147921 $('#character_cross').trigger('click'); //closes the advanced character editing popup
79157922 const fields = [
79167923 { id: '#character_name_pole', callback: value => create_save.name = value },
@@ -7945,33 +7952,29 @@ async function createOrEditCharacter(e) {
79457952
79467953 create_save.avatar = '';
79477954
7948- $('#create_button').removeAttr('disabled');
79497955 $('#add_avatar_button').replaceWith(
79507956 $('#add_avatar_button').val('').clone(true),
79517957 );
79527958
7953- $('#create_button').attr('value', '✅');
79547959 let oldSelectedChar = null;
79557960 if (this_chid !== undefined) {
79567961 oldSelectedChar = characters[this_chid].avatar;
79577962 }
79587963
79597964 console.log(`new avatar id: ${htmlavatarId}`);
79607965 createTagMapFromList('#tagList', htmlavatarId);
79617966 await getCharacters();
79627967
79637968 select_rm_info('char_create', htmlavatarId, oldSelectedChar);
79647969
79657970 crop_data = undefined;
7966- },
7971+
79677972 error: } functioncatch (jqXHR, exceptionerror) {
7968- $('#create_button').removeAttr('disabled');
7973+ console.error('Error creating character', error);
7969- },
7974+ toastr.error('Failed to create character');
7970- });
7971- } else {
7972- toastr.error('Name is required');
79737975 }
79747976 } else {
7977+ try {
79757978 let url = '/api/characters/edit';
79767979
79777980 if (crop_data != undefined) {
@@ -7986,19 +7989,16 @@ async function createOrEditCharacter(e) {
79867989 }
79877990 }
79887991
7989- await jQuery.ajax({
7992+ const fetchResult = await fetch(url, {
79907993 type method: 'POST',
79917994 url headers: urlheaders,
79927995 data body: formData,
7993- beforeSend: function () {
7996+ cache: 'no-cache',
7994- $('#create_button').attr('disabled', String(true));
7997+ });
7995- $('#create_button').attr('value', 'Save');
7998+
7996- },
7999+ if (!fetchResult.ok) {
7997- cache: false,
8000+ throw new Error('Fetch result is not ok');
7998- contentType: false,
8001+ }
7999- processData: false,
8000- success: async function (html) {
8001- $('#create_button').removeAttr('disabled');
80028002
80038003 await getOneCharacter(formData.get('avatar_url'));
80048004 favsToHotswap(); // Update fav state
@@ -8008,7 +8008,7 @@ async function createOrEditCharacter(e) {
80088008 );
80098009 $('#create_button').attr('value', 'Save');
80108010 crop_data = undefined;
80118011 await eventSource.emit(event_types.CHARACTER_EDITED, { detail: { id: this_chid, character: characters[this_chid] } });
80128012
80138013 // Recreate the chat if it hasn't been used at least once (i.e. with continue).
80148014 const message = getFirstMessage();
@@ -8028,13 +8028,10 @@ async function createOrEditCharacter(e) {
80288028 await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, messageId);
80298029 await saveChatConditional();
80308030 }
8031- },
8031+ } catch (error) {
8032- error: function (jqXHR, exception) {
8032+ console.log(error);
8033- $('#create_button').removeAttr('disabled');
8034- console.log('Error! Either a file with the same name already existed, or the image file provided was in an invalid format. Double check that the image is not a webp.');
80358033 toastr.error('Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.');
8036- },
8034+ }
8037- });
80388035 }
80398036}
80408037
@@ -8526,7 +8523,7 @@ async function selectContextCallback(args, name) {
85268523
85278524async function selectInstructCallback(args, name) {
85288525 if (!name) {
8529- return power_user.instruct.preset;
8526+ return power_user.instruct.enabled || isTrueBoolean(args?.forceGet) ? power_user.instruct.preset : '';
85308527 }
85318528
85328529 const quiet = isTrueBoolean(args?.quiet);
@@ -9227,6 +9224,13 @@ jQuery(async function () {
92279224 defaultValue: 'false',
92289225 enumList: commonEnumProviders.boolean('trueFalse')(),
92299226 }),
9227+ SlashCommandNamedArgument.fromProps({
9228+ name: 'forceGet',
9229+ description: 'Force getting a name even if instruct mode is disabled',
9230+ typeList: [ARGUMENT_TYPE.BOOLEAN],
9231+ defaultValue: 'false',
9232+ enumList: commonEnumProviders.boolean('trueFalse')(),
9233+ }),
92309234 ],
92319235 unnamedArgumentList: [
92329236 SlashCommandArgument.fromProps({
@@ -9237,7 +9241,8 @@ jQuery(async function () {
92379241 ],
92389242 helpString: `
92399243 <div>
92409244 Selects instruct mode template by name. Gets the currentEnables instruct templatemode if no namenot isalready providedenabled.
9245+ Gets the current instruct template if no name is provided and instruct mode is enabled or <code>forceGet=true</code> is passed.
92419246 </div>
92429247 <div>
92439248 <strong>Example:</strong>
public/scripts/slash-commands.js+39 -29
@@ -24,6 +24,7 @@ import {
2424 main_api,
2525 name1,
2626 name2,
27+ neutralCharacterName,
2728 reloadCurrentChat,
2829 removeMacros,
2930 renameCharacter,
@@ -427,6 +428,7 @@ export function initDefaultSlashCommands() {
427428 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
428429 name: 'ask',
429430 callback: askCharacter,
431+ returns: 'the generated text',
430432 namedArgumentList: [
431433 SlashCommandNamedArgument.fromProps({
432434 name: 'name',
@@ -438,7 +440,7 @@ export function initDefaultSlashCommands() {
438440 ],
439441 unnamedArgumentList: [
440442 new SlashCommandArgument(
441443 'prompt', [ARGUMENT_TYPE.STRING], truefalse, false,
442444 ),
443445 ],
444446 helpString: 'Asks a specified character card a prompt. Character name must be provided in a named argument.',
@@ -2467,44 +2469,40 @@ async function askCharacter(args, text) {
24672469 // Not supported in group chats
24682470 // TODO: Maybe support group chats?
24692471 if (selected_group) {
24702472 toastr.error('Cannot run this/ask command in a group chat!');
2471- return '';
2472- }
2473-
2474- if (!text) {
2475- console.warn('WARN: No text provided for /ask command');
2476- toastr.warning('No text provided for /ask command');
24772473 return '';
24782474 }
24792475
24802476 let name = '';
2481- let mesText = '';
24822477
24832478 if (args?.name) {
24842479 name = args.name.trim();
2485- mesText = text.trim();
24862480
24872481 if (!name && !mesText) {
24882482 toastr.warning('You must specify a name andof textthe character to ask.');
24892483 return '';
24902484 }
24912485 }
24922486
2493- mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND);
2494-
24952487 const prevChId = this_chid;
24962488
24972489 // Find the character
24982490 const chId = characters.findIndex((e) => e.name === name || e.avatar === name);
24992491 if (!characters[chId] || chId === -1) {
25002492 toastr.error('Character not found.');
25012493 return '';
25022494 }
25032495
2496+ if (text) {
2497+ const mesText = getRegexedString(text.trim(), regex_placement.SLASH_COMMAND);
2498+ // Sending a message implicitly saves the chat, so this needs to be done before changing the character
2499+ // Otherwise, a corruption will occur
2500+ await sendMessageAsUser(mesText, '');
2501+ }
2502+
25042503 // Override character and send a user message
25052504 setCharacterId(String(chId));
25062505
2507- // TODO: Maybe look up by filename instead of name
25082506 const character = characters[chId];
25092507 let force_avatar, original_avatar;
25102508
@@ -2519,9 +2517,11 @@ async function askCharacter(args, text) {
25192517
25202518 setCharacterName(character.name);
25212519
2522- await sendMessageAsUser(mesText, '');
2523-
25242520 const restoreCharacter = () => {
2521+ if (String(this_chid) !== String(chId)) {
2522+ return;
2523+ }
2524+
25252525 setCharacterId(prevChId);
25262526 setCharacterName(characters[prevChId].name);
25272527
@@ -2532,23 +2532,27 @@ async function askCharacter(args, text) {
25322532 lastMessage.force_avatar = force_avatar;
25332533 lastMessage.original_avatar = original_avatar;
25342534 }
2535-
2536- // Kill this callback once the event fires
2537- eventSource.removeListener(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
25382535 };
25392536
2540- // Run generate and restore previous character on error
2537+ let askResult = '';
2538+
2539+ // Run generate and restore previous character
25412540 try {
2541+ eventSource.once(event_types.MESSAGE_RECEIVED, restoreCharacter);
25422542 toastr.info(`Asking ${character.name} something...`);
25432543 askResult = await Generate('ask_command');
25442544 } catch (error) {
25452545 restoreCharacter();
2546+ console.error('Error running /ask command', error);
2547+ } finally {
2548+ if (String(this_chid) === String(prevChId)) {
2549+ await saveChatConditional();
2550+ } else {
2551+ toastr.error('It is strongly recommended to reload the page.', 'Something went wrong');
2552+ }
25462553 }
25472554
2548- // Restore previous character once message renders
2555+ return askResult;
2549- // Hack for generate
2550- eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
2551- return '';
25522556}
25532557
25542558async function hideMessageCallback(_, arg) {
@@ -3129,7 +3133,13 @@ export async function sendMessageAs(args, text) {
31293133 const character = characters.find(x => x.avatar === name) ?? characters.find(x => x.name === name);
31303134 let force_avatar, original_avatar;
31313135
3132- if (character && character.avatar !== 'none') {
3136+ const chatCharacter = this_chid !== undefined ? characters[this_chid] : null;
3137+ const isNeutralCharacter = !chatCharacter && name2 === neutralCharacterName && name === neutralCharacterName;
3138+
3139+ if (chatCharacter === character || isNeutralCharacter) {
3140+ // If the targeted character is the currently selected one in a solo chat, we don't need to force any avatars
3141+ }
3142+ else if (character && character.avatar !== 'none') {
31333143 force_avatar = getThumbnailUrl('avatar', character.avatar);
31343144 original_avatar = character.avatar;
31353145 }
public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js+10 -7
@@ -1,4 +1,4 @@
11import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types, name2, neutralCharacterName } from '../../script.js';
22import { extension_settings } from '../extensions.js';
33import { getGroupMembers, groups } from '../group-chats.js';
44import { power_user } from '../power-user.js';
@@ -6,7 +6,9 @@ import { searchCharByName, getTagsList, tags } from '../tags.js';
66import { world_names } from '../world-info.js';
77import { SlashCommandClosure } from './SlashCommandClosure.js';
88import { SlashCommandEnumValue, enumTypes } from './SlashCommandEnumValue.js';
9-import { SlashCommandScope } from "./SlashCommandScope.js";
9+
10+/** @typedef {import('./SlashCommandExecutor.js').SlashCommandExecutor} SlashCommandExecutor */
11+/** @typedef {import('./SlashCommandScope.js').SlashCommandScope} SlashCommandScope */
1012
1113/**
1214 * A collection of regularly used enum icons
@@ -140,7 +142,7 @@ export const commonEnumProviders = {
140142 * @param {...('global'|'local'|'scope'|'all')} type - The type of variables to include in the array. Can be 'all', 'global', or 'local'.
141143 * @returns {(executor:SlashCommandExecutor, scope:SlashCommandScope) => SlashCommandEnumValue[]}
142144 */
143145 variables: (...type) => (executor_, scope) => {
144146 const types = type.flat();
145147 const isAll = types.includes('all');
146148 return [
@@ -160,6 +162,7 @@ export const commonEnumProviders = {
160162 return [
161163 ...['all', 'character'].includes(mode) ? characters.map(char => new SlashCommandEnumValue(char.name, null, enumTypes.name, enumIcons.character)) : [],
162164 ...['all', 'group'].includes(mode) ? groups.map(group => new SlashCommandEnumValue(group.name, null, enumTypes.qr, enumIcons.group)) : [],
165+ ...(name2 === neutralCharacterName) ? [new SlashCommandEnumValue(neutralCharacterName, null, enumTypes.name, '🥸')] : [],
163166 ];
164167 },
165168
@@ -184,7 +187,7 @@ export const commonEnumProviders = {
184187 * @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show
185188 * @returns {() => SlashCommandEnumValue[]}
186189 */
187190 tagsForChar: (mode = 'all') => (/** @type {import('./SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => {
188191 // Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.
189192 const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;
190193 if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');
@@ -202,13 +205,13 @@ export const commonEnumProviders = {
202205 * @param {object} [options={}] - Optional arguments
203206 * @param {boolean} [options.allowIdAfter=false] - Whether to add an enum option for the new message id after the last message
204207 * @param {boolean} [options.allowVars=false] - Whether to add enum option for variable names
205208 * @returns {(executor:SlashCommandExecutor, scope:SlashCommandScope) => SlashCommandEnumValue[]}
206209 */
207210 messages: ({ allowIdAfter = false, allowVars = false } = {}) => (_, scope) => {
208211 return [
209212 ...chat.map((message, index) => new SlashCommandEnumValue(String(index), `${message.name}: ${message.mes}`, enumTypes.number, message.is_user ? enumIcons.user : message.is_system ? enumIcons.system : enumIcons.assistant)),
210213 ...allowIdAfter ? [new SlashCommandEnumValue(String(chat.length), '>> After Last Message >>', enumTypes.enum, '➕')] : [],
211214 ...allowVars ? commonEnumProviders.variables('all')(_, scope) : [],
212215 ];
213216 },
214217
public/style.css+1 -1
@@ -993,7 +993,7 @@ body .panelControlBar {
993993 padding: 0;
994994 font-family: var(--mainFontFamily);
995995 font-weight: 400;
996996 align-self: self-endcenter;
997997}
998998
999999.swipe_left {
server.js+24 -0
@@ -37,6 +37,7 @@ util.inspect.defaultOptions.depth = 4;
3737const userModule = require('./src/users');
3838const basicAuthMiddleware = require('./src/middleware/basicAuth');
3939const whitelistMiddleware = require('./src/middleware/whitelist');
40+const initRequestProxy = require('./src/request-proxy');
4041const contentManager = require('./src/endpoints/content-manager');
4142const {
4243 getVersion,
@@ -75,6 +76,10 @@ const DEFAULT_AVOID_LOCALHOST = false;
7576const DEFAULT_AUTORUN_HOSTNAME = 'auto';
7677const DEFAULT_AUTORUN_PORT = -1;
7778
79+const DEFAULT_PROXY_ENABLED = false;
80+const DEFAULT_PROXY_URL = '';
81+const DEFAULT_PROXY_BYPASS = [];
82+
7883const cliArguments = yargs(hideBin(process.argv))
7984 .usage('Usage: <your-start-script> <command> [options]')
8085 .option('enableIPv6', {
@@ -145,6 +150,18 @@ const cliArguments = yargs(hideBin(process.argv))
145150 type: 'boolean',
146151 default: null,
147152 describe: 'Enables basic authentication',
153+ }).option('requestProxyEnabled', {
154+ type: 'boolean',
155+ default: null,
156+ describe: 'Enables a use of proxy for outgoing requests',
157+ }).option('requestProxyUrl', {
158+ type: 'string',
159+ default: null,
160+ describe: 'Request proxy URL (HTTP or SOCKS protocols)',
161+ }).option('requestProxyBypass', {
162+ type: 'array',
163+ default: null,
164+ describe: 'Request proxy bypass list (space separated list of hosts)',
148165 }).parseSync();
149166
150167// change all relative paths
@@ -181,6 +198,10 @@ const dnsPreferIPv6 = cliArguments.dnsPreferIPv6 ?? getConfigValue('dnsPreferIPv
181198
182199const avoidLocalhost = cliArguments.avoidLocalhost ?? getConfigValue('avoidLocalhost', DEFAULT_AVOID_LOCALHOST);
183200
201+const proxyEnabled = cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', DEFAULT_PROXY_ENABLED);
202+const proxyUrl = cliArguments.requestProxyUrl ?? getConfigValue('requestProxy.url', DEFAULT_PROXY_URL);
203+const proxyBypass = cliArguments.requestProxyBypass ?? getConfigValue('requestProxy.bypass', DEFAULT_PROXY_BYPASS);
204+
184205if (dnsPreferIPv6) {
185206 // Set default DNS resolution order to IPv6 first
186207 dns.setDefaultResultOrder('ipv6first');
@@ -662,6 +683,9 @@ const preSetupTasks = async function () {
662683 console.error('Uncaught exception:', err);
663684 exitProcess();
664685 });
686+
687+ // Add request proxy.
688+ initRequestProxy({ enabled: proxyEnabled, url: proxyUrl, bypass: proxyBypass });
665689};
666690
667691/**
src/request-proxy.js+56 -0
@@ -0,0 +1,56 @@
1+const http = require('node:http');
2+const https = require('node:https');
3+
4+const { isValidUrl, color } = require('./util.js');
5+
6+const LOG_HEADER = '[Request Proxy]';
7+
8+/**
9+ * Initialize request proxy.
10+ * @param {ProxySettings} settings Proxy settings.
11+ * @typedef {object} ProxySettings
12+ * @property {boolean} enabled Whether proxy is enabled.
13+ * @property {string} url Proxy URL.
14+ * @property {string[]} bypass List of URLs to bypass proxy.
15+ */
16+function initRequestProxy({ enabled, url, bypass }) {
17+ try {
18+ const { ProxyAgent } = require('proxy-agent');
19+
20+ // No proxy is enabled, so return
21+ if (!enabled) {
22+ return;
23+ }
24+
25+ if (!url) {
26+ console.error(color.red(LOG_HEADER), 'No proxy URL provided');
27+ return;
28+ }
29+
30+ if (!isValidUrl(url)) {
31+ console.error(color.red(LOG_HEADER), 'Invalid proxy URL provided');
32+ return;
33+ }
34+
35+ // ProxyAgent uses proxy-from-env under the hood
36+ // Reference: https://github.com/Rob--W/proxy-from-env
37+ process.env.all_proxy = url;
38+
39+
40+ if (Array.isArray(bypass) && bypass.length > 0) {
41+ process.env.no_proxy = bypass.join(',');
42+ }
43+
44+ const proxyAgent = new ProxyAgent();
45+ http.globalAgent = proxyAgent;
46+ https.globalAgent = proxyAgent;
47+
48+ console.log();
49+ console.log(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url));
50+ console.log();
51+ } catch (error) {
52+ console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error);
53+ }
54+}
55+
56+module.exports = initRequestProxy;