center">
-
-
- |
-
- |
-
-
- |
-
-
-> 📋 **[Hardware Compatibility List](docs/hardware-compatibility.md)** — See all tested boards, from $5 RISC-V to Raspberry Pi to Android phones. Your board not listed? Submit a PR!
-
-## 🦾 Demonstration
-
-### 🛠️ Standard Assistant Workflows
-
-🧩 Full-Stack Engineer |
- 🗂️ Logging & Planning Management |
- 🔎 Web Search & Learning |
-
|---|---|---|
|
-
|
-
|
-
| Develop • Deploy • Scale | -Schedule • Automate • Memory | -Discovery • Insights • Trends | -
-
-### 🐜 Innovative Low-Footprint Deploy
-
-PicoClaw can be deployed on almost any Linux device!
-
-- $9.9 [LicheeRV-Nano](https://www.aliexpress.com/item/1005006519668532.html) E(Ethernet) or W(WiFi6) version, for Minimal Home Assistant
-- $30~50 [NanoKVM](https://www.aliexpress.com/item/1005007369816019.html), or $100 [NanoKVM-Pro](https://www.aliexpress.com/item/1005010048471263.html) for Automated Server Maintenance
-- $50 [MaixCAM](https://www.aliexpress.com/item/1005008053333693.html) or $100 [MaixCAM2](https://www.kickstarter.com/projects/zepan/maixcam2-build-your-next-gen-4k-ai-camera) for Smart Monitoring
-
-
-
-##
+WeChat:
+
diff --git a/README.zh.md b/README.zh.md
index db34f57da..1bc5d1a4b 100644
--- a/README.zh.md
+++ b/README.zh.md
@@ -218,6 +218,7 @@ make install
| 命令 | 说明 |
| ------------------------- | ---------------------- |
| `picoclaw onboard` | 初始化配置与工作区 |
+| `picoclaw onboard weixin` | 扫码连接微信个人号 |
| `picoclaw agent -m "..."` | 与 Agent 对话 |
| `picoclaw agent` | 交互式对话模式 |
| `picoclaw gateway` | 启动网关 |
diff --git a/assets/wechat.png b/assets/wechat.png
index 6512421ed..effb4dab9 100644
Binary files a/assets/wechat.png and b/assets/wechat.png differ
diff --git a/cmd/picoclaw/internal/agent/helpers.go b/cmd/picoclaw/internal/agent/helpers.go
index c3ddbb77f..0af743bb5 100644
--- a/cmd/picoclaw/internal/agent/helpers.go
+++ b/cmd/picoclaw/internal/agent/helpers.go
@@ -23,16 +23,16 @@ func agentCmd(message, sessionKey, model string, debug bool) error {
sessionKey = "cli:default"
}
- if debug {
- logger.SetLevel(logger.DEBUG)
- fmt.Println("🔍 Debug mode enabled")
- }
-
cfg, err := internal.LoadConfig()
if err != nil {
return fmt.Errorf("error loading config: %w", err)
}
+ if debug {
+ logger.SetLevel(logger.DEBUG)
+ fmt.Println("🔍 Debug mode enabled")
+ }
+
if model != "" {
cfg.Agents.Defaults.ModelName = model
}
diff --git a/cmd/picoclaw/internal/helpers.go b/cmd/picoclaw/internal/helpers.go
index 2a59433ed..156a22726 100644
--- a/cmd/picoclaw/internal/helpers.go
+++ b/cmd/picoclaw/internal/helpers.go
@@ -6,6 +6,7 @@ import (
"github.com/sipeed/picoclaw/pkg"
"github.com/sipeed/picoclaw/pkg/config"
+ "github.com/sipeed/picoclaw/pkg/logger"
)
const Logo = pkg.Logo
@@ -28,7 +29,12 @@ func GetConfigPath() string {
}
func LoadConfig() (*config.Config, error) {
- return config.LoadConfig(GetConfigPath())
+ cfg, err := config.LoadConfig(GetConfigPath())
+ if err != nil {
+ return nil, err
+ }
+ logger.SetLevelFromString(cfg.Agents.Defaults.LogLevel)
+ return cfg, nil
}
// FormatVersion returns the version string with optional git commit
diff --git a/cmd/picoclaw/internal/onboard/command.go b/cmd/picoclaw/internal/onboard/command.go
index 9f8b288c6..1f94c6718 100644
--- a/cmd/picoclaw/internal/onboard/command.go
+++ b/cmd/picoclaw/internal/onboard/command.go
@@ -16,14 +16,22 @@ func NewOnboardCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "onboard",
Aliases: []string{"o"},
- Short: "Initialize picoclaw configuration and workspace",
+ Short: "Initialize picoclaw configuration, workspace, and channel accounts",
+ // Run without subcommands → original onboard flow
Run: func(cmd *cobra.Command, args []string) {
- onboard(encrypt)
+ if len(args) == 0 {
+ onboard(encrypt)
+ } else {
+ _ = cmd.Help()
+ }
},
}
cmd.Flags().BoolVar(&encrypt, "enc", false,
"Enable credential encryption (generates SSH key and prompts for passphrase)")
+ // Channel onboarding subcommands
+ cmd.AddCommand(newWeixinCommand())
+
return cmd
}
diff --git a/cmd/picoclaw/internal/onboard/command_test.go b/cmd/picoclaw/internal/onboard/command_test.go
index 56936190b..6b9fb6e95 100644
--- a/cmd/picoclaw/internal/onboard/command_test.go
+++ b/cmd/picoclaw/internal/onboard/command_test.go
@@ -13,7 +13,7 @@ func TestNewOnboardCommand(t *testing.T) {
require.NotNil(t, cmd)
assert.Equal(t, "onboard", cmd.Use)
- assert.Equal(t, "Initialize picoclaw configuration and workspace", cmd.Short)
+ assert.Equal(t, "Initialize picoclaw configuration, workspace, and channel accounts", cmd.Short)
assert.Len(t, cmd.Aliases, 1)
assert.True(t, cmd.HasAlias("o"))
@@ -28,5 +28,6 @@ func TestNewOnboardCommand(t *testing.T) {
encFlag := cmd.Flags().Lookup("enc")
require.NotNil(t, encFlag, "expected --enc flag to be registered")
assert.Equal(t, "false", encFlag.DefValue, "--enc should default to false")
- assert.False(t, cmd.HasSubCommands())
+ assert.True(t, cmd.HasSubCommands())
+ assert.NotNil(t, cmd.Commands())
}
diff --git a/cmd/picoclaw/internal/onboard/weixin.go b/cmd/picoclaw/internal/onboard/weixin.go
new file mode 100644
index 000000000..2e1c2ad75
--- /dev/null
+++ b/cmd/picoclaw/internal/onboard/weixin.go
@@ -0,0 +1,124 @@
+package onboard
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/spf13/cobra"
+
+ "github.com/sipeed/picoclaw/cmd/picoclaw/internal"
+ "github.com/sipeed/picoclaw/pkg/channels/weixin"
+ "github.com/sipeed/picoclaw/pkg/config"
+)
+
+func newWeixinCommand() *cobra.Command {
+ var baseURL string
+ var proxy string
+ var timeout int
+
+ cmd := &cobra.Command{
+ Use: "weixin",
+ Short: "Connect a WeChat personal account via QR code",
+ Long: `Start the interactive Weixin (WeChat personal) QR code login flow.
+
+A QR code is displayed in the terminal. Scan it with the WeChat mobile app
+to authorize your account. On success, the bot token is saved to the picoclaw
+config so you can start the gateway immediately.
+
+Example:
+ picoclaw onboard weixin`,
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ return runWeixinOnboard(baseURL, proxy, time.Duration(timeout)*time.Second)
+ },
+ }
+
+ cmd.Flags().StringVar(&baseURL, "base-url", "https://ilinkai.weixin.qq.com/", "iLink API base URL")
+ cmd.Flags().StringVar(&proxy, "proxy", "", "HTTP proxy URL (e.g. http://localhost:7890)")
+ cmd.Flags().IntVar(&timeout, "timeout", 300, "Login timeout in seconds")
+
+ return cmd
+}
+
+func runWeixinOnboard(baseURL, proxy string, timeout time.Duration) error {
+ fmt.Println("Starting Weixin (WeChat personal) login...")
+ fmt.Println()
+
+ botToken, userID, accountID, returnedBaseURL, err := weixin.PerformLoginInteractive(
+ context.Background(),
+ weixin.AuthFlowOpts{
+ BaseURL: baseURL,
+ Timeout: timeout,
+ Proxy: proxy,
+ },
+ )
+ if err != nil {
+ return fmt.Errorf("login failed: %w", err)
+ }
+
+ fmt.Println()
+ fmt.Println("✅ Login successful!")
+ fmt.Printf(" Account ID : %s\n", accountID)
+ if userID != "" {
+ fmt.Printf(" User ID : %s\n", userID)
+ }
+ fmt.Println()
+
+ // Prefer the server-returned base URL (may be region-specific)
+ effectiveBaseURL := returnedBaseURL
+ if effectiveBaseURL == "" {
+ effectiveBaseURL = baseURL
+ }
+
+ if err := saveWeixinConfig(botToken, effectiveBaseURL, proxy); err != nil {
+ fmt.Printf("⚠️ Could not auto-save to config: %v\n", err)
+ printManualWeixinConfig(botToken, effectiveBaseURL)
+ return nil
+ }
+
+ fmt.Println("✓ Config updated. Start the gateway with:")
+ fmt.Println()
+ fmt.Println(" picoclaw gateway")
+ fmt.Println()
+ fmt.Println("To restrict which WeChat users can send messages, add their user IDs")
+ fmt.Println("to channels.weixin.allow_from in your config.")
+
+ return nil
+}
+
+// saveWeixinConfig patches channels.weixin in the config and saves it.
+func saveWeixinConfig(token, baseURL, proxy string) error {
+ cfgPath := internal.GetConfigPath()
+
+ cfg, err := config.LoadConfig(cfgPath)
+ if err != nil {
+ return fmt.Errorf("failed to load config: %w", err)
+ }
+
+ cfg.Channels.Weixin.Enabled = true
+ cfg.Channels.Weixin.SetToken(token)
+ const defaultBase = "https://ilinkai.weixin.qq.com/"
+ if baseURL != "" && baseURL != defaultBase {
+ cfg.Channels.Weixin.BaseURL = baseURL
+ }
+ if proxy != "" {
+ cfg.Channels.Weixin.Proxy = proxy
+ }
+
+ return config.SaveConfig(cfgPath, cfg)
+}
+
+func printManualWeixinConfig(token, baseURL string) {
+ fmt.Println()
+ fmt.Println("Add the following to the channels section of your picoclaw config:")
+ fmt.Println()
+ fmt.Println(` "weixin": {`)
+ fmt.Println(` "enabled": true,`)
+ fmt.Printf(" \"token\": %q,\n", token)
+ const defaultBase = "https://ilinkai.weixin.qq.com/"
+ if baseURL != "" && baseURL != defaultBase {
+ fmt.Printf(" \"base_url\": %q,\n", baseURL)
+ }
+ fmt.Println(` "allow_from": []`)
+ fmt.Println(` }`)
+}
diff --git a/config/config.example.json b/config/config.example.json
index 81c9014ec..69e8feeae 100644
--- a/config/config.example.json
+++ b/config/config.example.json
@@ -1,6 +1,7 @@
{
"agents": {
"defaults": {
+ "log_level": "fatal",
"workspace": "~/.picoclaw/workspace",
"restrict_to_workspace": true,
"model_name": "gpt-5.4",
diff --git a/docker/Dockerfile.heavy b/docker/Dockerfile.heavy
new file mode 100644
index 000000000..cbc243e39
--- /dev/null
+++ b/docker/Dockerfile.heavy
@@ -0,0 +1,67 @@
+# ============================================================
+# 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: Node.js runtime with Python + MCP support
+# ============================================================
+FROM node:24-alpine3.23
+
+RUN apk add --no-cache \
+ ca-certificates \
+ curl \
+ git \
+ python3 \
+ py3-pip \
+ chromium \
+ jq
+
+# Install Playwright browsers for agent-browser
+ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
+RUN npm install -g agent-browser && \
+ npx playwright install chromium && \
+ chmod -R o+rx $PLAYWRIGHT_BROWSERS_PATH
+
+# Install uv
+RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
+ ln -s /root/.local/bin/uv /usr/local/bin/uv && \
+ ln -s /root/.local/bin/uvx /usr/local/bin/uvx && \
+ uv --version
+
+# 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
+
+# Reuse existing node user (UID/GID 1000) — rename to picoclaw
+RUN deluser node 2>/dev/null; delgroup node 2>/dev/null; \
+ addgroup -g 1000 picoclaw 2>/dev/null; \
+ adduser -D -u 1000 -G picoclaw -h /home/picoclaw picoclaw 2>/dev/null || true
+
+USER picoclaw
+
+# Run onboard to create initial directories and config
+RUN /usr/local/bin/picoclaw onboard
+
+# Copy default workspace
+COPY --chown=picoclaw:picoclaw workspace/ /home/picoclaw/.picoclaw/workspace/
+
+VOLUME /home/picoclaw/.picoclaw/workspace
+
+ENTRYPOINT ["picoclaw"]
+CMD ["gateway"]
diff --git a/docs/channels/weixin/README.md b/docs/channels/weixin/README.md
new file mode 100644
index 000000000..22687fec4
--- /dev/null
+++ b/docs/channels/weixin/README.md
@@ -0,0 +1,58 @@
+# 💬 Weixin (WeChat Personal) Channel
+
+PicoClaw supports connecting to your personal WeChat account using the official Tencent iLink API.
+
+## 🚀 Quick Onboarding
+
+The easiest way to set up the Weixin channel is using the interactive onboarding command:
+
+```bash
+picoclaw onboard weixin
+```
+
+This command will:
+1. Request a QR code from the iLink API and display it in your terminal.
+2. Wait for you to scan the QR code with your WeChat mobile app.
+3. Upon approval, automatically save the generated access token to your `~/.picoclaw/config.json`.
+
+After onboarding, you can start the gateway:
+
+```bash
+picoclaw gateway
+```
+
+---
+
+## ⚙️ Configuration
+
+You can also manually configure the filter rules in `config.json` under the `channels.weixin` section.
+
+```json
+{
+ "channels": {
+ "weixin": {
+ "enabled": true,
+ "token": "YOUR_WEIXIN_TOKEN",
+ "allow_from": [
+ "user_id_1",
+ "user_id_2"
+ ],
+ "proxy": ""
+ }
+ }
+}
+```
+
+### Configuration Fields
+
+| Field | Description |
+|---|---|
+| `enabled` | Set to `true` to enable the channel at startup. |
+| `token` | The authentication token obtained via QR login. |
+| `allow_from` | (Optional) List of WeChat User IDs permitted to interact with the bot. If empty, anyone who can send messages to the connected account can trigger the bot. |
+| `proxy` | (Optional) HTTP proxy address (e.g. `http://localhost:7890`) for environments where connection to `ilinkai.weixin.qq.com` is restricted. |
+
+## ⚠️ Important Notes
+
+- **One Account Only**: The iLink token binds to a single session. Starting a new interaction generally invalidates older tokens if another device authorizes.
+- **Message Rate Limits**: To avoid getting your account restricted by WeChat anti-spam systems, avoid loop triggers or high-frequency broadcasts.
diff --git a/docs/channels/weixin/README.zh.md b/docs/channels/weixin/README.zh.md
new file mode 100644
index 000000000..d5e6f0a49
--- /dev/null
+++ b/docs/channels/weixin/README.zh.md
@@ -0,0 +1,58 @@
+# 💬 微信个人号渠道 (Weixin)
+
+PicoClaw 支持使用腾讯官方 iLink API 连接您的个人微信账号。
+
+## 🚀 快速激活
+
+最简单的方法是使用交互式 onboarding 命令进行一键激活:
+
+```bash
+picoclaw onboard weixin
+```
+
+该命令将:
+1. 从 iLink API 获取二维码并在终端中打印。
+2. 等待您使用手机微信 App 扫码。
+3. 扫码确认后,自动将生成的 Access Token 保存至您的 `~/.picoclaw/config.json` 中。
+
+配置完成后,即可启动网关:
+
+```bash
+picoclaw gateway
+```
+
+---
+
+## ⚙️ 配置说明
+
+您也可以在 `config.json` 的 `channels.weixin` 段目下进行手动维护。
+
+```json
+{
+ "channels": {
+ "weixin": {
+ "enabled": true,
+ "token": "YOUR_WEIXIN_TOKEN",
+ "allow_from": [
+ "user_id_1",
+ "user_id_2"
+ ],
+ "proxy": ""
+ }
+ }
+}
+```
+
+### 字段解析
+
+| 字段 | 说明 |
+|---|---|
+| `enabled` | 设置为 `true` 以在启动时激活该频道。 |
+| `token` | 通过扫码获取的认证令牌。 |
+| `allow_from` | (可选) 允许与机器人交互的微信 User ID 列表。如果为空,任何能给此微信号发消息的人都可以触发机器人。 |
+| `proxy` | (可选) HTTP 代理地址(例如 `http://localhost:7890`),适合网络访问受限环境。 |
+
+## ⚠️ 注意事项
+
+- **单端绑定**: iLink 令牌通常与单个会话绑定。在其他地方重新扫码激活可能会导致旧令牌失效。
+- **频率控制**: 为避免触发微信的风控反垃圾机制,请避免设置死循环触发、高频广播等恶意行为。
diff --git a/docs/chat-apps.md b/docs/chat-apps.md
index 3ed37e814..07297952a 100644
--- a/docs/chat-apps.md
+++ b/docs/chat-apps.md
@@ -13,6 +13,7 @@ Talk to your picoclaw through Telegram, Discord, WhatsApp, Matrix, QQ, DingTalk,
| **Telegram** | ⭐ Easy | Recommended, voice-to-text, long polling (no public IP needed) | [Docs](../channels/telegram/README.md) |
| **Discord** | ⭐ Easy | Socket Mode, group/DM support, rich bot ecosystem | [Docs](../channels/discord/README.md) |
| **WhatsApp** | ⭐ Easy | Native (QR scan) or Bridge URL | [Docs](#whatsapp) |
+| **Weixin** | ⭐ Easy | Native QR scan (Tencent iLink API) | [Docs](../channels/weixin/README.md) |
| **Slack** | ⭐ Easy | **Socket Mode** (no public IP needed), enterprise | [Docs](../channels/slack/README.md) |
| **Matrix** | ⭐⭐ Medium | Federated protocol, self-hosting supported | [Docs](../channels/matrix/README.md) |
| **QQ** | ⭐⭐ Medium | Official bot API, Chinese community | [Docs](../channels/qq/README.md) |
@@ -169,6 +170,39 @@ If `session_store_path` is empty, the session is stored in `