From f75daba6c0206f80e1d1a048df9546de5b847b9a Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 21 Apr 2024 23:38:18 +0300
Subject: [PATCH 01/21] Image inlining hint always visible
---
public/css/toggle-dependent.css | 8 --------
1 file changed, 8 deletions(-)
diff --git a/public/css/toggle-dependent.css b/public/css/toggle-dependent.css
index 30fb33bcf..834e31c31 100644
--- a/public/css/toggle-dependent.css
+++ b/public/css/toggle-dependent.css
@@ -433,14 +433,6 @@ body.expandMessageActions .mes .mes_buttons .extraMesButtonsHint {
display: none !important;
}
-#openai_image_inlining:not(:checked)~#image_inlining_hint {
- display: none;
-}
-
-#openai_image_inlining:checked~#image_inlining_hint {
- display: block;
-}
-
#smooth_streaming:not(:checked)~#smooth_streaming_speed_control {
display: none;
}
From bc9c70556e8e3ff8c07aaf7a0aed5dc307c0cecb Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 21 Apr 2024 23:53:46 +0300
Subject: [PATCH 02/21] Clean-up mentions of /public/
---
.github/workflows/update-docs.yml | 43 -------------------
Update-Instructions.txt | 18 +++++---
.../extensions/expressions/settings.html | 2 +-
src/util.js | 6 +--
4 files changed, 16 insertions(+), 53 deletions(-)
delete mode 100644 .github/workflows/update-docs.yml
diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml
deleted file mode 100644
index 567cac607..000000000
--- a/.github/workflows/update-docs.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: Update SillyTavern-Docs
-
-on:
- push:
- branches:
- - main
-
-jobs:
- update_docs:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout current repository
- uses: actions/checkout@v2
-
- - name: Checkout SillyTavern-Docs repository
- uses: actions/checkout@v2
- with:
- repository: SillyTavern/SillyTavern-Docs
- path: SillyTavern-Docs
-
- - name: Clone SillyTavern wiki into SillyTavern-Docs/extensions
- run: rm -rf SillyTavern-Docs/extensions && git clone https://github.com/SillyTavern/SillyTavern.wiki.git SillyTavern-Docs/extensions && rm -rf SillyTavern-Docs/extensions/.git
-
- - name: Copy files
- run: |
- cp public/notes/content.md SillyTavern-Docs/guidebook.md
- cp faq.md SillyTavern-Docs/faq.md
- cp readme.md SillyTavern-Docs/readme.md
- cp public/notes/update.md SillyTavern-Docs/update.md
-
- - name: Deploy to external repository
- uses: cpina/github-action-push-to-another-repository@main
- env:
- SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
- with:
- # GitHub Action output files
- source-directory: SillyTavern-Docs/
- destination-github-username: SillyTavern
- destination-repository-name: SillyTavern-Docs
- user-email: github-actions[bot]@users.noreply.github.com
- user-name: "GitHub Actions"
- target-branch: "main"
diff --git a/Update-Instructions.txt b/Update-Instructions.txt
index f153660b2..6e7071184 100644
--- a/Update-Instructions.txt
+++ b/Update-Instructions.txt
@@ -33,7 +33,14 @@ If you insist on installing via a zip, here is the tedious process for doing the
2. Unzip it into a folder OUTSIDE of your current ST installation.
3. Do the usual setup procedure for your OS to install the NodeJS requirements.
-4. Copy the following files/folders as necessary(*) from your old ST installation:
+4a. Updating 1.12.0 and above
+
+Copy the user data directory from your data root into the data root of the new install.
+
+By default: /data/default-user
+
+4a. Migrating from <1.12.0 to >=1.20.0
+Copy the following files/folders as necessary(*) from your old ST installation:
- Assets
- Backgrounds
@@ -54,16 +61,15 @@ If you insist on installing via a zip, here is the tedious process for doing the
- Worlds
- User
- settings.json
- - secrets.json <---- this one is in the base folder, not /public/
+ - secrets.json <---- This one is in the base folder, not /public/
(*) 'As necessary' = "If you made any custom content related to those folders".
None of the folders are mandatory, so only copy what you need.
**NB: DO NOT COPY THE ENTIRE /PUBLIC/ FOLDER.**
Doing so could break the new install and prevent new features from being present.
+ Paste those items into the /data/default-user folder of the new install.
-5. Paste those items into the /Public/ folder of the new install.
+5. Start SillyTavern once again with the method appropriate to your OS, and pray you got it right.
-6. Start SillyTavern once again with the method appropriate to your OS, and pray you got it right.
-
-7. If everything shows up, you can safely delete the old ST folder.
+6. If everything shows up, you can safely delete the old ST folder.
diff --git a/public/scripts/extensions/expressions/settings.html b/public/scripts/extensions/expressions/settings.html
index 4a7347a74..e8b1484b2 100644
--- a/public/scripts/extensions/expressions/settings.html
+++ b/public/scripts/extensions/expressions/settings.html
@@ -78,7 +78,7 @@
Remove all image overrides
-
Hint:Create new folder in the public/characters/ folder and name it as the name of the character.
+
Hint:Create new folder in the /characters/ folder of your user data directory and name it as the name of the character.
Put images with expressions there. File names should follow the pattern: [expression_label].[image_format]
diff --git a/src/util.js b/src/util.js
index e1410eee8..ab19f3ccf 100644
--- a/src/util.js
+++ b/src/util.js
@@ -311,9 +311,9 @@ function tryParse(str) {
}
/**
- * Takes a path to a client-accessible file in the `public` folder and converts it to a relative URL segment that the
- * client can fetch it from. This involves stripping the `public/` prefix and always using `/` as the separator.
- * @param {string} root The root directory of the public folder.
+ * Takes a path to a client-accessible file in the data folder and converts it to a relative URL segment that the
+ * client can fetch it from. This involves stripping the data root path prefix and always using `/` as the separator.
+ * @param {string} root The root directory of the user data folder.
* @param {string} inputPath The path to be converted.
* @returns The relative URL path from which the client can access the file.
*/
From df93d43c36de5ffe4cbb28930c6e68ad05f09388 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 22 Apr 2024 00:02:48 +0300
Subject: [PATCH 03/21] Remove obnoxious mobile padding on right panel
---
public/css/mobile-styles.css | 2 ++
1 file changed, 2 insertions(+)
diff --git a/public/css/mobile-styles.css b/public/css/mobile-styles.css
index 68534d8a2..8456d54a0 100644
--- a/public/css/mobile-styles.css
+++ b/public/css/mobile-styles.css
@@ -231,9 +231,11 @@
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
}
+ /*
#right-nav-panel {
padding-right: 15px;
}
+ */
#floatingPrompt,
#cfgConfig,
From 41ad7c5d266d33b9432ab94b5cded10ffab29f1a Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 22 Apr 2024 02:34:50 +0300
Subject: [PATCH 04/21] Verify data bank attachments
---
public/scripts/chats.js | 52 ++++++++++++++++++++++++++++++++++++++---
src/endpoints/files.js | 25 ++++++++++++++++++++
2 files changed, 74 insertions(+), 3 deletions(-)
diff --git a/public/scripts/chats.js b/public/scripts/chats.js
index a4082f6f0..19e316fa5 100644
--- a/public/scripts/chats.js
+++ b/public/scripts/chats.js
@@ -592,9 +592,10 @@ async function deleteMessageImage() {
/**
* Deletes file from the server.
* @param {string} url Path to the file on the server
+ * @param {boolean} [silent=false] If true, do not show error messages
* @returns {Promise} True if file was deleted, false otherwise.
*/
-async function deleteFileFromServer(url) {
+async function deleteFileFromServer(url, silent = false) {
try {
const result = await fetch('/api/files/delete', {
method: 'POST',
@@ -602,7 +603,7 @@ async function deleteFileFromServer(url) {
body: JSON.stringify({ path: url }),
});
- if (!result.ok) {
+ if (!result.ok && !silent) {
const error = await result.text();
throw new Error(error);
}
@@ -702,7 +703,8 @@ async function deleteAttachment(attachment, source, callback, confirm = true) {
break;
}
- await deleteFileFromServer(attachment.url);
+ const silent = confirm === false;
+ await deleteFileFromServer(attachment.url, silent);
callback();
}
@@ -756,6 +758,7 @@ async function openAttachmentManager() {
for (const attachment of sortedAttachmentList) {
const attachmentTemplate = template.find('.attachmentListItemTemplate .attachmentListItem').clone();
+ attachmentTemplate.find('.attachmentFileIcon').attr('title', attachment.url);
attachmentTemplate.find('.attachmentListItemName').text(attachment.name);
attachmentTemplate.find('.attachmentListItemSize').text(humanFileSize(attachment.size));
attachmentTemplate.find('.attachmentListItemCreated').text(new Date(attachment.created).toLocaleString());
@@ -883,6 +886,7 @@ async function openAttachmentManager() {
});
const cleanupFn = await renderButtons();
+ await verifyAttachments();
await renderAttachments();
await callGenericPopup(template, POPUP_TYPE.TEXT, '', { wide: true, large: true, okButton: 'Close' });
@@ -1039,6 +1043,48 @@ export function getDataBankAttachmentsForSource(source) {
}
}
+/**
+ * Verifies all attachments in the Data Bank.
+ * @returns {Promise} A promise that resolves when attachments are verified.
+ */
+async function verifyAttachments() {
+ for (const source of Object.values(ATTACHMENT_SOURCE)) {
+ await verifyAttachmentsForSource(source);
+ }
+}
+
+/**
+ * Verifies all attachments for a specific source.
+ * @param {string} source Attachment source
+ * @returns {Promise} A promise that resolves when attachments are verified.
+ */
+async function verifyAttachmentsForSource(source) {
+ try {
+ const attachments = getDataBankAttachmentsForSource(source);
+ const urls = attachments.map(a => a.url);
+ const response = await fetch('/api/files/verify', {
+ method: 'POST',
+ headers: getRequestHeaders(),
+ body: JSON.stringify({ urls }),
+ });
+
+ if (!response.ok) {
+ const error = await response.text();
+ throw new Error(error);
+ }
+
+ const verifiedUrls = await response.json();
+ for (const attachment of attachments) {
+ if (verifiedUrls[attachment.url] === false) {
+ console.log('Deleting orphaned attachment', attachment);
+ await deleteAttachment(attachment, source, () => { }, false);
+ }
+ }
+ } catch (error) {
+ console.error('Attachment verification failed', error);
+ }
+}
+
/**
* Registers a file converter function.
* @param {string} mimeType MIME type
diff --git a/src/endpoints/files.js b/src/endpoints/files.js
index 1c66273bd..371381c21 100644
--- a/src/endpoints/files.js
+++ b/src/endpoints/files.js
@@ -57,4 +57,29 @@ router.post('/delete', jsonParser, async (request, response) => {
}
});
+router.post('/verify', jsonParser, async (request, response) => {
+ try {
+ if (!Array.isArray(request.body.urls)) {
+ return response.status(400).send('No URLs specified');
+ }
+
+ const verified = {};
+
+ for (const url of request.body.urls) {
+ const pathToVerify = path.join(request.user.directories.root, url);
+ if (!pathToVerify.startsWith(request.user.directories.files)) {
+ console.debug(`File verification: Invalid path: ${pathToVerify}`);
+ continue;
+ }
+ const fileExists = fs.existsSync(pathToVerify);
+ verified[url] = fileExists;
+ }
+
+ return response.send(verified);
+ } catch (error) {
+ console.log(error);
+ return response.sendStatus(500);
+ }
+});
+
module.exports = { router };
From 2f45f50d370afc8de4bf3cc2be61c7161e66790c Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 22 Apr 2024 15:52:59 +0300
Subject: [PATCH 05/21] Add config value for forwarded IPs whitelisting
---
default/config.yaml | 2 ++
src/middleware/whitelist.js | 9 +++++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/default/config.yaml b/default/config.yaml
index 8966447d0..355573d96 100644
--- a/default/config.yaml
+++ b/default/config.yaml
@@ -9,6 +9,8 @@ port: 8000
# -- SECURITY CONFIGURATION --
# Toggle whitelist mode
whitelistMode: true
+# Whitelist will also verify IP in X-Forwarded-For / X-Real-IP headers
+enableForwardedWhitelist: true
# Whitelist of allowed IP addresses
whitelist:
- 127.0.0.1
diff --git a/src/middleware/whitelist.js b/src/middleware/whitelist.js
index def408650..24c1af8e5 100644
--- a/src/middleware/whitelist.js
+++ b/src/middleware/whitelist.js
@@ -6,6 +6,7 @@ const { getIpFromRequest } = require('../express-common');
const { color, getConfigValue } = require('../util');
const whitelistPath = path.join(process.cwd(), './whitelist.txt');
+const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false);
let whitelist = getConfigValue('whitelist', []);
let knownIPs = new Set();
@@ -24,14 +25,18 @@ if (fs.existsSync(whitelistPath)) {
* @returns {string|undefined} The client IP address
*/
function getForwardedIp(req) {
+ if (!enableForwardedWhitelist) {
+ return undefined;
+ }
+
// Check if X-Real-IP is available
if (req.headers['x-real-ip']) {
- return req.headers['x-real-ip'];
+ return req.headers['x-real-ip'].toString();
}
// Check for X-Forwarded-For and parse if available
if (req.headers['x-forwarded-for']) {
- const ipList = req.headers['x-forwarded-for'].split(',').map(ip => ip.trim());
+ const ipList = req.headers['x-forwarded-for'].toString().split(',').map(ip => ip.trim());
return ipList[0];
}
From 5a5463bd5d0123b1d82090ddc08b19db0196f15d Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 22 Apr 2024 16:02:50 +0300
Subject: [PATCH 06/21] #2095 Suppress auto-execution on streamed swiped
generations.
---
public/scripts/extensions/quick-reply/index.js | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/public/scripts/extensions/quick-reply/index.js b/public/scripts/extensions/quick-reply/index.js
index 7b58f4aaa..74cdbeb78 100644
--- a/public/scripts/extensions/quick-reply/index.js
+++ b/public/scripts/extensions/quick-reply/index.js
@@ -1,4 +1,4 @@
-import { chat_metadata, eventSource, event_types, getRequestHeaders } from '../../../script.js';
+import { chat, chat_metadata, eventSource, event_types, getRequestHeaders } from '../../../script.js';
import { extension_settings } from '../../extensions.js';
import { QuickReplyApi } from './api/QuickReplyApi.js';
import { AutoExecuteHandler } from './src/AutoExecuteHandler.js';
@@ -238,7 +238,12 @@ const onUserMessage = async () => {
};
eventSource.on(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args));
-const onAiMessage = async () => {
+const onAiMessage = async (messageId) => {
+ if (['...'].includes(chat[messageId]?.mes)) {
+ log('QR auto-execution suppressed for swiped message');
+ return;
+ }
+
await autoExec.handleAi();
};
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args));
From 776260c85ad4c7d731e9f3059f868fba3e6106da Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 22 Apr 2024 16:25:46 +0300
Subject: [PATCH 07/21] Add Data Bank to attachments extension display name
---
public/scripts/extensions/attachments/manifest.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/scripts/extensions/attachments/manifest.json b/public/scripts/extensions/attachments/manifest.json
index 2037168c2..27f55f77c 100644
--- a/public/scripts/extensions/attachments/manifest.json
+++ b/public/scripts/extensions/attachments/manifest.json
@@ -1,5 +1,5 @@
{
- "display_name": "Chat Attachments",
+ "display_name": "Data Bank (Chat Attachments)",
"loading_order": 3,
"requires": [],
"optional": [],
From 6d1933c8f35c7702cb04a1904b1e12f9a3198d05 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 22 Apr 2024 17:35:42 +0300
Subject: [PATCH 08/21] Escape name regex in message formatting function
---
public/script.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/script.js b/public/script.js
index 9bbb8dfa0..3f28b2e7b 100644
--- a/public/script.js
+++ b/public/script.js
@@ -1840,7 +1840,7 @@ function messageFormatting(mes, ch_name, isSystem, isUser, messageId) {
*/
if (!power_user.allow_name2_display && ch_name && !isUser && !isSystem) {
- mes = mes.replace(new RegExp(`(^|\n)${ch_name}:`, 'g'), '$1');
+ mes = mes.replace(new RegExp(`(^|\n)${escapeRegex(ch_name)}:`, 'g'), '$1');
}
/** @type {any} */
From 4370db6bdc7f6959caa378c27add3d0c16508eb8 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Tue, 23 Apr 2024 03:09:52 +0300
Subject: [PATCH 09/21] Implement World Info activation using Vector Storage
---
public/index.html | 11 +-
public/script.js | 1 +
public/scripts/extensions/vectors/index.js | 135 ++++++++++++++++++
.../scripts/extensions/vectors/settings.html | 40 ++++++
public/scripts/world-info.js | 77 ++++++++--
src/endpoints/characters.js | 1 +
6 files changed, 250 insertions(+), 15 deletions(-)
diff --git a/public/index.html b/public/index.html
index ab7329d76..75aaf4eac 100644
--- a/public/index.html
+++ b/public/index.html
@@ -4955,10 +4955,11 @@
-