301 lines
11 KiB
Org Mode
301 lines
11 KiB
Org Mode
#+TITLE: Setup & Onboarding (setup.org)
|
|
#+AUTHOR: Amr
|
|
#+FILETAGS: :harness:setup:onboarding:
|
|
#+STARTUP: content
|
|
|
|
* Overview: The Zero-to-One Experience
|
|
The *Setup & Onboarding* process ensures that users can boot the ~org-agent~ Lisp Machine with zero friction. We follow the *Appliance Paradigm* for standard users (Docker-first) and provide a *Power User Path* (Baremetal) for those wanting deep native integration.
|
|
|
|
This file is a Literate Devops document. Tangling it generates the Docker configuration, the one-liner installation script, and the baremetal onboarding script.
|
|
|
|
* 1. The Appliance Paradigm (Docker First)
|
|
The easiest way to run the agent is via Docker. This prevents the user from having to manually manage SBCL, Quicklisp, Python virtual environments, Playwright binaries, and Java (for Signal).
|
|
|
|
** The Dockerfile
|
|
The container wraps all messy OS dependencies and pre-caches the Lisp environment for rapid booting.
|
|
|
|
#+begin_src dockerfile :tangle ../Dockerfile
|
|
# ORG-AGENT v1.0 Production Environment
|
|
FROM debian:bookworm-slim
|
|
|
|
# Prevent interactive prompts during build
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
|
|
# 1. Install System Dependencies
|
|
# - sbcl: The Lisp Runtime
|
|
# - curl/git/unzip: Standard tools for Quicklisp and binaries
|
|
# - default-jre: Required by signal-cli
|
|
# - python3/pip: Required for Playwright bridge
|
|
RUN apt-get update && apt-get install -y \
|
|
sbcl \
|
|
curl \
|
|
git \
|
|
unzip \
|
|
default-jre \
|
|
libsqlite3-0 \
|
|
python3 \
|
|
python3-pip \
|
|
python3-venv \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# 2. Setup Playwright (High-Fidelity Browsing)
|
|
RUN python3 -m venv /opt/venv
|
|
ENV PATH="/opt/venv/bin:$PATH"
|
|
RUN pip install playwright \
|
|
&& playwright install --with-deps chromium
|
|
|
|
# 3. Install signal-cli (v0.14.0)
|
|
ENV SIGNAL_CLI_VERSION=0.14.0
|
|
RUN curl -L https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/signal-cli-${SIGNAL_CLI_VERSION}-Linux.tar.gz | tar xz -C /opt \
|
|
&& ln -s /opt/signal-cli-${SIGNAL_CLI_VERSION}/bin/signal-cli /usr/local/bin/signal-cli
|
|
|
|
# 4. Install Quicklisp & Pin Distribution
|
|
# Pinned to 2026-04-01 for bit-rot resistance.
|
|
WORKDIR /root
|
|
RUN curl -O https://beta.quicklisp.org/quicklisp.lisp \
|
|
&& sbcl --non-interactive \
|
|
--load quicklisp.lisp \
|
|
--eval '(quicklisp-quickstart:install)' \
|
|
--eval '(ql-dist:install-dist "http://beta.quicklisp.org/dist/quicklisp/2026-04-01/distinfo.txt" :prompt nil :replace t)'
|
|
|
|
# 5. Configure SBCL to load Quicklisp on startup
|
|
RUN echo '(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))) (when (probe-file quicklisp-init) (load quicklisp-init)))' > /root/.sbclrc
|
|
|
|
# 6. Setup Application Directory
|
|
WORKDIR /app
|
|
COPY . /app/projects/org-agent
|
|
|
|
# 7. Pre-cache Lisp Dependencies
|
|
RUN sbcl --non-interactive \
|
|
--eval '(push #p"/app/projects/org-agent/" asdf:*central-registry*)' \
|
|
--eval '(ql:quickload :org-agent)'
|
|
|
|
# 8. Environment & Volumes
|
|
# The host's memex root should be mounted to /memex
|
|
ENV MEMEX_DIR=/memex
|
|
VOLUME ["/memex"]
|
|
|
|
# Default Ports
|
|
EXPOSE 9105 8080
|
|
|
|
# Entrypoint
|
|
CMD ["sbcl", "--non-interactive", \
|
|
"--eval", "(push #p\"/app/projects/org-agent/\" asdf:*central-registry*)", \
|
|
"--eval", "(ql:quickload :org-agent)", \
|
|
"--eval", "(org-agent:main)"]
|
|
#+end_src
|
|
|
|
** Docker Compose
|
|
#+begin_src yaml :tangle ../docker-compose.yml
|
|
services:
|
|
org-agent:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
container_name: org-agent
|
|
env_file: .env
|
|
volumes:
|
|
# Mount the entire memex directory (2 levels up from projects/org-agent)
|
|
- ../..:/memex
|
|
# Ensure signal-cli state is preserved
|
|
- signal-state:/root/.local/share/signal-cli
|
|
ports:
|
|
- "${ORG_AGENT_DAEMON_PORT:-9105}:9105"
|
|
- "${ORG_AGENT_WEB_PORT:-8080}:8080"
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
signal-state:
|
|
#+end_src
|
|
|
|
* 2. The Interactive One-Liner (install.sh)
|
|
This script is what the user pipes to ~bash~ from curl. It detects the OS, installs Docker if missing (after asking permission), clones the repo, interactively generates the ~.env~, and launches the container.
|
|
|
|
#+begin_src bash :tangle ../scripts/install.sh :shebang "#!/bin/bash"
|
|
set -e
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
YELLOW='\033[0;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
echo -e "${BLUE}==================================================${NC}"
|
|
echo -e "${BLUE} org-agent: Sovereign Intelligence Onboarding ${NC}"
|
|
echo -e "${BLUE}==================================================${NC}"
|
|
|
|
# --- OS & Docker Detection ---
|
|
echo -e "\n${BLUE}[1/4] Verifying Environment...${NC}"
|
|
|
|
command_exists() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
install_docker() {
|
|
echo -e "${YELLOW}Docker is required to run org-agent natively without messy dependencies.${NC}"
|
|
read -p "Would you like me to attempt to install Docker? [Y/n]: " install_choice
|
|
install_choice=${install_choice:-Y}
|
|
if [[ "$install_choice" =~ ^[Yy]$ ]]; then
|
|
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
if command_exists apt-get; then
|
|
echo "Installing Docker via apt..."
|
|
sudo apt-get update
|
|
sudo apt-get install -y docker.io docker-compose
|
|
elif command_exists dnf; then
|
|
echo "Installing Docker via dnf..."
|
|
sudo dnf install -y docker docker-compose
|
|
sudo systemctl start docker
|
|
sudo systemctl enable docker
|
|
else
|
|
echo -e "${RED}Unsupported package manager. Please install Docker manually.${NC}"
|
|
exit 1
|
|
fi
|
|
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
|
if command_exists brew; then
|
|
echo "Installing Docker Desktop via Homebrew..."
|
|
brew install --cask docker
|
|
echo -e "${YELLOW}Please start Docker Desktop from your Applications folder, then re-run this script.${NC}"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}Homebrew not found. Please install Docker Desktop for Mac manually.${NC}"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo -e "${RED}Unsupported OS for automated Docker installation. Please install manually.${NC}"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo -e "${RED}Docker is required. Aborting.${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
if ! command_exists docker || ! command_exists docker-compose; then
|
|
install_docker
|
|
else
|
|
echo -e "${GREEN}✓ Docker and docker-compose detected.${NC}"
|
|
fi
|
|
|
|
# --- Repository Setup ---
|
|
echo -e "\n${BLUE}[2/4] Downloading Kernel...${NC}"
|
|
MEMEX_DEFAULT="$HOME/memex"
|
|
read -p "Where is your Memex located? (default: $MEMEX_DEFAULT): " MEMEX_TARGET
|
|
MEMEX_TARGET=${MEMEX_TARGET:-$MEMEX_DEFAULT}
|
|
|
|
mkdir -p "$MEMEX_TARGET/projects"
|
|
cd "$MEMEX_TARGET/projects"
|
|
|
|
if [ ! -d "org-agent" ]; then
|
|
echo "Cloning org-agent..."
|
|
git clone https://github.com/gharbeia/org-agent.git
|
|
cd org-agent
|
|
else
|
|
echo -e "${GREEN}✓ Repository already exists.${NC}"
|
|
cd org-agent
|
|
git pull origin main
|
|
fi
|
|
|
|
# --- Interactive Configuration ---
|
|
echo -e "\n${BLUE}[3/4] Neural & Identity Calibration...${NC}"
|
|
if [ ! -f .env ]; then
|
|
cp .env.example .env
|
|
fi
|
|
|
|
# Ask for Name
|
|
read -p "What is your name? (default: User): " USER_NAME
|
|
USER_NAME=${USER_NAME:-User}
|
|
sed -i "s/MEMEX_USER=.*/MEMEX_USER=\"$USER_NAME\"/g" .env
|
|
|
|
# Ask for Assistant Name
|
|
read -p "What shall we name your Assistant? (default: Agent): " AGENT_NAME
|
|
AGENT_NAME=${AGENT_NAME:-Agent}
|
|
sed -i "s/MEMEX_ASSISTANT=.*/MEMEX_ASSISTANT=\"$AGENT_NAME\"/g" .env
|
|
|
|
# Ask for LLM
|
|
echo -e "\nSelect your primary neural provider:"
|
|
echo "1) Google Gemini (Free Tier / Official)"
|
|
echo "2) OpenRouter (Unified / Paid)"
|
|
echo "3) Anthropic (Claude / API Key)"
|
|
echo "4) OpenAI (GPT / API Key)"
|
|
read -p "Choice [1-4]: " LLM_CHOICE
|
|
|
|
case $LLM_CHOICE in
|
|
2) read -p "Enter OpenRouter API Key: " INPUT; sed -i "s/OPENROUTER_API_KEY=.*/OPENROUTER_API_KEY=\"$INPUT\"/g" .env ;;
|
|
3) read -p "Enter Anthropic API Key: " INPUT; sed -i "s/ANTHROPIC_API_KEY=.*/ANTHROPIC_API_KEY=\"$INPUT\"/g" .env ;;
|
|
4) read -p "Enter OpenAI API Key: " INPUT; sed -i "s/OPENAI_API_KEY=.*/OPENAI_API_KEY=\"$INPUT\"/g" .env ;;
|
|
*) read -p "Enter Gemini API Key: " INPUT; sed -i "s/GEMINI_API_KEY=.*/GEMINI_API_KEY=\"$INPUT\"/g" .env ;;
|
|
esac
|
|
|
|
# Seed Core Skills
|
|
echo -e "\n${BLUE}[4/4] Seeding Skills...${NC}"
|
|
# In Docker, the host's memex maps to /memex. The skills should be saved in the host's memex notes folder.
|
|
SKILLS_DIR="$MEMEX_TARGET/notes"
|
|
mkdir -p "$SKILLS_DIR"
|
|
cp -n skills/*.org "$SKILLS_DIR/" 2>/dev/null || true
|
|
echo -e "${GREEN}✓ Core skills seeded to $SKILLS_DIR.${NC}"
|
|
|
|
# Ensure proper ownership if sudo was used for apt
|
|
if [ -n "$SUDO_USER" ]; then
|
|
chown -R "$SUDO_USER" "$MEMEX_TARGET/projects/org-agent"
|
|
fi
|
|
|
|
echo -e "\n${GREEN}==================================================${NC}"
|
|
echo -e "${GREEN} Onboarding Complete! ${NC}"
|
|
echo -e "${GREEN} Booting your sovereign brain in the background...${NC}"
|
|
echo -e "${GREEN}==================================================${NC}"
|
|
|
|
# Launch
|
|
docker-compose up -d --build
|
|
echo -e "\n${YELLOW}To view logs, run: cd $MEMEX_TARGET/projects/org-agent && docker-compose logs -f${NC}"
|
|
#+end_src
|
|
|
|
* 3. The Power-User Path (Baremetal Onboarding)
|
|
For users who want to run the Lisp Machine natively on their host OS (typically Emacs users who want the agent to directly manipulate their local =.emacs.d=), we provide the baremetal setup script. This script verifies the host has SBCL and Quicklisp installed, and configures the paths natively.
|
|
|
|
#+begin_src bash :tangle ../scripts/onboard-baremetal.sh :shebang "#!/bin/bash"
|
|
set -e
|
|
RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; NC='\033[0m'
|
|
|
|
echo -e "${BLUE}=== org-agent: Baremetal Power-User Setup ===${NC}"
|
|
|
|
if ! command -v sbcl >/dev/null 2>&1; then
|
|
echo -e "${RED}✗ SBCL not found. Please install it first.${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -d "$HOME/quicklisp" ] && [ ! -d "$HOME/.quicklisp" ]; then
|
|
echo -e "${RED}✗ Quicklisp not found. Please install Quicklisp.${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f .env ]; then cp .env.example .env; fi
|
|
|
|
read -p "What is your name? (default: User): " USER_NAME
|
|
USER_NAME=${USER_NAME:-User}
|
|
sed -i "s/MEMEX_USER=.*/MEMEX_USER=\"$USER_NAME\"/g" .env
|
|
|
|
read -p "What shall we name your Assistant? (default: Agent): " AGENT_NAME
|
|
AGENT_NAME=${AGENT_NAME:-Agent}
|
|
sed -i "s/MEMEX_ASSISTANT=.*/MEMEX_ASSISTANT=\"$AGENT_NAME\"/g" .env
|
|
|
|
echo "Select primary neural provider:"
|
|
echo "1) Gemini"; echo "2) OpenRouter"; echo "3) Anthropic"; echo "4) OpenAI"
|
|
read -p "Choice [1-4]: " LLM_CHOICE
|
|
case $LLM_CHOICE in
|
|
2) read -p "Enter OpenRouter Key: " INPUT; sed -i "s/OPENROUTER_API_KEY=.*/OPENROUTER_API_KEY=\"$INPUT\"/g" .env ;;
|
|
3) read -p "Enter Anthropic Key: " INPUT; sed -i "s/ANTHROPIC_API_KEY=.*/ANTHROPIC_API_KEY=\"$INPUT\"/g" .env ;;
|
|
4) read -p "Enter OpenAI Key: " INPUT; sed -i "s/OPENAI_API_KEY=.*/OPENAI_API_KEY=\"$INPUT\"/g" .env ;;
|
|
*) read -p "Enter Gemini Key: " INPUT; sed -i "s/GEMINI_API_KEY=.*/GEMINI_API_KEY=\"$INPUT\"/g" .env ;;
|
|
esac
|
|
|
|
# Update baremetal paths based on current directory structure
|
|
PROJECT_ROOT=$(pwd)
|
|
PARENT_DIR=$(dirname "$PROJECT_ROOT")
|
|
sed -i "s|MEMEX_DIR=.*|MEMEX_DIR=\"$PARENT_DIR\"|g" .env
|
|
sed -i "s|ZETTELKASTEN_DIR=.*|ZETTELKASTEN_DIR=\"$PARENT_DIR/notes\"|g" .env
|
|
sed -i "s|SKILLS_DIR=.*|SKILLS_DIR=\"$PARENT_DIR/notes\"|g" .env
|
|
|
|
mkdir -p "$PARENT_DIR/notes"
|
|
cp -n skills/*.org "$PARENT_DIR/notes/" 2>/dev/null || true
|
|
|
|
echo -e "${GREEN}Baremetal setup complete. Run 'make run' to start.${NC}"
|
|
#+end_src
|