OpenClaw 源码升级实录:版本迁移、Node 环境统一与飞书插件兼容修复
本文由 AI 辅助整理,基于 2026-10-06 至 2026-10-07 的实际维护记录。由作者人工审核技术事实、命令示例和脱敏内容。
一次 OpenClaw 升级涉及的不只是源码版本。Node、状态数据库、插件 SDK、服务配置和实际业务链路,都可能随着版本变化需要调整。
这次维护将一台 Debian 设备上的 OpenClaw 从 2026.6.10 升级至当时核实的稳定版 2026.9.8。最终验收方式是向现有飞书机器人发送指令,让它创建一篇飞书文档、读回正文并返回真实链接。
这个验收步骤揭示了一个关键问题:进程启动成功、插件加载成功、健康检查通过,仍然不能替代真实业务验收。
一、升级目标与约束
原安装运行在一个增加了 GPT-5.6 harness 支持的定制源码分支上,Node 版本为 nvm 管理的 22.22.1。
本次升级遵循四项约束:
- OpenClaw 保持源码安装,不通过 npm 安装 OpenClaw 本体。
- Node 版本继续由 nvm 管理。
- 依赖 Node 的 skills、plugins 和子进程应与 gateway 使用一致的 Node 环境。
- 一次性暂存文件在任务完成后清理,恢复备份和验证证据保留。
需要区分“OpenClaw 本体安装”和“源码依赖安装”。源码构建仍然需要按照仓库要求,通过固定版本的 pnpm 安装依赖。
最终主要变化如下:
| 项目 | 升级前 | 升级后 |
|---|---|---|
| OpenClaw | 2026.6.10 定制分支 | 2026.9.8 官方稳定版源码 |
| 源码提交 | 定制提交 | fc23bc8 |
| Node | nvm 22.22.1 | nvm 24.19.0 |
| 状态管理 | 旧配置和会话存储 | 官方迁移后的配置与 SQLite |
| Lark 插件 | 适配旧 SDK | 保留原包版本,修复六个文件 |
| 托管服务 | 旧源码与旧 Node | 新源码与 Node 24.19.0 |
| 默认模型 | openai/gpt-5.6-sol | 保持不变 |
升级软件提供了新的模型接入能力,但不会自动把现有机器人的默认模型切换到新型号。
二、先准备恢复条件,再开始迁移
这次没有直接覆盖原定制源码,而是保留原目录,在独立目录部署稳定版源码。
这样可以保留:
- 原定制分支及本地修改;
- 原目录中的未跟踪文档;
- 原服务定义;
- 升级前的配置和状态;
- 对比和排查所需的历史证据。
同时创建并校验了完整状态备份,包含状态目录及其内部的 agent 工作目录。备份约 4.2 GB,恢复目录和备份文件使用限制访问的权限。
这里需要强调:原源码目录保留,并不代表随时切换回旧二进制就能安全回滚。
一旦状态数据库和配置完成迁移,回滚还需要评估旧版本是否能够读取新状态。恢复计划必须覆盖源码、Node、配置和数据库,而不能只保存 Git 提交。
三、跨版本升级需要经过迁移桥接版本
根据本次核对的官方升级指南,2026 年 7 月之前的版本需要先经过 2026.9.5,再升级至目标稳定版。
实际路径为:
2026.6.10
↓
2026.9.5:执行官方状态迁移
↓
2026.9.8:部署目标稳定版并验证
迁移不仅调整版本号,还处理了旧配置和存储结构。例如:
- agent 配置由
agents.list迁移至agents.entries; - 历史数据导入 SQLite;
- 旧心跳设置迁移为定时任务;
- 多个旧配置字段由 Doctor 处理;
- 工作区中的旧指令文件按官方迁移逻辑合并并保留原件。
迁移后,两个 agent 各选取五个近期会话标识进行检查,均确认保留。这个检查证明了所选样本的连续性,不等同于对全部历史数据逐条完成校验。
经验是:跨越存储或配置架构变化的版本时,应先阅读迁移要求,再安排构建与启动顺序。
四、构建遇到 SIGKILL:先确认参数在哪一层生效
目标稳定版第一次构建被 SIGKILL 终止。
当时的主要假设是内存压力,但当前身份无法读取内核日志,因此没有把 OOM 当作已确认结论。仅凭 SIGKILL,不能证明进程一定被 OOM killer 终止。
进一步检查构建脚本发现,通用 NODE_OPTIONS 中的堆限制会被构建链路覆盖,实际用于相关构建进程的上限约为 5,734 MB。
目标版本提供了针对 tsdown 的参数:
export OPENCLAW_TSDOWN_MAX_OLD_SPACE_MB=4608
经过确认后,使用 4,608 MB 上限重试,最终完成运行时、96 个插件包和控制界面的构建,耗时约 3 分 25 秒。
本次构建以部署运行时为目标,并跳过了 SDK 类型声明构建,因此这组构建设置不能直接当作开发或发布 SDK 的通用流程。
这里有两条经验:
-
环境变量被设置,不代表最终子进程一定采用它。
要检查构建入口和脚本如何重写参数。 -
构建堆限制与 gateway 运行堆限制是两项独立设置。
本次构建使用 4,608 MB;服务安装器后来为 gateway 计算出的上限是 3,922 MiB。二者用途不同,也不代表进程会立即占用对应大小的内存。
五、飞书插件兼容修复分三个阶段完成
原 Lark 插件包版本为 2026.6.10。
核查当时发布的厂商稳定版和 beta 后,发现它们仍包含相同的旧 SDK 导入路径。因此,单纯更新插件包不能直接解决这次接口兼容问题。
最终保留原插件包版本,对六个文件进行增量修复,并保存原件与补丁。
1. 修复已停止导出的 SDK 路径
| 文件 | 原导入 | 替代导入 |
|---|---|---|
index.js |
openclaw/plugin-sdk |
openclaw/plugin-sdk/channel-plugin-common |
src/card/reply-dispatcher.js |
openclaw/plugin-sdk/channel-runtime |
openclaw/plugin-sdk/channel-outbound |
替代路径依据目标版本实际导出的模块和插件使用的函数确定。
2. 修复 CommonJS 与 import.meta 混用
两个 JavaScript 文件使用 CommonJS,却混入了 import.meta.url:
src/core/version.jssrc/core/token-store.js
修复思路是统一使用 CommonJS 已提供的 __filename 和 __dirname。
例如:
// 原写法
const _require = createRequire(
typeof __filename !== "undefined" ? __filename : import.meta.url
);
// 修复后
const _require = createRequire(__filename);
这里不应只因为报错,就给整个插件包增加 type: module 或 type: commonjs。包级模块类型会影响更多文件,需要先确定实际发布产物的模块格式。
完成前四个文件的修复后,插件能够正常加载,注册了飞书通道和 39 个工具,诊断信息为空。
但这仍然没有覆盖消息处理回调。
3. 真实消息暴露配置读取 API 的变化
向机器人发送创建文档的指令后,gateway 收到了消息,却在处理阶段报错:
runtime.config.loadConfig is not a function
目标 SDK 提供的接口已经变为:
runtime.config.current()
受影响的实际调用位于:
src/channel/monitor.jssrc/core/lark-client.js
修复这两个调用,并保留原有 fallback 逻辑后,消息才成功进入 agent。
这个过程说明:插件注册阶段的成功,只证明初始化路径可用;按需执行的消息回调和业务工具,还需要单独验证。
六、systemd 服务迁移与权限检查
新版安装器拒绝了原服务目录和文件,因为它们允许用户组写入。
排查过程中先后发现四个相关路径:
- systemd 配置目录;
- systemd 用户服务目录;
- gateway 服务文件;
- 同名
.service.bak备份文件。
最后一个备份文件是在前面三个路径修正后才被安装器报告出来的。
这次权限调整逐个确认所有者和路径,只修改目标本身,没有递归修改整个配置目录,也没有通过 sudo 绕过检查。
服务重新生成后,服务文件及备份文件的最终权限为 0600。
部分服务策略也发生了变化:
| 策略 | 原值 | 新值 |
|---|---|---|
TimeoutStopSec |
30 | 330 |
KillMode |
control-group | mixed |
StartLimitBurst |
5 | 10 |
StartLimitIntervalSec |
60 | 300 |
OOMPolicy |
未显式设置 | continue |
这些策略为 gateway 排空正在执行的工作提供更充分的时间,并调整子进程退出和重启行为。
托管服务最终确认:
- enabled;
- active / running;
- 用户 Linger 已启用;
- IPv4 和 IPv6 回环监听正常。
七、Node 一致性不能只检查 node -v
服务切换成功后,出现了一处容易忽略的差异:
实际 gateway Node:24.19.0
PATH 首项:24.19.0
NVM_BIN:22.22.1
这时 gateway 已经运行在 Node 24 上。旧 NVM_BIN 不会自动改变正在运行的 Node 进程。
但如果某个脚本通过下面的方式启动 Node:
"$NVM_BIN/node"
它仍然会选择旧版本。
正常情况下,nvm use 会同步更新 PATH 和 NVM_BIN。systemd 服务拥有独立的运行环境,这次旧值来自服务继承的环境。
最终通过 systemd drop-in 统一该变量。脱敏示例如下:
[Service]
Environment=NVM_BIN=/home/<user>/.nvm/versions/node/v24.19.0/bin
重新加载并重启后,实际可执行文件、PATH 和 NVM_BIN 全部指向 Node 24.19.0。观察到的运行时子进程也使用相同版本。
检查服务环境时,可以分别查看实际可执行文件和目标变量:
gateway_pid=$(systemctl --user show \
openclaw-gateway.service -p MainPID --value)
readlink "/proc/$gateway_pid/exe"
tr '\0' '\n' < "/proc/$gateway_pid/environ" |
grep -E '^(PATH|NVM_BIN)='
不要完整打印服务环境,其中可能包含凭据。
另外,固定版本的 drop-in 是需要维护的配置。以后升级 Node 时,应同时检查服务命令、PATH 和这项覆盖值。
八、用业务结果完成最终验收
最终验收通过现有飞书私聊进行:
- 向机器人发送创建文档的指令;
- 确认 gateway 收到消息;
- 确认消息进入 agent;
- 由机器人实际创建飞书文档;
- 由机器人读回标题和正文;
- 返回真实文档链接;
- 再通过用户身份读取文档,核验访问权限和内容。
运行过程中还出现过模型提供方失败及自动重试,最终文档创建、读回和链接回复成功。因此,这次验收证明了该业务链路能够完成,不能据此宣称所有模型路由都已无异常。
最终只读 Doctor 检查的五项兼容性探针通过,findings 为空。
同时,观察到一个独立的工作区备份自动任务报告退出码 2。该问题未在本次文档验收中处置,原因尚未诊断,也不能直接认定由升级导致。
验收报告应保留这些边界,避免将“关键链路通过”写成“所有功能完全正常”。
九、清理暂存,同时保留恢复证据
本次清理的一次性材料包括:
- 迁移桥接版本源码;
- 候选插件包及解包副本;
- 隔离插件验证目录和测试配置;
- 备份 staging 数据;
- 已失效的前台 gateway PID 标记。
清理前确认活动配置和入口没有引用这些位置,新稳定版 Git 仓库也没有通过对象 alternates 依赖临时源码目录。删除后再次确认 gateway 健康状态和飞书运行状态。
保留的材料包括:
- 原定制源码及本地文档;
- 正式部署的稳定版源码和依赖;
- 升级前完整状态备份;
- 插件修复前原件及补丁;
- 构建、迁移和验收日志;
- 设备已有的 pnpm、Corepack 等共享缓存。
临时目录应当可以随时删除。正式入口、恢复证据和长期文档不能依赖它。
十、这次升级留下的经验
升级路径由状态兼容性决定
源码能够构建,不代表旧状态可以直接被新版本安全使用。先核对官方迁移要求,再决定部署顺序。
原源码保留与状态备份缺一不可
定制分支提供代码层面的恢复和对比条件;完整状态备份提供配置与数据库层面的恢复条件。
构建参数要追到实际子进程
通用环境变量可能被脚本覆盖。遇到内存相关问题时,应先确认最终生效的参数,再调整限制。
插件加载成功只是一个检查点
初始化、消息接收、工具调用、鉴权和外部资源访问是不同阶段。最终应以真实业务结果验收。
Node 一致性需要检查多个入口
除了 gateway 的实际可执行文件,还应检查 PATH、NVM_BIN 和实际子进程。只查看当前终端的 node -v 不够。
本地补丁需要成为可维护资产
记录包版本、修复原因、原件和精确 diff。后续插件更新可能覆盖补丁,更新后必须重新验证兼容性。
验收结论要说明范围
本次完成的是源码升级、运行环境统一、插件兼容修复和飞书文档链路验证。未诊断的自动任务告警继续保留在待办中。

浙公网安备 33010602011771号