sbqr / entrypoint.sh
Mhadis's picture
Upload 4 files
826037d verified
#!/bin/sh
set -e
CONFIG_FILE="${APP_HOME}/config.yaml"
# Priority 1: Use USERNAME/PASSWORD if both are provided
if [ -n "${USERNAME}" ] && [ -n "${PASSWORD}" ]; then
echo "--- Basic auth enabled: Creating config.yaml with provided credentials. ---"
cat <<EOT > ${CONFIG_FILE}
dataRoot: ./data
listen: true
listenAddress:
ipv4: 0.0.0.0
ipv6: '[::]'
protocol:
ipv4: true
ipv6: false
dnsPreferIPv6: false
autorunHostname: "auto"
port: 8000
autorunPortOverride: -1
ssl:
enabled: false
certPath: "./certs/cert.pem"
keyPath: "./certs/privkey.pem"
whitelistMode: false
enableForwardedWhitelist: false
whitelist:
- ::1
- 127.0.0.1
whitelistDockerHosts: true
basicAuthMode: true
basicAuthUser:
username: "${USERNAME}"
password: "${PASSWORD}"
enableCorsProxy: false
requestProxy:
enabled: false
url: "socks5://username:password@example.com:1080"
bypass:
- localhost
- 127.0.0.1
enableUserAccounts: false
enableDiscreetLogin: false
autheliaAuth: false
perUserBasicAuth: false
sessionTimeout: -1
disableCsrfProtection: false
securityOverride: false
logging:
enableAccessLog: true
minLogLevel: 0
rateLimiting:
preferRealIpHeader: false
autorun: false
avoidLocalhost: false
backups:
common:
numberOfBackups: 50
chat:
enabled: true
checkIntegrity: true
maxTotalBackups: -1
throttleInterval: 10000
thumbnails:
enabled: true
format: "jpg"
quality: 95
dimensions: { 'bg': [160, 90], 'avatar': [96, 144] }
performance:
lazyLoadCharacters: false
memoryCacheCapacity: '100mb'
useDiskCache: true
allowKeysExposure: true
skipContentCheck: false
whitelistImportDomains:
- localhost
- cdn.discordapp.com
- files.catbox.moe
- raw.githubusercontent.com
requestOverrides: []
extensions:
enabled: true
autoUpdate: false
models:
autoDownload: true
classification: Cohee/distilbert-base-uncased-go-emotions-onnx
captioning: Xenova/vit-gpt2-image-captioning
embedding: Cohee/jina-embeddings-v2-base-en
speechToText: Xenova/whisper-small
textToSpeech: Xenova/speecht5_tts
enableDownloadableTokenizers: true
promptPlaceholder: "[Start a new chat]"
openai:
randomizeUserId: false
captionSystemPrompt: ""
deepl:
formality: default
mistral:
enablePrefix: false
ollama:
keepAlive: -1
batchSize: -1
claude:
enableSystemPromptCache: false
cachingAtDepth: -1
enableServerPlugins: true
enableServerPluginsAutoUpdate: false
EOT
# Priority 2: Use CONFIG_YAML if provided (and username/password are not)
elif [ -n "${CONFIG_YAML}" ]; then
echo "--- Found CONFIG_YAML, creating config.yaml from environment variable. ---"
printf '%s\n' "${CONFIG_YAML}" > ${CONFIG_FILE}
# Priority 3: No config provided, let the app use its defaults
else
echo "--- No user/pass or CONFIG_YAML provided. App will use its default settings. ---"
fi
# --- BEGIN: Update SillyTavern Core at Runtime ---
echo '--- Attempting to update SillyTavern Core from GitHub (staging branch) ---'
if [ -d ".git" ] && [ "$(git rev-parse --abbrev-ref HEAD)" = "staging" ]; then
echo 'Existing staging branch found. Resetting and pulling latest changes...'
git reset --hard HEAD && \
git pull origin staging || echo 'WARN: git pull failed, continuing with code from build time.'
echo '--- SillyTavern Core update check finished. ---'
else
echo 'WARN: .git directory not found or not on staging branch. Skipping runtime update. Code from build time will be used.'
fi
# --- END: Update SillyTavern Core at Runtime ---
# --- BEGIN: Configure Git default identity at Runtime ---
echo '--- Configuring Git default user identity at runtime ---'
git config --global user.name "SillyTavern Sync" && \
git config --global user.email "sillytavern-sync@example.com" && \
git config --global --add safe.directory "${APP_HOME}/data"
echo '--- Git identity configured for runtime user. ---'
# --- END: Configure Git default identity at Runtime ---
# --- BEGIN: Dynamically Install Plugins at Runtime ---
echo '--- Checking for PLUGINS environment variable ---'
if [ -n "$PLUGINS" ]; then
echo "*** Installing Plugins specified in PLUGINS environment variable: $PLUGINS ***"
# Ensure plugins directory exists
mkdir -p ./plugins && chown node:node ./plugins
# Set comma as delimiter
IFS=','
# Loop through each plugin URL
for plugin_url in $PLUGINS; do
# Trim leading/trailing whitespace
plugin_url=$(echo "$plugin_url" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -z "$plugin_url" ]; then continue; fi
# Extract plugin name
plugin_name_git=$(basename "$plugin_url")
plugin_name=${plugin_name_git%.git}
plugin_dir="./plugins/$plugin_name"
echo "--- Installing plugin: $plugin_name from $plugin_url into $plugin_dir ---"
# Remove existing dir if it exists
rm -rf "$plugin_dir"
# Clone the plugin (run as root, fix perms later)
git clone --depth 1 "$plugin_url" "$plugin_dir"
if [ -f "$plugin_dir/package.json" ]; then
echo "--- Installing dependencies for $plugin_name ---"
(cd "$plugin_dir" && npm install --no-audit --no-fund --loglevel=error --no-progress --omit=dev --force && npm cache clean --force) || echo "WARN: Failed to install dependencies for $plugin_name"
else
echo "--- No package.json found for $plugin_name, skipping dependency install. ---"
fi || echo "WARN: Failed to clone $plugin_name from $plugin_url, skipping..."
# Configure cloud-saves plugin if this is the cloud-saves plugin
if [ "$plugin_name" = "cloud-saves" ]; then
echo "--- Detected cloud-saves plugin, checking for configuration environment variables ---"
# Set default values
REPO_URL_VALUE=${REPO_URL:-"https://github.com/fuwei99/sillytravern"}
GITHUB_TOKEN_VALUE=${GITHUB_TOKEN:-""}
AUTOSAVE_INTERVAL_VALUE=${AUTOSAVE_INTERVAL:-30}
AUTOSAVE_TARGET_TAG_VALUE=${AUTOSAVE_TARGET_TAG:-""}
# Always set autosave to false as required
AUTOSAVE_ENABLED="false"
echo "--- Creating cloud-saves plugin configuration file ---"
CONFIG_JSON_FILE="$plugin_dir/config.json"
# Generate config.json file
cat <<EOT > ${CONFIG_JSON_FILE}
{
"repo_url": "${REPO_URL_VALUE}",
"branch": "main",
"username": "cloud-saves",
"github_token": "${GITHUB_TOKEN_VALUE}",
"display_name": "",
"is_authorized": true,
"last_save": null,
"current_save": null,
"has_temp_stash": false,
"autoSaveEnabled": ${AUTOSAVE_ENABLED},
"autoSaveInterval": ${AUTOSAVE_INTERVAL_VALUE},
"autoSaveTargetTag": "${AUTOSAVE_TARGET_TAG_VALUE}"
}
EOT
# Set correct permissions for config file
chown node:node ${CONFIG_JSON_FILE}
echo "--- cloud-saves plugin configuration file created at: ${CONFIG_JSON_FILE} ---"
fi
done
# Reset IFS
unset IFS
# Fix permissions for plugins directory after installation
echo "--- Setting permissions for plugins directory ---"
chown -R node:node ./plugins
echo "*** Plugin installation finished. ***"
else
echo 'PLUGINS environment variable is not set or empty, skipping runtime plugin installation.'
fi
# --- END: Dynamically Install Plugins at Runtime ---
echo "*** Starting SillyTavern... ***"
node ${APP_HOME}/server.js &
SERVER_PID=$!
echo "SillyTavern server started with PID ${SERVER_PID}. Waiting for it to become responsive..."
# --- Health Check Logic ---
HEALTH_CHECK_URL="http://localhost:8000/"
CURL_COMMAND="curl -sf"
# If basic auth is enabled, provide credentials to curl for health checks
if [ -n "${USERNAME}" ] && [ -n "${PASSWORD}" ]; then
echo "--- Health check will use basic auth credentials. ---"
# The -u flag provides user:password for basic auth
CURL_COMMAND="curl -sf -u \"${USERNAME}:${PASSWORD}\""
fi
# Health check loop
RETRY_COUNT=0
MAX_RETRIES=12 # Wait for 60 seconds max
# Use eval to correctly execute the command string with quotes
while ! eval "${CURL_COMMAND} ${HEALTH_CHECK_URL}" > /dev/null; do
RETRY_COUNT=$((RETRY_COUNT+1))
if [ ${RETRY_COUNT} -ge ${MAX_RETRIES} ]; then
echo "SillyTavern failed to start. Exiting."
kill ${SERVER_PID}
exit 1
fi
echo "SillyTavern is still starting or not responsive on port 8000, waiting 5 seconds..."
sleep 5
done
echo "SillyTavern started successfully! Beginning periodic keep-alive..."
# --- BEGIN: Install Extensions after SillyTavern startup ---
install_extensions() {
echo "--- Waiting 40 seconds before installing extensions... ---"
sleep 40
echo "--- Checking for EXTENSIONS environment variable ---"
if [ -n "$EXTENSIONS" ]; then
echo "*** Installing Extensions specified in EXTENSIONS environment variable: $EXTENSIONS ***"
# Determine installation directory based on INSTALL_FOR_ALL_USERS
if [ "$INSTALL_FOR_ALL_USERS" = "true" ]; then
# System-level installation (for all users)
EXTENSIONS_DIR="./public/scripts/extensions/third-party"
echo "Installing extensions for all users in: $EXTENSIONS_DIR"
else
# User-level installation (for default user only)
EXTENSIONS_DIR="./data/default-user/extensions"
echo "Installing extensions for default user in: $EXTENSIONS_DIR"
fi
# Ensure extensions directory exists
mkdir -p "$EXTENSIONS_DIR" && chown node:node "$EXTENSIONS_DIR"
# Set comma as delimiter
IFS=','
# Loop through each extension URL
for extension_url in $EXTENSIONS; do
# Trim leading/trailing whitespace
extension_url=$(echo "$extension_url" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -z "$extension_url" ]; then continue; fi
# Extract extension name
extension_name_git=$(basename "$extension_url")
extension_name=${extension_name_git%.git}
extension_dir="$EXTENSIONS_DIR/$extension_name"
echo "--- Installing extension: $extension_name from $extension_url into $extension_dir ---"
# Remove existing dir if it exists
rm -rf "$extension_dir"
# Clone the extension
git clone --depth 1 "$extension_url" "$extension_dir"
# Check if extension has package.json and install dependencies if needed
if [ -f "$extension_dir/package.json" ]; then
echo "--- Installing dependencies for $extension_name ---"
(cd "$extension_dir" && npm install --no-audit --no-fund --loglevel=error --no-progress --omit=dev --force && npm cache clean --force) || echo "WARN: Failed to install dependencies for $extension_name"
else
echo "--- No package.json found for $extension_name, skipping dependency install. ---"
fi || echo "WARN: Failed to clone $extension_name from $extension_url, skipping..."
done
# Reset IFS
unset IFS
# Fix permissions for extensions directory after installation
echo "--- Setting permissions for extensions directory ---"
chown -R node:node "$EXTENSIONS_DIR"
echo "*** Extensions installation finished. ***"
else
echo 'EXTENSIONS environment variable is not set or empty, skipping extensions installation.'
fi
}
# Run the extension installation in the background
install_extensions &
# --- END: Install Extensions after SillyTavern startup ---
# Keep-alive loop
while kill -0 ${SERVER_PID} 2>/dev/null; do
echo "Sending keep-alive request to ${HEALTH_CHECK_URL}"
# Use eval here as well for the keep-alive command
eval "${CURL_COMMAND} ${HEALTH_CHECK_URL}" > /dev/null || echo "Keep-alive request failed."
echo "Keep-alive request sent. Sleeping for 30 minutes."
sleep 1800
done &
wait ${SERVER_PID}