#!/bin/bash # Oh My OpenCode Free - Interactive TUI # Beautiful terminal interface for managing the OpenCode server # Created by: Oussama Douhou set -e RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' MAGENTA='\033[0;35m' CYAN='\033[0;36m' WHITE='\033[1;37m' NC='\033[0m' BOLD='\033[1m' DIM='\033[2m' PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOCKER_DIR="$PROJECT_DIR/docker" CONFIG_DIR="$DOCKER_DIR/shared-config" CONTAINER_NAME="leader-custom" # Detect server port from running container detect_server_port() { if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q .; then SERVER_PORT=$($CONTAINER_CMD inspect "$CONTAINER_NAME" --format='{{(index (index .NetworkSettings.Ports "8080/tcp") 0).HostPort}}' 2>/dev/null) if [ -z "$SERVER_PORT" ]; then SERVER_PORT="8085" # fallback to default external port fi else SERVER_PORT="8085" # fallback when container not running fi } detect_server_port SERVER_URL="http://localhost:$SERVER_PORT" # Check if dependencies are available check_dependencies() { local missing_deps=() if ! command -v docker &> /dev/null && ! command -v podman &> /dev/null; then missing_deps+=("docker or podman") fi if ! command -v curl &> /dev/null; then missing_deps+=("curl") fi if ! command -v jq &> /dev/null; then missing_deps+=("jq") fi if [ ${#missing_deps[@]} -ne 0 ]; then echo -e "${RED}❌ Missing dependencies: ${missing_deps[*]}${NC}" echo -e "${YELLOW}Please install them and try again.${NC}" exit 1 fi if command -v docker &> /dev/null; then CONTAINER_CMD="docker" else CONTAINER_CMD="podman" fi } # ASCII Art Header show_header() { clear echo -e "${CYAN}" cat << 'EOF' ╔══════════════════════════════════════════════════════════════╗ ║ ║ ║ ███████╗ ██╗ ██╗ ███╗ ███╗██╗ ██╗ ║ ║ ██╔════╝ ██║ ██║ ████╗ ████║╚██╗ ██╔╝ ║ ║ ███████╗ ███████║ ██╔████╔██║ ╚████╔╝ ║ ║ ╚════██║ ██╔══██║ ██║╚██╔╝██║ ╚██╔╝ ║ ║ ███████║ ██║ ██║ ██║ ╚═╝ ██║ ██║ ║ ║ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ║ ║ ║ ║ ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ║ ║ ██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝ ║ ║ ██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ███╗ ║ ║ ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ║ ║ ╚██████╔╝██║ ███████╗██║ ╚████║╚██████╔╝ ║ ║ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ║ ║ ║ ║ ███████╗██████╗ ███████╗███████╗ ║ ║ ██╔════╝██╔══██╗██╔════╝██╔════╝ ║ ║ █████╗ ██████╔╝█████╗ █████╗ ║ ║ ██╔══╝ ██╔══██╗██╔══╝ ██╔══╝ ║ ║ ██║ ██║ ██║███████╗███████╗ ║ ║ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ║ ║ ║ ║ 🌟 100% FREE AI CODING ASSISTANT 🌟 ║ ║ ║ ║ Created by: Oussama Douhou ║ ║ Email: oussama.douhou@gmail.com ║ ║ ║ ╚══════════════════════════════════════════════════════════════╝ EOF echo -e "${NC}" } # Status indicators show_status() { echo -e "${BLUE}📊 System Status:${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -n "🐳 Container: " if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q .; then echo -e "${GREEN}● Running${NC}" else echo -e "${RED}● Stopped${NC}" fi echo -n "🌐 Server: " if curl -s -f --max-time 2 "$SERVER_URL" > /dev/null 2>&1; then echo -e "${GREEN}● Running${NC} (Port $SERVER_PORT)" else echo -e "${RED}● Stopped${NC}" fi echo -n "🤖 OpenCode: " if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q . && \ $CONTAINER_CMD exec "$CONTAINER_NAME" pgrep -f "opencode serve" > /dev/null 2>&1; then echo -e "${GREEN}● Active${NC} (oh-my-opencode loaded)" else echo -e "${RED}● Inactive${NC}" fi # Server status if curl -s -f --max-time 2 "$SERVER_URL" > /dev/null 2>&1; then echo -e "🌐 Server: ${GREEN}● Running${NC} (Port $SERVER_PORT)" else echo -e "🌐 Server: ${RED}● Stopped${NC}" fi # OpenCode status if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q . && \ $CONTAINER_CMD exec "$CONTAINER_NAME" pgrep -f "opencode serve" > /dev/null 2>&1; then echo -e "🤖 OpenCode: ${GREEN}● Active${NC} (oh-my-opencode loaded)" else echo -e "🤖 OpenCode: ${RED}● Inactive${NC}" fi echo } # Main menu show_main_menu() { echo -e "${YELLOW}🎛️ Main Menu:${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "1) 🚀 Start Server - Launch OpenCode server" echo -e "2) 🛑 Stop Server - Shutdown server and container" echo -e "3) 🔄 Reload Configuration - Apply config changes" echo -e "4) 🔗 Attach to Server - Connect to running server" echo -e "5) 📝 Edit Configuration - Modify agent/model settings" echo -e "6) 📊 View Logs - Check server and container logs" echo -e "7) 🔍 Server Health Check - Test server connectivity" echo -e "8) 📚 Documentation - View project documentation" echo -e "9) ⚙️ System Information - Show system and version info" echo -e "0) 👋 Exit - Quit the application" echo } # Start server start_server() { echo -e "${BLUE}🚀 Starting OpenCode Server...${NC}" if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q .; then echo -e "${YELLOW}⚠️ Container is already running. Use reload if you made changes.${NC}" read -p "Press Enter to continue..." return fi echo -e "🏗️ Building and starting container..." if $CONTAINER_CMD compose -f "$DOCKER_DIR/docker-compose.yml" up -d --build; then echo -e "${GREEN}✅ Container started successfully!${NC}" echo -e "⏳ Waiting for server to initialize..." for i in {1..10}; do if curl -s -f --max-time 2 "$SERVER_URL" > /dev/null 2>&1; then echo -e "${GREEN}🎉 Server is ready at $SERVER_URL${NC}" echo echo -e "${CYAN}📋 Quick Start Commands:${NC}" echo -e " Attach locally: ${WHITE}opencode attach $SERVER_URL${NC}" echo -e " Attach remotely: ${WHITE}opencode attach http://YOUR_IP:$SERVER_PORT${NC}" break fi echo -n "." sleep 1 done if [ $i -eq 10 ]; then echo -e "${YELLOW}⚠️ Server may still be starting. Check status in main menu.${NC}" fi else echo -e "${RED}❌ Failed to start container. Check logs for details.${NC}" fi echo read -p "Press Enter to return to main menu..." } # Stop server stop_server() { echo -e "${BLUE}🛑 Stopping OpenCode Server...${NC}" if ! $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q .; then echo -e "${YELLOW}⚠️ Container is not running.${NC}" read -p "Press Enter to continue..." return fi if $CONTAINER_CMD compose -f "$DOCKER_DIR/docker-compose.yml" down; then echo -e "${GREEN}✅ Server stopped successfully!${NC}" else echo -e "${RED}❌ Failed to stop server properly.${NC}" fi echo read -p "Press Enter to return to main menu..." } # Reload configuration reload_config() { echo -e "${BLUE}🔄 Reloading Configuration...${NC}" if [ ! -f "$PROJECT_DIR/reload.sh" ]; then echo -e "${RED}❌ Reload script not found at $PROJECT_DIR/reload.sh${NC}" read -p "Press Enter to continue..." return fi echo -e "🔍 Analyzing changes..." bash "$PROJECT_DIR/reload.sh" echo read -p "Press Enter to return to main menu..." } # Attach to server attach_server() { echo -e "${BLUE}🔗 Attaching to OpenCode Server...${NC}" if ! curl -s -f --max-time 2 "$SERVER_URL" > /dev/null 2>&1; then echo -e "${RED}❌ Server is not running or not accessible.${NC}" echo -e "${YELLOW}💡 Start the server first using option 1.${NC}" read -p "Press Enter to continue..." return fi echo -e "${GREEN}✅ Server is accessible at $SERVER_URL${NC}" echo echo -e "${CYAN}Choose attachment method:${NC}" echo -e "1) Local terminal attachment" echo -e "2) Remote attachment (show command only)" echo -e "3) Back to main menu" echo read -p "Enter choice (1-3): " choice case $choice in 1) echo -e "${YELLOW}🔗 Attaching to server...${NC}" echo -e "${DIM}(Press Ctrl+C to detach)${NC}" sleep 2 opencode attach "$SERVER_URL" ;; 2) echo -e "${WHITE}📋 Remote attachment command:${NC}" echo -e "${CYAN}opencode attach $SERVER_URL${NC}" echo echo -e "${YELLOW}💡 Replace 'localhost' with your server's IP address for remote access.${NC}" ;; 3) return ;; *) echo -e "${RED}❌ Invalid choice.${NC}" ;; esac echo read -p "Press Enter to return to main menu..." } # Edit configuration edit_config() { echo -e "${BLUE}📝 Configuration Editor${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${CYAN}Available configuration files:${NC}" echo -e "1) oh-my-opencode.json - Agent and model settings" echo -e "2) opencode.jsonc - OpenCode core configuration" echo -e "3) Back to main menu" echo read -p "Enter choice (1-3): " choice case $choice in 1) if command -v nano &> /dev/null; then nano "$CONFIG_DIR/oh-my-opencode.json" elif command -v vim &> /dev/null; then vim "$CONFIG_DIR/oh-my-opencode.json" else echo -e "${YELLOW}⚠️ No editor found. Please edit manually:${NC}" echo -e "${WHITE}$CONFIG_DIR/oh-my-opencode.json${NC}" fi ;; 2) if command -v nano &> /dev/null; then nano "$CONFIG_DIR/opencode.jsonc" elif command -v vim &> /dev/null; then vim "$CONFIG_DIR/opencode.jsonc" else echo -e "${YELLOW}⚠️ No editor found. Please edit manually:${NC}" echo -e "${WHITE}$CONFIG_DIR/opencode.jsonc${NC}" fi ;; 3) return ;; *) echo -e "${RED}❌ Invalid choice.${NC}" ;; esac echo read -p "Press Enter to return to main menu..." } # View logs view_logs() { echo -e "${BLUE}📊 Log Viewer${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${CYAN}Choose log type:${NC}" echo -e "1) Container logs - Docker container output" echo -e "2) OpenCode logs - Server application logs" echo -e "3) Build logs - Last build/reload output" echo -e "4) Back to main menu" echo read -p "Enter choice (1-4): " choice case $choice in 1) echo -e "${YELLOW}🐳 Container Logs (last 50 lines):${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" $CONTAINER_CMD logs --tail 50 "$CONTAINER_NAME" 2>/dev/null || echo -e "${RED}❌ Container not running or logs unavailable${NC}" ;; 2) if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q .; then echo -e "${YELLOW}🤖 OpenCode Server Logs:${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" $CONTAINER_CMD exec "$CONTAINER_NAME" journalctl -u opencode -n 50 --no-pager 2>/dev/null || \ $CONTAINER_CMD exec "$CONTAINER_NAME" tail -50 /var/log/opencode.log 2>/dev/null || \ echo -e "${YELLOW}ℹ️ Logs not available through standard locations${NC}" else echo -e "${RED}❌ Container not running${NC}" fi ;; 3) echo -e "${YELLOW}🏗️ Build/Reload Logs:${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" if [ -f "$PROJECT_DIR/.last_reload" ]; then echo -e "${GREEN}Last reload: $(date -r "$PROJECT_DIR/.last_reload")${NC}" else echo -e "${YELLOW}No reload history found${NC}" fi ;; 4) return ;; *) echo -e "${RED}❌ Invalid choice.${NC}" ;; esac echo read -p "Press Enter to return to main menu..." } # Health check health_check() { echo -e "${BLUE}🔍 Server Health Check${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -n "🐳 Container status: " if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q .; then echo -e "${GREEN}● Running${NC}" else echo -e "${RED}● Stopped${NC}" fi echo -n "🌐 Server connectivity: " if curl -s -f --max-time 5 "$SERVER_URL" > /dev/null 2>&1; then echo -e "${GREEN}● Connected${NC}" response_time=$(curl -s -w "%{time_total}" -o /dev/null "$SERVER_URL" 2>/dev/null || echo "0") if (( $(echo "$response_time > 0" | bc -l 2>/dev/null || echo "0") )); then echo -e "⏱️ Response time: ${GREEN}${response_time}s${NC}" fi else echo -e "${RED}● Disconnected${NC}" fi echo -n "🔌 oh-my-opencode plugin: " if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q .; then # Check if server is responding (main indicator) if curl -s --max-time 2 "$SERVER_URL" > /dev/null 2>&1; then # Check for plugin-specific indicators in logs if $CONTAINER_CMD exec "$CONTAINER_NAME" sh -c "cat /var/log/opencode.log 2>/dev/null | grep -q 'oh-my-opencode'" 2>/dev/null; then echo -e "${GREEN}● Loaded${NC}" else echo -e "${YELLOW}● Basic server only${NC}" fi else echo -e "${RED}● Server not responding${NC}" fi else echo -e "${YELLOW}● N/A (container stopped)${NC}" fi if $CONTAINER_CMD ps -q -f name="$CONTAINER_NAME" | grep -q .; then echo echo -e "${CYAN}📊 Resource Usage:${NC}" $CONTAINER_CMD stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" "$CONTAINER_NAME" 2>/dev/null || echo -e "${YELLOW}Resource stats unavailable${NC}" fi echo read -p "Press Enter to return to main menu..." } # Documentation show_docs() { echo -e "${BLUE}📚 Documentation${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" if [ -f "$PROJECT_DIR/README.md" ]; then echo -e "${CYAN}Opening README.md...${NC}" if command -v less &> /dev/null; then less "$PROJECT_DIR/README.md" elif command -v more &> /dev/null; then more "$PROJECT_DIR/README.md" else cat "$PROJECT_DIR/README.md" fi else echo -e "${RED}❌ README.md not found${NC}" fi echo read -p "Press Enter to return to main menu..." } # System information show_system_info() { echo -e "${BLUE}⚙️ System Information${NC}" echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${CYAN}🖥️ Host System:${NC}" echo -e " OS: $(uname -s) $(uname -r)" echo -e " Shell: $SHELL" echo -e " User: $(whoami)" echo echo -e "${CYAN}🐳 Container Runtime:${NC}" echo -e " Engine: $CONTAINER_CMD" if command -v "$CONTAINER_CMD" &> /dev/null; then echo -e " Version: $($CONTAINER_CMD --version)" fi echo echo -e "${CYAN}📁 Project Information:${NC}" echo -e " Project: $PROJECT_DIR" echo -e " Config: $CONFIG_DIR" echo -e " Port: $SERVER_PORT" echo echo -e "${CYAN}🤖 OpenCode Status:${NC}" if command -v opencode &> /dev/null; then opencode --version 2>/dev/null || echo -e " Version: Unknown" else echo -e " Status: ${RED}Not installed${NC}" fi echo echo -e "${CYAN}📊 Configuration Summary:${NC}" if [ -f "$CONFIG_DIR/oh-my-opencode.json" ]; then agent_count=$(jq '.agents | length' "$CONFIG_DIR/oh-my-opencode.json" 2>/dev/null || echo "Unknown") echo -e " Agents: $agent_count configured" fi if [ -f "$CONFIG_DIR/opencode.jsonc" ]; then model_count=$(jq '.provider.opencode.models | length' "$CONFIG_DIR/opencode.jsonc" 2>/dev/null || echo "Unknown") echo -e " Models: $model_count available" fi echo read -p "Press Enter to return to main menu..." } # Main function main() { check_dependencies while true; do show_header show_status show_main_menu read -p "Enter your choice (0-9): " choice case $choice in 1) start_server ;; 2) stop_server ;; 3) reload_config ;; 4) attach_server ;; 5) edit_config ;; 6) view_logs ;; 7) health_check ;; 8) show_docs ;; 9) show_system_info ;; 0) echo -e "${GREEN}👋 Thank you for using Oh My OpenCode Free!${NC}" echo -e "${CYAN}Created by Oussama Douhou${NC}" echo -e "${DIM}oussama.douhou@gmail.com${NC}" echo -e "${CYAN}Happy coding! 🚀${NC}" exit 0 ;; *) echo -e "${RED}❌ Invalid choice. Please enter 0-9.${NC}" sleep 2 ;; esac done } # Run main function main