11 KiB
OpenClaw 多实例兼容性优化方案
更新时间:2026-03-31 01:07:53 +08:00
背景
当前 ClawPanel 已经具备“实例切换”的一部分界面与数据结构,但底层仍然以单一 OpenClaw 根目录为默认前提。这会在以下场景中产生明显冲突:
- 同一台机器存在多个 OpenClaw 安装目录。
- 用户手动切换了实例,但某些页面仍然读写旧路径。
- Tauri 桌面端与 Web dev-api 对实例的理解不一致。
- 多个 OpenClaw 同时运行时,Gateway 名称、Bonjour 广播、端口、配置文件读写可能相互干扰。
这个问题不是单个页面写死路径,而是“实例选择层”和“本地路径解析层”没有完成统一抽象。
现状诊断
1. 现有能力
当前仓库已经有三类相关能力:
- 面板设置页支持单个自定义 OpenClaw 路径。
- 前端侧边栏支持实例切换 UI。
- Web dev-api 具备实例列表、添加、删除、切换能力。
2. 当前架构缺口
2.1 单路径配置不等于多实例支持
Tauri 侧当前通过 clawpanel.json.openclawDir 决定唯一生效目录,本质上仍然是“全局单路径覆盖”,不是“多实例上下文切换”。
2.2 大量命令直接依赖单一根目录
Rust 侧很多命令直接调用统一的 openclaw_dir(),例如:
- Agent 管理
- Memory
- Skills
- Messaging
- Service
- Pairing
- Config 读写
这意味着只要当前根目录解析不正确,多个页面都会一起读错目录。
2.3 桌面端与 Web 端实例模型不一致
前端 API 里 instance_* 被标记为仅 Web 后端实现,说明“实例管理”目前主要停留在 dev-api 层,而桌面端大量本地读写命令仍走 Tauri Rust 本地目录解析。
结果就是:
- 前端能显示实例切换。
- 真实文件读写却未必跟随实例切换。
- 用户会感觉“切了实例,但操作的还是另一个 OpenClaw”。
2.4 本地多实例冲突缺少显式选择
当系统中检测到多个 OpenClaw 安装时,当前没有统一的冲突选择弹窗,也没有清晰的“当前操作对象是谁”的确认流程。对于会修改配置、插件、Agent 文件的操作,这个缺口风险很高。
根因
根因可以归纳为一句话:
ClawPanel 目前有“实例列表”,但没有“实例上下文驱动的路径解析内核”。
也就是说,实例是 UI 概念,不是系统级资源定位概念。
目标
本次优化的目标不是简单把路径输入框改成下拉框,而是建立完整的一套实例上下文机制。
功能目标
- 支持同时管理多个本地 OpenClaw 安装目录。
- 支持远程实例、Docker 实例、本地实例统一出现在实例中心。
- 所有本地文件读写类能力都基于“当前激活实例”解析路径。
- 检测到多个候选 OpenClaw 时,必须弹窗让用户明确选择。
- 用户可以手动新增、重命名、移除、设为默认本地实例。
- 高风险操作前能明确显示当前目标实例与路径。
体验目标
- 不允许“静默写错目录”。
- 不允许“界面切换了实例,后端仍操作旧实例”。
- 当前激活实例必须在侧边栏、详情页、设置页都可见。
- 冲突时优先询问用户,不做隐式猜测。
设计原则
- 统一实例抽象,不再区分“本地路径选择”和“实例切换”两套逻辑。
- 本地实例必须有稳定 ID,不能只靠路径字符串临时判断。
- 路径解析必须收敛到单一入口函数,禁止业务模块自行拼接根目录。
- 冲突选择必须是显式交互,不能偷偷回退默认目录。
- Web 模式与桌面模式的数据模型必须一致。
数据模型改造
建议将“实例”扩展为统一模型:
{
"activeInstanceId": "local-main",
"instances": [
{
"id": "local-main",
"name": "本机主实例",
"type": "local",
"openclawDir": "C:/Users/user/.openclaw",
"gatewayPort": 18789,
"version": "3.28.0",
"detected": true,
"isDefault": true,
"fingerprint": "sha1:...",
"lastSeenAt": 1774890473
},
{
"id": "local-dev",
"name": "开发实例",
"type": "local",
"openclawDir": "D:/OpenClaw/dev",
"gatewayPort": 28789,
"version": "3.28.0",
"detected": false,
"isDefault": false,
"fingerprint": "sha1:...",
"lastSeenAt": 1774890473
},
{
"id": "remote-xxxx",
"name": "远程节点",
"type": "remote",
"endpoint": "http://192.168.1.8:18789"
}
]
}
字段说明
id:稳定实例 ID。type:local、remote、docker。openclawDir:仅本地实例必填。fingerprint:用于识别是否是同一个 OpenClaw 实例,避免路径变更后丢失绑定关系。activeInstanceId:全局激活实例,不再由单独的openclawDir决定一切。
路径解析内核
统一入口
新增统一上下文解析函数:
- Rust:
resolve_active_instance_context() - Node dev-api:
resolveActiveInstanceContext()
返回结构建议为:
{
"id": "local-dev",
"type": "local",
"name": "开发实例",
"openclawDir": "D:/OpenClaw/dev",
"configPath": "D:/OpenClaw/dev/openclaw.json",
"agentsDir": "D:/OpenClaw/dev/agents",
"workspaceDir": "D:/OpenClaw/dev/workspace"
}
禁止继续直接使用全局根目录
后续所有本地资源读写都应从实例上下文取值,不再在业务代码中直接调用全局默认目录。需要逐步替换以下模式:
openclaw_dir().join("openclaw.json")openclaw_dir().join("agents")OPENCLAW_DIR + ...- 基于固定
~/.openclaw的路径常量
实例发现与冲突检测
自动发现来源
建议本地实例发现至少覆盖以下来源:
- 默认目录:
~/.openclaw - 面板历史记录中的自定义目录
- 用户手动添加过的目录
- 最近成功运行 Gateway 的目录
判定一个目录是不是有效 OpenClaw
满足以下条件之一即可视为候选实例:
- 存在
openclaw.json - 存在
agents、logs、workspace等关键结构 - 通过读取配置可得到有效 Gateway 配置或版本信息
冲突弹窗触发条件
出现以下任一情况时必须弹窗:
- 启动时发现 2 个及以上本地有效实例,且尚未指定默认实例。
- 当前默认实例路径不存在,但发现其他可用实例。
- 用户执行高风险写操作时,当前实例存在歧义。
- 发现 Bonjour 名称或 Gateway 端口冲突,需要区分具体实例。
交互方案
1. 启动冲突选择弹窗
当发现多个本地 OpenClaw 时,弹出实例选择框,展示:
- 实例名称
- 完整路径
- OpenClaw 版本
- Gateway 端口
- 最近使用时间
- 配置文件状态
提供按钮:
- 设为当前实例
- 设为默认实例
- 查看详情
- 手动选择其他目录
2. 侧边栏实例切换器增强
当前侧边栏已有实例切换区域,后续应增强为:
- 本地实例与远程实例分组显示
- 当前实例显示路径简写
- 高风险页面顶部显示“当前实例路径”
- 切换实例后触发全局上下文刷新
3. 设置页改造
当前“OpenClaw 安装路径”单输入框应升级为“本地实例管理器”:
- 列出全部本地实例
- 支持新增目录
- 支持校验目录有效性
- 支持设为默认
- 支持删除失效记录
实施分期
Phase 1:先打通实例上下文内核
目标:所有本地读写命令都能跟随当前实例。
改造项:
- 定义统一实例模型。
- 将
activeInstanceId作为全局当前实例标识。 - 在 Rust 与 dev-api 中新增统一上下文解析函数。
- Agent、Config、Memory、Skills 先切到新解析层。
Phase 2:补齐实例发现与冲突弹窗
目标:多实例存在时不再隐式写默认目录。
改造项:
- 启动扫描候选实例。
- 新增实例冲突弹窗。
- 新增“记住我的选择”。
- 启动阶段写入最近使用实例。
Phase 3:统一桌面端与 Web 端实例能力
目标:两套运行模式具有一致的数据模型与行为。
改造项:
- 将
instance_*能力从仅 Web 实现,补齐到 Tauri 端或抽象成统一后端层。 - 清理前端
WEB_ONLY_CMDS中与实例管理相关的分支差异。 - 统一实例切换后的缓存失效与页面刷新策略。
Phase 4:增加保护性提示与审计
目标:降低误操作风险。
改造项:
- 高风险写操作展示当前实例标识。
- 写配置前生成实例级备份。
- 记录最近操作的实例与路径。
受影响模块
以下模块需要优先排查和改造:
src-tauri/src/commands/mod.rssrc-tauri/src/commands/config.rssrc-tauri/src/commands/agent.rssrc-tauri/src/commands/memory.rssrc-tauri/src/commands/skills.rssrc-tauri/src/commands/messaging.rssrc-tauri/src/commands/service.rsscripts/dev-api.jssrc/lib/tauri-api.jssrc/lib/app-state.jssrc/components/sidebar.jssrc/pages/settings.js
迁移建议
配置兼容
旧版本仅有:
{
"openclawDir": "D:/OpenClaw/dev"
}
迁移后建议自动转换为:
{
"activeInstanceId": "local-migrated",
"instances": [
{
"id": "local-migrated",
"name": "迁移实例",
"type": "local",
"openclawDir": "D:/OpenClaw/dev",
"isDefault": true
}
]
}
兼容策略
- 首次迁移保留旧字段一段时间,只读不再写。
- 新逻辑优先读取实例模型。
- 若实例模型缺失,再回退读取旧
openclawDir。 - 一旦成功迁移,可在后续版本移除旧字段写入。
验收标准
满足以下条件,才算多实例兼容完成:
- 两个本地 OpenClaw 共存时,用户启动面板会看到明确选择。
- 切换本地实例后,Agent、Config、Skills、Memory、Channels 页面都读写对应实例目录。
- Tauri 与 Web 模式下,实例切换行为一致。
- 当前实例信息在 UI 中可见,不存在“我不知道现在在改谁”的状态。
- 任意高风险写操作都不会静默落到错误目录。
明确不建议的方案
以下做法不建议采用:
- 继续在更多页面增加单独的路径输入框。
- 只在前端记住当前实例,不改底层路径解析。
- 发现多个实例时自动猜测“最近修改时间最新的那个”。
- 仅修补 Agent 页面,不统一 Config、Memory、Skills 等其他模块。
建议的下一步落地顺序
- 先把实例数据模型统一下来。
- 再实现 Rust 和 dev-api 的上下文解析内核。
- 然后改 Agent 与 Config 两条主链路做首批验证。
- 最后补冲突弹窗和设置页实例管理器。
这样可以避免 UI 先做完,底层路径仍然写错的问题再次出现。