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:
Iuliia Ivashko
2026-02-18 18:44:57 +02:00
committed by GitHub
parent 14737b6b28
commit e4a2486312
7 changed files with 0 additions and 1281 deletions
-292
View File
@@ -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
-167
View File
@@ -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!"
-182
View File
@@ -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!"
-233
View File
@@ -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!"