Merge branch 'staging' into small-bookmark-updates

This commit is contained in:
Cohee 2024-09-12 23:10:33 +03:00
commit d082c2f14d
13 changed files with 833 additions and 216 deletions

40
.github/readme.md vendored
View File

@ -276,6 +276,46 @@ In order to enable viewing your keys by clicking a button in the API block:
1. Set the value of `allowKeysExposure` to `true` in `config.yaml` file.
2. Restart the SillyTavern server.
## Command-line arguments
You can pass command-line arguments to SillyTavern server startup to override some settings in `config.yaml`.
### Examples
```shell
node server.js --port 8000 --listen false
# or
npm run start -- --port 8000 --listen false
# or (Windows only)
Start.bat --port 8000 --listen false
```
### Supported arguments
| Option | Description | Type | Default |
|-------------------------|------------------------------------------------------------------------------------------------------|----------|------------------------------|
| `--version` | Show version number | boolean | |
| `--enableIPv6` | Enables IPv6. | boolean | false |
| `--enableIPv4` | Enables IPv4. | boolean | true |
| `--port` | Sets the port under which SillyTavern will run. If not provided falls back to yaml config 'port'. | number | 8000 |
| `--dnsPreferIPv6` | Prefers IPv6 for dns. If not provided falls back to yaml config 'preferIPv6'. | boolean | false |
| `--autorun` | Automatically launch SillyTavern in the browser. If not provided falls back to yaml config 'autorun'.| boolean | false |
| `--autorunHostname` | The autorun hostname, probably best left on 'auto'. | string | null |
| `--autorunPortOverride` | Overrides the port for autorun. | string | null |
| `--listen` | SillyTavern is listening on all network interfaces. If not provided falls back to yaml config 'listen'.| boolean | false |
| `--corsProxy` | Enables CORS proxy. If not provided falls back to yaml config 'enableCorsProxy'. | boolean | false |
| `--disableCsrf` | Disables CSRF protection | boolean | null |
| `--ssl` | Enables SSL | boolean | false |
| `--certPath` | Path to your certificate file. | string | "certs/cert.pem" |
| `--keyPath` | Path to your private key file. | string | "certs/privkey.pem" |
| `--whitelist` | Enables whitelist mode | boolean | null |
| `--dataRoot` | Root directory for data storage | string | null |
| `--avoidLocalhost` | Avoids using 'localhost' for autorun in auto mode. | boolean | null |
| `--basicAuthMode` | Enables basic authentication | boolean | null |
| `--requestProxyEnabled` | Enables a use of proxy for outgoing requests | boolean | null |
| `--requestProxyUrl` | Request proxy URL (HTTP or SOCKS protocols) | string | null |
| `--requestProxyBypass` | Request proxy bypass list (space separated list of hosts) | array | null |
## Remote connections
Most 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.

View File

@ -37,6 +37,16 @@ basicAuthUser:
password: "password"
# Enables CORS proxy middleware
enableCorsProxy: false
# -- REQUEST PROXY CONFIGURATION --
requestProxy:
# If a proxy is enabled, all outgoing HTTP/HTTPS requests will be routed through it.
enabled: false
# Proxy URL. Possible protocols: http, https, socks, socks5, socks4, pac
url: "socks5://username:password@example.com:1080"
# Proxy bypass list. Requests to these hosts won't be routed through the proxy.
bypass:
- localhost
- 127.0.0.1
# Enable multi-user mode
enableUserAccounts: false
# Enable discreet login mode: hides user list on the login screen

516
package-lock.json generated
View File

@ -22,7 +22,7 @@
"cookie-session": "^2.1.0",
"cors": "^2.8.5",
"csrf-csrf": "^2.2.3",
"express": "^4.19.2",
"express": "^4.20.0",
"form-data": "^4.0.0",
"google-translate-api-browser": "^3.0.1",
"he": "^1.2.0",
@ -40,6 +40,7 @@
"png-chunk-text": "^1.0.0",
"png-chunks-encode": "^1.0.0",
"png-chunks-extract": "^1.0.0",
"proxy-agent": "^6.4.0",
"rate-limiter-flexible": "^5.0.0",
"response-time": "^2.3.2",
"sanitize-filename": "^1.6.3",
@ -941,6 +942,12 @@
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
"license": "MIT"
},
"node_modules/@tootallnate/quickjs-emscripten": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
"license": "MIT"
},
"node_modules/@types/cacheable-request": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
@ -1080,6 +1087,41 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/agent-base": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/agent-base/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/agent-base/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/agentkeepalive": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
@ -1382,6 +1424,18 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/ast-types": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
"integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.1"
},
"engines": {
"node": ">=4"
}
},
"node_modules/async": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
@ -1445,6 +1499,15 @@
],
"license": "MIT"
},
"node_modules/basic-ftp": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
"integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/bing-translate-api": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-2.9.1.tgz",
@ -2250,6 +2313,15 @@
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/data-uri-to-buffer": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
"integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -2316,6 +2388,20 @@
"node": ">=8"
}
},
"node_modules/degenerator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
"integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
"license": "MIT",
"dependencies": {
"ast-types": "^0.13.4",
"escodegen": "^2.1.0",
"esprima": "^4.0.1"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -2528,6 +2614,27 @@
"node": ">=0.8.0"
}
},
"node_modules/escodegen": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"license": "BSD-2-Clause",
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.2"
},
"bin": {
"escodegen": "bin/escodegen.js",
"esgenerate": "bin/esgenerate.js"
},
"engines": {
"node": ">=6.0"
},
"optionalDependencies": {
"source-map": "~0.6.1"
}
},
"node_modules/eslint": {
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
@ -2683,6 +2790,19 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"license": "BSD-2-Clause",
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/esquery": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
@ -2713,7 +2833,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@ -2723,7 +2842,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
@ -3071,6 +3189,20 @@
"node": ">= 0.6"
}
},
"node_modules/fs-extra": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=14.14"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3130,6 +3262,44 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-uri": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz",
"integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==",
"license": "MIT",
"dependencies": {
"basic-ftp": "^5.0.2",
"data-uri-to-buffer": "^6.0.2",
"debug": "^4.3.4",
"fs-extra": "^11.2.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/get-uri/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/get-uri/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/gifwrap": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz",
@ -3369,6 +3539,42 @@
"node": ">= 0.8"
}
},
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/http-proxy-agent/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/http-proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/http2-wrapper": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
@ -3382,6 +3588,42 @@
"node": ">=10.19.0"
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/https-proxy-agent/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/https-proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
@ -3485,6 +3727,19 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
"license": "MIT",
"dependencies": {
"jsbn": "1.1.0",
"sprintf-js": "^1.1.3"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/ip-matching": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ip-matching/-/ip-matching-2.1.2.tgz",
@ -3669,6 +3924,12 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
"license": "MIT"
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@ -3761,6 +4022,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/keygrip": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
@ -4053,6 +4326,15 @@
"node": ">= 0.6"
}
},
"node_modules/netmask": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@ -4323,6 +4605,61 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pac-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz",
"integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==",
"license": "MIT",
"dependencies": {
"@tootallnate/quickjs-emscripten": "^0.23.0",
"agent-base": "^7.0.2",
"debug": "^4.3.4",
"get-uri": "^6.0.1",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.5",
"pac-resolver": "^7.0.1",
"socks-proxy-agent": "^8.0.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/pac-proxy-agent/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/pac-proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/pac-resolver": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
"integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
"license": "MIT",
"dependencies": {
"degenerator": "^5.0.0",
"netmask": "^2.0.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
@ -4618,6 +4955,57 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-agent": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz",
"integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "^4.3.4",
"http-proxy-agent": "^7.0.1",
"https-proxy-agent": "^7.0.3",
"lru-cache": "^7.14.1",
"pac-proxy-agent": "^7.0.1",
"proxy-from-env": "^1.1.0",
"socks-proxy-agent": "^8.0.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/proxy-agent/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/proxy-agent/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@ -5006,36 +5394,6 @@
"node": ">= 0.8.0"
}
},
"node_modules/serve-static/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/serve-static/node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@ -5165,6 +5523,83 @@
"integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==",
"license": "MIT"
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"license": "MIT",
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
"integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
"license": "MIT",
"dependencies": {
"ip-address": "^9.0.5",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks-proxy-agent": {
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
"integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.1",
"debug": "^4.3.4",
"socks": "^2.8.3"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/socks-proxy-agent/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socks-proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"license": "BSD-3-Clause"
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@ -5378,6 +5813,12 @@
"utf8-byte-length": "^1.0.1"
}
},
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"license": "0BSD"
},
"node_modules/tsscmp": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
@ -5437,6 +5878,15 @@
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"license": "MIT"
},
"node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",

View File

@ -12,7 +12,7 @@
"cookie-session": "^2.1.0",
"cors": "^2.8.5",
"csrf-csrf": "^2.2.3",
"express": "^4.19.2",
"express": "^4.20.0",
"form-data": "^4.0.0",
"google-translate-api-browser": "^3.0.1",
"he": "^1.2.0",
@ -30,6 +30,7 @@
"png-chunk-text": "^1.0.0",
"png-chunks-encode": "^1.0.0",
"png-chunks-extract": "^1.0.0",
"proxy-agent": "^6.4.0",
"rate-limiter-flexible": "^5.0.0",
"response-time": "^2.3.2",
"sanitize-filename": "^1.6.3",
@ -62,6 +63,9 @@
},
"node-fetch": {
"whatwg-url": "^14.0.0"
},
"express": {
"send": "^0.19.0"
}
},
"name": "sillytavern",

View File

@ -6479,8 +6479,8 @@
</div>
<!-- Script includes -->
<script src="lib/polyfill.js"></script>
<script src="lib/diff_match_patch.js"></script>
<script src="lib/object-hasown.js"></script>
<script src="lib/jquery-3.5.1.min.js"></script>
<script src="lib/jquery-ui.min.js"></script>
<script src="lib/jquery.transit.min.js"></script>

View File

@ -1,4 +0,0 @@
// Polyfill for old Safari versions
if (!Object.hasOwn) {
Object.hasOwn = function (obj, prop) { return obj.hasOwnProperty(prop); }
}

19
public/lib/polyfill.js Normal file
View File

@ -0,0 +1,19 @@
// Polyfills for old Safari versions
if (!Object.hasOwn) {
Object.hasOwn = function (obj, prop) { return obj.hasOwnProperty(prop); }
}
if (!Array.prototype.findLastIndex) {
Array.prototype.findLastIndex = function (callback, thisArg) {
for (let i = this.length - 1; i >= 0; i--) {
if (callback.call(thisArg, this[i], i, this)) return i;
}
return -1;
};
}
if (!Array.prototype.toSorted) {
Array.prototype.toSorted = function (compareFunction) {
return this.slice().sort(compareFunction);
};
}

View File

@ -7472,7 +7472,7 @@ export function showSwipeButtons() {
const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`);
const swipeId = chat[chat.length - 1].swipe_id;
var swipesCounterHTML = (`${(swipeId + 1)}/${(chat[chat.length - 1].swipes.length)}`);
const swipeCounterText = (`${(swipeId + 1)}\u200B/\u200b${(chat[chat.length - 1].swipes.length)}`);
if (swipeId !== undefined && (chat[chat.length - 1].swipes.length > 1 || swipeId > 0)) {
currentMessage.children('.swipe_left').css('display', 'flex');
@ -7489,7 +7489,7 @@ export function showSwipeButtons() {
}
//console.log(swipesCounterHTML);
$('.swipes-counter').html(swipesCounterHTML);
$('.swipes-counter').text(swipeCounterText);
//console.log(swipeId);
//console.log(chat[chat.length - 1].swipes.length);
@ -7878,13 +7878,19 @@ async function createOrEditCharacter(e) {
formData.set('avatar', convertedFile);
}
if ($('#form_create').attr('actiontype') == 'createcharacter') {
if (String($('#character_name_pole').val()).length > 0) {
if (is_group_generating || is_send_press) {
toastr.error('Cannot create characters while generating. Stop the request and try again.', 'Creation aborted');
throw new Error('Cannot import character while generating');
}
const headers = getRequestHeaders();
delete headers['Content-Type'];
if ($('#form_create').attr('actiontype') == 'createcharacter') {
if (String($('#character_name_pole').val()).length === 0) {
toastr.error('Name is required');
return;
}
if (is_group_generating || is_send_press) {
toastr.error('Cannot create characters while generating. Stop the request and try again.', 'Creation aborted');
return;
}
try {
//if the character name text area isn't empty (only posible when creating a new character)
let url = '/api/characters/create';
@ -7899,142 +7905,133 @@ async function createOrEditCharacter(e) {
formData.append('extensions', JSON.stringify(create_save.extensions));
await jQuery.ajax({
type: 'POST',
url: url,
data: formData,
beforeSend: function () {
$('#create_button').attr('disabled', String(true));
$('#create_button').attr('value', '⏳');
},
cache: false,
contentType: false,
processData: false,
success: async function (html) {
$('#character_cross').trigger('click'); //closes the advanced character editing popup
const fields = [
{ id: '#character_name_pole', callback: value => create_save.name = value },
{ id: '#description_textarea', callback: value => create_save.description = value },
{ id: '#creator_notes_textarea', callback: value => create_save.creator_notes = value },
{ id: '#character_version_textarea', callback: value => create_save.character_version = value },
{ id: '#post_history_instructions_textarea', callback: value => create_save.post_history_instructions = value },
{ id: '#system_prompt_textarea', callback: value => create_save.system_prompt = value },
{ id: '#tags_textarea', callback: value => create_save.tags = value },
{ id: '#creator_textarea', callback: value => create_save.creator = value },
{ id: '#personality_textarea', callback: value => create_save.personality = value },
{ id: '#firstmessage_textarea', callback: value => create_save.first_message = value },
{ id: '#talkativeness_slider', callback: value => create_save.talkativeness = value, defaultValue: talkativeness_default },
{ id: '#scenario_pole', callback: value => create_save.scenario = value },
{ id: '#depth_prompt_prompt', callback: value => create_save.depth_prompt_prompt = value },
{ id: '#depth_prompt_depth', callback: value => create_save.depth_prompt_depth = value, defaultValue: depth_prompt_depth_default },
{ id: '#depth_prompt_role', callback: value => create_save.depth_prompt_role = value, defaultValue: depth_prompt_role_default },
{ id: '#mes_example_textarea', callback: value => create_save.mes_example = value },
{ id: '#character_json_data', callback: () => { } },
{ id: '#alternate_greetings_template', callback: value => create_save.alternate_greetings = value, defaultValue: [] },
{ id: '#character_world', callback: value => create_save.world = value },
{ id: '#_character_extensions_fake', callback: value => create_save.extensions = {} },
];
fields.forEach(field => {
const fieldValue = field.defaultValue !== undefined ? field.defaultValue : '';
$(field.id).val(fieldValue);
field.callback && field.callback(fieldValue);
});
$('#character_popup-button-h3').text('Create character');
create_save.avatar = '';
$('#create_button').removeAttr('disabled');
$('#add_avatar_button').replaceWith(
$('#add_avatar_button').val('').clone(true),
);
$('#create_button').attr('value', '✅');
let oldSelectedChar = null;
if (this_chid !== undefined) {
oldSelectedChar = characters[this_chid].avatar;
}
console.log(`new avatar id: ${html}`);
createTagMapFromList('#tagList', html);
await getCharacters();
select_rm_info('char_create', html, oldSelectedChar);
crop_data = undefined;
},
error: function (jqXHR, exception) {
$('#create_button').removeAttr('disabled');
},
const fetchResult = await fetch(url, {
method: 'POST',
headers: headers,
body: formData,
cache: 'no-cache',
});
} else {
toastr.error('Name is required');
if (!fetchResult.ok) {
throw new Error('Fetch result is not ok');
}
const avatarId = await fetchResult.text();
$('#character_cross').trigger('click'); //closes the advanced character editing popup
const fields = [
{ id: '#character_name_pole', callback: value => create_save.name = value },
{ id: '#description_textarea', callback: value => create_save.description = value },
{ id: '#creator_notes_textarea', callback: value => create_save.creator_notes = value },
{ id: '#character_version_textarea', callback: value => create_save.character_version = value },
{ id: '#post_history_instructions_textarea', callback: value => create_save.post_history_instructions = value },
{ id: '#system_prompt_textarea', callback: value => create_save.system_prompt = value },
{ id: '#tags_textarea', callback: value => create_save.tags = value },
{ id: '#creator_textarea', callback: value => create_save.creator = value },
{ id: '#personality_textarea', callback: value => create_save.personality = value },
{ id: '#firstmessage_textarea', callback: value => create_save.first_message = value },
{ id: '#talkativeness_slider', callback: value => create_save.talkativeness = value, defaultValue: talkativeness_default },
{ id: '#scenario_pole', callback: value => create_save.scenario = value },
{ id: '#depth_prompt_prompt', callback: value => create_save.depth_prompt_prompt = value },
{ id: '#depth_prompt_depth', callback: value => create_save.depth_prompt_depth = value, defaultValue: depth_prompt_depth_default },
{ id: '#depth_prompt_role', callback: value => create_save.depth_prompt_role = value, defaultValue: depth_prompt_role_default },
{ id: '#mes_example_textarea', callback: value => create_save.mes_example = value },
{ id: '#character_json_data', callback: () => { } },
{ id: '#alternate_greetings_template', callback: value => create_save.alternate_greetings = value, defaultValue: [] },
{ id: '#character_world', callback: value => create_save.world = value },
{ id: '#_character_extensions_fake', callback: value => create_save.extensions = {} },
];
fields.forEach(field => {
const fieldValue = field.defaultValue !== undefined ? field.defaultValue : '';
$(field.id).val(fieldValue);
field.callback && field.callback(fieldValue);
});
$('#character_popup-button-h3').text('Create character');
create_save.avatar = '';
$('#add_avatar_button').replaceWith(
$('#add_avatar_button').val('').clone(true),
);
let oldSelectedChar = null;
if (this_chid !== undefined) {
oldSelectedChar = characters[this_chid].avatar;
}
console.log(`new avatar id: ${avatarId}`);
createTagMapFromList('#tagList', avatarId);
await getCharacters();
select_rm_info('char_create', avatarId, oldSelectedChar);
crop_data = undefined;
} catch (error) {
console.error('Error creating character', error);
toastr.error('Failed to create character');
}
} else {
let url = '/api/characters/edit';
try {
let url = '/api/characters/edit';
if (crop_data != undefined) {
url += `?crop=${encodeURIComponent(JSON.stringify(crop_data))}`;
}
formData.delete('alternate_greetings');
const chid = $('.open_alternate_greetings').data('chid');
if (chid && Array.isArray(characters[chid]?.data?.alternate_greetings)) {
for (const value of characters[chid].data.alternate_greetings) {
formData.append('alternate_greetings', value);
if (crop_data != undefined) {
url += `?crop=${encodeURIComponent(JSON.stringify(crop_data))}`;
}
}
await jQuery.ajax({
type: 'POST',
url: url,
data: formData,
beforeSend: function () {
$('#create_button').attr('disabled', String(true));
$('#create_button').attr('value', 'Save');
},
cache: false,
contentType: false,
processData: false,
success: async function (html) {
$('#create_button').removeAttr('disabled');
await getOneCharacter(formData.get('avatar_url'));
favsToHotswap(); // Update fav state
$('#add_avatar_button').replaceWith(
$('#add_avatar_button').val('').clone(true),
);
$('#create_button').attr('value', 'Save');
crop_data = undefined;
eventSource.emit(event_types.CHARACTER_EDITED, { detail: { id: this_chid, character: characters[this_chid] } });
// Recreate the chat if it hasn't been used at least once (i.e. with continue).
const message = getFirstMessage();
const shouldRegenerateMessage =
!isNewChat &&
message.mes &&
!selected_group &&
!chat_metadata['tainted'] &&
(chat.length === 0 || (chat.length === 1 && !chat[0].is_user && !chat[0].is_system));
if (shouldRegenerateMessage) {
chat.splice(0, chat.length, message);
const messageId = (chat.length - 1);
await eventSource.emit(event_types.MESSAGE_RECEIVED, messageId);
await clearChat();
await printMessages();
await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, messageId);
await saveChatConditional();
formData.delete('alternate_greetings');
const chid = $('.open_alternate_greetings').data('chid');
if (chid && Array.isArray(characters[chid]?.data?.alternate_greetings)) {
for (const value of characters[chid].data.alternate_greetings) {
formData.append('alternate_greetings', value);
}
},
error: function (jqXHR, exception) {
$('#create_button').removeAttr('disabled');
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.');
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.');
},
});
}
const fetchResult = await fetch(url, {
method: 'POST',
headers: headers,
body: formData,
cache: 'no-cache',
});
if (!fetchResult.ok) {
throw new Error('Fetch result is not ok');
}
await getOneCharacter(formData.get('avatar_url'));
favsToHotswap(); // Update fav state
$('#add_avatar_button').replaceWith(
$('#add_avatar_button').val('').clone(true),
);
$('#create_button').attr('value', 'Save');
crop_data = undefined;
await eventSource.emit(event_types.CHARACTER_EDITED, { detail: { id: this_chid, character: characters[this_chid] } });
// Recreate the chat if it hasn't been used at least once (i.e. with continue).
const message = getFirstMessage();
const shouldRegenerateMessage =
!isNewChat &&
message.mes &&
!selected_group &&
!chat_metadata['tainted'] &&
(chat.length === 0 || (chat.length === 1 && !chat[0].is_user && !chat[0].is_system));
if (shouldRegenerateMessage) {
chat.splice(0, chat.length, message);
const messageId = (chat.length - 1);
await eventSource.emit(event_types.MESSAGE_RECEIVED, messageId);
await clearChat();
await printMessages();
await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, messageId);
await saveChatConditional();
}
} catch (error) {
console.log(error);
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.');
}
}
}
@ -8526,7 +8523,7 @@ async function selectContextCallback(args, name) {
async function selectInstructCallback(args, name) {
if (!name) {
return power_user.instruct.preset;
return power_user.instruct.enabled || isTrueBoolean(args?.forceGet) ? power_user.instruct.preset : '';
}
const quiet = isTrueBoolean(args?.quiet);
@ -9227,6 +9224,13 @@ jQuery(async function () {
defaultValue: 'false',
enumList: commonEnumProviders.boolean('trueFalse')(),
}),
SlashCommandNamedArgument.fromProps({
name: 'forceGet',
description: 'Force getting a name even if instruct mode is disabled',
typeList: [ARGUMENT_TYPE.BOOLEAN],
defaultValue: 'false',
enumList: commonEnumProviders.boolean('trueFalse')(),
}),
],
unnamedArgumentList: [
SlashCommandArgument.fromProps({
@ -9237,7 +9241,8 @@ jQuery(async function () {
],
helpString: `
<div>
Selects instruct mode template by name. Gets the current instruct template if no name is provided.
Selects instruct mode template by name. Enables instruct mode if not already enabled.
Gets the current instruct template if no name is provided and instruct mode is enabled or <code>forceGet=true</code> is passed.
</div>
<div>
<strong>Example:</strong>

View File

@ -24,6 +24,7 @@ import {
main_api,
name1,
name2,
neutralCharacterName,
reloadCurrentChat,
removeMacros,
renameCharacter,
@ -427,6 +428,7 @@ export function initDefaultSlashCommands() {
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'ask',
callback: askCharacter,
returns: 'the generated text',
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'name',
@ -438,7 +440,7 @@ export function initDefaultSlashCommands() {
],
unnamedArgumentList: [
new SlashCommandArgument(
'prompt', [ARGUMENT_TYPE.STRING], true, false,
'prompt', [ARGUMENT_TYPE.STRING], false, false,
),
],
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) {
// Not supported in group chats
// TODO: Maybe support group chats?
if (selected_group) {
toastr.error('Cannot run this command in a group chat!');
return '';
}
if (!text) {
console.warn('WARN: No text provided for /ask command');
toastr.warning('No text provided for /ask command');
toastr.error('Cannot run /ask command in a group chat!');
return '';
}
let name = '';
let mesText = '';
if (args?.name) {
name = args.name.trim();
mesText = text.trim();
if (!name && !mesText) {
toastr.warning('You must specify a name and text to ask.');
if (!name) {
toastr.warning('You must specify a name of the character to ask.');
return '';
}
}
mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND);
const prevChId = this_chid;
// Find the character
const chId = characters.findIndex((e) => e.name === name);
const chId = characters.findIndex((e) => e.name === name || e.avatar === name);
if (!characters[chId] || chId === -1) {
toastr.error('Character not found.');
return '';
}
if (text) {
const mesText = getRegexedString(text.trim(), regex_placement.SLASH_COMMAND);
// Sending a message implicitly saves the chat, so this needs to be done before changing the character
// Otherwise, a corruption will occur
await sendMessageAsUser(mesText, '');
}
// Override character and send a user message
setCharacterId(String(chId));
// TODO: Maybe look up by filename instead of name
const character = characters[chId];
let force_avatar, original_avatar;
@ -2519,9 +2517,11 @@ async function askCharacter(args, text) {
setCharacterName(character.name);
await sendMessageAsUser(mesText, '');
const restoreCharacter = () => {
if (String(this_chid) !== String(chId)) {
return;
}
setCharacterId(prevChId);
setCharacterName(characters[prevChId].name);
@ -2532,23 +2532,27 @@ async function askCharacter(args, text) {
lastMessage.force_avatar = force_avatar;
lastMessage.original_avatar = original_avatar;
}
// Kill this callback once the event fires
eventSource.removeListener(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
};
// Run generate and restore previous character on error
let askResult = '';
// Run generate and restore previous character
try {
eventSource.once(event_types.MESSAGE_RECEIVED, restoreCharacter);
toastr.info(`Asking ${character.name} something...`);
await Generate('ask_command');
} catch {
askResult = await Generate('ask_command');
} catch (error) {
restoreCharacter();
console.error('Error running /ask command', error);
} finally {
if (String(this_chid) === String(prevChId)) {
await saveChatConditional();
} else {
toastr.error('It is strongly recommended to reload the page.', 'Something went wrong');
}
}
// Restore previous character once message renders
// Hack for generate
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
return '';
return askResult;
}
async function hideMessageCallback(_, arg) {
@ -3129,7 +3133,13 @@ export async function sendMessageAs(args, text) {
const character = characters.find(x => x.avatar === name) ?? characters.find(x => x.name === name);
let force_avatar, original_avatar;
if (character && character.avatar !== 'none') {
const chatCharacter = this_chid !== undefined ? characters[this_chid] : null;
const isNeutralCharacter = !chatCharacter && name2 === neutralCharacterName && name === neutralCharacterName;
if (chatCharacter === character || isNeutralCharacter) {
// If the targeted character is the currently selected one in a solo chat, we don't need to force any avatars
}
else if (character && character.avatar !== 'none') {
force_avatar = getThumbnailUrl('avatar', character.avatar);
original_avatar = character.avatar;
}

View File

@ -1,4 +1,4 @@
import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from '../../script.js';
import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types, name2, neutralCharacterName } from '../../script.js';
import { extension_settings } from '../extensions.js';
import { getGroupMembers, groups } from '../group-chats.js';
import { power_user } from '../power-user.js';
@ -6,7 +6,9 @@ import { searchCharByName, getTagsList, tags } from '../tags.js';
import { world_names } from '../world-info.js';
import { SlashCommandClosure } from './SlashCommandClosure.js';
import { SlashCommandEnumValue, enumTypes } from './SlashCommandEnumValue.js';
import { SlashCommandScope } from "./SlashCommandScope.js";
/** @typedef {import('./SlashCommandExecutor.js').SlashCommandExecutor} SlashCommandExecutor */
/** @typedef {import('./SlashCommandScope.js').SlashCommandScope} SlashCommandScope */
/**
* A collection of regularly used enum icons
@ -140,7 +142,7 @@ export const commonEnumProviders = {
* @param {...('global'|'local'|'scope'|'all')} type - The type of variables to include in the array. Can be 'all', 'global', or 'local'.
* @returns {(executor:SlashCommandExecutor, scope:SlashCommandScope) => SlashCommandEnumValue[]}
*/
variables: (...type) => (executor, scope) => {
variables: (...type) => (_, scope) => {
const types = type.flat();
const isAll = types.includes('all');
return [
@ -160,6 +162,7 @@ export const commonEnumProviders = {
return [
...['all', 'character'].includes(mode) ? characters.map(char => new SlashCommandEnumValue(char.name, null, enumTypes.name, enumIcons.character)) : [],
...['all', 'group'].includes(mode) ? groups.map(group => new SlashCommandEnumValue(group.name, null, enumTypes.qr, enumIcons.group)) : [],
...(name2 === neutralCharacterName) ? [new SlashCommandEnumValue(neutralCharacterName, null, enumTypes.name, '🥸')] : [],
];
},
@ -184,7 +187,7 @@ export const commonEnumProviders = {
* @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show
* @returns {() => SlashCommandEnumValue[]}
*/
tagsForChar: (mode = 'all') => (/** @type {import('./SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => {
tagsForChar: (mode = 'all') => (/** @type {SlashCommandExecutor} */ executor) => {
// Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.
const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;
if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');
@ -202,13 +205,13 @@ export const commonEnumProviders = {
* @param {object} [options={}] - Optional arguments
* @param {boolean} [options.allowIdAfter=false] - Whether to add an enum option for the new message id after the last message
* @param {boolean} [options.allowVars=false] - Whether to add enum option for variable names
* @returns {() => SlashCommandEnumValue[]}
* @returns {(executor:SlashCommandExecutor, scope:SlashCommandScope) => SlashCommandEnumValue[]}
*/
messages: ({ allowIdAfter = false, allowVars = false } = {}) => () => {
messages: ({ allowIdAfter = false, allowVars = false } = {}) => (_, scope) => {
return [
...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)),
...allowIdAfter ? [new SlashCommandEnumValue(String(chat.length), '>> After Last Message >>', enumTypes.enum, '')] : [],
...allowVars ? commonEnumProviders.variables('all')() : [],
...allowVars ? commonEnumProviders.variables('all')(_, scope) : [],
];
},

View File

@ -993,7 +993,7 @@ body .panelControlBar {
padding: 0;
font-family: var(--mainFontFamily);
font-weight: 400;
align-self: self-end;
align-self: center;
}
.swipe_left {

View File

@ -37,6 +37,7 @@ util.inspect.defaultOptions.depth = 4;
const userModule = require('./src/users');
const basicAuthMiddleware = require('./src/middleware/basicAuth');
const whitelistMiddleware = require('./src/middleware/whitelist');
const initRequestProxy = require('./src/request-proxy');
const contentManager = require('./src/endpoints/content-manager');
const {
getVersion,
@ -75,6 +76,10 @@ const DEFAULT_AVOID_LOCALHOST = false;
const DEFAULT_AUTORUN_HOSTNAME = 'auto';
const DEFAULT_AUTORUN_PORT = -1;
const DEFAULT_PROXY_ENABLED = false;
const DEFAULT_PROXY_URL = '';
const DEFAULT_PROXY_BYPASS = [];
const cliArguments = yargs(hideBin(process.argv))
.usage('Usage: <your-start-script> <command> [options]')
.option('enableIPv6', {
@ -145,6 +150,18 @@ const cliArguments = yargs(hideBin(process.argv))
type: 'boolean',
default: null,
describe: 'Enables basic authentication',
}).option('requestProxyEnabled', {
type: 'boolean',
default: null,
describe: 'Enables a use of proxy for outgoing requests',
}).option('requestProxyUrl', {
type: 'string',
default: null,
describe: 'Request proxy URL (HTTP or SOCKS protocols)',
}).option('requestProxyBypass', {
type: 'array',
default: null,
describe: 'Request proxy bypass list (space separated list of hosts)',
}).parseSync();
// change all relative paths
@ -181,6 +198,10 @@ const dnsPreferIPv6 = cliArguments.dnsPreferIPv6 ?? getConfigValue('dnsPreferIPv
const avoidLocalhost = cliArguments.avoidLocalhost ?? getConfigValue('avoidLocalhost', DEFAULT_AVOID_LOCALHOST);
const proxyEnabled = cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', DEFAULT_PROXY_ENABLED);
const proxyUrl = cliArguments.requestProxyUrl ?? getConfigValue('requestProxy.url', DEFAULT_PROXY_URL);
const proxyBypass = cliArguments.requestProxyBypass ?? getConfigValue('requestProxy.bypass', DEFAULT_PROXY_BYPASS);
if (dnsPreferIPv6) {
// Set default DNS resolution order to IPv6 first
dns.setDefaultResultOrder('ipv6first');
@ -662,6 +683,9 @@ const preSetupTasks = async function () {
console.error('Uncaught exception:', err);
exitProcess();
});
// Add request proxy.
initRequestProxy({ enabled: proxyEnabled, url: proxyUrl, bypass: proxyBypass });
};
/**

56
src/request-proxy.js Normal file
View File

@ -0,0 +1,56 @@
const http = require('node:http');
const https = require('node:https');
const { isValidUrl, color } = require('./util.js');
const LOG_HEADER = '[Request Proxy]';
/**
* Initialize request proxy.
* @param {ProxySettings} settings Proxy settings.
* @typedef {object} ProxySettings
* @property {boolean} enabled Whether proxy is enabled.
* @property {string} url Proxy URL.
* @property {string[]} bypass List of URLs to bypass proxy.
*/
function initRequestProxy({ enabled, url, bypass }) {
try {
const { ProxyAgent } = require('proxy-agent');
// No proxy is enabled, so return
if (!enabled) {
return;
}
if (!url) {
console.error(color.red(LOG_HEADER), 'No proxy URL provided');
return;
}
if (!isValidUrl(url)) {
console.error(color.red(LOG_HEADER), 'Invalid proxy URL provided');
return;
}
// ProxyAgent uses proxy-from-env under the hood
// Reference: https://github.com/Rob--W/proxy-from-env
process.env.all_proxy = url;
if (Array.isArray(bypass) && bypass.length > 0) {
process.env.no_proxy = bypass.join(',');
}
const proxyAgent = new ProxyAgent();
http.globalAgent = proxyAgent;
https.globalAgent = proxyAgent;
console.log();
console.log(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url));
console.log();
} catch (error) {
console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error);
}
}
module.exports = initRequestProxy;