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