chore(pr239): split out coolify deployment changes

This commit is contained in:
mrbeandev 2026-02-17 12:38:09 +05:30
parent 179527dcce
commit e7a33416a6
11 changed files with 75 additions and 843 deletions

View file

@ -1,18 +1,12 @@
name: 🐳 Build & Push Docker Image
on:
workflow_call:
inputs:
tag:
description: "Release tag"
required: true
type: string
release:
types: [published]
env:
GHCR_REGISTRY: ghcr.io
GHCR_IMAGE_NAME: ${{ github.repository_owner }}/picoclaw
DOCKERHUB_REGISTRY: docker.io
DOCKERHUB_IMAGE_NAME: ${{ vars.DOCKERHUB_REPOSITORY }}
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository_owner }}/picoclaw
jobs:
build:
@ -26,8 +20,6 @@ jobs:
# ── Checkout ──────────────────────────────
- name: 📥 Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}
# ── Docker Buildx ─────────────────────────
- name: 🔧 Set up Docker Buildx
@ -35,42 +27,36 @@ jobs:
# ── Login to GHCR ─────────────────────────
- name: 🔑 Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.GHCR_REGISTRY }}
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# ── Login to Docker Hub ────────────────────
- name: 🔑 Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKERHUB_REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# ── Metadata (tags & labels) ──────────────
- name: 🏷️ Prepare image tags
id: tags
shell: bash
run: |
tag="${{ inputs.tag }}"
echo "ghcr_tag=${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:${tag}" >> "$GITHUB_OUTPUT"
echo "ghcr_latest=${{ env.GHCR_REGISTRY }}/${{ env.GHCR_IMAGE_NAME }}:latest" >> "$GITHUB_OUTPUT"
echo "dockerhub_tag=${{ env.DOCKERHUB_REGISTRY }}/${{ env.DOCKERHUB_IMAGE_NAME }}:${tag}" >> "$GITHUB_OUTPUT"
echo "dockerhub_latest=${{ env.DOCKERHUB_REGISTRY }}/${{ env.DOCKERHUB_IMAGE_NAME }}:latest" >> "$GITHUB_OUTPUT"
- name: 🏷️ Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value={{date 'YYYYMMDD-HHmmss'}},enable={{is_default_branch}}
# ── Build & Push ──────────────────────────
- name: 🚀 Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ steps.tags.outputs.ghcr_tag }}
${{ steps.tags.outputs.ghcr_latest }}
${{ steps.tags.outputs.dockerhub_tag }}
${{ steps.tags.outputs.dockerhub_latest }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64,linux/riscv64
platforms: linux/amd64,linux/arm64

View file

@ -38,18 +38,14 @@ jobs:
git tag -a "${{ inputs.tag }}" -m "Release ${{ inputs.tag }}"
git push origin "${{ inputs.tag }}"
release:
name: GoReleaser Release
build-binaries:
name: Build Release Binaries
needs: create-tag
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout tag
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.tag }}
- name: Setup Go from go.mod
@ -57,42 +53,47 @@ jobs:
with:
go-version-file: go.mod
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Build all binaries
run: make build-all
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Generate checksums
shell: bash
run: |
shasum -a 256 build/picoclaw-* > build/sha256sums.txt
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
- name: Upload release binaries artifact
uses: actions/upload-artifact@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
name: picoclaw-binaries
path: |
build/picoclaw-*
build/sha256sums.txt
if-no-files-found: error
- name: Login to Docker Hub
uses: docker/login-action@v3
create-release:
name: Create GitHub Release
needs: [create-tag, build-binaries]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
registry: docker.io
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
path: release-artifacts
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
- name: Show downloaded files
run: ls -R release-artifacts
- name: Create release
uses: softprops/action-gh-release@v2
with:
distribution: goreleaser
version: ~> v2
args: release --clean
tag_name: ${{ inputs.tag }}
name: ${{ inputs.tag }}
draft: ${{ inputs.draft }}
prerelease: ${{ inputs.prerelease }}
files: |
release-artifacts/**/*
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
DOCKERHUB_IMAGE_NAME: ${{ vars.DOCKERHUB_REPOSITORY }}
- name: Apply release flags
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release edit "${{ inputs.tag }}" \
--draft=${{ inputs.draft }} \
--prerelease=${{ inputs.prerelease }}

View file

@ -5,11 +5,9 @@ version: 2
before:
hooks:
- go mod tidy
- go generate ./cmd/picoclaw
builds:
- id: picoclaw
env:
- env:
- CGO_ENABLED=0
goos:
- linux
@ -28,22 +26,6 @@ builds:
- goos: windows
goarch: arm
dockers_v2:
- id: picoclaw
dockerfile: Dockerfile.goreleaser
ids:
- picoclaw
images:
- "ghcr.io/{{ .Env.GITHUB_REPOSITORY_OWNER }}/picoclaw"
- "docker.io/{{ .Env.DOCKERHUB_IMAGE_NAME }}"
tags:
- "{{ .Tag }}"
- "latest"
platforms:
- linux/amd64
- linux/arm64
- linux/riscv64
archives:
- formats: [tar.gz]
# this name template makes the OS and Arch compatible with the results of `uname`.
@ -66,10 +48,10 @@ changelog:
- "^docs:"
- "^test:"
# upx:
# - enabled: true
# compress: best
# lzma: true
upx:
- enabled: true
compress: best
lzma: true
release:
footer: >-

View file

@ -1,326 +0,0 @@
# ☁️ 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/<your-app-uuid>/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 <uuid> with your app's UUID)
nano /data/coolify/applications/<uuid>/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 <name>` | Switch the **model name** (keeping current provider). |
| `/model <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.
---
## <20> 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
```

View file

@ -22,15 +22,16 @@ FROM alpine:3.23
RUN apk add --no-cache ca-certificates tzdata curl
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -q --spider http://localhost:18790/health || exit 1
# Copy binary
COPY --from=builder /src/build/picoclaw /usr/local/bin/picoclaw
# Create picoclaw home directory
RUN /usr/local/bin/picoclaw onboard
# Set environment variables for testing
ENV PICOCLAW_AGENTS_DEFAULTS_PROVIDER="gemini"
ENV PICOCLAW_AGENTS_DEFAULTS_MODEL="gemini-1.5-flash"
ENV PICOCLAW_PROVIDERS_GEMINI_API_KEY=""
ENTRYPOINT ["picoclaw"]
CMD ["gateway"]

View file

@ -1,41 +0,0 @@
# ============================================================
# 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"]

View file

@ -119,7 +119,7 @@ clean:
@rm -rf $(BUILD_DIR)
@echo "Clean complete"
## vet: Run go vet for static analysis
## fmt: Format Go code
vet:
@$(GO) vet ./...
@ -131,19 +131,11 @@ test:
fmt:
@$(GO) fmt ./...
## deps: Download dependencies
## deps: Update dependencies
deps:
@$(GO) mod download
@$(GO) mod verify
## update-deps: Update dependencies
update-deps:
@$(GO) get -u ./...
@$(GO) mod tidy
## check: Run vet, fmt, and verify dependencies
check: deps fmt vet test
## run: Build and run picoclaw
run: build
@$(BUILD_DIR)/$(BINARY_NAME) $(ARGS)

View file

@ -44,7 +44,7 @@
> * **NO CRYPTO:** PicoClaw has **NO** official token/coin. All claims on `pump.fun` or other trading platforms are **SCAMS**.
> * **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.
>
## 📢 News
@ -172,32 +172,6 @@ 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]

View file

@ -45,8 +45,8 @@
> * **无加密货币 (NO CRYPTO):** PicoClaw **没有** 发行任何官方代币、Token 或虚拟货币。所有在 `pump.fun` 或其他交易平台上的相关声称均为 **诈骗**
> * **官方域名:** 唯一的官方网站是 **[picoclaw.io](https://picoclaw.io)**,公司官网是 **[sipeed.com](https://sipeed.com)**。
> * **警惕:** 许多 `.ai/.org/.com/.net/...` 后缀的域名被第三方抢注,请勿轻信。
> * **注意:** picoclaw正在初期的快速功能开发阶段可能有尚未修复的网络安全问题在1.0正式版发布前,请不要将其部署到生产环境中
>
>
## 📢 新闻 (News)

View file

@ -1,167 +0,0 @@
# ============================================================
# 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

View file

@ -1,170 +0,0 @@
#!/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/<uuid>/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}" <<ENDOFCONFIG
{
"agents": {
"defaults": {
"workspace": "~/.picoclaw/workspace",
"restrict_to_workspace": true,
"provider": "${R_PROVIDER}",
"model": "${R_MODEL}",
"max_tokens": ${PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS:-8192},
"temperature": ${PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE:-0.7},
"max_tool_iterations": ${PICOCLAW_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS:-20}
}
},
"channels": {
"telegram": { "enabled": ${TG_ON}, "token": "${R_TELEGRAM}", "allow_from": ${TELEGRAM_ALLOW} },
"discord": { "enabled": ${DC_ON}, "token": "${R_DISCORD}", "allow_from": ${DISCORD_ALLOW} },
"slack": { "enabled": ${PICOCLAW_CHANNELS_SLACK_ENABLED:-false}, "bot_token": "${PICOCLAW_CHANNELS_SLACK_BOT_TOKEN:-}", "app_token": "${PICOCLAW_CHANNELS_SLACK_APP_TOKEN:-}", "allow_from": ${SLACK_ALLOW} },
"line": { "enabled": ${LN_ON}, "channel_secret": "${R_LINE_SECRET}", "channel_access_token": "${R_LINE_ACCESS}", "webhook_host": "0.0.0.0", "webhook_port": 18791, "webhook_path": "/webhook/line", "allow_from": ${LINE_ALLOW} },
"maixcam": { "enabled": false, "host": "0.0.0.0", "port": 18790, "allow_from": [] },
"whatsapp": { "enabled": false, "bridge_url": "ws://localhost:3001", "allow_from": [] },
"feishu": { "enabled": false, "app_id": "", "app_secret": "", "encrypt_key": "", "verification_token": "", "allow_from": [] },
"dingtalk": { "enabled": false, "client_id": "", "client_secret": "", "allow_from": [] },
"onebot": { "enabled": false, "ws_url": "ws://127.0.0.1:3001", "access_token": "", "reconnect_interval": 5, "group_trigger_prefix": [], "allow_from": [] }
},
"providers": {
"gemini": { "api_key": "${R_GEMINI_KEY}", "api_base": "${PICOCLAW_PROVIDERS_GEMINI_API_BASE:-}" },
"openrouter": { "api_key": "${R_OPENROUTER_KEY}", "api_base": "${PICOCLAW_PROVIDERS_OPENROUTER_API_BASE:-}" },
"openai": { "api_key": "${R_OPENAI_KEY}", "api_base": "${PICOCLAW_PROVIDERS_OPENAI_API_BASE:-}" },
"anthropic": { "api_key": "${R_ANTHROPIC_KEY}", "api_base": "${PICOCLAW_PROVIDERS_ANTHROPIC_API_BASE:-}" },
"groq": { "api_key": "${R_GROQ_KEY}", "api_base": "${PICOCLAW_PROVIDERS_GROQ_API_BASE:-}" },
"mistral": { "api_key": "${R_MISTRAL_KEY}", "api_base": "${PICOCLAW_PROVIDERS_MISTRAL_API_BASE:-}" },
"zhipu": { "api_key": "${PICOCLAW_PROVIDERS_ZHIPU_API_KEY:-}", "api_base": "${PICOCLAW_PROVIDERS_ZHIPU_API_BASE:-}" },
"moonshot": { "api_key": "${PICOCLAW_PROVIDERS_MOONSHOT_API_KEY:-}", "api_base": "${PICOCLAW_PROVIDERS_MOONSHOT_API_BASE:-}" },
"deepseek": { "api_key": "${R_DEEPSEEK_KEY}", "api_base": "${PICOCLAW_PROVIDERS_DEEPSEEK_API_BASE:-}" },
"nvidia": { "api_key": "${PICOCLAW_PROVIDERS_NVIDIA_API_KEY:-}", "api_base": "${PICOCLAW_PROVIDERS_NVIDIA_API_BASE:-}" },
"vllm": { "api_key": "${R_VLLM_KEY}", "api_base": "${R_VLLM_BASE}" }
},
"tools": {
"web": {
"brave": { "enabled": ${R_BRAVE_ON}, "api_key": "${R_BRAVE_KEY}", "max_results": 5 },
"duckduckgo": { "enabled": ${R_DDG_ON}, "max_results": 5 }
},
"firecrawl": { "enabled": ${PICOCLAW_TOOLS_FIRECRAWL_ENABLED:-false}, "api_key": "${PICOCLAW_TOOLS_FIRECRAWL_API_KEY:-}", "api_base": "https://api.firecrawl.dev/v1" },
"serpapi": { "enabled": ${PICOCLAW_TOOLS_SERPAPI_ENABLED:-false}, "api_key": "${PICOCLAW_TOOLS_SERPAPI_API_KEY:-}", "max_results": 10 }
},
"heartbeat": { "enabled": ${R_HB_ON}, "interval": ${R_HB_INT} },
"devices": { "enabled": false, "monitor_usb": false },
"gateway": { "host": "0.0.0.0", "port": 18790 }
}
ENDOFCONFIG
exec picoclaw "$@"