Files
openchamber/scripts/persistence-save.sh
T

234 lines
8.2 KiB
Bash
Raw Normal View History

#!/bin/bash
# ==============================================================================
# OpenCode Persistence Save Script
# ==============================================================================
# This script prepares all session data for artifact upload.
# It saves:
# - Configuration files (~/.config/opencode/)
# - Session data, messages, chats (~/.local/share/opencode/storage/)
# - Project snapshots (~/.local/share/opencode/snapshot/)
# - Authentication tokens (~/.local/share/opencode/auth.json)
# - Logs (~/.local/share/opencode/log/)
#
# Excludes (to keep artifact size manageable):
# - node_modules/ directories
# - bin/ directory (will be reinstalled)
# - tool-output/ (temporary tool outputs)
#
# If OPENCODE_SERVER_PASSWORD is set, the artifact will be encrypted.
# ==============================================================================
set -euo pipefail
SAVE_DIR="${SAVE_DIR:-/tmp/opencode-save}"
CONFIG_DIR="$HOME/.config/opencode"
SHARE_DIR="$HOME/.local/share/opencode"
ENCRYPTION_PASSWORD="${OPENCODE_SERVER_PASSWORD:-}"
echo "=== OpenCode Session Save ==="
echo "Save directory: $SAVE_DIR"
echo "Config directory: $CONFIG_DIR"
echo "Share directory: $SHARE_DIR"
if [ -n "$ENCRYPTION_PASSWORD" ]; then
echo "Encryption: ENABLED"
else
echo "Encryption: DISABLED (set OPENCODE_SERVER_PASSWORD to enable)"
fi
echo ""
# ------------------------------------------------------------------------------
# Clean up and create save directory
# ------------------------------------------------------------------------------
rm -rf "$SAVE_DIR"
mkdir -p "$SAVE_DIR/config"
mkdir -p "$SAVE_DIR/share"
# ------------------------------------------------------------------------------
# Save configuration files
# ------------------------------------------------------------------------------
save_config() {
echo "=== Saving Configuration Files ==="
if [ -d "$CONFIG_DIR" ] && [ "$(ls -A "$CONFIG_DIR" 2>/dev/null)" ]; then
# Save all config files (excluding node_modules)
for item in "$CONFIG_DIR"/*; do
if [ -e "$item" ]; then
local basename=$(basename "$item")
# Skip node_modules and bun.lock (will be reinstalled)
if [ "$basename" = "node_modules" ] || [ "$basename" = "bun.lock" ]; then
echo "Skipping: $basename (will be reinstalled)"
continue
fi
# Copy file or directory
if [ -f "$item" ]; then
cp -v "$item" "$SAVE_DIR/config/" 2>/dev/null || true
fi
fi
done
local count=$(find "$SAVE_DIR/config" -type f 2>/dev/null | wc -l)
echo "Config files saved: $count"
else
echo "No config data to save"
fi
}
# ------------------------------------------------------------------------------
# Save share data (sessions, messages, snapshots, auth)
# ------------------------------------------------------------------------------
save_share() {
echo ""
echo "=== Saving Share Data ==="
if [ ! -d "$SHARE_DIR" ]; then
echo "No share directory found"
return
fi
# Save auth.json (authentication tokens - critical!)
if [ -f "$SHARE_DIR/auth.json" ]; then
cp -v "$SHARE_DIR/auth.json" "$SAVE_DIR/share/" 2>/dev/null || true
echo "Auth data saved"
fi
# Save storage directory (sessions, messages, parts, projects)
if [ -d "$SHARE_DIR/storage" ] && [ "$(ls -A "$SHARE_DIR/storage" 2>/dev/null)" ]; then
echo "Saving storage data (sessions, messages, chats)..."
mkdir -p "$SAVE_DIR/share/storage"
# Copy all storage subdirectories
for subdir in message migration part project session session_diff; do
if [ -d "$SHARE_DIR/storage/$subdir" ]; then
cp -r "$SHARE_DIR/storage/$subdir" "$SAVE_DIR/share/storage/" 2>/dev/null || true
fi
done
local storage_count=$(find "$SAVE_DIR/share/storage" -type f 2>/dev/null | wc -l)
echo "Storage files saved: $storage_count"
fi
# Save snapshot directory (project snapshots for undo/rollback)
if [ -d "$SHARE_DIR/snapshot" ] && [ "$(ls -A "$SHARE_DIR/snapshot" 2>/dev/null)" ]; then
echo "Saving snapshot data..."
mkdir -p "$SAVE_DIR/share/snapshot"
cp -r "$SHARE_DIR/snapshot/"* "$SAVE_DIR/share/snapshot/" 2>/dev/null || true
local snapshot_count=$(find "$SAVE_DIR/share/snapshot" -type f 2>/dev/null | wc -l)
echo "Snapshot files saved: $snapshot_count"
fi
# Save log directory (useful for debugging)
if [ -d "$SHARE_DIR/log" ] && [ "$(ls -A "$SHARE_DIR/log" 2>/dev/null)" ]; then
echo "Saving log data..."
mkdir -p "$SAVE_DIR/share/log"
cp -r "$SHARE_DIR/log/"* "$SAVE_DIR/share/log/" 2>/dev/null || true
echo "Log files saved"
fi
}
# ------------------------------------------------------------------------------
# Create manifest with metadata
# ------------------------------------------------------------------------------
create_manifest() {
echo ""
echo "=== Creating Manifest ==="
local manifest="$SAVE_DIR/manifest.json"
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
local config_count=$(find "$SAVE_DIR/config" -type f 2>/dev/null | wc -l)
local share_count=$(find "$SAVE_DIR/share" -type f 2>/dev/null | wc -l)
local total_size=$(du -sh "$SAVE_DIR" 2>/dev/null | cut -f1)
cat << EOF > "$manifest"
{
"version": "2.0",
"timestamp": "$timestamp",
"hostname": "$(hostname)",
"stats": {
"config_files": $config_count,
"share_files": $share_count,
"total_size": "$total_size"
},
"contents": {
"config": $([ -d "$SAVE_DIR/config" ] && ls "$SAVE_DIR/config" 2>/dev/null | jq -R -s -c 'split("\n") | map(select(length > 0))' || echo '[]'),
"share": $([ -d "$SAVE_DIR/share" ] && ls "$SAVE_DIR/share" 2>/dev/null | jq -R -s -c 'split("\n") | map(select(length > 0))' || echo '[]')
}
}
EOF
echo "Manifest created: $manifest"
cat "$manifest"
}
# ==============================================================================
# Main Save Process
# ==============================================================================
save_config
save_share
create_manifest
# ------------------------------------------------------------------------------
# Final Summary
# ------------------------------------------------------------------------------
echo ""
echo "=== Save Summary ==="
# Calculate totals
total_files=$(find "$SAVE_DIR" -type f 2>/dev/null | wc -l)
total_size=$(du -sh "$SAVE_DIR" 2>/dev/null | cut -f1)
echo "Save directory structure:"
if command -v tree &> /dev/null; then
tree -L 2 "$SAVE_DIR" 2>/dev/null || ls -laR "$SAVE_DIR" | head -50
else
ls -laR "$SAVE_DIR" | head -50
fi
echo ""
echo "Total files to upload: $total_files"
echo "Total size: $total_size"
echo ""
# Verify we have something to save
if [ "$total_files" -gt 1 ]; then
echo "Session data prepared successfully for artifact upload!"
else
echo "Warning: Minimal data to save. Creating placeholder..."
echo "{\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"status\": \"empty\"}" > "$SAVE_DIR/placeholder.json"
fi
# ------------------------------------------------------------------------------
# Optional Encryption (if OPENCODE_SERVER_PASSWORD is set)
# ------------------------------------------------------------------------------
if [ -n "$ENCRYPTION_PASSWORD" ]; then
echo ""
echo "=== Encrypting Session Data ==="
TEMP_ARCHIVE="/tmp/opencode-session-data.tar.gz"
ENCRYPTED_FILE="$SAVE_DIR.enc"
tar -czf "$TEMP_ARCHIVE" -C "$SAVE_DIR" .
openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
-in "$TEMP_ARCHIVE" \
-out "$ENCRYPTED_FILE" \
-pass pass:"$ENCRYPTION_PASSWORD"
rm -f "$TEMP_ARCHIVE"
rm -rf "$SAVE_DIR"
mkdir -p "$SAVE_DIR"
mv "$ENCRYPTED_FILE" "$SAVE_DIR/session.enc"
echo '{"encrypted": true, "algorithm": "aes-256-cbc", "kdf": "pbkdf2", "iterations": 100000}' > "$SAVE_DIR/manifest.json"
echo "Encryption complete. Artifact is password-protected."
echo "Encrypted file: $SAVE_DIR/session.enc"
fi
echo ""
echo "Save location: $SAVE_DIR"
echo "Ready for artifact upload!"