OpenClaw 升级助手

SKILL运维和基础设施
运维和基础设施 未标注权限 许可证不明确
安装这个技能

完整的 OpenClaw 升级工作流(macOS),包含前置健康基线、备份、doctor 校验、服务一致性修复、launchd 重新加载、gateway 重启,以及 KeepAlive 自愈验证。适用于 OpenClaw 9.x 及以上版本。当用户说"升级 OpenClaw / upgrade OpenClaw / update OpenClaw / 更新 OpenClaw"或类似指令时触发。

一句话选型结论

按资料字段整理

OpenClaw 升级助手是运维和基础设施类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类运维和基础设施 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_22ea8151/openclaw-update
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

运维和基础设施
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_22ea8151仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791673717
正文体量28.3 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
查服务与日志

按现象定位服务状态与日志线索。

写部署与巡检脚本

把重复的部署、巡检写成脚本。

故障排查

按步骤排查网络、依赖与配置。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在运维和基础设施场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
OpenClaw 升级助手当前 未标注权限 未标注 未标注 未标注 有
Azure 部署 调用外部服务 GitHub 仓库 664.2 万 Codex、Claude、Cursor 有
Azure diagnostics 问题管理 调用外部服务 GitHub 仓库 657.6 万 Codex、Claude、Cursor 有
Azure aigateway 部署 调用外部服务 GitHub 仓库 655.3 万 Codex、Claude、Cursor 有
Azure AI 搜索 调用外部服务 GitHub 仓库 651.4 万 Codex、Claude、Cursor 有
Azure Cloud migrate 部署 调用外部服务 GitHub 仓库 650.7 万 Codex、Claude、Cursor 有

SKILL.md 全文

28.3 KB
模型实际读到的说明书原文,来自技能仓库

OpenClaw 升级助手

> 平台要求: 仅限 macOS(依赖 launchd)。OpenClaw 9.x 及以上版本。同时支持 Apple Silicon(/opt/homebrew/...)和 Intel Mac(/usr/local/...)— 详见 Environment 一节。

完整的升级工作流,全闭环(无需人工干预):版本检查 → 备份 → 升级 → doctor 自检 → 重写 plist + 加载到 launchd(两步缺一不可) → gateway 重启 → 验证 launchd 已接管 → KeepAlive 破坏性测试 → 最终汇报。

> 重写 LaunchAgent plist 时有两个强制步骤: openclaw gateway install --force 只会写 plist 文件 —— 它 不会 把 plist 注册到 launchd。你必须额外显式跑 launchctl load -w,否则 gateway 会卡在"LaunchAgent installed but not loaded"状态。用户通常会去终端手动启动 gateway,这会创建一个孤儿进程(PPID = zsh 而非 1),阻塞 launchd 接管。本 skill 把 install --force + launchctl load -w 当作两个强制、串行的步骤。

环境配置(Environment)

推荐使用一条能解析当前 OpenClaw + npm 工具链的 PATH,避免锁死过时的 Node 主版本号。Homebrew 前缀因 Mac 架构而异:

# Apple Silicon (M1 / M2 / M3 / M4)
BREW_BIN="/opt/homebrew/bin"
# Intel Mac — uncomment the next line if you have an Intel Mac
# BREW_BIN="/usr/local/bin"

PATH="$BREW_BIN:/usr/bin:/bin"

如果 openclaw、npm 或 node 仍然找不到,先检查实际安装路径,不要假设某个特定的 Node formula。

Step 0:升级前健康基线(Pre-upgrade Health Baseline,跑在 Step 2 之前)

> ⚠️ 为什么需要这一步: 2026.7.1 收紧了 models.json 的 schema、并内置了 memory-core 插件,这会 追溯性地把旧配置里的脏数据暴露成问题。如果你在已有烂配置的基础上直接升级,升级后的 doctor 会报一堆看似"新增"的错误,但其实它们是早就存在的——然后你会去修一个错误的方向。所以升级前先抓一份基线。

# 0a. Save full doctor output to a timestamped file
PATH="..." openclaw doctor 2>&1 | tee /tmp/openclaw-doctor-before-$(date +%Y%m%d-%H%M).txt | tail -40

# 0b. Snapshot the things doctor checks that may need post-upgrade repair
{
  echo "=== openclaw.json plugins.allow + entries ==="
  python3 -c "import json; d=json.load(open('~/.openclaw/openclaw.json')); p=d.get('plugins',{}); print('allow:', p.get('allow',[])); print('entries keys:', list(p.get('entries',{}).keys()))"
  echo ""
  echo "=== agents/*/agent/models.json with non-canonical input arrays ==="
  for f in ~/.openclaw/agents/*/agent/models.json; do
    python3 -c "
import json, sys
d = json.load(open('$f'))
for pname, p in d.get('providers',{}).items():
    for m in p.get('models',[]):
        inp = m.get('input', [])
        if inp and len(inp) > 2: print('  $f', '$pname', m.get('id'), inp)
"
  done
} | tee /tmp/openclaw-baseline-$(date +%Y%m%d-%H%M).txt

解析基线输出并 向用户汇报:

已存在的异常 处理动作
无异常 静默继续
model input 有 3 项及以上(含 audio/video) 警告:升级后会失败,Step 4.5 会修复
Provider <X>: apiKey required 警告:Step 4.5 会修复
allow 中的插件配置损坏(如 mem9 等) 警告:Step 4.6 会清理
上表未列出的真实预存错误(如插件加载失败) 先问用户 是否要带病升级

Step 1:检查当前版本与最新版本

PATH="..." openclaw --version
PATH="..." npm show openclaw version

对比两个版本号。如果一致,告诉用户并停止——无需升级。

如果有新版本可用,汇报当前 → 最新版本,然后继续。

Step 2:备份

PATH="..." openclaw backup create --output ~/backups/pre-upgrade/

如果备份失败,停止并报告。没有备份就不升级。

⚠️ 必须用 --output(2026-07-23 立条,fleet-wide 永久铁律):
- 默认行为是 "current directory" → 会把 ~/<ISO时间戳>-openclaw-backup.tar.gz 落到 home 根目录
- 历史上 4 次升级都漏了这一项,每次都在 home 根目录多一个 400MB–5GB 的备份
- 必须显式 --output ~/backups/pre-upgrade/(保留 8 个最近,按 mtime 自动清理)

Step 3:升级

PATH="..." npm install -g openclaw@latest

等待最多 120 秒。然后验证已安装版本:

PATH="..." openclaw --version

Step 4:doctor 检查与自动修复

PATH="..." openclaw doctor --fix

自动跑 --fix(无需向用户确认)。解析输出并汇报:
- 升级前不存在的 error / warning
- 状态完整性问题
- 频道 / skills / plugins 状态
- 自动修复了哪些

注意: doctor 在中途可能触发 gateway 重启。如果出现 Cannot find module 'task-registry.maintenance-*.js' 这种错误,那是 npm 升级分块模块的暂时不一致,doctor 会通过重启自愈。不要中断。

Step 4.5:schema 校验清理(2026.7.1+)

> ⚠️ 为什么需要这一步: 基础的 openclaw doctor --fix 不会清理 models.json 的 schema 违规。你会看到 [agents/model-registry] model catalog load issue: Invalid models.json schema,每个受影响的 agent 都报一行。Step 4.5 修复这个问题。

# 4.5a. Save post-upgrade doctor output
PATH="..." openclaw doctor 2>&1 | tee /tmp/openclaw-doctor-after-$(date +%Y%m%d-%H%M).txt > /dev/null

# 4.5b. Parse and fix models.json schema errors
python3 << 'PYEOF'
import json, re, subprocess
from pathlib import Path

# Find every schema error in doctor output
doctor_out = subprocess.run(
    ["$BREW_BIN/openclaw", "doctor"],
    capture_output=True, text=True, env={"PATH": "$BREW_BIN:/usr/bin:/bin"}
).stdout + subprocess.run(
    ["$BREW_BIN/openclaw", "doctor"],
    capture_output=True, text=True, env={"PATH": "$BREW_BIN:/usr/bin:/bin"}
).stderr

errors = re.findall(r"File: (\S+)", doctor_out)

# For each affected file, fix input arrays
for f in set(errors):
    p = Path(f)
    if not p.exists(): continue
    d = json.loads(p.read_text())
    if 'providers' not in d:
        # Old-schema file with no providers (e.g. {"version":1,"lastGood":{},"usageStats":{}})
        # Reset to minimal valid form
        p.write_text(json.dumps({"providers": {}}, indent=2))
        print(f"  RESET {f} (old schema → minimal valid)")
        continue
    changed = False
    for pname, pdata in d.get('providers', {}).items():
        for i, m in enumerate(pdata.get('models', []) or []):
            inp = m.get('input', [])
            if not inp or len(inp) <= 2:
                continue
            new_inp = [x for x in inp if x in ('text', 'image')]
            if not new_inp: new_inp = ['text']
            m['input'] = new_inp
            changed = True
            print(f"  {f} providers.{pname}.models.{i}.input  {inp} → {new_inp}")
    if changed:
        p.write_text(json.dumps(d, indent=2, ensure_ascii=False) + '\n')
PYEOF

# 4.5c. Apply the same fix via openclaw config set (so it survives gateway restart!)
# CRITICAL: Gateway restarts write agent/models.json from in-memory state.
# Your step 4.5b file edits WILL BE REVERTED. The real fix is `config set`.
for f in ~/.openclaw/agents/*/agent/models.json; do
  python3 -c "
import json, subprocess
d = json.load(open('$f'))
for pname, pdata in d.get('providers', {}).items():
    for i, m in enumerate(pdata.get('models', []) or []):
        inp = m.get('input', [])
        if not inp or len(inp) <= 2: continue
        new_inp = [x for x in inp if x in ('text', 'image')]
        if not new_inp: new_inp = ['text']
        # CRITICAL: set global config, not file edit
        path = f'agents.{pname}-agent.providers.{pname}.models.{i}.input'  # placeholder
        subprocess.run(['$BREW_BIN/openclaw', 'config', 'set',
                        f'agents.{pname}.models.providers.{pname}.models.{i}.input',
                        json.dumps(new_inp), '--strict-json'],
                       capture_output=True)
"
done

> 🚨 关键教训(2026-07-14): 直接编辑 agent/models.json 文件 会被 gateway 重启回滚 —— gateway 每次重启都会把它的 in-memory 模型注册表写回磁盘,覆盖你的修改。永远用 openclaw config set 改全局配置路径(如 models.providers.<provider>.models.<i>.input)。文件编辑只能用来 识别 哪些需要修,真正修要用 config set。

Step 4.6:插件工具名冲突清理(2026.7.1+)

# 4.6a. Look for plugin tool name conflicts in doctor output
grep -iE "plugin tool name conflict|disabled.*but config is present|memory.*public surface" /tmp/openclaw-doctor-after-*.txt

# 4.6b. If mem9 (or any other 'memory' plugin) is enabled but exporting tools that
# now conflict with bundled memory-core: disable it and uninstall it

# Disable the conflicting plugin
PATH="..." openclaw plugins disable <id>
# (e.g. mem9)

# Remove from openclaw.json plugins.allow AND plugins.entries to clear "config present" warning
python3 << 'PYEOF'
import json
f = '~/.openclaw/openclaw.json'
d = json.load(open(f))
plugins = d.get('plugins', {})
for sect in ('allow', 'entries'):
    s = plugins.get(sect, {})
    if sect == 'allow' and isinstance(s, list):
        plugins[sect] = [x for x in s if x != '<id>']
    elif sect == 'entries' and isinstance(s, dict):
        s.pop('<id>', None)
json.dump(d, open(f, 'w'), indent=2, ensure_ascii=False)
PYEOF

# Fully uninstall (removes npm install record + plugin dir)
PATH="..." openclaw plugins uninstall <id> --force
# Trash any leftover empty npm project dir (uninstall leaves it)
trash ~/.openclaw/npm/projects/<id>-* 2>/dev/null
echo "trashed leftover npm dir"

# 4.6c. Enable the bundled replacement (e.g. memory-core)
python3 << 'PYEOF'
import json
f = '~/.openclaw/openclaw.json'
d = json.load(open(f))
plugins = d.setdefault('plugins', {}).setdefault('allow', [])
if '<replacement_id>' not in plugins:
    plugins.append('<replacement_id>')
d.setdefault('plugins', {}).setdefault('entries', {})['<replacement_id>'] = {'enabled': True}
json.dump(d, open(f, 'w'), indent=2, ensure_ascii=False)
PYEOF

# 4.6d. Restart gateway to pick up the new plugin set
PATH="..." openclaw gateway restart
sleep 12

# 4.6e. Verify no more conflicts
PATH="..." openclaw doctor 2>&1 | grep -iE "plugin tool name conflict|disabled.*but config|memory.*public"
# (Empty output = clean)

Step 5:重写 plist + 加载到 launchd(两个强制步骤)

> ⚠️ 关键: openclaw gateway install --force 只写新的 plist 文件,不会把 plist 注册到 launchd。你必须额外显式跑 launchctl load -w,否则 gateway 会卡在"LaunchAgent installed but not loaded"状态,用户只能在终端手动启动(产生孤儿进程)。

# 5a. Write fresh plist (vN+1 metadata, correct ProgramArguments, RunAtLoad, KeepAlive)
PATH="..." openclaw gateway install --force

# 5b. Force-launchd to load the plist (THIS IS THE STEP THAT WAS MISSING)
launchctl load -w ~/Library/LaunchAgents/ai.openclaw.gateway.plist
#   -w: writes "disabled=false" into the plist, so launchd auto-loads on next reboot too
#   If you see "service already loaded", that's fine — `load -w` is idempotent
#   If you see "Input/output error", run `launchctl bootout gui/$UID/ai.openclaw.gateway` first, then retry

# 5c. Verify launchd has the task
launchctl list | grep openclaw
#   Expected output:  <pid-or-status>  0  ai.openclaw.gateway
#   The middle column "0" = last exit code. Don't worry if it's not "0" right now; the
#   important thing is the line exists, proving launchd has registered the job.

如果 load -w 之后 launchctl list 仍然为空,参见下方的错误处理 § B:launchd load 失败。

Step 6:等待 KeepAlive 拉起进程,然后验证

sleep 8   # plist has ThrottleInterval=10; give it a bit
PATH="..." openclaw gateway status

确认以下各项:
- Runtime: running
- Service: LaunchAgent (loaded)
- Connectivity probe: ok
- Listening: *:18789
- 不要出现 Service config issue: Gateway service was installed by OpenClaw X; current CLI is Y 警告

然后 显式验证 launchd 是父进程(这是用户可以安全关闭终端的证据):

GW_PID=$(pgrep -f "openclaw/dist/index.js.*gateway" | head -1)
if [ -n "$GW_PID" ]; then
  GW_PPID=$(ps -o ppid= -p $GW_PID | tr -d ' ')
  if [ "$GW_PPID" = "1" ]; then
    echo "[OK] gateway pid=$GW_PPID PPID=1 (launchd-managed)"
  else
    echo "[FAIL] gateway pid=$GW_PID PPID=$GW_PPID — not launchd-managed, see [§ A: Orphan process]"
  fi
fi

Step 7:KeepAlive 自愈验证(破坏性测试)

这一步专门抓“plist 看着对但 KeepAlive 实际坏了”——这种状态在 gateway status 里看不出来,要等用户真去 kill 进程或关终端时才暴露。

# 7a. Note current gateway PID
OLD_PID=$(pgrep -f "openclaw/dist/index.js.*gateway" | head -1)
echo "Killing gateway pid=$OLD_PID to test KeepAlive..."

# 7b. Kill it (simulates crash / manual kill)
[ -n "$OLD_PID" ] && kill -9 $OLD_PID

# 7c. Wait for launchd to respawn (ThrottleInterval=10s, give it 15s)
sleep 15

# 7d. Verify launchd spawned a new one with PPID=1
NEW_PID=$(pgrep -f "openclaw/dist/index.js.*gateway" | head -1)
if [ -n "$NEW_PID" ] && [ "$NEW_PID" != "$OLD_PID" ]; then
  NEW_PPID=$(ps -o ppid= -p $NEW_PID | tr -d ' ')
  if [ "$NEW_PPID" = "1" ]; then
    echo "[OK] KeepAlive works: new pid=$NEW_PID, PPID=1 (launchd)"
  else
    echo "[FAIL] new pid=$NEW_PID, PPID=$NEW_PPID — not launchd"
  fi
else
  echo "[FAIL] launchd did not respawn gateway"
fi

如果 KeepAlive 失败,用户离 gateway 死掉只差一次崩溃或一次关终端。不要宣布升级完成——参见错误处理 § C:KeepAlive 不工作。

Step 8:清理残留的孤儿 CLI 进程

当用户在终端手动跑 openclaw gateway 时,那个 CLI 父进程 会挂在 zsh 里等它启动的 gateway 子进程退出。升级完成后,那个 CLI 父进程还活着(在旧的终端里)——它不阻塞任何东西(已经不再占端口),但出于整洁应该 kill 掉。不要 kill zsh 父进程 —— 只 kill openclaw CLI 子进程。

# Find any openclaw CLI parents still hung in terminals
ps -ef | awk '/openclaw/ && !/node/ && $3 != 1 {print}'
# Example: "<uid> <pid> <ppid> ... openclaw"  (PID=<pid>, PPID=<ppid>=zsh)

# Kill ONLY the openclaw CLI child, not its zsh parent
HUNG_CLI=$(ps -ef | awk '/openclaw/ && !/node/ && $3 != 1 {print $2}' | head -1)
[ -n "$HUNG_CLI" ] && kill -9 $HUNG_CLI

Step 9:向用户汇报

OpenClaw upgrade complete:
  Previous: <old-version>
  Current:  <new-version> (commit-hash)
  Backup:   <path-to-backup-tarball>
  Doctor:   clean (or list of fixed issues)
  Gateway:  pid=<X>, PPID=1, launchd-managed, KeepAlive verified
  Plugin drift: <list, or "none">

然后告诉用户最关键的一句保证:"现在可以安全关闭终端了 —— KeepAlive 已验证,launchd 会保持 gateway 一直在跑。"


常见症状与修复

症状 根因 修复
报 LaunchAgent installed but not loaded plist 已写但未注册到 launchd 跑 launchctl load -w ~/Library/LaunchAgents/ai.openclaw.gateway.plist
报 Service config issue: Gateway service was installed by OpenClaw X; current CLI is Y plist 元数据版本比当前 CLI 旧 跑 openclaw gateway install --force + launchctl load -w
报 Runtime: unknown (Could not find service "ai.openclaw.gateway") launchd 完全没有 plist 记录 跑 launchctl load -w ...(新版 macOS 用 launchctl bootstrap gui/$UID ...)
报 Cannot find module 'task-registry.maintenance-*.js' npm 升级分块模块暂时不一致 doctor 会通过重启自愈;持续出现就 openclaw gateway restart
报 Plugin version drift: feishu X → expected Y 插件 npm 包落后于 core 仅是 warning,不是 error —— 跑 openclaw plugins update <id> 会装上 npm 上最新的版本,但仍然可能比预期旧
报 State dir migration skipped: target already exists 历史遗留状态,无害 忽略
重启后报 Connectivity probe: failed gateway 还在启动或端口冲突 sleep 5 && openclaw gateway status;仍失败就 lsof -nP -iTCP:18789(PATH 里没 lsof 就用 sudo lsof)
报 Invalid models.json schema: providers.X.models.0.input.2: must be equal to constant 2026.7.1+ 收紧了 input array schema;旧配置含 audio/video Step 4.5 —— 跑 openclaw config set 'models.providers.<X>.models.<i>.input',不要编辑文件
报 Provider X: "apiKey" is required when defining custom models 2026.7.1+ 即使 authHeader:true 也必须有 apiKey Step 4.5c —— 跑 openclaw config set 'models.providers.<X>.apiKey' '...'
报 plugin tool name conflict (mem9): memory_search 旧插件导出了内置工具名 Step 4.6 —— openclaw plugins disable mem9、清理 allow+entries、plugins uninstall --force、启用 memory-core
报 plugin disabled (disabled in config) but config is present 已禁用的插件配置没清干净 Step 4.6 —— 还要从 openclaw.json 的 plugins.allow 和 plugins.entries 里删掉
报 Memory artifact repair could not be completed: Bundled plugin public surface access blocked for "memory-core" mem9 冲突阻塞了 memory-core 的运行时 Step 4.6 —— 同上;mem9 一删自动恢复

错误处理(Error Handling)

Z. 关键 exec 编排铁律:杀/启 gateway 的 exec 里绝对不要调 openclaw gateway status(避免 probe hang 整个 exec)

症状: exec 输出 missing tool result in session history; inserted synthetic error result for transcript repair。表面看像"工具调用失败",实际上是 exec 里某条命令 hang 死导致整个 exec 超时。

根因: openclaw gateway status 内部会 WS probe ws://127.0.0.1:18789。当 18789 端口正在被重启(被 kill 后 launchd 还没拉起新的)时,那个 probe 会 hang 死,整个 exec 卡住直到超时 → OpenClaw 标成 missing tool result → 看起来失败。

铁律:

  1. kill -9 <孤儿pid> + launchctl bootout + launchctl load -w 这三步单独一个 exec,秒级返回,不加 sleep,不调 status:
    bash kill -9 <orphan_pid> 2>/dev/null launchctl bootout gui/$UID/ai.openclaw.gateway 2>/dev/null launchctl load -w ~/Library/LaunchAgents/ai.openclaw.gateway.plist echo "kill+load done"

  2. verify 用 ps / pgrep / launchctl list,不用 openclaw gateway status。看 PPID 是否等于 1。

  3. openclaw gateway status 单独放在下一个 exec,此时 gateway 已稳定运行,probe 不会 hang。

  4. 每个 exec 控制在 10s 以内(短命令)或单独跑 sleep 12 然后 verify(长命令)。绝不在同一个 exec 里先 kill 进程再 sleep 再 verify status。

  5. zsh 里 PPID 是只读变量,赋值 PPID=$(...) 会报 read-only variable: PPID。要用别的变量名,比如 PARENT_PID。

反模式(不要再写):

# ❌ 千万别这么写
kill -9 <orphan_pid>
sleep 2
openclaw gateway status       # ← probe hang 整个 exec
launchctl load -w ...
sleep 12
openclaw gateway status       # ← 又 hang

正确写法:

# exec 1(<2s):纯操作,不 verify
kill -9 <orphan_pid> 2>/dev/null
launchctl bootout gui/$UID/ai.openclaw.gateway 2>/dev/null
launchctl load -w ~/Library/LaunchAgents/ai.openclaw.gateway.plist
echo "done"

# exec 2(~12s):sleep + 用 ps 验 PPID=1(不调 status probe)
sleep 12
GW_PID=$(pgrep -f 'openclaw/dist/index.js.*gateway' | head -1)
PARENT_PID=$(ps -o ppid= -p "$GW_PID" | tr -d ' ')
[ "$PARENT_PID" = "1" ] && echo OK

# exec 3(<2s):这时才调 status,gateway 已稳定
openclaw gateway status

A. 孤儿进程(PPID ≠ 1,"service not loaded" 但 gateway 在跑)

根因: 用户在终端跑了 openclaw gateway,CLI 启动了一个绑定 18789 的 node 子进程;但 launchd 的 plist 从来没注册过,launchd 无法重新拉起 —— 用户以为自己必须手动启动。

症状:
- openclaw gateway status 显示 LaunchAgent installed but not loaded,但同时显示 Listening: *:18789(那个手动起的进程)
- ps -ef | grep openclaw | grep gateway 显示 PPID 是 tty/zsh 的 pid,不是 1
- launchctl list | grep openclaw 为空

修复(不要动 zsh 父进程):

# 1. 找出占端口的 node 子进程
PIDS=$(lsof -nP -iTCP:18789 -sTCP:LISTEN -t 2>/dev/null)
#    (PATH 里没 lsof 就用:/usr/sbin/lsof ...)

# 2. 只 kill 这些子进程(不要 kill zsh 父进程!)
[ -n "$PIDS" ] && kill -9 $PIDS
sleep 2

# 3. 验证端口已释放
lsof -nP -iTCP:18789 -sTCP:LISTEN 2>&1 | head -3
#    (空输出 = 端口已释放)

# 4. 注册 plist 到 launchd
launchctl load -w ~/Library/LaunchAgents/ai.openclaw.gateway.plist
sleep 8

# 5. 验证是 launchd 拉起的
GW_PID=$(pgrep -f "openclaw/dist/index.js.*gateway" | head -1)
[ -n "$GW_PID" ] && ps -o ppid= -p $GW_PID | grep -q 1 && echo "[OK]"

# 6. 跑 KeepAlive 测试(Step 7)

> ⚠️ 不要 kill zsh 父进程。 2026-06-10 那次事故文档说要两个都 kill,但那是个特例(launchd 重试时序问题)。正常情况下只 kill 子进程才对,不会让用户终端也跟着断。旧建议过于一刀切。

B. launchctl load 失败

# 报错:"service already loaded" → 忽略,这是 no-op
# 报错:"Input/output error" → 先 bootout 再重试
launchctl bootout gui/$UID/ai.openclaw.gateway
launchctl load -w ~/Library/LaunchAgents/ai.openclaw.gateway.plist

# 报错:"Operation not permitted" → 当前用户没法跟 launchd 通信。检查
#                                       Console.app → "launchd" 子系统。通常
#                                       是权限/sandbox 问题。

C. KeepAlive 不工作

如果 Step 7 失败(kill 后 15 秒内 launchd 没拉起 gateway):

  1. 检查 plist 本身 —— KeepAlive 真的是 true 吗?ProgramArguments 对吗?
    bash /usr/libexec/PlistBuddy -c "Print :KeepAlive" ~/Library/LaunchAgents/ai.openclaw.gateway.plist /usr/libexec/PlistBuddy -c "Print :RunAtLoad" ~/Library/LaunchAgents/ai.openclaw.gateway.plist /usr/libexec/PlistBuddy -c "Print :ProgramArguments" ~/Library/LaunchAgents/ai.openclaw.gateway.plist
  2. 手动测试 ProgramArguments —— 把它们复制到 shell 里跑:
    bash /path/to/openclaw/service-env/ai.openclaw.gateway-env-wrapper.sh \ /path/to/openclaw/service-env/ai.openclaw.gateway.env \ /opt/homebrew/opt/node/bin/node \ /opt/homebrew/lib/node_modules/openclaw/dist/index.js \ gateway --port 18789
    如果这里报错(比如 node 模块路径不对),launchd 的 KeepAlive 会静默失败。
  3. 看 service 日志:
    bash tail -50 ~/Library/Logs/openclaw/gateway.log
  4. 重装并重新加载:
    bash openclaw gateway install --force launchctl bootout gui/$UID/ai.openclaw.gateway 2>/dev/null launchctl load -w ~/Library/LaunchAgents/ai.openclaw.gateway.plist

D. 备份失败

立即停止。不要继续升级。把错误告诉用户。

E. npm install 失败

报告错误。Gateway 还跑在旧版本上,不要继续后续步骤。

F. doctor 报新的 critical error(非 transient module-not-found)

立即告诉用户。不要盲目自动修。

G. Node / 路径问题

不要假设是 node@22 或某个特定 formula。先检查:

which node
which openclaw
node --version
ls -la $(which openclaw)

如果 openclaw 是 Homebrew 软链接,它实际依赖的 node 二进制在 $(dirname $(readlink -f $(which openclaw)))/../... —— 但 macOS 上 readlink -f 不存在,用:

node -e "console.log(require.resolve.paths(''))"   # shows node module search paths
which -a node                                         # all node binaries in PATH

H. gateway 重启后 agent/models.json schema 错误被回滚

直接编辑 ~/.openclaw/agents/<id>/agent/models.json 完全无效,因为 gateway 在每次 restart 时把 in-memory model registry 写回磁盘。你的修改被覆盖。

症状:
- Step 4.5 文件编辑后,doctor 不报错了
- 但下次 openclaw gateway restart 之后又报同样错
- 文件 mtime 跟 restart 时间对齐

根因: Gateway 启动时从全局 openclaw.json 的 models.providers.* 读取模型定义,运行中维护自己的 in-memory registry,写盘时按 in-memory 状态落盘。

修复:

# ❌ 反模式:编辑文件(会回滚)
python3 -c "...; json.dump(d, open(..., 'w'))"

# ✅ 正确:用 openclaw config set 改全局(不会回滚)
openclaw config set 'models.providers.<provider>.models.<i>.input' '["text","image"]' --strict-json
openclaw config set 'models.providers.<provider>.apiKey' '<key>' --strict-json

这会写到 ~/.openclaw/openclaw.json,gateway 每次启动都从这读,不会覆盖。

I. 升级后插件工具名冲突(mem9 vs memory-core)

症状:
- doctor 日志里有 20+ 行 [plugins] plugin tool name conflict (mem9): memory_search
- doctor 同时报:Memory artifact repair could not be completed: Bundled plugin public surface access blocked for "memory-core"
- memory_search / memory_get 完全不能用(mem9 抢注,memory-core 被 block)

根因: 2026.7.1 内置了 memory-core plugin 导出 memory_search 和 memory_get。原来装的 mem9 (@mem9/mem9 第三方) 也导出同名工具,导致工具冲突。

修复(按顺序):

# 1. 关掉冲突的第三方插件
PATH="..." openclaw plugins disable mem9

# 2. 干净清理 openclaw.json(disable 不会自动删除配置,留着会一直 warning)
python3 << 'PYEOF'
import json
f = '~/.openclaw/openclaw.json'
d = json.load(open(f))
plugins = d.setdefault('plugins', {})
if isinstance(plugins.get('allow'), list):
    plugins['allow'] = [x for x in plugins['allow'] if x != 'mem9']
if isinstance(plugins.get('entries'), dict):
    plugins['entries'].pop('mem9', None)
json.dump(d, open(f, 'w'), indent=2, ensure_ascii=False)
PYEOF

# 3. 干净卸载(卸 install record + npm dir)
PATH="..." openclaw plugins uninstall mem9 --force

# 4. 清掉残留空目录(uninstall 不删外层 dir)
trash ~/.openclaw/npm/projects/mem9-* 2>/dev/null

# 5. 启用内置 memory-core
python3 << 'PYEOF'
import json
f = '~/.openclaw/openclaw.json'
d = json.load(open(f))
plugins = d.setdefault('plugins', {})
allow = plugins.setdefault('allow', [])
if 'memory-core' not in allow:
    allow.append('memory-core')
entries = plugins.setdefault('entries', {})
entries['memory-core'] = {'enabled': True}
json.dump(d, open(f, 'w'), indent=2, ensure_ascii=False)
PYEOF

# 6. 重启 gateway
PATH="..." openclaw gateway restart
sleep 12

# 7. 验
PATH="..." openclaw doctor 2>&1 | grep -iE "plugin tool name|memory.*plugin|memory search"
# 空输出 = 干净

为什么"关掉终端 —— KeepAlive 会保持 gateway 运行"这句话是可靠的

这是面向用户的承诺,必须是真的。要做到这点:

  1. plist 必须已注册到 launchd(Step 5b)。
  2. plist 必须有 KeepAlive = true(Step 5a 默认写入正确的 plist)。
  3. ProgramArguments 必须真的能跑(Step 7 的破坏性测试已经验证)。
  4. 用户必须明白 旧 CLI 父进程(挂在 zsh 下的那个独立 openclaw 进程)并不是保持 gateway 运行的家伙 —— 真正干这事的是 launchd。

四个条件都满足时,用户关掉终端、旧 zsh 死、旧 CLI 父进程死、端口空出约 10 秒,然后 launchd 重新拉起一个 PPID=1 的新 gateway。在维护者的机器上已经端到端验证过 —— 用户关终端后,gateway pid <X> 死,launchd 在 10 秒内重新拉起 pid <Y>(PPID=1),服务不中断。

常见问题

5 问
答案由本站依据条目字段整理
OpenClaw 升级助手怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_22ea8151/openclaw-update。安装方式为未标注。装完刷新技能列表或重开客户端。

OpenClaw 升级助手支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

OpenClaw 升级助手权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

OpenClaw 升级助手收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

OpenClaw 升级助手用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。