Files
jarvis-ai/setup_wayland_jarvis.sh

333 lines
14 KiB
Bash
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.
#!/bin/bash
# Abbrechen bei Fehlern
set -e
echo "===================================================="
echo "🚀 Starte J.A.R.V.I.S. Desktop OS - Pure Local Setup"
echo "===================================================="
# Benutzererkennung
REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || whoami)}"
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
if [ -z "$REAL_USER" ] || [ "$REAL_USER" = "root" ]; then
REAL_USER=$(id -nu 1000 2>/dev/null || echo "meik")
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
fi
JARVIS_DIR="$REAL_HOME/jarvis-ai"
# 1. System aktualisieren & Basispakete installieren
echo "📦 Aktualisiere Paketquellen und installiere Systemkomponenten..."
sudo apt update
sudo apt install -y labwc firefox-esr curl wget git sudo python3 python3-pip python3-venv original-awk tilix geany waybar fonts-noto-color-emoji wofi
# 1.1 Gruppenrechte für Grafik und Eingabe
echo "👥 Füge Benutzer '$REAL_USER' zu den Grafik- und Input-Gruppen hinzu..."
sudo usermod -aG video,render,input "$REAL_USER"
# 2. Architektur erkennen und wdotool installieren
ARCH=$(uname -m)
echo "🔍 Erkannte Systemarchitektur: $ARCH"
if [ "$ARCH" = "x86_64" ]; then
wget https://github.com/cushycush/wdotool/releases/download/v0.5.3/wdotool_0.5.3-1_amd64.deb -O /tmp/wdotool.deb
sudo apt install -y /tmp/wdotool.deb
rm /tmp/wdotool.deb
elif [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/cushycush/wdotool/releases/download/v0.5.3/wdotool-installer.sh | sh
else
echo "❌ Unbekannte Architektur: $ARCH. Installation abgebrochen."
exit 1
fi
# 3. Den realen Installationspfad von wdotool ermitteln
echo "🛠️ Konfiguriere globalen wdotool-Wrapper..."
if [ -f "/usr/bin/wdotool" ]; then REAL_WDOTOOL="/usr/bin/wdotool"
elif [ -f "$REAL_HOME/.cargo/bin/wdotool" ]; then REAL_WDOTOOL="$REAL_HOME/.cargo/bin/wdotool"
elif [ -f "$REAL_HOME/.local/bin/wdotool" ]; then REAL_WDOTOOL="$REAL_HOME/.local/bin/wdotool"
else REAL_WDOTOOL=$(which wdotool || true); fi
if [ -z "$REAL_WDOTOOL" ]; then echo "❌ Fehler: wdotool nicht gefunden!"; exit 1; fi
sudo mv "$REAL_WDOTOOL" /usr/local/bin/wdotool.real
# 4. wdotool Wrapper erstellen
sudo cat << 'EOF' | sudo tee /usr/local/bin/wdotool > /dev/null
#!/bin/bash
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export WAYLAND_DISPLAY=wayland-0
exec /usr/local/bin/wdotool.real "$@"
EOF
sudo chmod +x /usr/local/bin/wdotool
# 5. Desktop-Konfiguration (labwc & environment)
echo "📂 Konfiguriere labwc Autostart und Tastaturlayout..."
mkdir -p "$REAL_HOME/.config/labwc"
cat << 'EOF' > "$REAL_HOME/.config/labwc/environment"
XKB_DEFAULT_LAYOUT=de
EOF
# 6. Auto-Start in .profile eintragen
echo "⚙️ Richte .profile für Wayland Auto-Start ein..."
if ! grep -q "labwc" "$REAL_HOME/.profile"; then
cat << 'EOF' >> "$REAL_HOME/.profile"
if [ "$(tty)" = "/dev/tty1" ]; then
export WLR_RENDERER=pixman
export WLR_NO_HARDWARE_CURSORS=1
export LIBGL_ALWAYS_SOFTWARE=1
dbus-run-session labwc > ~/labwc.log 2>&1
logout
fi
EOF
fi
# 7. Waybar & Wofi Powermenu
echo "📊 Konfiguriere Waybar und Wofi Power-Menü..."
mkdir -p "$REAL_HOME/.config/waybar"
cat << 'EOF' > "$REAL_HOME/.config/waybar/config"
{
"layer": "top", "position": "bottom", "height": 34,
"modules-left": ["custom/terminal", "custom/browser", "wlr/taskbar"],
"modules-right": ["clock", "custom/logout"],
"custom/terminal": { "format": "📁 Term", "on-click": "tilix", "tooltip": false },
"custom/browser": { "format": "🌐 Web", "on-click": "MOZ_WEBRENDER=software MOZ_ENABLE_WAYLAND=1 firefox-esr", "tooltip": false },
"wlr/taskbar": { "format": "{icon}", "icon-size": 16, "on-click": "activate", "on-click-right": "minimize" },
"clock": { "format": "🕒 {:%H:%M:%S}", "interval": 1 },
"custom/logout": { "format": "⚙️ System ", "on-click": "~/.config/labwc/powermenu.sh", "tooltip": false }
}
EOF
cat << 'EOF' > "$REAL_HOME/.config/waybar/style.css"
* { font-family: sans-serif; font-size: 12px; }
window#waybar { background-color: #1e293b; color: white; border-top: 1px solid #334155; }
#custom-terminal, #custom-browser { padding: 0 8px; background: #334155; margin: 3px 2px; border-radius: 3px; }
#custom-terminal:hover, #custom-browser:hover { background: #475569; }
#taskbar button { padding: 0 10px; color: #94a3b8; }
#taskbar button.active { background-color: #0f172a; color: #38bdf8; }
#clock { padding: 0 15px; background-color: #0f172a; }
#custom-logout { padding: 0 12px; background-color: #ef4444; color: white; }
#custom-logout:hover { background-color: #dc2626; }
EOF
cat << 'EOF' > "$REAL_HOME/.config/labwc/powermenu.sh"
#!/bin/bash
OPTIONS="🚪 Abmelden\n🔄 Neu starten\n🛑 Herunterfahren"
CHOICE=$(echo -e "$OPTIONS" | wofi --dmenu --prompt "Systemaktion wählen:" --width 280 --height 180 --style "$HOME/.config/labwc/wofi-power.css")
case "$CHOICE" in
*"Abmelden") labwc --exit ;;
*"Neu starten") sudo systemctl reboot ;;
*"Herunterfahren") sudo systemctl poweroff ;;
esac
EOF
chmod +x "$REAL_HOME/.config/labwc/powermenu.sh"
cat << 'EOF' > "$REAL_HOME/.config/labwc/wofi-power.css"
window { background-color: #1e293b; color: white; border: 2px solid #334155; border-radius: 8px; font-family: sans-serif; }
#entry { padding: 8px; color: white; }
#entry:selected { background-color: #334155; color: #38bdf8; }
#input { background-color: #0f172a; color: white; border: 1px solid #334155; margin: 5px; }
EOF
echo "%sudo ALL=(ALL) NOPASSWD: /usr/bin/systemctl poweroff, /usr/bin/systemctl reboot" | sudo tee /etc/sudoers.d/powermenu > /dev/null
sudo chmod 0440 /etc/sudoers.d/powermenu
# ===================================================================
# 8. J.A.R.V.I.S. REIN LOKALE INSTALLATION
# ===================================================================
echo "🧠 Installiere J.A.R.V.I.S. AI Desktop Backend..."
mkdir -p "$JARVIS_DIR"/{config,workspace}
# J.A.R.V.I.S. Requirements
cat << 'EOF' > "$JARVIS_DIR/requirements.txt"
openai
google-genai
python-dotenv
EOF
# J.A.R.V.I.S. .env Template
cat << 'EOF' > "$JARVIS_DIR/config/.env"
WEB_USER_NAME=Meik
AI_PROVIDER=nvidia
GOOGLE_API_KEY=dein-google-key
NVIDIA_API_KEY=dein-nvidia-key
NVIDIA_MODEL=moonshotai/kimi-k2.5
GOOGLE_MODEL=gemini-2.5-flash
OLLAMA_BASE_URL=http://127.0.0.1:11434/v1
OLLAMA_MODEL=llama3
EOF
# J.A.R.V.I.S. Optimierter System-Prompt inkl. wdotool-Handbuch
cat << 'EOF' > "$JARVIS_DIR/config/system_prompt.txt"
Du bist J.A.R.V.I.S., ein KI-Systemassistent, der direkt auf einem Debian Wayland-Desktop läuft. Du hast vollen lokalen Zugriff auf das System.
UMGEBUNG & GEDÄCHTNIS:
- Arbeitsverzeichnis: {workspace_dir}
- Notizen: {notes_file}
- Todos: {todo_file}
DESKTOP STEUERUNG & WIDOTOOL HANDBUCH:
Um die grafische Oberfläche (Wayland/labwc) zu steuern, nutzt du das Tool 'wdotool'. Hier ist deine Befehlsreferenz:
- Tastatur: wdotool key ctrl+l, wdotool key alt+Tab, wdotool type "Hallo", wdotool type --delay 30 "Langsam tippen"
- Maus: wdotool mousemove 500 400 (absolut), wdotool click 1 (1=links, 3=rechts), wdotool scroll 0 3 (runter scrollen)
- Fenster-Management:
wdotool search --name "Firefox" (liefert die Fenster-<id>)
wdotool search --class tilix (liefert die <id> basierend auf der App-ID)
wdotool getactivewindow (liefert <id> des aktuellen Fokus-Fensters)
wdotool windowactivate <id> (holt das Fenster in den Vordergrund)
wdotool windowclose <id> (schließt das Fenster)
WICHTIGE REGEL: Wenn du eine Systemaktion ausführen möchtest, umschließe den Linux-Bash-Befehl EXAKT mit <EXECUTE> und </EXECUTE>.
Beispiel 1 (Fenster in den Vordergrund holen): <EXECUTE>wdotool search --name "Firefox" | xargs wdotool windowactivate</EXECUTE>
Beispiel 2 (Tastenkürzel): <EXECUTE>wdotool key ctrl+c</EXECUTE>
Du darfst mehrere Befehle mit && oder Pipes (|) verketten.
Schreibe immer eine kurze Textantwort dazu, was du gerade tust. Du duzt {user_name} konsequent, dein Tonfall ist locker und technisch versiert.
EOF
# J.A.R.V.I.S. Python Backend (Kein SQLite, schlanker Regex)
cat << 'EOF' > "$JARVIS_DIR/jarvis.py"
import os, re, asyncio, subprocess, openai
from google import genai
from google.genai import types
from datetime import datetime
from pathlib import Path
from dotenv import load_dotenv
BASE_DIR = Path(__file__).resolve().parent
CONFIG_DIR = BASE_DIR / "config"
WORKSPACE_DIR = BASE_DIR / "workspace"
ENV_FILE = CONFIG_DIR / ".env"
load_dotenv(ENV_FILE)
NOTES_FILE = WORKSPACE_DIR / "NOTIZEN.md"
TODO_FILE = WORKSPACE_DIR / "TODO.md"
WEB_USER_NAME = os.getenv("WEB_USER_NAME", "Meik")
for f in [NOTES_FILE, TODO_FILE]:
if not f.exists(): f.write_text(f"# {f.name}\nHier fängt dein Gedächtnis an, J.A.R.V.I.S.\n", encoding="utf-8")
AI_PROVIDER = os.getenv("AI_PROVIDER", "google").lower()
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "")
NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY", "")
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://127.0.0.1:11434/v1")
GOOGLE_MODEL = os.getenv("GOOGLE_MODEL", "gemini-2.5-flash")
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o")
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3")
NVIDIA_MODEL = os.getenv("NVIDIA_MODEL", "moonshotai/kimi-k2.6")
def get_system_prompt():
prompt_path = CONFIG_DIR / "system_prompt.txt"
prompt = prompt_path.read_text(encoding="utf-8") if prompt_path.exists() else f"Hallo {WEB_USER_NAME}."
prompt = prompt.replace("{user_name}", WEB_USER_NAME)
prompt = prompt.replace("{workspace_dir}", str(WORKSPACE_DIR))
prompt = prompt.replace("{notes_file}", str(NOTES_FILE))
prompt = prompt.replace("{todo_file}", str(TODO_FILE))
return prompt
async def get_ai_response(user_msg, system_prompt, history_list):
try:
if AI_PROVIDER in ["openai", "ollama", "nvidia"]:
messages = [{"role": "system", "content": system_prompt}] + history_list
if AI_PROVIDER == "ollama": url, key, model = OLLAMA_BASE_URL.rstrip('/') + '/v1', "ollama", OLLAMA_MODEL
elif AI_PROVIDER == "nvidia": url, key, model = "https://integrate.api.nvidia.com/v1", NVIDIA_API_KEY, NVIDIA_MODEL
else: url, key, model = None, OPENAI_API_KEY, OPENAI_MODEL
client = openai.AsyncOpenAI(base_url=url, api_key=key)
response = await client.chat.completions.create(model=model, messages=messages)
return response.choices[0].message.content
elif AI_PROVIDER == "google":
client = genai.Client(api_key=GOOGLE_API_KEY)
google_history = [types.Content(role="user" if msg["role"] == "user" else "model", parts=[types.Part.from_text(text=msg["content"])]) for msg in history_list[:-1]]
chat = client.chats.create(model=GOOGLE_MODEL, config=types.GenerateContentConfig(system_instruction=system_prompt), history=google_history)
return chat.send_message(user_msg).text
except Exception as e: return f"Fehler bei der KI-Anfrage: {e}"
async def run_task(cmd):
print(f"\n⚙ J.A.R.V.I.S. führt aus: {cmd}")
try:
proc = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
stdout, _ = await proc.communicate()
output = stdout.decode('utf-8', errors='ignore').strip()
print(f"💻 Output:\n{output or '✅ Ohne Ausgabe beendet.'}\n")
return output
except Exception as e: return f"❌ Fehler: {e}"
async def listen_to_user():
return await asyncio.to_thread(input, "\nDu: ")
async def speak_to_user(text):
print(f"\n🤖 J.A.R.V.I.S.:\n{text}")
async def main_chat_loop():
print("====================================================")
print("🤖 J.A.R.V.I.S. Desktop Terminal geladen.")
print(f"Provider: {AI_PROVIDER.upper()}")
print("====================================================")
chat_history = []
while True:
user_msg = await listen_to_user()
if user_msg.lower().strip() in ['exit', 'quit']: break
if not user_msg.strip(): continue
now = datetime.now().strftime("%d.%m.%Y %H:%M")
chat_history.append({"role": "user", "content": user_msg, "timestamp": now})
ai_response = await get_ai_response(user_msg, get_system_prompt(), chat_history)
# EXAKT nach deinem neuen Prompt-Format matchen
commands = re.findall(r'<EXECUTE>(.*?)</EXECUTE>', ai_response, re.I | re.S)
clean_msg = re.sub(r'<EXECUTE>.*?</EXECUTE>', '', ai_response, flags=re.I | re.S).strip()
if clean_msg:
await speak_to_user(clean_msg)
chat_history.append({"role": "assistant", "content": clean_msg, "timestamp": now})
if commands:
for cmd in commands:
output = await run_task(cmd.strip())
sys_now = datetime.now().strftime("%d.%m.%Y %H:%M")
chat_history.append({"role": "user", "content": f"[SYSTEM] Befehl '{cmd}' abgeschlossen. Output:\n{output}", "timestamp": sys_now})
if len(chat_history) > 20: chat_history = chat_history[-20:]
if __name__ == "__main__":
try: asyncio.run(main_chat_loop())
except KeyboardInterrupt: print("\nJ.A.R.V.I.S. hart beendet.")
EOF
# J.A.R.V.I.S. Start-Skript
cat << 'EOF' > "$JARVIS_DIR/start.sh"
#!/bin/bash
cd "$(dirname "$0")"
source venv/bin/activate
python3 jarvis.py
EOF
chmod +x "$JARVIS_DIR/start.sh"
# Rechte korrigieren
chown -R "$REAL_USER:$REAL_USER" "$JARVIS_DIR"
chown -R "$REAL_USER:$REAL_USER" "$REAL_HOME/.config"
chown "$REAL_USER:$REAL_USER" "$REAL_HOME/.profile"
# Python venv installieren
echo "🐍 Erstelle Python-Umgebung für J.A.R.V.I.S...."
sudo -u "$REAL_USER" bash -c "cd $JARVIS_DIR && python3 -m venv venv && ./venv/bin/pip install --upgrade pip && ./venv/bin/pip install -r requirements.txt"
# Autostart (Waybar & JARVIS direkt in Tilix öffnen)
cat << EOF > "$REAL_HOME/.config/labwc/autostart"
waybar &
tilix --title="J.A.R.V.I.S. Terminal" -e "$JARVIS_DIR/start.sh" &
EOF
chmod +x "$REAL_HOME/.config/labwc/autostart"
chown "$REAL_USER:$REAL_USER" "$REAL_HOME/.config/labwc/autostart"
echo "===================================================="
echo "✅ Lokales Setup erfolgreich abgeschlossen!"
echo "👉 1. Trage deine API-Keys in $JARVIS_DIR/config/.env ein."
echo "👉 2. Starte das System neu oder logge dich neu ein."
echo "===================================================="