From 58e09e2f3b00da0fdcd4093e52794137428d1b8f Mon Sep 17 00:00:00 2001 From: mrbeandev Date: Tue, 17 Feb 2026 12:43:56 +0530 Subject: [PATCH] feat(deploy): add dedicated Coolify deployment files and guide --- COOLIFY.md | 326 +++++++++++++++++++++++++++++++++++++ Dockerfile.coolify | 41 +++++ README.md | 29 +++- docker-compose-coolify.yml | 167 +++++++++++++++++++ entrypoint-coolify.sh | 170 +++++++++++++++++++ 5 files changed, 730 insertions(+), 3 deletions(-) create mode 100644 COOLIFY.md create mode 100644 Dockerfile.coolify create mode 100644 docker-compose-coolify.yml create mode 100644 entrypoint-coolify.sh diff --git a/COOLIFY.md b/COOLIFY.md new file mode 100644 index 000000000..4f8b78ddb --- /dev/null +++ b/COOLIFY.md @@ -0,0 +1,326 @@ +# โ˜๏ธ Deploying PicoClaw on Coolify + +Deploy PicoClaw as a self-hosted AI assistant on [Coolify](https://coolify.io) โ€” the open-source Heroku/Vercel alternative. + +> **Looking for the quick 3-step version?** See [README.md โ†’ Deploy on Coolify](README.md#%EF%B8%8F-deploy-on-coolify-3-steps) + +--- + +## ๐Ÿ“‹ Prerequisites + +- A Coolify instance (v4+) +- A GitHub account (to fork the repo) +- At least one LLM API key (e.g., [Gemini](https://aistudio.google.com/apikey), [OpenRouter](https://openrouter.ai/keys)) + +## ๐Ÿš€ Quick Deploy + +### Step 1: Fork the Repository + +Fork [mrbeandev/picoclaw](https://github.com/mrbeandev/picoclaw) to your GitHub account. + +### Step 2: Create a New Service in Coolify + +1. Go to your Coolify dashboard โ†’ **Projects** โ†’ select or create a project +2. Click **+ New** โ†’ **Docker Compose** +3. Connect your forked GitHub repo +4. Set the following: + - **Branch:** `deploy/coolify` + - **Docker Compose File:** `docker-compose-coolify.yml` + - **Base Directory:** `/` (root) + +### Step 3: Configure Environment Variables + +Go to **Environment Variables** tab and add your keys. See [Configuration](#-configuration) below. + +### Step 4: Deploy! + +Hit **Deploy** and wait for the build to complete (~30 seconds). + +--- + +## โš™๏ธ Configuration + +PicoClaw on Coolify supports **3 configuration methods**. The entrypoint script checks them in order โ€” **first match wins**. + +### Which Method Should I Use? + +| | Method 1: JSON Env Var | Method 2: Mounted File | Method 3: Individual Env Vars | +|---|---|---|---| +| **Difficulty** | Medium | Easy | Easiest | +| **Flexibility** | โœ… Full control | โœ… Full control | โš ๏ธ Limited | +| **Custom providers (Ollama, vLLM)** | โœ… Yes | โœ… Yes | โŒ No | +| **Allow-lists** | โœ… Yes | โœ… Yes | โœ… Yes (comma-separated) | +| **Feishu, DingTalk, QQ, OneBot** | โœ… Yes | โœ… Yes | โŒ No | +| **Custom API base URLs** | โœ… Yes | โœ… Yes | โŒ No | +| **Requires JSON minification** | โš ๏ธ Yes | โŒ No | โŒ No | +| **Edit without rebuild** | โœ… Redeploy only | โœ… Restart only | โœ… Redeploy only | +| **Pretty-printed JSON** | โŒ Must minify | โœ… Yes | N/A | + +--- + +### Method 1: Full JSON Config (Most Flexible) โญ + +**Best for:** Full control, custom providers (Ollama, vLLM), complex setups. + +Paste your **entire** `config.json` as a single environment variable: + +| Key | Value | +|-----|-------| +| `PICOCLAW_CONFIG_JSON` | `{"agents":{"defaults":{"provider":"gemini",...}},...}` | + +#### Example: Gemini + Telegram + Ollama + +```json +{ + "agents": { + "defaults": { + "provider": "gemini", + "model": "gemini-2.5-flash-lite", + "max_tokens": 8192, + "temperature": 0.7, + "max_tool_iterations": 20, + "workspace": "~/.picoclaw/workspace", + "restrict_to_workspace": true + } + }, + "providers": { + "gemini": { + "api_key": "AIzaSy..." + }, + "vllm": { + "api_key": "dummy", + "api_base": "http://your-ollama-server:11434/v1" + }, + "openrouter": { + "api_key": "sk-or-..." + } + }, + "channels": { + "telegram": { + "enabled": true, + "token": "123456:ABC-DEF...", + "allow_from": ["your_telegram_user_id"] + }, + "discord": { + "enabled": false, + "token": "", + "allow_from": [] + } + }, + "tools": { + "web": { + "duckduckgo": { "enabled": true, "max_results": 5 }, + "brave": { "enabled": false, "api_key": "", "max_results": 5 } + }, + "firecrawl": { "enabled": false, "api_key": "", "api_base": "https://api.firecrawl.dev/v1" }, + "serpapi": { "enabled": false, "api_key": "", "max_results": 10 } + }, + "heartbeat": { "enabled": true, "interval": 30 }, + "gateway": { "host": "0.0.0.0", "port": 18790 }, + "devices": { "enabled": false, "monitor_usb": false } +} +``` + +#### โš ๏ธ Important: You MUST minify the JSON! + +Coolify environment variables are single-line. You need to compress the JSON into **one line** before pasting. + +**๐Ÿ”ง JSON Tools for Minifying:** + +| Tool | Type | URL | +|------|------|-----| +| **JSON Minifier** | Web | [jsonformatter.org/json-minify](https://jsonformatter.org/json-minify) | +| **JSON Formatter** | Web | [jsonformatter.curiousconcept.com](https://jsonformatter.curiousconcept.com/) | +| **JSON Editor Online** | Web | [jsoneditoronline.org](https://jsoneditoronline.org/) โ€” edit visually, then copy minified | +| **jq** | CLI | `cat config.json \| jq -c .` โ€” outputs minified JSON | +| **Python** | CLI | `python3 -c "import json,sys;print(json.dumps(json.load(sys.stdin)))" < config.json` | +| **Node.js** | CLI | `node -e "process.stdin.on('data',d=>console.log(JSON.stringify(JSON.parse(d))))"< config.json` | + +**Workflow:** +1. Write your config in a pretty-printed JSON editor +2. Validate it (the tools above show errors) +3. Minify / compress to one line +4. Paste the single line as the `PICOCLAW_CONFIG_JSON` value in Coolify + +**Example minified output:** +``` +{"agents":{"defaults":{"provider":"gemini","model":"gemini-2.5-flash-lite","max_tokens":8192,"temperature":0.7,"max_tool_iterations":20,"workspace":"~/.picoclaw/workspace","restrict_to_workspace":true}},"providers":{"gemini":{"api_key":"AIzaSy..."},"vllm":{"api_key":"dummy","api_base":"http://ollama:11434/v1"}},"channels":{"telegram":{"enabled":true,"token":"123456:ABC-DEF...","allow_from":["123456789"]}},"tools":{"web":{"duckduckgo":{"enabled":true,"max_results":5}}},"heartbeat":{"enabled":true,"interval":30},"gateway":{"host":"0.0.0.0","port":18790},"devices":{"enabled":false,"monitor_usb":false}} +``` + +--- + +### Method 2: Mounted Config File + +**Best for:** Users who prefer editing a normal file, and want pretty-printed JSON without minification. + +Use Coolify's **Storages** feature to mount a config file into the container: + +#### Step-by-step: + +1. Go to your PicoClaw service in Coolify +2. Click the **Storages** tab +3. Click **+ Add** and configure: + - **Source Path:** Leave empty (Coolify auto-creates it) or set to `/data/coolify/applications//config.json` + - **Destination Path:** `/config/config.json` +4. Save the storage mount +5. SSH into your Coolify server and create the config file: + ```bash + # Find your app's data directory + ls /data/coolify/applications/ + + # Create the config file (replace with your app's UUID) + nano /data/coolify/applications//config.json + ``` +6. Paste your full config JSON (pretty-printed is fine!) and save +7. **Restart** the service in Coolify (no rebuild needed) + +The entrypoint will automatically detect `/config/config.json` and use it. + +#### โœ… Advantages +- **Pretty-printed JSON** โ€” no minification needed, easy to read and edit +- **Full control** โ€” same flexibility as Method 1 +- **Edit without rebuild** โ€” just edit the file on disk and restart the container +- **Custom providers** โ€” Ollama, vLLM, and any other custom provider work fine + +#### โŒ Limitations +- **Requires SSH access** โ€” you need SSH into the Coolify server to create/edit the file +- **No Coolify UI editing** โ€” you can't edit the file content from Coolify's web UI (only set the mount path) +- **File must exist before starting** โ€” if the file doesn't exist, this method is skipped and it falls through to Method 3 +- **Not portable** โ€” the config lives on the server's filesystem, not in Coolify's database + +--- + +### Method 3: Individual Environment Variables + +**Best for:** Simple setups โ€” just Gemini + one channel, defaults for everything else. + +Set these in Coolify's **Environment Variables** tab: + +#### Required + +| Variable | Description | Example | +|----------|-------------|---------| +| `PICOCLAW_PROVIDERS_GEMINI_API_KEY` | Gemini API key | `AIzaSy...` | + +#### Provider Keys (optional) + +| Variable | Description | +|----------|-------------| +| `PICOCLAW_PROVIDERS_OPENROUTER_API_KEY` | OpenRouter API key | +| `PICOCLAW_PROVIDERS_OPENAI_API_KEY` | OpenAI API key | +| `PICOCLAW_PROVIDERS_ANTHROPIC_API_KEY` | Anthropic API key | +| `PICOCLAW_PROVIDERS_GROQ_API_KEY` | Groq API key | +| `PICOCLAW_PROVIDERS_MISTRAL_API_KEY` | Mistral API key | +| `PICOCLAW_PROVIDERS_DEEPSEEK_API_KEY` | DeepSeek API key | + +#### Channel Config (optional) + +| Variable | Description | +|----------|-------------| +| `PICOCLAW_CHANNELS_TELEGRAM_ENABLED` | `true` / `false` | +| `PICOCLAW_CHANNELS_TELEGRAM_TOKEN` | Telegram bot token | +| `PICOCLAW_CHANNELS_TELEGRAM_ALLOW_FROM` | Comma-separated user IDs (e.g., `123,456`) | +| `PICOCLAW_CHANNELS_DISCORD_ENABLED` | `true` / `false` | +| `PICOCLAW_CHANNELS_DISCORD_TOKEN` | Discord bot token | +| `PICOCLAW_CHANNELS_DISCORD_ALLOW_FROM` | Comma-separated user IDs | + +#### Model Config (optional) + +| Variable | Default | Description | +|----------|---------|-------------| +| `PICOCLAW_AGENTS_DEFAULTS_PROVIDER` | `gemini` | LLM provider name | +| `PICOCLAW_AGENTS_DEFAULTS_MODEL` | `gemini-2.5-flash-lite` | Model name | +| `PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS` | `8192` | Max output tokens | +| `PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE` | `0.7` | Temperature | + +#### Other (optional) + +| Variable | Default | Description | +|----------|---------|-------------| +| `TZ` | `UTC` | Timezone (e.g., `Asia/Kolkata` for IST) | +| `PICOCLAW_HEARTBEAT_ENABLED` | `true` | Enable heartbeat | +| `PICOCLAW_HEARTBEAT_INTERVAL` | `30` | Heartbeat interval (minutes) | +| `PICOCLAW_TOOLS_WEB_DUCKDUCKGO_ENABLED` | `true` | DuckDuckGo search | +| `PICOCLAW_TOOLS_WEB_BRAVE_ENABLED` | `false` | Brave search | +| `PICOCLAW_TOOLS_WEB_BRAVE_API_KEY` | | Brave API key | + +#### โœ… Advantages +- **Zero JSON knowledge needed** โ€” just set key=value pairs +- **Easiest to set up** โ€” add a few env vars and deploy +- **Good for quick testing** โ€” get running in under a minute + +#### โŒ Limitations +- **No custom providers** โ€” only the built-in providers are supported (Gemini, OpenRouter, OpenAI, Anthropic, Groq, Mistral, DeepSeek, Zhipu, Moonshot, Nvidia, vLLM). You **cannot** add Ollama or other custom OpenAI-compatible providers +- **No custom API base URLs** โ€” you can't override `api_base` for providers (needed for self-hosted models) +- **Limited channel support** โ€” only Telegram, Discord, Slack, and LINE are configurable. Feishu, DingTalk, QQ, WhatsApp, MaixCam, and OneBot are **not** configurable via env vars +- **No proxy settings** โ€” provider proxy configuration is not available +- **Hardcoded defaults** โ€” many settings like `max_results`, `webhook_port`, etc. use hardcoded defaults that can't be changed +- **Allow-lists are comma-separated strings** โ€” works but less flexible than JSON arrays (no spaces in IDs) + +> **๐Ÿ’ก Tip:** Start with Method 3 to get running quickly, then switch to Method 1 when you need more control. + +--- + +## ๐Ÿ”ง Running Agent & Doctor Commands + +The gateway service runs automatically. To run one-shot commands, SSH into your Coolify server and use: + +```bash +# Run agent mode (one-shot question) +docker compose -f docker-compose-coolify.yml --profile agent run --rm picoclaw-agent -m "Hello!" + +# Run agent mode (interactive) +docker compose -f docker-compose-coolify.yml --profile agent run --rm picoclaw-agent + +# Run doctor (diagnostics) +docker compose -f docker-compose-coolify.yml --profile doctor run --rm picoclaw-doctor +``` + +### ๐ŸŽฎ Chat Commands (Telegram/Discord) + +You can check status and swap models directly from your chat app: + +| Command | Action | +|---------|--------| +| `/models` | View active model, provider, and all configured endpoints. | +| `/model ` | Switch the **model name** (keeping current provider). | +| `/model /` | Switch **both** provider and model (e.g., `vllm/qwen3-coder-next:cloud`). | + +> [!NOTE] +> Changes made via `/model` are active in memory. If the container restarts, it will revert to the default model defined in your `PICOCLAW_CONFIG_JSON` or environment variables. + +--- + +## ๏ฟฝ๏ธ Troubleshooting + +### "No API key configured" +Your config isn't being loaded. Check: +- For Method 1: Is `PICOCLAW_CONFIG_JSON` set? Is it valid JSON? +- For Method 3: Is `PICOCLAW_PROVIDERS_GEMINI_API_KEY` set? +- Check container logs: `docker logs picoclaw-gateway` โ€” look for the `๐Ÿ“ Using config from...` line. + +### Container keeps restarting +Check logs: `docker logs picoclaw-gateway --tail 50` + +Common issues: +- Invalid JSON in `PICOCLAW_CONFIG_JSON` (use a validator!) +- Missing API key for the configured provider + +### Build fails +- Ensure you're using the `deploy/coolify` branch +- Check that both `Dockerfile.coolify` and `entrypoint-coolify.sh` exist in the repo + +--- + +## ๐Ÿ“ File Structure (Coolify-specific) + +``` +picoclaw/ +โ”œโ”€โ”€ docker-compose-coolify.yml # Coolify-optimized compose file +โ”œโ”€โ”€ Dockerfile.coolify # Coolify Dockerfile with entrypoint +โ”œโ”€โ”€ entrypoint-coolify.sh # Config generator script +โ”œโ”€โ”€ Dockerfile # Original Dockerfile (not used by Coolify) +โ”œโ”€โ”€ docker-compose.yml # Original compose (not used by Coolify) +โ””โ”€โ”€ config/ + โ””โ”€โ”€ config.example.json # Reference config with all options +``` diff --git a/Dockerfile.coolify b/Dockerfile.coolify new file mode 100644 index 000000000..8fe22ed43 --- /dev/null +++ b/Dockerfile.coolify @@ -0,0 +1,41 @@ +# ============================================================ +# PicoClaw Dockerfile โ€” Coolify Edition +# Uses an entrypoint script that generates config.json from +# environment variables at container startup. +# ============================================================ + +# Stage 1: Build the picoclaw binary +FROM golang:1.26.0-alpine AS builder + +RUN apk add --no-cache git make + +WORKDIR /src + +# Cache dependencies +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source and build +COPY . . +RUN make build + +# Stage 2: Minimal runtime image +FROM alpine:3.23 + +RUN apk add --no-cache ca-certificates tzdata curl + +# Copy binary +COPY --from=builder /src/build/picoclaw /usr/local/bin/picoclaw + +# Create picoclaw home directory +RUN /usr/local/bin/picoclaw onboard + +# Copy the entrypoint script that generates config from env vars +COPY entrypoint-coolify.sh /usr/local/bin/entrypoint-coolify.sh +RUN chmod +x /usr/local/bin/entrypoint-coolify.sh + +# Default env vars (can be overridden by Coolify) +ENV TZ=UTC + +ENTRYPOINT ["/usr/local/bin/entrypoint-coolify.sh"] +CMD ["gateway"] diff --git a/README.md b/README.md index 0a9dacce6..06b09f0dd 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,8 @@ > * **OFFICIAL DOMAIN:** The **ONLY** official website is **[picoclaw.io](https://picoclaw.io)**, and company website is **[sipeed.com](https://sipeed.com)** > * **Warning:** Many `.ai/.org/.com/.net/...` domains are registered by third parties. > * **Warning:** picoclaw is in early development now and may have unresolved network security issues. Do not deploy to production environments before the v1.0 release. -> * **Note:** picoclaw has recently merged a lot of PRs, which may result in a larger memory footprint (10โ€“20MB) in the latest versions. We plan to prioritize resource optimization as soon as the current feature set reaches a stable state. - ## ๐Ÿ“ข News -2026-02-16 ๐ŸŽ‰ PicoClaw hit 12K stars in one week! Thank you all for your support! PicoClaw is growing faster than we ever imagined. Given the high volume of PRs, we urgently need community maintainers. Our volunteer roles and roadmap are officially posted [here](doc/picoclaw_community_roadmap_260216.md) โ€”we canโ€™t wait to have you on board! 2026-02-13 ๐ŸŽ‰ PicoClaw hit 5000 stars in 4days! Thank you for the community! There are so many PRs&issues come in (during Chinese New Year holidays), we are finalizing the Project Roadmap and setting up the Developer Group to accelerate PicoClaw's development. ๐Ÿš€ Call to Action: Please submit your feature requests in GitHub Discussions. We will review and prioritize them during our upcoming weekly meeting. @@ -175,6 +172,32 @@ docker compose --profile gateway build --no-cache docker compose --profile gateway up -d ``` +### โ˜๏ธ Deploy on Coolify (3 Steps) + +Deploy PicoClaw on [Coolify](https://coolify.io) in under 5 minutes: + +**1. Create service** โ€” In Coolify: **+ New** โ†’ **Docker Compose** โ†’ connect your fork of this repo + - Branch: `main` + - Compose file: `docker-compose-coolify.yml` + +**2. Add env vars** โ€” Go to **Environment Variables** tab and add: + +| Variable | Value | +|----------|-------| +| `PICOCLAW_PROVIDERS_GEMINI_API_KEY` | Your [Gemini API key](https://aistudio.google.com/apikey) | +| `PICOCLAW_CHANNELS_TELEGRAM_ENABLED` | `true` *(if using Telegram)* | +| `PICOCLAW_CHANNELS_TELEGRAM_TOKEN` | Your bot token from [@BotFather](https://t.me/BotFather) | +| `TZ` | `Asia/Kolkata` *(or your timezone)* | + +**3. Deploy!** ๐Ÿš€ + +> [!TIP] +> **Need more control?** (custom providers like Ollama, allowlists, multiple channels) +> Set `PICOCLAW_CONFIG_JSON` with your full config as a single env var. +> See the complete guide: **[COOLIFY.md](COOLIFY.md)** + +--- + ### ๐Ÿš€ Quick Start > [!TIP] diff --git a/docker-compose-coolify.yml b/docker-compose-coolify.yml new file mode 100644 index 000000000..6f03e08b3 --- /dev/null +++ b/docker-compose-coolify.yml @@ -0,0 +1,167 @@ +# ============================================================ +# PicoClaw โ€” Coolify-Optimized Docker Compose +# ============================================================ +# +# DEPLOYMENT STEPS (Coolify): +# 1. Create a new service โ†’ Docker Compose +# 2. Point source to this repository +# 3. Set "Docker Compose file" path to: picoclaw/docker-compose-coolify.yml +# 4. Add environment variables in Coolify's UI (see below) +# 5. Deploy! +# +# RECOMMENDED: Use PICOCLAW_CONFIG_JSON for full control: +# PICOCLAW_CONFIG_JSON โ€” Full JSON config (overrides everything below) +# +# ALTERNATIVE: Individual environment variables: +# GEMINI_API_KEY โ€” Your Gemini API key +# +# OPTIONAL ENVIRONMENT VARIABLES: +# LLM_PROVIDER โ€” LLM provider (e.g. gemini, vllm, openrouter) +# LLM_MODEL โ€” Model name (e.g. gemini-2.5-flash-lite) +# TELEGRAM_BOT_TOKEN โ€” Telegram bot token +# DISCORD_BOT_TOKEN โ€” Discord bot token +# OPENROUTER_API_KEY โ€” OpenRouter API key +# OPENAI_API_KEY โ€” OpenAI API key +# ANTHROPIC_API_KEY โ€” Anthropic API key +# GROQ_API_KEY โ€” Groq API key (also enables voice transcription) +# BRAVE_SEARCH_API_KEY โ€” Brave Search API key +# LINE_CHANNEL_SECRET โ€” LINE channel secret +# LINE_CHANNEL_ACCESS_TOKEN โ€” LINE channel access token +# TZ โ€” Timezone (default: UTC) +# +# ============================================================ + +services: + # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + # PicoClaw Gateway (Long-running Bot) + # This is the main service that Coolify will auto-start + # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + picoclaw-gateway: + build: + context: . + dockerfile: Dockerfile.coolify + container_name: picoclaw-gateway + restart: unless-stopped + + # โ”€โ”€ Environment Variables โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + # If PICOCLAW_CONFIG_JSON is set, it takes full priority. + # Individual vars below are only used as fallback (Method 3). + environment: + # -- Full JSON config (Method 1 โ€” overrides everything below) -- + - PICOCLAW_CONFIG_JSON=${PICOCLAW_CONFIG_JSON:-} + + # -- Core LLM Config (only used if PICOCLAW_CONFIG_JSON is empty) -- + - LLM_PROVIDER=${LLM_PROVIDER:-} + - LLM_MODEL=${LLM_MODEL:-} + + # -- Provider API Keys -- + - GEMINI_API_KEY=${GEMINI_API_KEY:-} + - OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-} + - OPENAI_API_KEY=${OPENAI_API_KEY:-} + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} + - GROQ_API_KEY=${GROQ_API_KEY:-} + + # -- Chat Channel Tokens -- + - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-} + - DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN:-} + - LINE_CHANNEL_SECRET=${LINE_CHANNEL_SECRET:-} + - LINE_CHANNEL_ACCESS_TOKEN=${LINE_CHANNEL_ACCESS_TOKEN:-} + + # -- Web Search -- + - BRAVE_SEARCH_API_KEY=${BRAVE_SEARCH_API_KEY:-} + - BRAVE_SEARCH_ENABLED=${BRAVE_SEARCH_ENABLED:-} + - DUCKDUCKGO_ENABLED=${DUCKDUCKGO_ENABLED:-} + + # -- Heartbeat -- + - HEARTBEAT_ENABLED=${HEARTBEAT_ENABLED:-} + - HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-} + + # -- Timezone -- + - TZ=${TZ:-UTC} + + # โ”€โ”€ Volumes โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + volumes: + # Persistent workspace โ€” sessions, memory, logs survive redeploys + # Note: config.json is created by 'picoclaw onboard' during Docker build + # and all settings are overridden via environment variables above + - picoclaw-workspace:/root/.picoclaw/workspace + + # โ”€โ”€ Ports โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + # Expose ports if using LINE webhook or MaixCAM channel + # Coolify will auto-detect and configure these + ports: + - "18790:18790" # MaixCAM / Gateway + - "18791:18791" # LINE Webhook + + # โ”€โ”€ Health Check โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + # Coolify uses this to determine if the container is healthy + healthcheck: + test: ["CMD", "picoclaw", "status"] + interval: 60s + timeout: 10s + retries: 3 + start_period: 30s + + # โ”€โ”€ Command โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + command: ["gateway"] + + # โ”€โ”€ Logging โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + # PicoClaw Agent (One-shot / Interactive) + # Not auto-started by Coolify (has profile). + # Run manually via SSH: + # docker compose -f docker-compose-coolify.yml run --rm picoclaw-agent -m "Hello" + # docker compose -f docker-compose-coolify.yml run --rm picoclaw-agent + # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + picoclaw-agent: + build: + context: . + dockerfile: Dockerfile + container_name: picoclaw-agent + profiles: + - agent + environment: + - PICOCLAW_AGENTS_DEFAULTS_PROVIDER=${LLM_PROVIDER:-} + - PICOCLAW_AGENTS_DEFAULTS_MODEL=${LLM_MODEL:-} + - PICOCLAW_PROVIDERS_GEMINI_API_KEY=${GEMINI_API_KEY:-} + - PICOCLAW_PROVIDERS_OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-} + - PICOCLAW_PROVIDERS_OPENAI_API_KEY=${OPENAI_API_KEY:-} + - PICOCLAW_PROVIDERS_ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} + - TZ=${TZ:-UTC} + volumes: + - picoclaw-workspace:/root/.picoclaw/workspace + entrypoint: ["picoclaw", "agent"] + stdin_open: true + tty: true + + # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + # PicoClaw Doctor (Diagnostic Tool) + # Not auto-started by Coolify (has profile). + # Run manually via SSH: + # docker compose -f docker-compose-coolify.yml run --rm picoclaw-doctor + # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + picoclaw-doctor: + build: + context: . + dockerfile: Dockerfile + container_name: picoclaw-doctor + profiles: + - doctor + environment: + - PICOCLAW_AGENTS_DEFAULTS_PROVIDER=${LLM_PROVIDER:-} + - PICOCLAW_AGENTS_DEFAULTS_MODEL=${LLM_MODEL:-} + - PICOCLAW_PROVIDERS_GEMINI_API_KEY=${GEMINI_API_KEY:-} + - TZ=${TZ:-UTC} + volumes: + - picoclaw-workspace:/root/.picoclaw/workspace + entrypoint: ["picoclaw", "doctor"] + +volumes: + picoclaw-workspace: + driver: local diff --git a/entrypoint-coolify.sh b/entrypoint-coolify.sh new file mode 100644 index 000000000..373dcfbd4 --- /dev/null +++ b/entrypoint-coolify.sh @@ -0,0 +1,170 @@ +#!/bin/sh +# ============================================================ +# PicoClaw Coolify Entrypoint +# Generates config.json before starting PicoClaw +# +# CONFIG PRIORITY (first match wins): +# 1. PICOCLAW_CONFIG_JSON env var โ€” paste your entire JSON config +# 2. Mounted file at /config/config.json โ€” use Coolify Storages +# 3. Auto-generated from individual env vars (basic setup) +# ============================================================ +set -e + +CONFIG_DIR="/root/.picoclaw" +CONFIG_FILE="${CONFIG_DIR}/config.json" + +mkdir -p "${CONFIG_DIR}" + +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# METHOD 1: Full JSON config via env var +# Set PICOCLAW_CONFIG_JSON in Coolify env vars +# with your entire config.json content +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +if [ -n "${PICOCLAW_CONFIG_JSON}" ]; then + echo "๐Ÿ“ Using config from PICOCLAW_CONFIG_JSON env var" + echo "${PICOCLAW_CONFIG_JSON}" > "${CONFIG_FILE}" + + # CRITICAL: Unset all PICOCLAW_* env vars (except PICOCLAW_CONFIG_JSON) + # so that Go's env.Parse() does NOT override values from the JSON file. + # Without this, Dockerfile ENV defaults (e.g. PICOCLAW_AGENTS_DEFAULTS_PROVIDER=gemini) + # would silently overwrite the user's JSON config every time. + for var in $(env | grep '^PICOCLAW_' | cut -d= -f1); do + if [ "$var" != "PICOCLAW_CONFIG_JSON" ]; then + unset "$var" + fi + done + + exec picoclaw "$@" +fi + +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# METHOD 2: Mounted config file +# In Coolify โ†’ Storages โ†’ Add: +# Source: /data/coolify/applications//config.json +# Destination: /config/config.json +# Then paste your JSON in the file content +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +if [ -f "/config/config.json" ]; then + echo "๐Ÿ“ Using mounted config from /config/config.json" + cp /config/config.json "${CONFIG_FILE}" + + # Same protection as Method 1: unset PICOCLAW_* env vars + for var in $(env | grep '^PICOCLAW_' | cut -d= -f1); do + unset "$var" + done + + exec picoclaw "$@" +fi + +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# METHOD 3: Auto-generate from individual env vars +# Good for simple setups (Gemini + Telegram, etc.) +# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +echo "๐Ÿ“ Generating config from individual env vars" + +# Helper: comma-separated string โ†’ JSON array +csv_to_json_array() { + input="$1" + if [ -z "$input" ]; then echo "[]"; return; fi + result="[" + first=true + OLD_IFS="$IFS"; IFS="," + for item in $input; do + item=$(echo "$item" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + if [ -n "$item" ]; then + if [ "$first" = true ]; then first=false; else result="${result},"; fi + result="${result}\"${item}\"" + fi + done + IFS="$OLD_IFS" + echo "${result}]" +} + +# โ”€โ”€ Resolve env vars โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# Accept both Coolify-friendly short names and PICOCLAW_* internal names +R_PROVIDER="${PICOCLAW_AGENTS_DEFAULTS_PROVIDER:-${LLM_PROVIDER:-gemini}}" +R_MODEL="${PICOCLAW_AGENTS_DEFAULTS_MODEL:-${LLM_MODEL:-gemini-2.5-flash-lite}}" + +R_GEMINI_KEY="${PICOCLAW_PROVIDERS_GEMINI_API_KEY:-${GEMINI_API_KEY:-}}" +R_OPENROUTER_KEY="${PICOCLAW_PROVIDERS_OPENROUTER_API_KEY:-${OPENROUTER_API_KEY:-}}" +R_OPENAI_KEY="${PICOCLAW_PROVIDERS_OPENAI_API_KEY:-${OPENAI_API_KEY:-}}" +R_ANTHROPIC_KEY="${PICOCLAW_PROVIDERS_ANTHROPIC_API_KEY:-${ANTHROPIC_API_KEY:-}}" +R_GROQ_KEY="${PICOCLAW_PROVIDERS_GROQ_API_KEY:-${GROQ_API_KEY:-}}" +R_MISTRAL_KEY="${PICOCLAW_PROVIDERS_MISTRAL_API_KEY:-${MISTRAL_API_KEY:-}}" +R_DEEPSEEK_KEY="${PICOCLAW_PROVIDERS_DEEPSEEK_API_KEY:-${DEEPSEEK_API_KEY:-}}" +R_VLLM_KEY="${PICOCLAW_PROVIDERS_VLLM_API_KEY:-${VLLM_API_KEY:-}}" +R_VLLM_BASE="${PICOCLAW_PROVIDERS_VLLM_API_BASE:-${VLLM_API_BASE:-}}" + +R_TELEGRAM="${PICOCLAW_CHANNELS_TELEGRAM_TOKEN:-${TELEGRAM_BOT_TOKEN:-}}" +R_DISCORD="${PICOCLAW_CHANNELS_DISCORD_TOKEN:-${DISCORD_BOT_TOKEN:-}}" +R_LINE_SECRET="${PICOCLAW_CHANNELS_LINE_CHANNEL_SECRET:-${LINE_CHANNEL_SECRET:-}}" +R_LINE_ACCESS="${PICOCLAW_CHANNELS_LINE_CHANNEL_ACCESS_TOKEN:-${LINE_CHANNEL_ACCESS_TOKEN:-}}" + +R_BRAVE_KEY="${PICOCLAW_TOOLS_WEB_BRAVE_API_KEY:-${BRAVE_SEARCH_API_KEY:-}}" +R_BRAVE_ON="${PICOCLAW_TOOLS_WEB_BRAVE_ENABLED:-${BRAVE_SEARCH_ENABLED:-false}}" +R_DDG_ON="${PICOCLAW_TOOLS_WEB_DUCKDUCKGO_ENABLED:-${DUCKDUCKGO_ENABLED:-true}}" +R_HB_ON="${PICOCLAW_HEARTBEAT_ENABLED:-${HEARTBEAT_ENABLED:-true}}" +R_HB_INT="${PICOCLAW_HEARTBEAT_INTERVAL:-${HEARTBEAT_INTERVAL:-30}}" + +# โ”€โ”€ Auto-enable channels when token is provided โ”€โ”€ +if [ -n "$R_TELEGRAM" ]; then TG_ON="true"; else TG_ON="${PICOCLAW_CHANNELS_TELEGRAM_ENABLED:-false}"; fi +if [ -n "$R_DISCORD" ]; then DC_ON="true"; else DC_ON="${PICOCLAW_CHANNELS_DISCORD_ENABLED:-false}"; fi +if [ -n "$R_LINE_SECRET" ] && [ -n "$R_LINE_ACCESS" ]; then LN_ON="true"; else LN_ON="${PICOCLAW_CHANNELS_LINE_ENABLED:-false}"; fi + +TELEGRAM_ALLOW=$(csv_to_json_array "${PICOCLAW_CHANNELS_TELEGRAM_ALLOW_FROM:-}") +DISCORD_ALLOW=$(csv_to_json_array "${PICOCLAW_CHANNELS_DISCORD_ALLOW_FROM:-}") +SLACK_ALLOW=$(csv_to_json_array "${PICOCLAW_CHANNELS_SLACK_ALLOW_FROM:-}") +LINE_ALLOW=$(csv_to_json_array "${PICOCLAW_CHANNELS_LINE_ALLOW_FROM:-}") + +cat > "${CONFIG_FILE}" <