Files
oh-my-opencode-free-fork/tui.sh
Oussama Douhou 6b68c0a926 feat(logging): log full content - prompts, responses, tool I/O
- Log full message content instead of just hash
- Log full tool input arguments
- Log full tool output/results
- Add model info to session and token events

No privacy restrictions for internal monitoring.
2026-01-10 15:34:21 +01:00

547 lines
21 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Oh My OpenCode Free - Interactive TUI
# Beautiful terminal interface for managing the OpenCode server
# Created by: Oussama Douhou <oussama.douhou@gmail.com>
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