Skill: milkteawithsoybeanmilktast/openclaw-multi-instance (version 1.0.0)

--- name: openclaw-multi-instance description: "OpenClaw 多实例互联。当用户有多台机器运行 OpenClaw，想让它们互相通信、传文件、共享记忆时使用。触发词：多实例、互联、另一台服务器、同步记忆、实例通信。" --- # OpenClaw 多实例互联 让多台机器上的 OpenClaw 实例互相通信、传文件、共享记忆，实现无感切换。 ## 什么时候用这个 Skill - 用户说"我还有一台服务器也跑了 OpenClaw" - 用户想从一台 OpenClaw 操控另一台 - 用户想让多台实例共享记忆/身份 - 用户提到多实例互联、同步等需求 ## 架构概览 ``` ┌──────────────────┐ ┌──────────────────┐ │ 实例 A (本地) │ │ 实例 B (远程) │ │ │ SSH │ │ │ OpenClaw ────┼────────▶│ OpenClaw │ │ │ API │ │ └──────────────────┘ └──────────────────┘ │ │ └──── 定期对账（记忆叠加合并） ──┘ ``` 三种能力： 1. **API 通信** — 实例之间直接对话 2. **SSH 传文件** — 免密传输任意大小文件 3. **记忆同步** — 定期合并记忆，不覆盖，只叠加 ## 搭建流程 ### Step 1: 开启远程实例的 API Endpoint 远程实例需要在配置中开启 Chat Completions endpoint： ```json5 // ~/.openclaw/openclaw.json { gateway: { http: { endpoints: { chatCompletions: { enabled: true }, }, }, }, } ``` 然后重启 Gateway：`openclaw gateway restart` **验证**：用 curl 测试： ```bash curl -sS http://<远程IP>:<端口>/v1/chat/completions \ -H 'Authorization: Bearer <TOKEN>' \ -H 'Content-Type: application/json' \ -d '{"model":"openclaw:main","messages":[{"role":"user","content":"ping"}]}' ``` **获取 Token**：查看远程实例的 `gateway.auth.token` 或环境变量 `OPENCLAW_GATEWAY_TOKEN`。 ### Step 2: 配置 SSH 免密登录 **如果本地有公钥**，直接让远程实例帮忙加： ``` 通过 API 告诉远程实例： "请把以下公钥加到 authorized_keys： <本地 cat ~/.ssh/id_ed25519.pub 的内容> 追加到 ~/.ssh/authorized_keys，chmod 600" ``` **如果远程有公钥**，本地加： ```bash # 获取远程公钥（通过 API 问远程实例要） # 然后本地： echo "<公钥内容>" >> ~/.ssh/authorized_keys ``` **测试**：`ssh <用户>@<远程IP> "echo OK"` ### Step 3: 同步核心文件 通过 scp 把身份和记忆同步过去： ```bash scp IDENTITY.md SOUL.md USER.md MEMORY.md TOOLS.md AGENTS.md \ <用户>@<远程IP>:<远程workspace路径>/ scp -r memory/ <用户>@<远程IP>:<远程workspace路径>/memory/ ``` **注意权限**：远程文件要 chown 给正确的用户。 ### Step 4: 设置定期记忆同步 创建 cron job，定期执行记忆合并。 **合并原则（核心）**： - **叠加，不覆盖** — 只增不减 - MEMORY.md：对比两边，互相补齐对方没有的内容段 - memory/ 日记（YYYY-MM-DD.md）：按日期补齐缺失文件，同一天的去重合并 - 冲突内容保留两份，标记来源 `<!-- 来源: 实例A -->` **Cron 配置建议**：每周一次，比如周日 00:00 ``` sessionTarget: isolated schedule: { kind: "cron", expr: "0 0 * * 0", tz: "Asia/Shanghai" } payload: { kind: "agentTurn", message: "执行记忆同步：<1> scp 拉取远程的 MEMORY.md 和 memory/ <2> 叠加合并 <3> 推送合并结果回远程 <4> 输出同步报告" } ``` ## 使用方式 搭建完成后，跟本地实例说： - "让服务器做 XXX" → 本地通过 API 转达给远程 - "把这个文件传到服务器" → scp 直传 - "查一下服务器的 XXX" → API 转达 跟远程实例说也一样，体验无感切换。 ## 文件说明 本 skill 包含： | 文件 | 作用 | |------|------| | `SKILL.md` | 本文件，给 OpenClaw 的搭建指南 | | `scripts/sync-memory.sh` | 记忆同步脚本（可选，也可以让 agent 自行合并） | | `references/peer-config.json5` | 远程实例连接信息模板 | ## 安全提醒 ⚠️ 必须告诉用户： - API [REDACTED_SECRET] - 公网 IP 的实例建议用 Tailscale，不要直接暴露 - SSH key 建议加密码保护 - TOOLS.md 里可能有 API key，同步时注意安全

path: README.md
content: # 🐱 OpenClaw 多实例互联

> 让你的多个 OpenClaw 实例互相通信、传文件、共享记忆，像用一个一样。

## 这是什么？

如果你有多台机器（Mac + 服务器、家里 + 公司、多台 VPS），每台都跑了 OpenClaw，默认它们是互相不知道的。

这个项目让你的多个 OpenClaw 实例：

- 🔗 **互相通信** — 一个实例可以通过 API 直接跟另一个对话
- 📁 **互传文件** — SSH 免密传输，大小不限
- 🧠 **共享记忆** — 定期自动合并记忆文件，两边切换无感知
- 🤝 **协同工作** — 跟任何一个实例说，都能指挥其他实例

## 能做什么？

### 🗣️ 互相通信 — 一个入口，指挥所有实例

跟任何一个实例聊天，都能操控其他机器：

```
你 → Mac 实例："让服务器重启一下 nginx"
     Mac 实例 → [API] → 服务器实例执行 → 结果返回给你

你 → 服务器实例："帮我整理一下 Mac 桌面的文件"
     服务器实例 → [API] → Mac 实例执行 → 结果返回给你
```

### 📁 互传文件 — 随时随地收发

```
你 → "把服务器上的 /var/log/nginx/error.log 拉过来看看"
     Mac 实例 → [scp] → 从服务器拉文件 → 展示内容给你

你 → "把这个脚本部署到服务器上"
     Mac 实例 → [scp] → 推到服务器 → 远程执行部署
```

### 🧠 共享记忆 — 两边切换无感知

在 Mac 上聊的事，服务器也知道；在服务器上的操作，Mac 也记得。

```
周一在 Mac 上："我最近在学 Rust"
周二在服务器上："帮我写个 Rust 的 hello world"  → 它知道你在学 Rust！

每周日 00:00 自动对账：
  Mac 的记忆 + 服务器的记忆 → 叠加合并 → 两边同步到最新
```

### 🤝 协同工作 — 复杂任务多机协作

```
你："我要部署一个新项目"
     Mac 实例：在本地打包构建
           ↓ 通过 scp 传到服务器
     服务器实例：部署、启动服务、检查健康状态
           ↓ 结果回传
     Mac 实例：汇总报告给你

你："查一下我所有机器的运行状态"
     Mac 实例 → 查本地 CPU/内存/磁盘
     Mac 实例 → [API] → 服务器实例查服务器状态
     → 汇总成一份报告展示给你
```

### 🔧 互相学习 — Skills 和配置同步

```
你："服务器上也装一下 tavily 搜索"
     Mac 实例 → [API] → 告诉服务器实例 clawhub install

你："把我这边的配置同步给服务器"
     Mac 实例 → [scp] → 传配置文件 → 服务器实例应用
```

### 架构总览

```
┌─────────────────────────────────────────────────┐
│                    你（一个聊天窗口就行）            │
└──────────┬──────────────────┬────────────────────┘
           │                  │
    ┌──────▼──────┐    ┌──────▼──────┐
    │  Mac 实例    │    │  服务器实例   │
    │  (实例 A 🐱)  │    │  (实例 B 🤖)  │
    └──┬───────┬──┘    └──┬───────┬──┘
       │       │          │       │
       │  API  ──────────▶│       │
       │  (对话/指挥)      │       │
       │       │          │       │
       │  scp  ──────────▶│       │
       │  (传文件)         │       │
       │       │          │       │
       │◀────── scp/API ──│       │
       │       │          │       │
    ┌──▼───────▼──────────▼───────▼──┐
    │     每周日 00:00 自动对账        │
    │     记忆叠加合并，永不覆盖        │
    └───────────────────────────────┘
```

## 使用方式

### 方式一：作为 Skill 安装（推荐）

```bash
clawhub install https://github.com/<你的用户名>/openclaw-multi-instance
```

安装后，跟你的 OpenClaw 说：

> "我还有一台服务器也跑了 OpenClaw，帮我设置互联，地址是 xxx"

你的 OpenClaw 会读取 SKILL.md，一步步引导你完成设置。

### 方式二：手动搭建

按照 SKILL.md 里的步骤操作，四步搞定：

1. **开启 API** — 远程实例配置里加一行
2. **SSH 免密** — 一条命令搞定
3. **同步文件** — scp 把身份和记忆传过去
4. **设置对账** — cron 定期合并记忆

## 文件结构

```
openclaw-multi-instance/
├── README.md                        # 你正在看的这个
├── SKILL.md                         # 给 OpenClaw 的搭建指南
├── scripts/
│   └── sync-memory.sh               # 记忆同步脚本
├── references/
│   └── peer-config.json5            # 远程实例配置模板
└── .gitignore
```

## 记忆同步原理

这是这个方案的核心——**叠加合并，永不覆盖**：

| 情况 | 处理方式 |
|------|---------|
| A 有 B 没有的记忆 | 补给 B |
| B 有 A 没有的记忆 | 补给 A |
| 两边都有相同内容 | 保留一份，去重 |
| 两边内容冲突 | 都保留，标记来源 |

默认每周日 00:00 自动执行，也可以手动触发。

## 安全提醒

- ⚠️ API Token 等于完全控制权，不要泄露
- ⚠️ 公网实例建议用 Tailscale 组网，不要直接暴露端口
- ⚠️ `peer-config.local.json5` 包含敏感信息，已在 .gitignore 中
- ⚠️ SSH key 建议加密码保护

## License

MIT

---

*多实例互联，一个聊天窗口搞定所有机器*


path: references/peer-config.json5
content: // 远程 OpenClaw 实例连接信息模板
// 复制为 peer-config.local.json5 并填入你的信息
// ⚠️ 不要把 .local.json5 提交到 Git！

{
  // 远程实例别名
  name: "我的服务器",

  // 连接信息
  host: "your-server-ip",          // IP 或域名
  port: 16841,                    // Gateway 端口
  basePath: "",                   // 如果有 basePath 则填写，如 "/718872eb"

  // API 认证
  [REDACTED_SECRET]",    // gateway.auth.token

  // SSH 连接（用于传文件）
  ssh: {
    user: "root",
    host: "your-server-ip",
    workspacePath: "/home/admin/.openclaw/workspace",  // 远程 workspace 路径
  },

  // 记忆同步
  sync: {
    enabled: true,
    schedule: "0 0 * * 0",        // cron 表达式，默认每周日 00:00
    timezone: "Asia/Shanghai",
  },
}


path: scripts/sync-memory.sh
content: #!/bin/bash
# OpenClaw 多实例记忆同步脚本
# 用法: ./sync-memory.sh <远程用户@远程IP> <远程workspace路径>
# 例: ./sync-memory.sh root@your-server-ip /home/user/.openclaw/workspace

set -euo pipefail

REMOTE="$1"
REMOTE_WS="$2"
LOCAL_WS="${3:-$HOME/.openclaw/workspace}"
TMP_DIR=$(mktemp -d)

echo "🧠 OpenClaw 记忆同步"
echo "===================="
echo "本地: $LOCAL_WS"
echo "远程: $REMOTE:$REMOTE_WS"
echo ""

# 1. 拉取远程文件
echo "📥 拉取远程记忆..."
scp "$REMOTE:$REMOTE_WS/MEMORY.md" "$TMP_DIR/remote-MEMORY.md" 2>/dev/null || echo "# 远程 MEMORY 为空" > "$TMP_DIR/remote-MEMORY.md"
mkdir -p "$TMP_DIR/remote-memory"
scp -r "$REMOTE:$REMOTE_WS/memory/"* "$TMP_DIR/remote-memory/" 2>/dev/null || true

# 2. 合并 MEMORY.md（叠加，不覆盖）
echo "🔄 合并 MEMORY.md..."
if [ -f "$LOCAL_WS/MEMORY.md" ]; then
    # 找出远程有但本地没有的段落
    # 简单策略：把远程内容追加到本地末尾（去重）
    python3 -c "
import sys

def read_file(path):
    try:
        with open(path, 'r') as f:
            return f.read()
    except:
        return ''

local = read_file('$LOCAL_WS/MEMORY.md')
remote = read_file('$TMP_DIR/remote-MEMORY.md')

# 按段落分割（以 ## 或 ### 开头的行作为分隔符）
local_lines = set(line.strip() for line in local.split('\n') if line.strip())
remote_lines = set(line.strip() for line in remote.split('\n') if line.strip())

# 远程有但本地没有的行
new_lines = [line for line in remote.split('\n') if line.strip() not in local_lines]

if new_lines:
    # 找出哪些段落是新的（以 # 开头）
    new_sections = []
    current_section = []
    for line in remote.split('\n'):
        stripped = line.strip()
        if stripped.startswith('## ') and stripped not in local:
            if current_section:
                new_sections.append('\n'.join(current_section))
            current_section = [line]
        elif current_section:
            if stripped and stripped not in local_lines:
                current_section.append(line)
        # skip lines that already exist locally
    
    # 追加到本地
    with open('$LOCAL_WS/MEMORY.md', 'a') as f:
        f.write('\n\n<!-- 来自远程实例的同步内容 -->\n')
        for section in new_sections:
            f.write(section + '\n')
    print(f'  本地新增 {len(new_sections)} 个段落')
else:
    print('  本地无需更新')

# 同样，本地有但远程没有的
local_only = [line for line in local.split('\n') if line.strip() not in remote_lines]
if local_only:
    with open('$TMP_DIR/merged-remote-MEMORY.md', 'w') as f:
        f.write(remote)
        f.write('\n\n<!-- 来自远程实例的同步内容 -->\n')
        for line in local_only:
            f.write(line + '\n')
    print(f'  远程新增 {len([l for l in local_only if l.startswith(\"##\")])} 个段落')
else:
    # 远程不需要更新
    import shutil
    shutil.copy('$TMP_DIR/remote-MEMORY.md', '$TMP_DIR/merged-remote-MEMORY.md')
    print('  远程无需更新')
" 2>&1
fi

# 3. 同步 memory/ 日记文件（按日期补齐）
echo "🔄 同步 memory/ 日记..."
mkdir -p "$LOCAL_WS/memory"

# 本地缺失的 → 从远程拉
for f in "$TMP_DIR/remote-memory/"*.md; do
    [ -f "$f" ] || continue
    fname=$(basename "$f")
    if [ ! -f "$LOCAL_WS/memory/$fname" ]; then
        cp "$f" "$LOCAL_WS/memory/$fname"
        echo "  📥 拉取: $fname"
    else
        # 同一天的文件，合并去重
        python3 -c "
local_lines = set()
remote_new = []
with open('$LOCAL_WS/memory/$fname', 'r') as f:
    for line in f:
        local_lines.add(line.strip())
with open('$f', 'r') as f:
    for line in f:
        if line.strip() and line.strip() not in local_lines:
            remote_new.append(line)
if remote_new:
    with open('$LOCAL_WS/memory/$fname', 'a') as f:
        f.write('\n<!-- 来自远程实例 -->\n')
        f.writelines(remote_new)
    print(f'  🔀 合并: $fname (+{len(remote_new)} 行)')
" 2>&1
    fi
done

# 远程缺失的 → 推过去
for f in "$LOCAL_WS/memory/"*.md; do
    [ -f "$f" ] || continue
    fname=$(basename "$f")
    if [ ! -f "$TMP_DIR/remote-memory/$fname" ]; then
        scp "$f" "$REMOTE:$REMOTE_WS/memory/$fname"
        echo "  📤 推送: $fname"
    fi
done

# 4. 推送合并后的 MEMORY.md 回远程
echo "📤 推送合并后的 MEMORY.md..."
scp "$TMP_DIR/merged-remote-MEMORY.md" "$REMOTE:$REMOTE_WS/MEMORY.md" 2>/dev/null || true

# 5. 清理
rm -rf "$TMP_DIR"

echo ""
echo "✅ 记忆同步完成！"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)