feat: added github action for starting opencode/openchamber in actions infrastructure with persistence encryption (#194)
* fix(ui): make chat message action icons always visible - Removes opacity-0 and pointer-events-none classes from UserMessageBody action buttons container. - Ensures icons are visible by default instead of only on hover/focus. - Addresses user feedback about empty space at the bottom of chat bubbles. * fix(ui): make chat message action icons always visible - Removes opacity-0 and pointer-events-none classes from UserMessageBody action buttons container. - Ensures icons are visible by default instead of only on hover/focus. - Addresses user feedback about empty space at the bottom of chat bubbles. * fix(ui): prevent diff comment input from closing on click (#3) Replaced global `document.querySelector('[data-comment-ui]')` with a local `useRef` to detect clicks inside the comment input area. This fixes an issue where clicking the input in one diff viewer would close it if another diff viewer (found first by querySelector) existed in the DOM. - Added `commentContainerRef` in `PierreDiffViewer.tsx`. - Updated `handleClickOutside` to check against the ref. - Attached ref to comment UI containers in both 'fill' and 'inline' layouts. - Preserved `data-comment-ui` attribute for backward compatibility. * feat(ui): add visual connector lines to file explorer (#2) - Replaced manual padding-based indentation with nested list structure - Added vertical connector lines using border-l on nested ul - Added horizontal connector lines using pseudo-elements on list items - Added tail masking for the last item in a branch to create the 'L' shape - Removed manual padding calculation from file/folder buttons * Create openchamber.yml * Create OPENCODE_FOR_ACTIONS.md * feat: update OpenChamber for Actions workflow and docs (#6) - Rename `docs/OPENCODE_FOR_ACTIONS.md` to `docs/OPENCHAMBER_FOR_ACTIONS.md`. - Update `README.md` to include a section on OpenChamber for Actions. - Update `.github/workflows/openchamber.yml` to allow users to select which service to expose (OpenChamber, OpenCode, or Both). - Update documentation to reflect the new workflow options. * feat: Add shimmer effect for generating messages (#7) * Create test_unreleased.yml * Update test_unreleased.yml * Delete test_unreleased.yml * 🧪 Labs: Enhanced File Explorer with Context Menus & UI Improvements (#13) * feat: add file management capabilities (create, rename, delete) (#11) Added backend endpoints for delete and rename operations. Extended FilesAPI interface and implementation. Updated FilesView with toolbar buttons for Create File/Folder. Added context menu to file tree items for Rename, Delete, and Copy Path. Implemented dialogs for all file operations. * Refactor UI/UX: Modernize styles, components, and layout architecture (#10) Refactored `packages/ui` CSS into modular files (design-system, typography, mobile). Modernized `Header` with pill-style tabs and semantic Flexbox layout. Enhanced `Chat` UI with threading visuals, improved typography, and better spacing. Updated `Sidebar` to match the new design system aesthetic. Verified with full workspace build. * Add workflow * Change to manual dispatch only * Delete test.yml * Update openchamber.yml * Fix styling and workflow improvements (#17) * Refactor OpenChamber workflow and update docs (#18) - Refactor `.github/workflows/openchamber.yml` to use modular helper scripts. - Add helper scripts in `scripts/`: `monitor.sh`, `persistence-restore.sh`, `persistence-save.sh`, `opencode-config.sh`. - Update `docs/OPENCHAMBER_FOR_ACTIONS.md` to reflect new inputs and features. - Implement robust monitoring with self-healing and proper URL logging. - Improve persistence robustness for file copying. * Expose Opencode Core, Web, and Chamber via Cloudflare tunnels (#19) This commit updates the GitHub Actions workflow and monitoring script to: 1. Install `wetty` to expose the core Opencode TTY over the web (interpreted from user request for "WiTTY"). 2. Start `opencode web` (8080), `openchamber` (9090), and `wetty` (3000) concurrently. 3. Configure Cloudflare to create three separate tunnels for these ports. 4. Update `monitor.sh` to accept and display URLs for all three services. * Update opencode-config.sh * Tighten actions persistence and UI polish * Delete openchamber.yml * Delete persistence-save.sh * Delete persistence-restore.sh * Delete opencode-config.sh * Delete monitor.sh * Delete install.sh * Fix OpenChamber workflow and scripts for multi-service tunnels (#21) * Create auto-opencode.yml * Update opencode.yml * feat: add password protection for OpenChamber in Actions (#22) - Introduce `OPENCODE_SERVER_PASSWORD` secret support in `.github/workflows/opencode.yml`. - Automatically set `OPENCHAMBER_UI_PASSWORD` and `OPENCODE_UI_PASSWORD` if the secret is provided. - Configure `ttyd` to use basic auth (`-c user:password`) when password is set. - Update `scripts/monitor.sh` to respect password settings during service restarts. - Significantly update `docs/OPENCHAMBER_FOR_ACTIONS.md` with an overview, password configuration instructions, and improved layout (collapsible sections). * use one password env variable * Update OPENCHAMBER_FOR_ACTIONS.md * feat: improve GITHUB_STEP_SUMMARY, fix TTY home logic and add encryption support in scripts * feat: lazy load large diffs to prevent page freeze (#186) When diff content exceeds 1500 lines or 150KB, show a placeholder with "Load Diff" button instead of parsing immediately. This prevents the page from freezing when viewing large diffs. Co-authored-by: Jovines <jovines@qq.com> * feat: add Web Push API support and PWA integration (#189) * feat: add Web Push API support and PWA integration Add web Push API with subscribe/unsubscribe and visibility endpoints Introduce usePushVisibilityBeacon and useSessionDeepLink hooks Integrate PWA with service worker, registerSW, and VAPID key persistence * feat: add heartbeat visibility beacon for web runtime Add a 10s heartbeat to ping visibility while visible Subscribe to visibilitychange, focus, blur, pageshow, and pagehide events to report state Clear heartbeat interval on unmount to avoid leaks * feat(UI): Introduce new UI components for file attachments and related views (#191) Add file management API and UI components Implement directory listing, search and CRUD operations in desktop backend Expose new Files API on frontend to list, search, and modify files * feat: add workbox-window and improve event stream cleanup Add workbox-window dependency to bun.lock and web package.json Cancel and release the stream reader on disconnect to avoid leaks * feat: provider config management (#193) * feat: support scoped removal of provider config (auth, user, project, custom) * feat: implement UI session token management with cookies for window visibility control * feature: header layout changes (#195) * fix(ui): remove fixed sessions button and mac titlebar coupling Remove fixed sessions button from header on desktop Mac Eliminate Mac titlebar spacer and drag-to-dock logic in sidebar Update layout to rely on standard header/sidebar without mac-specific tweaks * feat: improve mobile header with session toggle and back button Add back button in mobile header to exit session switcher Show Sessions label when session switcher is open in mobile header Toggle between opening sessions and back navigation based on session state * restore: add back scripts/install.sh from main branch * docs: improve Tech Stack section with organized label badges * Fix actions auth and remove shimmer
This commit is contained in:
Executable
+292
@@ -0,0 +1,292 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# OpenCode Monitor & Self-Heal Script
|
||||
# ==============================================================================
|
||||
# This script monitors OpenCode (TTY), OpenChamber, and OpenCode Web services,
|
||||
# automatically restarting them if they crash. It also manages the tunnel connections.
|
||||
#
|
||||
# Usage: ./monitor.sh <tunnel_provider> <timeout_minutes> <url_tty> <url_chamber> <url_web>
|
||||
#
|
||||
# Arguments:
|
||||
# tunnel_provider: "ngrok" or "cloudflare"
|
||||
# timeout_minutes: Auto-shutdown timeout in minutes
|
||||
# url_tty: Initial tunnel URL for OpenCode TTY
|
||||
# url_chamber: Initial tunnel URL for OpenChamber
|
||||
# url_web: Initial tunnel URL for OpenCode Web
|
||||
# ==============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
TUNNEL_PROVIDER="${1:-cloudflare}"
|
||||
TIMEOUT_MINUTES="${2:-300}"
|
||||
URL_TTY="${3:-}"
|
||||
URL_CHAMBER="${4:-}"
|
||||
URL_WEB="${5:-}"
|
||||
|
||||
echo "=============================================="
|
||||
echo "OpenCode Monitor & Self-Heal"
|
||||
echo "=============================================="
|
||||
echo "Tunnel Provider: $TUNNEL_PROVIDER"
|
||||
echo "Timeout: $TIMEOUT_MINUTES minute(s)"
|
||||
echo "----------------------------------------------"
|
||||
echo "OpenCode TTY URL: $URL_TTY"
|
||||
echo "OpenChamber URL: $URL_CHAMBER"
|
||||
echo "OpenCode Web URL: $URL_WEB"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Configuration
|
||||
# ------------------------------------------------------------------------------
|
||||
TTY_PORT=7681
|
||||
OPENCHAMBER_PORT=9090
|
||||
OPENCODE_WEB_PORT=8080
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
TIMEOUT_SECONDS=$((TIMEOUT_MINUTES * 60))
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Helper Functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||
}
|
||||
|
||||
check_port() {
|
||||
local port=$1
|
||||
lsof -i :"$port" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
get_remaining_time() {
|
||||
local elapsed=$(($(date +%s) - START_TIME))
|
||||
local remaining=$((TIMEOUT_SECONDS - elapsed))
|
||||
echo $remaining
|
||||
}
|
||||
|
||||
format_time() {
|
||||
local seconds=$1
|
||||
local minutes=$((seconds / 60))
|
||||
local hours=$((minutes / 60))
|
||||
minutes=$((minutes % 60))
|
||||
|
||||
if [ $hours -gt 0 ]; then
|
||||
echo "${hours}h ${minutes}m"
|
||||
else
|
||||
echo "${minutes}m"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Service Management
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
restart_opencode_tty() {
|
||||
log "Restarting OpenCode TTY on port $TTY_PORT..."
|
||||
pkill -f "ttyd" 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
if [ -n "${OPENCHAMBER_UI_PASSWORD:-}" ]; then
|
||||
log "Restarting TTY with password protection."
|
||||
nohup stdbuf -oL ttyd -c "user:${OPENCHAMBER_UI_PASSWORD}" -p $TTY_PORT bash -c "cd \$HOME && exec opencode" >> opencode_tty.log 2>&1 &
|
||||
else
|
||||
nohup stdbuf -oL ttyd -p $TTY_PORT bash -c "cd \$HOME && exec opencode" >> opencode_tty.log 2>&1 &
|
||||
fi
|
||||
sleep 5
|
||||
|
||||
if check_port $TTY_PORT; then
|
||||
log "OpenCode TTY restarted successfully"
|
||||
return 0
|
||||
else
|
||||
log "ERROR: Failed to restart OpenCode TTY"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restart_openchamber() {
|
||||
log "Restarting OpenChamber on port $OPENCHAMBER_PORT..."
|
||||
pkill -f "openchamber" 2>/dev/null || true
|
||||
sleep 2
|
||||
if [ -n "${OPENCHAMBER_UI_PASSWORD:-}" ]; then
|
||||
nohup stdbuf -oL openchamber --port $OPENCHAMBER_PORT --ui-password "$OPENCHAMBER_UI_PASSWORD" >> openchamber.log 2>&1 &
|
||||
else
|
||||
nohup stdbuf -oL openchamber --port $OPENCHAMBER_PORT >> openchamber.log 2>&1 &
|
||||
fi
|
||||
sleep 5
|
||||
|
||||
if check_port $OPENCHAMBER_PORT; then
|
||||
log "OpenChamber restarted successfully"
|
||||
return 0
|
||||
else
|
||||
log "ERROR: Failed to restart OpenChamber"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restart_opencode_web() {
|
||||
log "Restarting OpenCode Web on port $OPENCODE_WEB_PORT..."
|
||||
# Note: "opencode web" might be matched by "opencode" so we use full command in pkill if possible or handle order
|
||||
pkill -f "opencode web" 2>/dev/null || true
|
||||
sleep 2
|
||||
if [ -n "${OPENCHAMBER_UI_PASSWORD:-}" ]; then
|
||||
OPENCODE_SERVER_PASSWORD="$OPENCHAMBER_UI_PASSWORD" nohup stdbuf -oL opencode web --port $OPENCODE_WEB_PORT >> opencode_web.log 2>&1 &
|
||||
else
|
||||
nohup stdbuf -oL opencode web --port $OPENCODE_WEB_PORT >> opencode_web.log 2>&1 &
|
||||
fi
|
||||
sleep 5
|
||||
|
||||
if check_port $OPENCODE_WEB_PORT; then
|
||||
log "OpenCode Web restarted successfully"
|
||||
return 0
|
||||
else
|
||||
log "ERROR: Failed to restart OpenCode Web"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restart_tunnels() {
|
||||
log "Restarting tunnels ($TUNNEL_PROVIDER)..."
|
||||
|
||||
if [ "$TUNNEL_PROVIDER" = "ngrok" ]; then
|
||||
pkill -f "ngrok" 2>/dev/null || true
|
||||
sleep 2
|
||||
# Complex to manage multiple ngrok tunnels without config file.
|
||||
# For now, attempting to restore single tunnel logic or just warn.
|
||||
log "WARNING: Multi-tunnel restart for ngrok not fully supported in this script version."
|
||||
# Attempt to start tunnels again (blindly)
|
||||
nohup ngrok http 127.0.0.1:$OPENCHAMBER_PORT --log=stdout > tunnel_chamber.log 2>&1 &
|
||||
nohup ngrok http 127.0.0.1:$OPENCODE_WEB_PORT --log=stdout > tunnel_web.log 2>&1 &
|
||||
nohup ngrok http 127.0.0.1:$TTY_PORT --log=stdout > tunnel_tty.log 2>&1 &
|
||||
sleep 10
|
||||
else
|
||||
pkill -f "cloudflared" 2>/dev/null || true
|
||||
sleep 2
|
||||
nohup cloudflared tunnel --url http://127.0.0.1:$TTY_PORT > tunnel_tty.log 2>&1 &
|
||||
nohup cloudflared tunnel --url http://127.0.0.1:$OPENCHAMBER_PORT > tunnel_chamber.log 2>&1 &
|
||||
nohup cloudflared tunnel --url http://127.0.0.1:$OPENCODE_WEB_PORT > tunnel_web.log 2>&1 &
|
||||
sleep 15
|
||||
|
||||
# Get new URLs
|
||||
URL_TTY=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_tty.log 2>/dev/null | tail -n 1)
|
||||
URL_CHAMBER=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_chamber.log 2>/dev/null | tail -n 1)
|
||||
URL_WEB=$(grep -o 'https://[-a-z0-9.]*trycloudflare.com' tunnel_web.log 2>/dev/null | tail -n 1)
|
||||
fi
|
||||
|
||||
log "Tunnels restarted."
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "NEW ACCESS URLS:"
|
||||
echo "OpenCode TTY: $URL_TTY"
|
||||
echo "OpenChamber: $URL_CHAMBER"
|
||||
echo "OpenCode Web: $URL_WEB"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
check_tunnels() {
|
||||
if [ "$TUNNEL_PROVIDER" = "ngrok" ]; then
|
||||
pgrep -f "ngrok" > /dev/null 2>&1
|
||||
else
|
||||
pgrep -f "cloudflared" > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Graceful Shutdown
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
shutdown() {
|
||||
log "Initiating graceful shutdown..."
|
||||
|
||||
# Kill all services
|
||||
pkill -f "ttyd" 2>/dev/null || true
|
||||
pkill -f "opencode" 2>/dev/null || true
|
||||
pkill -f "openchamber" 2>/dev/null || true
|
||||
pkill -f "opencode web" 2>/dev/null || true
|
||||
pkill -f "ngrok" 2>/dev/null || true
|
||||
pkill -f "cloudflared" 2>/dev/null || true
|
||||
|
||||
log "All services stopped"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Trap signals for graceful shutdown
|
||||
trap shutdown SIGTERM SIGINT
|
||||
|
||||
# ==============================================================================
|
||||
# Main Monitoring Loop
|
||||
# ==============================================================================
|
||||
|
||||
log "Starting monitoring loop..."
|
||||
echo ""
|
||||
|
||||
# Write initial URLs to GitHub Step Summary (if running in GitHub Actions)
|
||||
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
|
||||
{
|
||||
echo "## OpenChamber for Actions"
|
||||
echo ""
|
||||
echo "| Service | URL |"
|
||||
echo "|---------|-----|"
|
||||
echo "| **OpenCode TTY** | $URL_TTY |"
|
||||
echo "| **OpenChamber** | $URL_CHAMBER |"
|
||||
echo "| **OpenCode Web** | $URL_WEB |"
|
||||
echo ""
|
||||
echo "_Timeout: ${TIMEOUT_MINUTES} minutes_"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
# Status display interval (every 5 minutes = 300 seconds)
|
||||
LAST_STATUS_TIME=0
|
||||
STATUS_INTERVAL=300
|
||||
|
||||
while true; do
|
||||
REMAINING=$(get_remaining_time)
|
||||
|
||||
# Check for timeout
|
||||
if [ "$REMAINING" -le 0 ]; then
|
||||
log "Timeout reached. Initiating graceful shutdown..."
|
||||
shutdown
|
||||
fi
|
||||
|
||||
# Periodic status update (every 5 minutes)
|
||||
CURRENT_TIME=$(date +%s)
|
||||
if [ $((CURRENT_TIME - LAST_STATUS_TIME)) -ge $STATUS_INTERVAL ]; then
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
log "Status Update"
|
||||
echo "Time remaining: $(format_time "$REMAINING")"
|
||||
echo "OpenCode TTY: $URL_TTY"
|
||||
echo "OpenChamber: $URL_CHAMBER"
|
||||
echo "OpenCode Web: $URL_WEB"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
LAST_STATUS_TIME=$CURRENT_TIME
|
||||
fi
|
||||
|
||||
# Check OpenCode TTY
|
||||
if ! check_port $TTY_PORT; then
|
||||
log "OpenCode TTY not responding on port $TTY_PORT"
|
||||
restart_opencode_tty
|
||||
fi
|
||||
|
||||
# Check OpenChamber
|
||||
if ! check_port $OPENCHAMBER_PORT; then
|
||||
log "OpenChamber not responding on port $OPENCHAMBER_PORT"
|
||||
restart_openchamber
|
||||
fi
|
||||
|
||||
# Check OpenCode Web
|
||||
if ! check_port $OPENCODE_WEB_PORT; then
|
||||
log "OpenCode Web not responding on port $OPENCODE_WEB_PORT"
|
||||
restart_opencode_web
|
||||
fi
|
||||
|
||||
# Check Tunnel Process (Basic check)
|
||||
if ! check_tunnels; then
|
||||
log "Tunnel processes not running"
|
||||
restart_tunnels
|
||||
fi
|
||||
|
||||
# Sleep before next check
|
||||
sleep 5
|
||||
done
|
||||
Executable
+167
@@ -0,0 +1,167 @@
|
||||
#!/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!"
|
||||
Executable
+182
@@ -0,0 +1,182 @@
|
||||
#!/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!"
|
||||
Executable
+233
@@ -0,0 +1,233 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================
|
||||
# OpenCode Persistence Save Script
|
||||
# ==============================================================================
|
||||
# This script prepares all session data for artifact upload.
|
||||
# It saves:
|
||||
# - Configuration files (~/.config/opencode/)
|
||||
# - Session data, messages, chats (~/.local/share/opencode/storage/)
|
||||
# - Project snapshots (~/.local/share/opencode/snapshot/)
|
||||
# - Authentication tokens (~/.local/share/opencode/auth.json)
|
||||
# - Logs (~/.local/share/opencode/log/)
|
||||
#
|
||||
# Excludes (to keep artifact size manageable):
|
||||
# - node_modules/ directories
|
||||
# - bin/ directory (will be reinstalled)
|
||||
# - tool-output/ (temporary tool outputs)
|
||||
#
|
||||
# If OPENCODE_SERVER_PASSWORD is set, the artifact will be encrypted.
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SAVE_DIR="${SAVE_DIR:-/tmp/opencode-save}"
|
||||
CONFIG_DIR="$HOME/.config/opencode"
|
||||
SHARE_DIR="$HOME/.local/share/opencode"
|
||||
ENCRYPTION_PASSWORD="${OPENCODE_SERVER_PASSWORD:-}"
|
||||
|
||||
echo "=== OpenCode Session Save ==="
|
||||
echo "Save directory: $SAVE_DIR"
|
||||
echo "Config directory: $CONFIG_DIR"
|
||||
echo "Share directory: $SHARE_DIR"
|
||||
if [ -n "$ENCRYPTION_PASSWORD" ]; then
|
||||
echo "Encryption: ENABLED"
|
||||
else
|
||||
echo "Encryption: DISABLED (set OPENCODE_SERVER_PASSWORD to enable)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Clean up and create save directory
|
||||
# ------------------------------------------------------------------------------
|
||||
rm -rf "$SAVE_DIR"
|
||||
mkdir -p "$SAVE_DIR/config"
|
||||
mkdir -p "$SAVE_DIR/share"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Save configuration files
|
||||
# ------------------------------------------------------------------------------
|
||||
save_config() {
|
||||
echo "=== Saving Configuration Files ==="
|
||||
|
||||
if [ -d "$CONFIG_DIR" ] && [ "$(ls -A "$CONFIG_DIR" 2>/dev/null)" ]; then
|
||||
# Save all config files (excluding node_modules)
|
||||
for item in "$CONFIG_DIR"/*; do
|
||||
if [ -e "$item" ]; then
|
||||
local basename=$(basename "$item")
|
||||
|
||||
# Skip node_modules and bun.lock (will be reinstalled)
|
||||
if [ "$basename" = "node_modules" ] || [ "$basename" = "bun.lock" ]; then
|
||||
echo "Skipping: $basename (will be reinstalled)"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Copy file or directory
|
||||
if [ -f "$item" ]; then
|
||||
cp -v "$item" "$SAVE_DIR/config/" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
local count=$(find "$SAVE_DIR/config" -type f 2>/dev/null | wc -l)
|
||||
echo "Config files saved: $count"
|
||||
else
|
||||
echo "No config data to save"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Save share data (sessions, messages, snapshots, auth)
|
||||
# ------------------------------------------------------------------------------
|
||||
save_share() {
|
||||
echo ""
|
||||
echo "=== Saving Share Data ==="
|
||||
|
||||
if [ ! -d "$SHARE_DIR" ]; then
|
||||
echo "No share directory found"
|
||||
return
|
||||
fi
|
||||
|
||||
# Save auth.json (authentication tokens - critical!)
|
||||
if [ -f "$SHARE_DIR/auth.json" ]; then
|
||||
cp -v "$SHARE_DIR/auth.json" "$SAVE_DIR/share/" 2>/dev/null || true
|
||||
echo "Auth data saved"
|
||||
fi
|
||||
|
||||
# Save storage directory (sessions, messages, parts, projects)
|
||||
if [ -d "$SHARE_DIR/storage" ] && [ "$(ls -A "$SHARE_DIR/storage" 2>/dev/null)" ]; then
|
||||
echo "Saving storage data (sessions, messages, chats)..."
|
||||
mkdir -p "$SAVE_DIR/share/storage"
|
||||
|
||||
# Copy all storage subdirectories
|
||||
for subdir in message migration part project session session_diff; do
|
||||
if [ -d "$SHARE_DIR/storage/$subdir" ]; then
|
||||
cp -r "$SHARE_DIR/storage/$subdir" "$SAVE_DIR/share/storage/" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
local storage_count=$(find "$SAVE_DIR/share/storage" -type f 2>/dev/null | wc -l)
|
||||
echo "Storage files saved: $storage_count"
|
||||
fi
|
||||
|
||||
# Save snapshot directory (project snapshots for undo/rollback)
|
||||
if [ -d "$SHARE_DIR/snapshot" ] && [ "$(ls -A "$SHARE_DIR/snapshot" 2>/dev/null)" ]; then
|
||||
echo "Saving snapshot data..."
|
||||
mkdir -p "$SAVE_DIR/share/snapshot"
|
||||
cp -r "$SHARE_DIR/snapshot/"* "$SAVE_DIR/share/snapshot/" 2>/dev/null || true
|
||||
|
||||
local snapshot_count=$(find "$SAVE_DIR/share/snapshot" -type f 2>/dev/null | wc -l)
|
||||
echo "Snapshot files saved: $snapshot_count"
|
||||
fi
|
||||
|
||||
# Save log directory (useful for debugging)
|
||||
if [ -d "$SHARE_DIR/log" ] && [ "$(ls -A "$SHARE_DIR/log" 2>/dev/null)" ]; then
|
||||
echo "Saving log data..."
|
||||
mkdir -p "$SAVE_DIR/share/log"
|
||||
cp -r "$SHARE_DIR/log/"* "$SAVE_DIR/share/log/" 2>/dev/null || true
|
||||
echo "Log files saved"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Create manifest with metadata
|
||||
# ------------------------------------------------------------------------------
|
||||
create_manifest() {
|
||||
echo ""
|
||||
echo "=== Creating Manifest ==="
|
||||
|
||||
local manifest="$SAVE_DIR/manifest.json"
|
||||
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
local config_count=$(find "$SAVE_DIR/config" -type f 2>/dev/null | wc -l)
|
||||
local share_count=$(find "$SAVE_DIR/share" -type f 2>/dev/null | wc -l)
|
||||
local total_size=$(du -sh "$SAVE_DIR" 2>/dev/null | cut -f1)
|
||||
|
||||
cat << EOF > "$manifest"
|
||||
{
|
||||
"version": "2.0",
|
||||
"timestamp": "$timestamp",
|
||||
"hostname": "$(hostname)",
|
||||
"stats": {
|
||||
"config_files": $config_count,
|
||||
"share_files": $share_count,
|
||||
"total_size": "$total_size"
|
||||
},
|
||||
"contents": {
|
||||
"config": $([ -d "$SAVE_DIR/config" ] && ls "$SAVE_DIR/config" 2>/dev/null | jq -R -s -c 'split("\n") | map(select(length > 0))' || echo '[]'),
|
||||
"share": $([ -d "$SAVE_DIR/share" ] && ls "$SAVE_DIR/share" 2>/dev/null | jq -R -s -c 'split("\n") | map(select(length > 0))' || echo '[]')
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Manifest created: $manifest"
|
||||
cat "$manifest"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# Main Save Process
|
||||
# ==============================================================================
|
||||
|
||||
save_config
|
||||
save_share
|
||||
create_manifest
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Final Summary
|
||||
# ------------------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "=== Save Summary ==="
|
||||
|
||||
# Calculate totals
|
||||
total_files=$(find "$SAVE_DIR" -type f 2>/dev/null | wc -l)
|
||||
total_size=$(du -sh "$SAVE_DIR" 2>/dev/null | cut -f1)
|
||||
|
||||
echo "Save directory structure:"
|
||||
if command -v tree &> /dev/null; then
|
||||
tree -L 2 "$SAVE_DIR" 2>/dev/null || ls -laR "$SAVE_DIR" | head -50
|
||||
else
|
||||
ls -laR "$SAVE_DIR" | head -50
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Total files to upload: $total_files"
|
||||
echo "Total size: $total_size"
|
||||
echo ""
|
||||
|
||||
# Verify we have something to save
|
||||
if [ "$total_files" -gt 1 ]; then
|
||||
echo "Session data prepared successfully for artifact upload!"
|
||||
else
|
||||
echo "Warning: Minimal data to save. Creating placeholder..."
|
||||
echo "{\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"status\": \"empty\"}" > "$SAVE_DIR/placeholder.json"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Optional Encryption (if OPENCODE_SERVER_PASSWORD is set)
|
||||
# ------------------------------------------------------------------------------
|
||||
if [ -n "$ENCRYPTION_PASSWORD" ]; then
|
||||
echo ""
|
||||
echo "=== Encrypting Session Data ==="
|
||||
|
||||
TEMP_ARCHIVE="/tmp/opencode-session-data.tar.gz"
|
||||
ENCRYPTED_FILE="$SAVE_DIR.enc"
|
||||
|
||||
tar -czf "$TEMP_ARCHIVE" -C "$SAVE_DIR" .
|
||||
|
||||
openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
|
||||
-in "$TEMP_ARCHIVE" \
|
||||
-out "$ENCRYPTED_FILE" \
|
||||
-pass pass:"$ENCRYPTION_PASSWORD"
|
||||
|
||||
rm -f "$TEMP_ARCHIVE"
|
||||
rm -rf "$SAVE_DIR"
|
||||
mkdir -p "$SAVE_DIR"
|
||||
mv "$ENCRYPTED_FILE" "$SAVE_DIR/session.enc"
|
||||
|
||||
echo '{"encrypted": true, "algorithm": "aes-256-cbc", "kdf": "pbkdf2", "iterations": 100000}' > "$SAVE_DIR/manifest.json"
|
||||
|
||||
echo "Encryption complete. Artifact is password-protected."
|
||||
echo "Encrypted file: $SAVE_DIR/session.enc"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Save location: $SAVE_DIR"
|
||||
echo "Ready for artifact upload!"
|
||||
Reference in New Issue
Block a user