I built a self-hosted AI workspace for macOS — meet Odysee
Source: Dev.to
I’ve been running Odysseus as my daily-driver AI interface — local LLMs, document RAG, email triage, calendar sync. It’s a web-based FastAPI app that works great in the browser, but I wanted a proper macOS app I could launch from my dock and distribute as a DMG. So I built one. Here’s what the app does and how I packaged it. 💬 Multi-Provider Chat — LM Studio, Ollama, DeepSeek, SiliconFlow, Anthropic, OpenAI 🤖 AI Agents — Custom assistants with MCP tools, web browsing, shell access 📄 Document RAG — PDFs, Office files, Markdown with full-text + vector search 📧 Email Client — IMAP/SMTP with AI triage 📅 Calendar — CalDAV sync (Nextcloud, Apple, Fastmail) 🍳 Cookbook — Download and serve models via llama.cpp / vLLM 🔬 Deep Research — Multi-step source synthesis 🧠 Memory & Skills — Persistent vector memory, your agent learns over time
Layer Tool
Web framework FastAPI (Python 3.11)
Frontend Vanilla JS + CSS (PWA)
LLM providers LM Studio, Ollama, DeepSeek, SiliconFlow, OpenAI, Anthropic
Vector store ChromaDB + fastembed
Model serving llama.cpp / vLLM
macOS app Shell launcher + pywebview + DMG
The base project already had a build-macos-app.sh script, but I rewrote it to produce a proper .app bundle and .dmg that anyone can build: ./build-macos-app.sh
This creates two things:
- dist/Odysseus.app — A macOS app bundle. The executable is a shell script that: Starts the Python backend (uvicorn app:app) Launches ChromaDB for vector search Opens the UI in a chromeless window (via pywebview, or falls back to Chromium) Cleans up all services when you close the window
- dist/Odysseus.dmg — A drag-to-Applications disk image for distribution. The .app is just a launcher — no embedded Python, no bundled binary. It drives the venv in the repo directory. The key piece is scripts/app_window.py, which uses pywebview to open the web UI in a native macOS window — no browser chrome, feels like a real app. If pywebview isn’t available, it falls back to opening a chromeless Chromium window via —app=. Closing the window stops everything: the web server, ChromaDB, all child processes. The cleanup trap in the launcher script makes sure no stray Python processes are left behind. Odysee/ ├── app.py # FastAPI entry point ├── src/ # Core logic │ ├── llm_core.py # Multi-provider LLM abstraction │ ├── agent_loop.py # Agent execution loop │ ├── chat_handler.py # Chat processing │ ├── model_discovery.py # Auto-detect local models │ └── mcp_manager.py # MCP server management ├── routes/ # API endpoints ├── services/ # Background services ├── static/ # Frontend (JS/CSS/HTML) ├── scripts/ # CLI tools + app_window.py ├── tests/ # ~350 tests └── data/ # Created on first run
uvicorn app:app —host 127.0.0.1 —port 7860
The flask-style backend runs on localhost:7860. The frontend is vanilla JS with no build step — just open index.html and it works. git clone https://github.com/AlexDesign420/Odysee-MacOS-App.git cd Odysee-MacOS-App python3.11 -m venv venv source venv/bin/activate pip install -r requirements.txt python setup.py ./venv/bin/uvicorn app:app —host 127.0.0.1 —port 7860
Open http://127.0.0.1:7860. First boot creates an admin account. To build the macOS app: ./build-macos-app.sh open dist/Odysseus.dmg
GitHub: AlexDesign420/Odysee-MacOS-App