diff --git a/openclaw/README.txt b/openclaw/README.txt new file mode 100644 index 0000000..5b8fa1c --- /dev/null +++ b/openclaw/README.txt @@ -0,0 +1,126 @@ +OpenClaw - Personal AI Assistant +================================= + +OpenClaw is an open-source AI assistant that runs on your own server and +communicates via messaging platforms you already use. It connects to LLM +providers (Claude, GPT, etc.) and can interact via Telegram, email, WhatsApp, +Signal, Discord, and many more channels. + +Website: https://openclaw.ai/ +GitHub: https://github.com/openclaw/openclaw + +Prerequisites +------------- + +1. An Anthropic API key (https://console.anthropic.com/settings/keys) + and/or an OpenRouter API key (https://openrouter.ai/settings/keys) + +2. For Telegram: a bot token from @BotFather on Telegram + +3. For Gmail: a Gmail App Password (NOT your real password) + - Enable 2FA on your Google account + - Go to: https://myaccount.google.com/apppasswords + - Create an app password for "Mail" + +Configuration +------------- + +Required settings in service.env: + + ANTHROPIC_API_KEY - Anthropic API key (primary LLM provider) + OPENROUTER_API_KEY - OpenRouter API key (fallback provider) + At least one of these must be set. + +Optional channel settings: + + TELEGRAM_BOT_TOKEN - Telegram bot token from @BotFather + GMAIL_ADDRESS - Gmail address for email integration + GMAIL_APP_PASSWORD - Gmail App Password (16-character code) + +Security settings: + + DM_POLICY - "pairing" (default), "allowlist", or "open" + +Ports +----- + + GATEWAY_PORT (default: 18789) - Web dashboard, WebChat, and API + +Setup +----- + +1. Configure your service.env with API keys and channel credentials + +2. Install the service: + ds install openclaw + +3. Note the gateway token printed during installation - you need it + to access the web dashboard + +4. Open the web dashboard: + http://:18789 + +5. Paste the gateway token in Settings to authenticate + +Channel Setup +------------- + +TELEGRAM (automated): + Set TELEGRAM_BOT_TOKEN in service.env before install. + After install, send a message to your bot on Telegram. + +WEBCHAT (always available): + Access via the web dashboard at http://:18789 + +EMAIL / GMAIL (post-install): + After install, set up the email skill: + 1. docker exec openclaw skills install imap-smtp-email + 2. Configure in the Control UI with: + IMAP: imap.gmail.com:993 (TLS) + SMTP: smtp.gmail.com:587 (STARTTLS) + Username: your Gmail address + Password: your Gmail App Password + +WHATSAPP (interactive, post-install): + Requires scanning a QR code with your phone: + docker exec -it openclaw channels login + +SIGNAL (interactive, post-install): + Requires phone number verification: + docker exec -it openclaw channels add --channel signal + +Security Notes +-------------- + +- API keys are stored in openclaw.json inside the data directory +- Gmail App Passwords are scoped credentials, not your real password +- Telegram bot tokens only control the bot, not your Telegram account +- WhatsApp/Signal use session-based auth, no passwords stored +- Set DM_POLICY="pairing" (default) to require a pairing code + before the bot responds to new contacts +- The gateway token controls dashboard access - keep it secret + +Data Storage +------------ + +All data is stored in: ${DATA_PATH} + config/ - OpenClaw configuration (openclaw.json) + workspace/ - Agent workspace and session data + +The gateway token is saved in: ${DATA_PATH}/gateway.token + +Retrieving Your Gateway Token +------------------------------ + +If you lose your gateway token: + cat ${DATA_PATH}/gateway.token + +Or check the config file: + cat ${DATA_PATH}/config/openclaw.json | grep token + +More Information +---------------- + +Documentation: https://docs.openclaw.ai/ +Channel Guide: https://docs.openclaw.ai/gateway/configuration +Docker Guide: https://docs.openclaw.ai/install/docker diff --git a/openclaw/_volumes.sh b/openclaw/_volumes.sh new file mode 100755 index 0000000..c39558e --- /dev/null +++ b/openclaw/_volumes.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Define volume items for openclaw container +# These are used across backup, restore, create, and destroy operations + +get_openclaw_volumes() { + echo "path:data:${DATA_PATH}" +} diff --git a/openclaw/backup.sh b/openclaw/backup.sh new file mode 100755 index 0000000..edf28ed --- /dev/null +++ b/openclaw/backup.sh @@ -0,0 +1,24 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/_volumes.sh" +_check_required_env_vars "CONTAINER_NAME" "DATA_PATH" + +# Export variables for docker compose +export CONTAINER_NAME DATA_PATH GATEWAY_PORT +export IMAGE_REGISTRY IMAGE_REPO IMAGE_TAG +export ANTHROPIC_API_KEY OPENROUTER_API_KEY + +cd "$SCRIPT_DIR" || _die "Failed to change to script directory" + +# Stop containers for consistent backup +docker compose -p "${CONTAINER_NAME}" stop + +# Backup using dropshell's backup system +# shellcheck disable=SC2046 +backup_items $(get_openclaw_volumes) || _die "Failed to create backup" + +# Restart containers +docker compose -p "${CONTAINER_NAME}" start + +echo "Backup created successfully" diff --git a/openclaw/config/service.env b/openclaw/config/service.env new file mode 100644 index 0000000..9a84eab --- /dev/null +++ b/openclaw/config/service.env @@ -0,0 +1,66 @@ +# OpenClaw - Personal AI Assistant +# https://openclaw.ai/ + +CONTAINER_NAME=openclaw +SSH_USER="root" +IMAGE_TAG="latest" + +# Data path - stores OpenClaw config, workspace, and session data +DATA_PATH="/home/dropshell/openclaw" + +# Gateway port (web dashboard + WebChat + API) +GATEWAY_PORT=18789 + +# --------------------------------------------------------------------------- +# LLM Provider API Keys +# REQUIRED: At least one of these must be set +# --------------------------------------------------------------------------- + +# Anthropic API key (PRIMARY) - recommended for best results +# Get from: https://console.anthropic.com/settings/keys +ANTHROPIC_API_KEY= + +# OpenRouter API key (FALLBACK) - routes to multiple LLM providers +# Get from: https://openrouter.ai/settings/keys +OPENROUTER_API_KEY= + +# Primary model to use (format: provider/model-name) +DEFAULT_MODEL="anthropic/claude-sonnet-4-5" + +# Fallback model (used when primary is unavailable) +FALLBACK_MODEL="openrouter/anthropic/claude-sonnet-4-5" + +# --------------------------------------------------------------------------- +# Telegram Bot Channel +# --------------------------------------------------------------------------- + +# Create a bot via @BotFather on Telegram and paste the token here +# Leave empty to disable Telegram +# Steps: 1. Open Telegram, search @BotFather +# 2. Send /newbot, follow prompts +# 3. Copy the bot token +TELEGRAM_BOT_TOKEN= + +# --------------------------------------------------------------------------- +# Gmail Email Integration (IMAP/SMTP) +# --------------------------------------------------------------------------- + +# Uses a Gmail App Password - NOT your real Gmail password! +# Steps: 1. Enable 2FA on your Google account +# 2. Go to: https://myaccount.google.com/apppasswords +# 3. Create an app password for "Mail" +# 4. Paste the 16-character app password below +GMAIL_ADDRESS= +GMAIL_APP_PASSWORD= + +# --------------------------------------------------------------------------- +# Security Settings +# --------------------------------------------------------------------------- + +# DM policy for messaging channels +# "pairing" - require a pairing code before responding (most secure) +# "allowlist" - only respond to approved senders +# "open" - respond to anyone (least secure) +DM_POLICY="pairing" + +TEMPLATE=openclaw diff --git a/openclaw/destroy.sh b/openclaw/destroy.sh new file mode 100755 index 0000000..bcdb19b --- /dev/null +++ b/openclaw/destroy.sh @@ -0,0 +1,21 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/_volumes.sh" +_check_required_env_vars "CONTAINER_NAME" "DATA_PATH" + +# Export variables for docker compose +export CONTAINER_NAME DATA_PATH GATEWAY_PORT +export IMAGE_REGISTRY IMAGE_REPO IMAGE_TAG +export ANTHROPIC_API_KEY OPENROUTER_API_KEY + +cd "$SCRIPT_DIR" || _die "Failed to change to script directory" + +# Stop and remove containers +docker compose -p "${CONTAINER_NAME}" down + +# Destroy data +# shellcheck disable=SC2046 +destroy_items $(get_openclaw_volumes) || _die "Failed to destroy data" + +echo "Destroyed ${CONTAINER_NAME} and all data" diff --git a/openclaw/docker-compose.yml b/openclaw/docker-compose.yml new file mode 100644 index 0000000..a7f629f --- /dev/null +++ b/openclaw/docker-compose.yml @@ -0,0 +1,19 @@ +services: + openclaw: + image: ${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG} + container_name: ${CONTAINER_NAME} + restart: unless-stopped + ports: + - "${GATEWAY_PORT}:18789" + volumes: + - ${DATA_PATH}/config:/home/node/.openclaw + - ${DATA_PATH}/workspace:/home/node/workspace + environment: + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + OPENROUTER_API_KEY: ${OPENROUTER_API_KEY} + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:18789/healthz"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s diff --git a/openclaw/install-pre.sh b/openclaw/install-pre.sh new file mode 100755 index 0000000..cf17a0e --- /dev/null +++ b/openclaw/install-pre.sh @@ -0,0 +1,13 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +_check_required_env_vars "CONTAINER_NAME" + +export CONTAINER_NAME DATA_PATH GATEWAY_PORT +export IMAGE_REGISTRY IMAGE_REPO IMAGE_TAG +export ANTHROPIC_API_KEY OPENROUTER_API_KEY + +cd "$SCRIPT_DIR" || _die "Failed to change to script directory" +docker compose -p "${CONTAINER_NAME}" pull || echo "Warning: pre-pull failed, install.sh will retry" + +echo "Pre-install complete" diff --git a/openclaw/install.sh b/openclaw/install.sh new file mode 100755 index 0000000..36047f7 --- /dev/null +++ b/openclaw/install.sh @@ -0,0 +1,143 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +_check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" "DATA_PATH" "GATEWAY_PORT" + +# Require at least one LLM provider +if [ -z "$ANTHROPIC_API_KEY" ] && [ -z "$OPENROUTER_API_KEY" ]; then + _die "At least one LLM API key must be set (ANTHROPIC_API_KEY or OPENROUTER_API_KEY) in config/service.env" +fi + +_check_docker_installed || _die "Docker test failed, aborting installation..." + +# Create data directories +mkdir -p "${DATA_PATH}/config" +mkdir -p "${DATA_PATH}/workspace" + +# Set ownership to node user (uid 1000) used by the OpenClaw container +chown -R 1000:1000 "${DATA_PATH}" + +# Generate gateway token if one doesn't already exist +TOKEN_FILE="${DATA_PATH}/gateway.token" +if [ -f "$TOKEN_FILE" ]; then + GATEWAY_TOKEN=$(cat "$TOKEN_FILE") +else + GATEWAY_TOKEN=$(openssl rand -hex 32) + echo -n "$GATEWAY_TOKEN" > "$TOKEN_FILE" + chown 1000:1000 "$TOKEN_FILE" +fi + +# Generate openclaw.json configuration +CONFIG_FILE="${DATA_PATH}/config/openclaw.json" + +# Build model configuration +MODEL_PRIMARY="${DEFAULT_MODEL:-anthropic/claude-sonnet-4-5}" +MODEL_FALLBACK="${FALLBACK_MODEL:-openrouter/anthropic/claude-sonnet-4-5}" + +# Build channels section +TELEGRAM_CONFIG="" +if [ -n "$TELEGRAM_BOT_TOKEN" ]; then + TELEGRAM_CONFIG=$(cat < "$CONFIG_FILE" </dev/null || true + +# Start containers +docker compose -p "${CONTAINER_NAME}" up -d || _die "Failed to start containers" + +echo "" +echo "==========================================" +echo "OpenClaw installation complete!" +echo "==========================================" +echo "" +echo "Gateway Token (save this!):" +echo " ${GATEWAY_TOKEN}" +echo "" +echo "Web Dashboard:" +echo " http://localhost:${GATEWAY_PORT}" +echo " Paste the gateway token above into Settings to authenticate." +echo "" +if [ -n "$TELEGRAM_BOT_TOKEN" ]; then + echo "Telegram: Enabled - send a message to your bot to start!" +fi +if [ -n "$GMAIL_ADDRESS" ] && [ -n "$GMAIL_APP_PASSWORD" ]; then + echo "" + echo "Gmail: To enable email, install the IMAP skill after the gateway is running:" + echo " docker exec ${CONTAINER_NAME} openclaw skills install imap-smtp-email" + echo " Then configure it in the Control UI (http://localhost:${GATEWAY_PORT})" + echo " IMAP: imap.gmail.com:993 (TLS) | SMTP: smtp.gmail.com:587 (STARTTLS)" + echo " Username: ${GMAIL_ADDRESS}" + echo " Password: (your app password from service.env)" +fi +echo "" +echo "WebChat: Always available at http://localhost:${GATEWAY_PORT}" +echo "" +echo "To add WhatsApp (interactive, requires QR code):" +echo " docker exec -it ${CONTAINER_NAME} openclaw channels login" +echo "" +echo "Check status: ds status openclaw" +echo "View logs: ds logs openclaw" diff --git a/openclaw/logs.sh b/openclaw/logs.sh new file mode 100755 index 0000000..d0e90cf --- /dev/null +++ b/openclaw/logs.sh @@ -0,0 +1,12 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +_check_required_env_vars "CONTAINER_NAME" + +# Export variables for docker compose +export CONTAINER_NAME DATA_PATH GATEWAY_PORT +export IMAGE_REGISTRY IMAGE_REPO IMAGE_TAG +export ANTHROPIC_API_KEY OPENROUTER_API_KEY + +cd "$SCRIPT_DIR" || _die "Failed to change to script directory" +docker compose -p "${CONTAINER_NAME}" logs "$@" diff --git a/openclaw/ports.sh b/openclaw/ports.sh new file mode 100755 index 0000000..24b152e --- /dev/null +++ b/openclaw/ports.sh @@ -0,0 +1,5 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "GATEWAY_PORT" + +echo "$GATEWAY_PORT" diff --git a/openclaw/restore.sh b/openclaw/restore.sh new file mode 100755 index 0000000..0fc19cd --- /dev/null +++ b/openclaw/restore.sh @@ -0,0 +1,35 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/_volumes.sh" +_check_required_env_vars "CONTAINER_NAME" "DATA_PATH" + +# Export variables for docker compose +export CONTAINER_NAME DATA_PATH GATEWAY_PORT +export IMAGE_REGISTRY IMAGE_REPO IMAGE_TAG +export ANTHROPIC_API_KEY OPENROUTER_API_KEY + +cd "$SCRIPT_DIR" || _die "Failed to change to script directory" + +# Stop and remove containers before restore +docker compose -p "${CONTAINER_NAME}" down + +# Restore files using dropshell's restore system +# shellcheck disable=SC2046 +restore_items $(get_openclaw_volumes) || _die "Failed to restore data from backup file" + +# Fix ownership after restore +chown -R 1000:1000 "${DATA_PATH}" + +# Start containers +docker compose -p "${CONTAINER_NAME}" up -d + +echo "Restore complete! Service is running." +echo "" +echo "Your gateway token:" +if [ -f "${DATA_PATH}/gateway.token" ]; then + cat "${DATA_PATH}/gateway.token" + echo "" +else + echo " (check ${DATA_PATH}/config/openclaw.json)" +fi diff --git a/openclaw/start.sh b/openclaw/start.sh new file mode 100755 index 0000000..9b2cb90 --- /dev/null +++ b/openclaw/start.sh @@ -0,0 +1,14 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +_check_required_env_vars "CONTAINER_NAME" "DATA_PATH" + +# Export variables for docker compose +export CONTAINER_NAME DATA_PATH GATEWAY_PORT +export IMAGE_REGISTRY IMAGE_REPO IMAGE_TAG +export ANTHROPIC_API_KEY OPENROUTER_API_KEY + +cd "$SCRIPT_DIR" || _die "Failed to change to script directory" +docker compose -p "${CONTAINER_NAME}" up -d || _die "Failed to start containers" + +echo "Container ${CONTAINER_NAME} started" diff --git a/openclaw/status.sh b/openclaw/status.sh new file mode 100755 index 0000000..6510fb4 --- /dev/null +++ b/openclaw/status.sh @@ -0,0 +1,25 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" + +if ! docker ps -a --format "{{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then + echo "Unknown" + exit 0 +fi + +STATE=$(docker inspect -f '{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null) +case "$STATE" in + running) + if docker inspect -f '{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null | grep -q "unhealthy"; then + echo "Error" + else + echo "Running" + fi + ;; + exited|stopped) + echo "Stopped" + ;; + *) + echo "Unknown" + ;; +esac diff --git a/openclaw/stop.sh b/openclaw/stop.sh new file mode 100755 index 0000000..5778224 --- /dev/null +++ b/openclaw/stop.sh @@ -0,0 +1,14 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +_check_required_env_vars "CONTAINER_NAME" + +# Export variables for docker compose +export CONTAINER_NAME DATA_PATH GATEWAY_PORT +export IMAGE_REGISTRY IMAGE_REPO IMAGE_TAG +export ANTHROPIC_API_KEY OPENROUTER_API_KEY + +cd "$SCRIPT_DIR" || _die "Failed to change to script directory" +docker compose -p "${CONTAINER_NAME}" stop || _die "Failed to stop containers" + +echo "Container ${CONTAINER_NAME} stopped" diff --git a/openclaw/template_info.env b/openclaw/template_info.env new file mode 100644 index 0000000..6ed384c --- /dev/null +++ b/openclaw/template_info.env @@ -0,0 +1,14 @@ +# DO NOT EDIT THIS FILE FOR YOUR SERVICE! +# This file is replaced from the template whenever there is an update. +# Edit the service.env file to make changes. + +# Template to use - always required! +TEMPLATE=openclaw +REQUIRES_HOST_ROOT=false +REQUIRES_DOCKER=true +REQUIRES_DOCKER_ROOT=false + +# Docker image settings +IMAGE_REGISTRY="ghcr.io" +IMAGE_REPO="openclaw/openclaw" +IMAGE_TAG="latest" diff --git a/openclaw/uninstall.sh b/openclaw/uninstall.sh new file mode 100755 index 0000000..7db1e29 --- /dev/null +++ b/openclaw/uninstall.sh @@ -0,0 +1,17 @@ +#!/bin/bash +source "${AGENT_PATH}/common.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +_check_required_env_vars "CONTAINER_NAME" + +# Export variables for docker compose +export CONTAINER_NAME DATA_PATH GATEWAY_PORT +export IMAGE_REGISTRY IMAGE_REPO IMAGE_TAG +export ANTHROPIC_API_KEY OPENROUTER_API_KEY + +cd "$SCRIPT_DIR" || _die "Failed to change to script directory" + +# Stop and remove containers (but preserve data) +docker compose -p "${CONTAINER_NAME}" down || _die "Failed to stop containers" + +echo "Uninstalled ${CONTAINER_NAME} (data preserved in ${DATA_PATH})" +echo "Note: Your gateway token and configuration are still in ${DATA_PATH}"