chore: remove GitHub Actions cloud runtime (workflow, scripts, docs) (#443)
Remove the 'OpenChamber for Actions' feature that ran OpenChamber on GitHub runners via Cloudflare/Ngrok tunnels. This was a separate deployment target with its own lifecycle scripts and documentation that added maintenance overhead without benefiting local usage. Deleted: - .github/workflows/opencode.yml (Actions workflow) - scripts/monitor.sh (service self-heal loop) - scripts/persistence-save.sh (artifact encryption/upload) - scripts/persistence-restore.sh (artifact decrypt/restore) - scripts/opencode-config.sh (Actions config bootstrap) - docs/OPENCHAMBER_FOR_ACTIONS.md (user guide) Updated: - README.md: removed 'GitHub Actions (Cloud Usage)' section Local Cloudflare Quick Tunnel support (--try-cf-tunnel) is unaffected.
This commit is contained in:
@@ -1,292 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# OpenCode Monitor & Self-Heal Script
|
||||
# ==============================================================================
|
||||
# This script monitors OpenCode (TTY), OpenChamber, and OpenCode Web services,
|
||||
# automatically restarting them if they crash. It also manages the tunnel connections.
|
||||
#
|
||||
# Usage: ./monitor.sh <tunnel_provider> <timeout_minutes> <url_tty> <url_chamber> <url_web>
|
||||
#
|
||||
# Arguments:
|
||||
# tunnel_provider: "ngrok" or "cloudflare"
|
||||
# timeout_minutes: Auto-shutdown timeout in minutes
|
||||
# url_tty: Initial tunnel URL for OpenCode TTY
|
||||
# url_chamber: Initial tunnel URL for OpenChamber
|
||||
# url_web: Initial tunnel URL for OpenCode Web
|
||||
# ==============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
TUNNEL_PROVIDER="${1:-cloudflare}"
|
||||
TIMEOUT_MINUTES="${2:-300}"
|
||||
URL_TTY="${3:-}"
|
||||
URL_CHAMBER="${4:-}"
|
||||
URL_WEB="${5:-}"
|
||||
|
||||
echo "=============================================="
|
||||
echo "OpenCode Monitor & Self-Heal"
|
||||
echo "=============================================="
|
||||
echo "Tunnel Provider: $TUNNEL_PROVIDER"
|
||||
echo "Timeout: $TIMEOUT_MINUTES minute(s)"
|
||||
echo "----------------------------------------------"
|
||||
echo "OpenCode TTY URL: $URL_TTY"
|
||||
echo "OpenChamber URL: $URL_CHAMBER"
|
||||
echo "OpenCode Web URL: $URL_WEB"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Configuration
|
||||
# ------------------------------------------------------------------------------
|
||||
TTY_PORT=7681
|
||||
OPENCHAMBER_PORT=9090
|
||||
OPENCODE_WEB_PORT=8080
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
TIMEOUT_SECONDS=$((TIMEOUT_MINUTES * 60))
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Helper Functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||
}
|
||||
|
||||
check_port() {
|
||||
local port=$1
|
||||
lsof -i :"$port" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
get_remaining_time() {
|
||||
local elapsed=$(($(date +%s) - START_TIME))
|
||||
local remaining=$((TIMEOUT_SECONDS - elapsed))
|
||||
echo $remaining
|
||||
}
|
||||
|
||||
format_time() {
|
||||
local seconds=$1
|
||||
local minutes=$((seconds / 60))
|
||||
local hours=$((minutes / 60))
|
||||
minutes=$((minutes % 60))
|
||||
|
||||
if [ $hours -gt 0 ]; then
|
||||
echo "${hours}h ${minutes}m"
|
||||
else
|
||||
echo "${minutes}m"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Service Management
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
restart_opencode_tty() {
|
||||
log "Restarting OpenCode TTY on port $TTY_PORT..."
|
||||
pkill -f "ttyd" 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
if [ -n "${OPENCHAMBER_UI_PASSWORD:-}" ]; then
|
||||
log "Restarting TTY with password protection."
|
||||
nohup stdbuf -oL ttyd -c "user:${OPENCHAMBER_UI_PASSWORD}" -p $TTY_PORT bash -c "cd \$HOME && exec opencode" >> opencode_tty.log 2>&1 &
|
||||
else
|
||||
nohup stdbuf -oL ttyd -p $TTY_PORT bash -c "cd \$HOME && exec opencode" >> opencode_tty.log 2>&1 &
|
||||
fi
|
||||
sleep 5
|
||||
|
||||
if check_port $TTY_PORT; then
|
||||
log "OpenCode TTY restarted successfully"
|
||||
return 0
|
||||
else
|
||||
log "ERROR: Failed to restart OpenCode TTY"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restart_openchamber() {
|
||||
log "Restarting OpenChamber on port $OPENCHAMBER_PORT..."
|
||||
pkill -f "openchamber" 2>/dev/null || true
|
||||
sleep 2
|
||||
if [ -n "${OPENCHAMBER_UI_PASSWORD:-}" ]; then
|
||||
nohup stdbuf -oL openchamber --port $OPENCHAMBER_PORT --ui-password "$OPENCHAMBER_UI_PASSWORD" >> openchamber.log 2>&1 &
|
||||
else
|
||||
nohup stdbuf -oL openchamber --port $OPENCHAMBER_PORT >> openchamber.log 2>&1 &
|
||||
fi
|
||||
sleep 5
|
||||
|
||||
if check_port $OPENCHAMBER_PORT; then
|
||||
log "OpenChamber restarted successfully"
|
||||
return 0
|
||||
else
|
||||
log "ERROR: Failed to restart OpenChamber"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restart_opencode_web() {
|
||||
log "Restarting OpenCode Web on port $OPENCODE_WEB_PORT..."
|
||||
# Note: "opencode web" might be matched by "opencode" so we use full command in pkill if possible or handle order
|
||||
pkill -f "opencode web" 2>/dev/null || true
|
||||
sleep 2
|
||||
if [ -n "${OPENCHAMBER_UI_PASSWORD:-}" ]; then
|
||||
OPENCODE_SERVER_PASSWORD="$OPENCHAMBER_UI_PASSWORD" nohup stdbuf -oL opencode web --port $OPENCODE_WEB_PORT >> opencode_web.log 2>&1 &
|
||||
else
|
||||
nohup stdbuf -oL opencode web --port $OPENCODE_WEB_PORT >> opencode_web.log 2>&1 &
|
||||
fi
|
||||
sleep 5
|
||||
|
||||
if check_port $OPENCODE_WEB_PORT; then
|
||||
log "OpenCode Web restarted successfully"
|
||||
return 0
|
||||
else
|
||||
log "ERROR: Failed to restart OpenCode Web"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restart_tunnels() {
|
||||
log "Restarting tunnels ($TUNNEL_PROVIDER)..."
|
||||
|
||||
if [ "$TUNNEL_PROVIDER" = "ngrok" ]; then
|
||||
pkill -f "ngrok" 2>/dev/null || true
|
||||
sleep 2
|
||||
# Complex to manage multiple ngrok tunnels without config file.
|
||||
# For now, attempting to restore single tunnel logic or just warn.
|
||||
log "WARNING: Multi-tunnel restart for ngrok not fully supported in this script version."
|
||||
# Attempt to start tunnels again (blindly)
|
||||
nohup ngrok http 127.0.0.1:$OPENCHAMBER_PORT --log=stdout > tunnel_chamber.log 2>&1 &
|
||||
nohup ngrok http 127.0.0.1:$OPENCODE_WEB_PORT --log=stdout > tunnel_web.log 2>&1 &
|
||||
nohup ngrok http 127.0.0.1:$TTY_PORT --log=stdout > tunnel_tty.log 2>&1 &
|
||||
sleep 10
|
||||
else
|
||||
pkill -f "cloudflared" 2>/dev/null || true
|
||||
sleep 2
|
||||
nohup cloudflared tunnel --url http://127.0.0.1:$TTY_PORT > tunnel_tty.log 2>&1 &
|
||||
nohup cloudflared tunnel --url http://127.0.0.1:$OPENCHAMBER_PORT > tunnel_chamber.log 2>&1 &
|
||||
nohup cloudflared tunnel --url http://127.0.0.1:$OPENCODE_WEB_PORT > tunnel_web.log 2>&1 &
|
||||
sleep 15
|
||||
|
||||
# Get new URLs
|
||||
URL_TTY=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_tty.log 2>/dev/null | tail -n 1)
|
||||
URL_CHAMBER=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_chamber.log 2>/dev/null | tail -n 1)
|
||||
URL_WEB=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_web.log 2>/dev/null | tail -n 1)
|
||||
fi
|
||||
|
||||
log "Tunnels restarted."
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "NEW ACCESS URLS:"
|
||||
echo "OpenCode TTY: $URL_TTY"
|
||||
echo "OpenChamber: $URL_CHAMBER"
|
||||
echo "OpenCode Web: $URL_WEB"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
check_tunnels() {
|
||||
if [ "$TUNNEL_PROVIDER" = "ngrok" ]; then
|
||||
pgrep -f "ngrok" > /dev/null 2>&1
|
||||
else
|
||||
pgrep -f "cloudflared" > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Graceful Shutdown
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
shutdown() {
|
||||
log "Initiating graceful shutdown..."
|
||||
|
||||
# Kill all services
|
||||
pkill -f "ttyd" 2>/dev/null || true
|
||||
pkill -f "opencode" 2>/dev/null || true
|
||||
pkill -f "openchamber" 2>/dev/null || true
|
||||
pkill -f "opencode web" 2>/dev/null || true
|
||||
pkill -f "ngrok" 2>/dev/null || true
|
||||
pkill -f "cloudflared" 2>/dev/null || true
|
||||
|
||||
log "All services stopped"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Trap signals for graceful shutdown
|
||||
trap shutdown SIGTERM SIGINT
|
||||
|
||||
# ==============================================================================
|
||||
# Main Monitoring Loop
|
||||
# ==============================================================================
|
||||
|
||||
log "Starting monitoring loop..."
|
||||
echo ""
|
||||
|
||||
# Write initial URLs to GitHub Step Summary (if running in GitHub Actions)
|
||||
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
|
||||
{
|
||||
echo "## OpenChamber for Actions"
|
||||
echo ""
|
||||
echo "| Service | URL |"
|
||||
echo "|---------|-----|"
|
||||
echo "| **OpenCode TTY** | $URL_TTY |"
|
||||
echo "| **OpenChamber** | $URL_CHAMBER |"
|
||||
echo "| **OpenCode Web** | $URL_WEB |"
|
||||
echo ""
|
||||
echo "_Timeout: ${TIMEOUT_MINUTES} minutes_"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
# Status display interval (every 5 minutes = 300 seconds)
|
||||
LAST_STATUS_TIME=0
|
||||
STATUS_INTERVAL=300
|
||||
|
||||
while true; do
|
||||
REMAINING=$(get_remaining_time)
|
||||
|
||||
# Check for timeout
|
||||
if [ "$REMAINING" -le 0 ]; then
|
||||
log "Timeout reached. Initiating graceful shutdown..."
|
||||
shutdown
|
||||
fi
|
||||
|
||||
# Periodic status update (every 5 minutes)
|
||||
CURRENT_TIME=$(date +%s)
|
||||
if [ $((CURRENT_TIME - LAST_STATUS_TIME)) -ge $STATUS_INTERVAL ]; then
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
log "Status Update"
|
||||
echo "Time remaining: $(format_time "$REMAINING")"
|
||||
echo "OpenCode TTY: $URL_TTY"
|
||||
echo "OpenChamber: $URL_CHAMBER"
|
||||
echo "OpenCode Web: $URL_WEB"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
LAST_STATUS_TIME=$CURRENT_TIME
|
||||
fi
|
||||
|
||||
# Check OpenCode TTY
|
||||
if ! check_port $TTY_PORT; then
|
||||
log "OpenCode TTY not responding on port $TTY_PORT"
|
||||
restart_opencode_tty
|
||||
fi
|
||||
|
||||
# Check OpenChamber
|
||||
if ! check_port $OPENCHAMBER_PORT; then
|
||||
log "OpenChamber not responding on port $OPENCHAMBER_PORT"
|
||||
restart_openchamber
|
||||
fi
|
||||
|
||||
# Check OpenCode Web
|
||||
if ! check_port $OPENCODE_WEB_PORT; then
|
||||
log "OpenCode Web not responding on port $OPENCODE_WEB_PORT"
|
||||
restart_opencode_web
|
||||
fi
|
||||
|
||||
# Check Tunnel Process (Basic check)
|
||||
if ! check_tunnels; then
|
||||
log "Tunnel processes not running"
|
||||
restart_tunnels
|
||||
fi
|
||||
|
||||
# Sleep before next check
|
||||
sleep 5
|
||||
done
|
||||
@@ -1,167 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# OpenCode Configuration Script
|
||||
# ==============================================================================
|
||||
# This script manages OpenCode configuration with intelligent detection of
|
||||
# restored artifacts. If a config file exists from a previous session (restored
|
||||
# via artifact), it will be preserved. Otherwise, a default config is generated.
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_DIR="$HOME/.config/opencode"
|
||||
RESTORE_DIR="${RESTORE_DIR:-/tmp/opencode-restore}"
|
||||
|
||||
echo "=== OpenCode Configuration Setup ==="
|
||||
echo "Config directory: $CONFIG_DIR"
|
||||
echo "Restore directory: $RESTORE_DIR"
|
||||
|
||||
# Create config directory if it doesn't exist
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Check for restored configuration (artifact preference)
|
||||
# ------------------------------------------------------------------------------
|
||||
check_restored_config() {
|
||||
local restored_config_json="$RESTORE_DIR/config/opencode.json"
|
||||
local restored_config_yml="$RESTORE_DIR/config/opencode.yml"
|
||||
local restored_config_yaml="$RESTORE_DIR/config/opencode.yaml"
|
||||
|
||||
# Priority: .yml > .yaml > .json from artifacts
|
||||
if [ -f "$restored_config_yml" ]; then
|
||||
echo "Found restored opencode.yml from artifact - using it"
|
||||
cp -v "$restored_config_yml" "$CONFIG_DIR/opencode.yml"
|
||||
return 0
|
||||
elif [ -f "$restored_config_yaml" ]; then
|
||||
echo "Found restored opencode.yaml from artifact - using it"
|
||||
cp -v "$restored_config_yaml" "$CONFIG_DIR/opencode.yaml"
|
||||
return 0
|
||||
elif [ -f "$restored_config_json" ]; then
|
||||
echo "Found restored opencode.json from artifact - using it"
|
||||
cp -v "$restored_config_json" "$CONFIG_DIR/opencode.json"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Check for existing configuration in config directory
|
||||
# ------------------------------------------------------------------------------
|
||||
check_existing_config() {
|
||||
if [ -f "$CONFIG_DIR/opencode.yml" ] || \
|
||||
[ -f "$CONFIG_DIR/opencode.yaml" ] || \
|
||||
[ -f "$CONFIG_DIR/opencode.json" ]; then
|
||||
echo "Existing configuration found in $CONFIG_DIR - keeping it"
|
||||
ls -la "$CONFIG_DIR"/opencode.* 2>/dev/null || true
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generate default configuration
|
||||
# ------------------------------------------------------------------------------
|
||||
generate_default_config() {
|
||||
echo "Generating default OpenCode configuration..."
|
||||
|
||||
cat << 'EOF' > "$CONFIG_DIR/opencode.json"
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"plugin": ["opencode-antigravity-auth@beta"],
|
||||
"provider": {
|
||||
"google": {
|
||||
"models": {
|
||||
"antigravity-gemini-3-pro": {
|
||||
"name": "Gemini 3 Pro (Antigravity)",
|
||||
"limit": { "context": 1048576, "output": 65535 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] },
|
||||
"variants": {
|
||||
"low": { "thinkingLevel": "low" },
|
||||
"high": { "thinkingLevel": "high" }
|
||||
}
|
||||
},
|
||||
"antigravity-gemini-3-flash": {
|
||||
"name": "Gemini 3 Flash (Antigravity)",
|
||||
"limit": { "context": 1048576, "output": 65536 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] },
|
||||
"variants": {
|
||||
"minimal": { "thinkingLevel": "minimal" },
|
||||
"low": { "thinkingLevel": "low" },
|
||||
"medium": { "thinkingLevel": "medium" },
|
||||
"high": { "thinkingLevel": "high" }
|
||||
}
|
||||
},
|
||||
"antigravity-claude-sonnet-4-5": {
|
||||
"name": "Claude Sonnet 4.5 (no thinking) (Antigravity)",
|
||||
"limit": { "context": 200000, "output": 64000 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
|
||||
},
|
||||
"antigravity-claude-sonnet-4-5-thinking": {
|
||||
"name": "Claude Sonnet 4.5 Thinking (Antigravity)",
|
||||
"limit": { "context": 200000, "output": 64000 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] },
|
||||
"variants": {
|
||||
"low": { "thinkingConfig": { "thinkingBudget": 8192 } },
|
||||
"max": { "thinkingConfig": { "thinkingBudget": 32768 } }
|
||||
}
|
||||
},
|
||||
"antigravity-claude-opus-4-5-thinking": {
|
||||
"name": "Claude Opus 4.5 Thinking (Antigravity)",
|
||||
"limit": { "context": 200000, "output": 64000 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] },
|
||||
"variants": {
|
||||
"low": { "thinkingConfig": { "thinkingBudget": 8192 } },
|
||||
"max": { "thinkingConfig": { "thinkingBudget": 32768 } }
|
||||
}
|
||||
},
|
||||
"gemini-2.5-flash": {
|
||||
"name": "Gemini 2.5 Flash (Gemini CLI)",
|
||||
"limit": { "context": 1048576, "output": 65536 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
"name": "Gemini 2.5 Pro (Gemini CLI)",
|
||||
"limit": { "context": 1048576, "output": 65536 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
|
||||
},
|
||||
"gemini-3-flash-preview": {
|
||||
"name": "Gemini 3 Flash Preview (Gemini CLI)",
|
||||
"limit": { "context": 1048576, "output": 65536 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
|
||||
},
|
||||
"gemini-3-pro-preview": {
|
||||
"name": "Gemini 3 Pro Preview (Gemini CLI)",
|
||||
"limit": { "context": 1048576, "output": 65535 },
|
||||
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Default configuration created at $CONFIG_DIR/opencode.json"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# Main Logic
|
||||
# ==============================================================================
|
||||
|
||||
# Step 1: Check if config was restored from artifact (highest priority)
|
||||
if check_restored_config; then
|
||||
echo "Using restored configuration from artifact"
|
||||
# Step 2: Check if config already exists (from previous run or manual setup)
|
||||
elif check_existing_config; then
|
||||
echo "Using existing configuration"
|
||||
# Step 3: No config found - generate default
|
||||
else
|
||||
generate_default_config
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Configuration Summary ==="
|
||||
echo "Active configuration files:"
|
||||
ls -la "$CONFIG_DIR"/*.json "$CONFIG_DIR"/*.yml "$CONFIG_DIR"/*.yaml 2>/dev/null || echo "No config files found"
|
||||
echo ""
|
||||
echo "Configuration setup complete!"
|
||||
@@ -1,182 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# OpenCode Persistence Restore Script
|
||||
# ==============================================================================
|
||||
# This script restores session data from GitHub Actions artifacts.
|
||||
# It handles the restoration of:
|
||||
# - 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)
|
||||
#
|
||||
# If the artifact is encrypted (session.enc exists), it will be decrypted
|
||||
# using OPENCODE_SERVER_PASSWORD.
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RESTORE_DIR="${RESTORE_DIR:-/tmp/opencode-restore}"
|
||||
CONFIG_DIR="$HOME/.config/opencode"
|
||||
SHARE_DIR="$HOME/.local/share/opencode"
|
||||
ENCRYPTION_PASSWORD="${OPENCODE_SERVER_PASSWORD:-}"
|
||||
|
||||
echo "=== OpenCode Session Restore ==="
|
||||
echo "Restore directory: $RESTORE_DIR"
|
||||
echo "Config directory: $CONFIG_DIR"
|
||||
echo "Share directory: $SHARE_DIR"
|
||||
echo ""
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Check for encrypted artifact and decrypt if needed
|
||||
# ------------------------------------------------------------------------------
|
||||
if [ -f "$RESTORE_DIR/session.enc" ]; then
|
||||
echo "=== Encrypted Artifact Detected ==="
|
||||
|
||||
if [ -z "$ENCRYPTION_PASSWORD" ]; then
|
||||
echo "ERROR: Encrypted artifact found but OPENCODE_SERVER_PASSWORD is not set."
|
||||
echo "Cannot decrypt session data. Starting fresh."
|
||||
rm -rf "$RESTORE_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Decrypting session data..."
|
||||
TEMP_ARCHIVE="/tmp/opencode-session-data.tar.gz"
|
||||
|
||||
if openssl enc -aes-256-cbc -d -salt -pbkdf2 -iter 100000 \
|
||||
-in "$RESTORE_DIR/session.enc" \
|
||||
-out "$TEMP_ARCHIVE" \
|
||||
-pass pass:"$ENCRYPTION_PASSWORD" 2>/dev/null; then
|
||||
|
||||
rm -rf "$RESTORE_DIR"
|
||||
mkdir -p "$RESTORE_DIR"
|
||||
tar -xzf "$TEMP_ARCHIVE" -C "$RESTORE_DIR"
|
||||
rm -f "$TEMP_ARCHIVE"
|
||||
echo "Decryption successful."
|
||||
else
|
||||
echo "ERROR: Decryption failed. Password may be incorrect."
|
||||
echo "Starting fresh session."
|
||||
rm -rf "$RESTORE_DIR"
|
||||
rm -f "$TEMP_ARCHIVE"
|
||||
exit 0
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Debug: Show what we're working with
|
||||
# ------------------------------------------------------------------------------
|
||||
echo "=== RESTORE DEBUG ==="
|
||||
echo "Contents of $RESTORE_DIR:"
|
||||
if [ -d "$RESTORE_DIR" ]; then
|
||||
ls -la "$RESTORE_DIR/" 2>/dev/null || echo "Directory exists but empty"
|
||||
echo ""
|
||||
echo "Full tree of restore directory:"
|
||||
find "$RESTORE_DIR" -type f 2>/dev/null | head -50 || echo "No files found"
|
||||
else
|
||||
echo "Restore directory does not exist - fresh start"
|
||||
exit 0
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Create target directories
|
||||
# ------------------------------------------------------------------------------
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
mkdir -p "$SHARE_DIR"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Restore configuration files
|
||||
# ------------------------------------------------------------------------------
|
||||
restore_config() {
|
||||
local src="$RESTORE_DIR/config"
|
||||
|
||||
if [ -d "$src" ] && [ "$(ls -A "$src" 2>/dev/null)" ]; then
|
||||
echo "Restoring configuration files..."
|
||||
|
||||
# Restore all config files except node_modules (will be reinstalled)
|
||||
find "$src" -maxdepth 1 -type f -exec cp -v {} "$CONFIG_DIR/" \; 2>/dev/null || true
|
||||
|
||||
# Count restored files
|
||||
local count=$(find "$src" -maxdepth 1 -type f 2>/dev/null | wc -l)
|
||||
echo "Restored $count configuration file(s)"
|
||||
else
|
||||
echo "No configuration files to restore"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Restore share data (sessions, messages, snapshots, etc.)
|
||||
# ------------------------------------------------------------------------------
|
||||
restore_share() {
|
||||
local src="$RESTORE_DIR/share"
|
||||
|
||||
if [ -d "$src" ] && [ "$(ls -A "$src" 2>/dev/null)" ]; then
|
||||
echo "Restoring share data..."
|
||||
|
||||
# Restore auth.json
|
||||
if [ -f "$src/auth.json" ]; then
|
||||
cp -v "$src/auth.json" "$SHARE_DIR/" 2>/dev/null || true
|
||||
echo "Authentication data restored"
|
||||
fi
|
||||
|
||||
# Restore storage directory (sessions, messages, parts, projects)
|
||||
if [ -d "$src/storage" ]; then
|
||||
mkdir -p "$SHARE_DIR/storage"
|
||||
cp -rv "$src/storage/"* "$SHARE_DIR/storage/" 2>/dev/null || true
|
||||
local storage_count=$(find "$SHARE_DIR/storage" -type f 2>/dev/null | wc -l)
|
||||
echo "Storage data restored: $storage_count file(s)"
|
||||
fi
|
||||
|
||||
# Restore snapshot directory (project snapshots)
|
||||
if [ -d "$src/snapshot" ]; then
|
||||
mkdir -p "$SHARE_DIR/snapshot"
|
||||
cp -rv "$src/snapshot/"* "$SHARE_DIR/snapshot/" 2>/dev/null || true
|
||||
local snapshot_count=$(find "$SHARE_DIR/snapshot" -type f 2>/dev/null | wc -l)
|
||||
echo "Snapshot data restored: $snapshot_count file(s)"
|
||||
fi
|
||||
|
||||
# Restore log directory
|
||||
if [ -d "$src/log" ]; then
|
||||
mkdir -p "$SHARE_DIR/log"
|
||||
cp -rv "$src/log/"* "$SHARE_DIR/log/" 2>/dev/null || true
|
||||
echo "Log data restored"
|
||||
fi
|
||||
else
|
||||
echo "No share data to restore"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# Main Restoration Process
|
||||
# ==============================================================================
|
||||
|
||||
echo "=== Starting Restoration ==="
|
||||
|
||||
restore_config
|
||||
echo ""
|
||||
|
||||
restore_share
|
||||
echo ""
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Summary
|
||||
# ------------------------------------------------------------------------------
|
||||
echo "=== Restoration Summary ==="
|
||||
|
||||
echo "Config directory contents:"
|
||||
ls -la "$CONFIG_DIR/" 2>/dev/null | head -10 || echo "Empty"
|
||||
echo ""
|
||||
|
||||
echo "Share directory structure:"
|
||||
if [ -d "$SHARE_DIR" ]; then
|
||||
du -sh "$SHARE_DIR"/* 2>/dev/null || echo "Empty"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Count total restored items
|
||||
config_files=$(find "$CONFIG_DIR" -maxdepth 1 -type f 2>/dev/null | wc -l)
|
||||
share_files=$(find "$SHARE_DIR" -type f 2>/dev/null | wc -l)
|
||||
|
||||
echo "Total restored: $config_files config files, $share_files share files"
|
||||
echo ""
|
||||
echo "Session restoration complete!"
|
||||
@@ -1,233 +0,0 @@
|
||||
#!/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!"
|
||||
Reference in New Issue
Block a user