chore(pr239): split out coolify deployment changes
This commit is contained in:
parent
179527dcce
commit
e7a33416a6
11 changed files with 75 additions and 843 deletions
60
.github/workflows/docker-build.yml
vendored
60
.github/workflows/docker-build.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
73
.github/workflows/release.yml
vendored
73
.github/workflows/release.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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: >-
|
||||
|
|
|
|||
326
COOLIFY.md
326
COOLIFY.md
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
12
Makefile
12
Makefile
|
|
@ -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)
|
||||
|
|
|
|||
28
README.md
28
README.md
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 "$@"
|
||||
Loading…
Add table
Reference in a new issue