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!"