diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml deleted file mode 100644 index 5b02a0c5..00000000 --- a/.github/workflows/opencode.yml +++ /dev/null @@ -1,231 +0,0 @@ -name: OpenChamber for Actions - -on: - workflow_dispatch: - inputs: - tunnel_provider: - description: 'Select Tunnel Provider' - required: true - default: 'cloudflare' - type: choice - options: - - ngrok - - cloudflare - timeout_minutes: - description: 'Auto-shutdown after (minutes)' - required: true - default: '300' - type: string - -jobs: - serve: - runs-on: ubuntu-latest - env: - OPENCODE_SERVER_PASSWORD: ${{ secrets.OPENCODE_SERVER_PASSWORD }} - - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - # ============================================================ - # PERSISTENCE: Restore previous session (OAuth, chats, config) - # ============================================================ - - name: Restore Session Data (Artifact) - if: ${{ env.OPENCODE_SERVER_PASSWORD != '' }} - uses: dawidd6/action-download-artifact@v6 - continue-on-error: true - with: - name: opencode-session - path: /tmp/opencode-restore - workflow: opencode.yml - workflow_conclusion: success - if_no_artifact_found: ignore - repo: ${{ github.repository }} - github_token: ${{ secrets.GITHUB_TOKEN }} - - - name: Apply Restored Session Data - if: ${{ env.OPENCODE_SERVER_PASSWORD != '' }} - env: - OPENCODE_SERVER_PASSWORD: ${{ env.OPENCODE_SERVER_PASSWORD }} - run: | - chmod +x scripts/persistence-restore.sh - RESTORE_DIR=/tmp/opencode-restore ./scripts/persistence-restore.sh - - # ============================================================ - # CACHE: Speed up npm installations - # ============================================================ - - name: Cache npm modules - uses: actions/cache@v4 - with: - path: | - ~/.npm - key: npm-cache-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-cache-${{ runner.os }}- - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - - - name: Install Tools - run: | - npm install -g opencode-ai @openchamber/web - sudo apt update && sudo apt install jq lsof coreutils openssl -y - - # Install ttyd for OpenCode TTY exposure - wget -q -O ttyd https://github.com/tsl0922/ttyd/releases/download/1.7.3/ttyd.x86_64 - chmod +x ttyd - sudo mv ttyd /usr/local/bin/ - - # ============================================================ - # CONFIGURATION: Setup OpenCode config (respects artifacts) - # ============================================================ - - name: Configure OpenCode - run: | - chmod +x scripts/opencode-config.sh - RESTORE_DIR=/tmp/opencode-restore ./scripts/opencode-config.sh - - # ============================================================ - # STARTUP: Launch services and tunnel - # ============================================================ - - name: Start Services - env: - OPENCODE_SERVER_PASSWORD: ${{ env.OPENCODE_SERVER_PASSWORD }} - run: | - # Setup Password Protection - TTYD_ARGS="" - if [[ -n "$OPENCODE_SERVER_PASSWORD" ]]; then - echo "Password protection enabled." - # Export for subsequent steps (Monitor script) and other services - echo "OPENCHAMBER_UI_PASSWORD=$OPENCODE_SERVER_PASSWORD" >> $GITHUB_ENV - echo "OPENCODE_UI_PASSWORD=$OPENCODE_SERVER_PASSWORD" >> $GITHUB_ENV - - # Use for current step - export OPENCHAMBER_UI_PASSWORD="$OPENCODE_SERVER_PASSWORD" - export OPENCODE_UI_PASSWORD="$OPENCODE_SERVER_PASSWORD" - - TTYD_ARGS="-c user:$OPENCODE_SERVER_PASSWORD" - else - echo "Password protection disabled (OPENCODE_SERVER_PASSWORD not set)." - fi - - # Start OpenCode TTY (via ttyd) - echo "Starting OpenCode TTY..." - nohup stdbuf -oL ttyd $TTYD_ARGS -p 7681 opencode > opencode_tty.log 2>&1 & - - # Start OpenChamber - echo "Starting OpenChamber..." - if [[ -n "$OPENCODE_SERVER_PASSWORD" ]]; then - nohup stdbuf -oL openchamber --port 9090 --ui-password "$OPENCODE_SERVER_PASSWORD" > openchamber.log 2>&1 & - else - nohup stdbuf -oL openchamber --port 9090 > openchamber.log 2>&1 & - fi - - # Start OpenCode Web - echo "Starting OpenCode Web..." - if [[ -n "$OPENCODE_SERVER_PASSWORD" ]]; then - OPENCODE_SERVER_PASSWORD="$OPENCODE_SERVER_PASSWORD" nohup stdbuf -oL opencode web --port 8080 > opencode_web.log 2>&1 & - else - nohup stdbuf -oL opencode web --port 8080 > opencode_web.log 2>&1 & - fi - - # Wait for services to initialize - sleep 20 - - echo "Services started. Checking status..." - lsof -i :7681 || echo "Warning: OpenCode TTY may not be running" - lsof -i :9090 || echo "Warning: OpenChamber may not be running" - lsof -i :8080 || echo "Warning: OpenCode Web may not be running" - - - name: Setup Tunnel - run: | - if [[ "${{ github.event.inputs.tunnel_provider }}" == "ngrok" ]]; then - echo "Setting up ngrok tunnel..." - curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null - echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | sudo tee /etc/apt/sources.list.d/ngrok.list - sudo apt update && sudo apt install ngrok -y - ngrok config add-authtoken "${{ secrets.NGROK_AUTH_TOKEN }}" - - # Note: Multiple ngrok tunnels on free plan might be limited or require config file - # Attempting to start tunnels blindly - nohup ngrok http 127.0.0.1:9090 --log=stdout > tunnel_chamber.log 2>&1 & - nohup ngrok http 127.0.0.1:8080 --log=stdout > tunnel_web.log 2>&1 & - nohup ngrok http 127.0.0.1:7681 --log=stdout > tunnel_tty.log 2>&1 & - sleep 10 - - # Extract URLs - URL_CHAMBER=$(curl -s http://localhost:4040/api/tunnels | jq -r '.tunnels[] | select(.config.addr | contains("9090")) | .public_url' || true) - URL_WEB=$(curl -s http://localhost:4040/api/tunnels | jq -r '.tunnels[] | select(.config.addr | contains("8080")) | .public_url' || true) - URL_TTY=$(curl -s http://localhost:4040/api/tunnels | jq -r '.tunnels[] | select(.config.addr | contains("7681")) | .public_url' || true) - - echo "URL_CHAMBER=$URL_CHAMBER" >> $GITHUB_ENV - echo "URL_WEB=$URL_WEB" >> $GITHUB_ENV - echo "URL_TTY=$URL_TTY" >> $GITHUB_ENV - - else - echo "Setting up Cloudflare tunnel..." - wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb - sudo dpkg -i cloudflared-linux-amd64.deb - - # Start 3 separate tunnels - nohup cloudflared tunnel --url http://127.0.0.1:7681 > tunnel_tty.log 2>&1 & - nohup cloudflared tunnel --url http://127.0.0.1:9090 > tunnel_chamber.log 2>&1 & - nohup cloudflared tunnel --url http://127.0.0.1:8080 > tunnel_web.log 2>&1 & - - echo "Waiting for Cloudflare Tunnel URLs..." - - # Loop to wait for URLs - for i in {1..30}; do - URL_TTY=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_tty.log 2>/dev/null | tail -n 1 || true) - URL_CHAMBER=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_chamber.log 2>/dev/null | tail -n 1 || true) - URL_WEB=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_web.log 2>/dev/null | tail -n 1 || true) - - if [[ -n "$URL_TTY" && -n "$URL_CHAMBER" && -n "$URL_WEB" ]]; then - echo "All tunnels established!" - break - fi - echo "Waiting for tunnels... ($i/30)" - sleep 2 - done - - if [[ -z "$URL_TTY" ]]; then echo "Warning: TTY Tunnel URL missing"; fi - if [[ -z "$URL_CHAMBER" ]]; then echo "Warning: Chamber Tunnel URL missing"; fi - if [[ -z "$URL_WEB" ]]; then echo "Warning: Web Tunnel URL missing"; fi - - echo "URL_TTY=$URL_TTY" >> $GITHUB_ENV - echo "URL_CHAMBER=$URL_CHAMBER" >> $GITHUB_ENV - echo "URL_WEB=$URL_WEB" >> $GITHUB_ENV - fi - - # ============================================================ - # MONITOR: Self-healing loop with status updates - # ============================================================ - - name: Monitor & Self-Heal - env: - OPENCHAMBER_UI_PASSWORD: ${{ env.OPENCODE_SERVER_PASSWORD }} - run: | - chmod +x scripts/monitor.sh - # Pass all 3 URLs to monitor script - ./scripts/monitor.sh "${{ github.event.inputs.tunnel_provider }}" "${{ github.event.inputs.timeout_minutes }}" "${{ env.URL_TTY }}" "${{ env.URL_CHAMBER }}" "${{ env.URL_WEB }}" - - # ============================================================ - # PERSISTENCE: Save all session data (always runs) - # ============================================================ - - name: Prepare Session Data for Save - if: ${{ always() && env.OPENCODE_SERVER_PASSWORD != '' }} - env: - OPENCODE_SERVER_PASSWORD: ${{ env.OPENCODE_SERVER_PASSWORD }} - run: | - chmod +x scripts/persistence-save.sh - SAVE_DIR=/tmp/opencode-save ./scripts/persistence-save.sh - - - name: Upload Session Data (Artifact) - if: ${{ always() && env.OPENCODE_SERVER_PASSWORD != '' }} - uses: actions/upload-artifact@v4 - with: - name: opencode-session - path: /tmp/opencode-save/ - retention-days: 90 - overwrite: true - if-no-files-found: warn diff --git a/README.md b/README.md index 7a52f9d4..ff16044a 100644 --- a/README.md +++ b/README.md @@ -80,16 +80,6 @@ The whole project was built entirely with AI coding agents under my supervision. - In-extension settings, responsive layout, and theme mapping that matches your editor - Hardened runtime lifecycle and health checks for faster startup and fewer stuck reconnect states -### GitHub Actions (Cloud Usage) - -Run OpenChamber remotely using GitHub Actions. No local computer required. - -* **Zero Setup:** Runs on GitHub's infrastructure. -* **Persistence:** Optional; enabled when `OPENCODE_SERVER_PASSWORD` is set (encrypted). -* **Remote Access:** Access via secure tunnel (Cloudflare/Ngrok). - -[**Read the Guide: OpenChamber for Actions**](docs/OPENCHAMBER_FOR_ACTIONS.md) - ### Custom Themes Create your own color schemes by dropping JSON files into `~/.config/openchamber/themes/`. Hot reload supported — no restart needed. diff --git a/docs/OPENCHAMBER_FOR_ACTIONS.md b/docs/OPENCHAMBER_FOR_ACTIONS.md deleted file mode 100644 index 6962f249..00000000 --- a/docs/OPENCHAMBER_FOR_ACTIONS.md +++ /dev/null @@ -1,166 +0,0 @@ -# OpenChamber for Actions - -**Version:** `v0.1.0-preview` - -OpenChamber for Actions allows you to run a full OpenChamber environment remotely using GitHub Actions infrastructure. It provides access to OpenCode, OpenChamber, and a web-based terminal without requiring any local hardware. - ---- - -## Overview - -OpenChamber for Actions is a "computer in the cloud" solution that leverages GitHub Runners to host a temporary development environment. It spins up three key services: -* **OpenCode TTY:** A web-based terminal for command-line access. -* **OpenChamber:** The core environment. -* **OpenCode Web:** The web interface for coding and interaction. - -These services are exposed via secure tunnels (Cloudflare or Ngrok), allowing you to access them from any browser. The session can persist data between runs using GitHub Artifacts. - ---- - -## Key Features - -* **No local hardware required:** Run OpenChamber on GitHub Actions infrastructure. -* **OpenCode integration:** Access the OpenCode suite and "Antigravity" model selection (including Gemini 3 and Claude 4.5). -* **Secure access (optional):** Protect OpenChamber, OpenCode Web, and the TTY with `OPENCODE_SERVER_PASSWORD`. -* **Persistent sessions (optional):** Save login state, OAuth tokens, and configuration as GitHub Artifacts when a password is set (encrypted). -* **Self-healing tunnels:** Background monitoring keeps the tunnel stable. - ---- - -## New to this? (Beginners' Guide) - -
-Click to expand: What is this and how does it help me? - -### 1. What exactly is this? -Think of this as a "computer in the cloud." Instead of running a local AI environment, GitHub Actions provides the compute for you. - -### 2. What do I need to start? -* A GitHub account. -* A web browser (Chrome, Edge, Safari, etc.). -* No special hardware required. - -### 3. Why is Cloudflare recommended? -Cloudflare Quick Tunnels create a secure public URL automatically. You do not need extra accounts or API keys, so it is the simplest option. - -### 4. What is persistence? -When a password is set, the workflow saves your login info and settings as an encrypted GitHub Artifact. The next run restores the session so you can continue without logging in again. -
- ---- - -
-Usage Limits & Quotas - -To ensure stability and compliance with GitHub’s Terms of Service, be aware of the following limits: - -| Limit Type | Constraint | Explanation | -| :--- | :--- | :--- | -| **Time Limit** | **6 Hours Max** | GitHub stops any job after 360 minutes. | -| **Storage** | **2GB** | Session artifacts cannot exceed 2GB per repository. | -| **Concurrency** | **1 Run Only** | Run a single OpenChamber instance at a time. | -| **Hardware** | **2-Core CPU** | Standard Linux runners (~7GB RAM). | - -
- ---- - -## Installation Guide - -### Option A: Easy Mode (Recommended) -Uses Cloudflare tunnels. No configuration required unless you want password protection. - -1. Fork this repository to your GitHub account. -2. Open the Actions tab in your fork. -3. Select the **OpenChamber for Actions** workflow. -4. (Optional) **Set a Password (recommended):** - * Go to **Settings** -> **Secrets and variables** -> **Actions**. - * Click **New repository secret**. - * **Name:** `OPENCODE_SERVER_PASSWORD` - * **Secret:** Your desired password. - * *Note: If this secret is set, it protects OpenCode TTY, OpenChamber, and OpenCode Web. It also enables encrypted persistence.* -5. Click **Run workflow**. - * **Tunnel Provider:** Choose `cloudflare` (default) or `ngrok`. - * **Auto-shutdown after (minutes):** Set the duration (default `300`). -6. Wait about 30 seconds for setup to finish. -7. Open the run, then open the `serve` job. -8. Expand the **Monitor & Self-Heal** step to find the URLs and open them. - -> [!TIP] -> Keep the repository visibility `Private` or use `OPENCODE_SERVER_PASSWORD` in Github Actions Secrets. Otherwise, your privacy can be violated. - ---- - -### Option B: Pro Mode (Ngrok) - -
-Click to expand: Step-by-step guide for setting up Ngrok - -If you prefer using Ngrok for a static domain, follow these steps to set up your API key. - -#### Step 1: Get your authtoken -1. Log in or sign up at dashboard.ngrok.com. -2. In the sidebar, click "Your Authtoken." -3. Copy the token string (for example, `21xYz...`). - -#### Step 2: Add the secret to GitHub -1. Open your forked repository on GitHub. -2. Open Settings. -3. In the sidebar, open Secrets and variables and click Actions. -4. Click New repository secret. -5. **Name:** `NGROK_AUTH_TOKEN`. -6. **Secret:** Paste the token. -7. (Optional) Add `OPENCODE_SERVER_PASSWORD` to enable password protection for all services and encrypted persistence. -8. Click Add secret. - -#### Step 3: Run with Ngrok -1. Go to the Actions tab. -2. Select **OpenChamber for Actions**. -3. Click **Run workflow**. -4. Select `ngrok` as the Tunnel Provider. -5. Find the URL in the "Monitor & Self-Heal" step. -
- ---- - -## Frequently Asked Questions (FAQ) - -
-Q: Is this completely free? -GitHub provides 2,000 minutes for private repositories and unlimited minutes for public repositories. This workflow uses those minutes. -
- -
-Q: Why did my session stop working after a few hours? -GitHub Actions has a 6-hour job limit. The environment shuts down automatically. If persistence is enabled, your session data is restored on the next run. -
- -
-Q: Can I use this on my phone or tablet? -Yes. Once you have a public URL (Cloudflare or Ngrok), open it in any mobile browser. -
- -
-Q: Is my data private? -If you fork this as a public repository, your code may be visible depending on how you save it. For maximum privacy, use a private fork. If you want persistence, set `OPENCODE_SERVER_PASSWORD` so the artifact is encrypted. -
- -
-Q: I see a "Connection Refused" error. What do I do? -Wait about 30 seconds and refresh the page. If it persists, check the Monitor logs in the Actions run. -
- ---- - -## Technical Specifications - -| Feature | Detail | -| :--- | :--- | -| **Version** | `v0.1.0-preview` | -| **OS** | Ubuntu 22.04 LTS (GitHub Runner) | -| **Node Version** | v20.x | -| **Tunnel Protocols** | `cloudflared` (Argon) / `ngrok` (HTTP) | -| **Artifact Retention** | 90 Days (Default GitHub Policy) | - -> [!NOTE] -> This project is for educational and development purposes. Do not use this workflow for mining crypto or other activities banned by the GitHub Acceptable Use Policy. diff --git a/scripts/monitor.sh b/scripts/monitor.sh deleted file mode 100755 index 757d9161..00000000 --- a/scripts/monitor.sh +++ /dev/null @@ -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 -# -# 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 diff --git a/scripts/opencode-config.sh b/scripts/opencode-config.sh deleted file mode 100755 index 4e319ca0..00000000 --- a/scripts/opencode-config.sh +++ /dev/null @@ -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!" diff --git a/scripts/persistence-restore.sh b/scripts/persistence-restore.sh deleted file mode 100755 index 5115c5bf..00000000 --- a/scripts/persistence-restore.sh +++ /dev/null @@ -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!" diff --git a/scripts/persistence-save.sh b/scripts/persistence-save.sh deleted file mode 100755 index 35a3cb9d..00000000 --- a/scripts/persistence-save.sh +++ /dev/null @@ -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!"