ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenClaw人人养虾:macOS 上 Gateway 的 launchd 守护与 Node 配置骨架

OpenClaw人人养虾:macOS 上 Gateway 的 launchd 守护与 Node 配置骨架 1. macOS 上 OpenClaw Gateway 为什么需要 launchd 守护OpenClaw 在 macOS 上的运行方式和很多人想的不太一样。它不是一个把 Node 运行时和 Gateway 全部打包进 App 的「一体化」程序而是把 Gateway 当作一个独立的用户级后台服务来管理。也就是说OpenClaw.app 本身不负责把 Gateway 作为子进程拉起来它期望你通过外部的openclawCLI 安装好运行时然后由 launchd 来常驻托管 Gateway。这个设计的好处是你关掉 App 窗口Gateway 依然在跑你重启登录 MacGateway 也能自动恢复。如果你只是临时在终端里敲一句openclaw gateway那关掉终端窗口进程就没了App 会连不上或者每次都要手动重启非常折腾。真正稳定的做法是写一个 LaunchAgent plist让 launchd 帮你守护这个 Node 服务。这篇就围绕 macOS 环境下 OpenClaw Gateway 的常驻运行问题给出可复制的 launchd plist 骨架、Node 启动参数配置以及用launchctl加载、看日志、验证 Gateway 存活的完整动作。适合谁看在 Mac 上跑 OpenClaw、希望 Gateway 开机自启且崩溃能自动拉起的开发者用 launchd 托管 Node 服务、想找一份能直接改的 plist 模板的人以及遇到 Gateway 时有时无、App 提示连不上、想搞清楚日志在哪的人。核心检索词就三个OpenClaw、macOS、launchd下面全部围绕它们展开。先说清楚一个前提OpenClaw.app 不再捆绑 Node/Bun 或 Gateway 运行时。你需要先在 Mac 上装好 Node 22再全局安装openclawCLI。macOS 应用里的 Install CLI 按钮本质上也是通过 npm/pnpm 执行同样的安装流程官方不推荐用 bun 作为 Gateway 运行时。所以第一步不是写 plist而是把 CLI 装对。# 确认 Node 版本必须是 22 及以上 node -v # 全局安装 openclaw CLI版本号替换成与你 App 匹配的 npm install -g openclaw版本 # 确认 CLI 可用 openclaw --version版本这块要特别注意macOS 应用会检查 gateway 版本与自身版本的兼容性。如果 CLI 版本和应用版本不匹配App 可能拒绝连接或提示不兼容。遇到这种情况直接更新全局 CLI 到与应用一致的版本即可。我一般会先openclaw --version记下当前版本再去核对 App 的版本号。2. TaoToken 前置把模型调用凭证准备好Gateway 跑起来之后真正干活的是背后的模型调用。OpenClaw 的 Gateway 需要能访问到模型服务这里我用 TaoToken 来做统一接入。它的官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。对本地 Gateway 来说你需要的是一把可用的 API Key以及一个稳定的接入地址。操作路径很直接登录后进入控制台在 API Keys 页面创建一把新 Key。创建时建议按用途命名比如openclaw-local-gateway方便以后区分。Key 只在创建时完整显示一次复制后先存到安全的地方别直接写进会提交到 Git 的配置文件里。拿到 Key 之后本地 Gateway 的模型请求就可以指向 TaoToken 的 API 地址。这里有个习惯值得养成把 Key 放进环境变量或独立的.env文件而不是硬编码在 plist 里。plist 是明文 XML放在~/Library/LaunchAgents/下虽然只有本机可读但一旦你截图分享或备份到云端Key 就泄露了。更稳妥的做法是让 plist 引用一个包装脚本脚本里再读取环境变量。如果你还没创建 Key可以直接走这个入口https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有各语言 SDK 的调用示例配置 Gateway 时对照着填 base_url 和 api_key 就行。需要提醒的是Gateway 本身不替代编辑器它只是把模型能力以本地服务的形式暴露出来。你的代码编辑、文件操作还是在原来的工具里完成Gateway 负责的是请求转发和会话管理。理解这一点后面排查问题时思路会清晰很多。3. 可复制的 launchd plist 骨架与 Node 启动参数现在进入正题。LaunchAgent 的 plist 放在用户级目录~/Library/LaunchAgents/下文件名建议用ai.openclaw.gateway.plist。Label 用ai.openclaw.gateway旧版本可能还残留com.openclaw.*的 Label如果你机器上有旧的先卸载再装新的避免两个服务抢同一个端口。下面是一份可以直接改的 plist 骨架。关键点我都在注释里标了注意 plist 不支持真正的注释语法这里的!-- --是给你看的实际使用时删掉或保留都不影响解析XML 注释是合法的。?xml version1.0 encodingUTF-8? !DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd plist version1.0 dict !-- 服务唯一标识建议与文件名一致 -- keyLabel/key stringai.openclaw.gateway/string !-- 启动命令用绝对路径launchd 不读你的 shell PATH -- keyProgramArguments/key array string/usr/local/bin/node/string string/usr/local/lib/node_modules/openclaw/bin/openclaw.js/string stringgateway/string string--port/string string18999/string string--bind/string stringloopback/string /array !-- 环境变量把模型接入信息放这里 -- keyEnvironmentVariables/key dict keyOPENCLAW_SKIP_CHANNELS/key string1/string keyOPENCLAW_SKIP_CANVAS_HOST/key string1/string keyOPENCLAW_API_BASE/key stringhttps://taotoken.net/api/string keyOPENCLAW_API_KEY/key string你的_API_Key/string keyPATH/key string/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin/string /dict !-- 崩溃或退出后自动重启 -- keyKeepAlive/key true/ !-- 登录时自动加载 -- keyRunAtLoad/key true/ !-- 日志输出目录要先手动创建 -- keyStandardOutPath/key string/tmp/openclaw/openclaw-gateway.log/string keyStandardErrorPath/key string/tmp/openclaw/openclaw-gateway.log/string !-- 工作目录 -- keyWorkingDirectory/key string/Users/你的用户名/string /dict /plist几个容易踩的坑我逐个说。第一ProgramArguments里的 node 路径必须是绝对路径。launchd 不加载你的 shell 配置所以which node出来的路径要写死。如果你用 nvm 管理 Node路径通常在~/.nvm/versions/node/vXX/bin/node这个路径会随版本变化升级 Node 后 plist 要同步改。第二openclaw.js的入口路径取决于你的全局安装位置用npm root -g可以查到全局模块目录再拼上openclaw/bin/openclaw.js。第三日志目录/tmp/openclaw/不会自动创建先手动mkdir -p /tmp/openclaw否则 launchd 写日志会失败服务可能起不来。关于 Node 启动参数--port 18999和--bind loopback是冒烟测试里用的组合正式跑也可以沿用。--bind loopback表示只监听本地回环地址外部网络访问不到安全性更好。如果你需要局域网内其他设备访问再考虑改成对应网卡地址但那样要额外做访问控制。环境变量里OPENCLAW_SKIP_CHANNELS1和OPENCLAW_SKIP_CANVAS_HOST1是跳过一些非必要子系统的开关本地跑 Gateway 时能减少资源占用和启动报错。OPENCLAW_API_BASE指向 TaoToken 的 API 地址OPENCLAW_API_KEY填你创建的 Key。再次强调如果这份 plist 会被分享把 Key 换成从外部文件读取的方式。4. 用 launchctl 加载、查看日志、验证 Gateway 存活plist 写好后先做语法检查再加载。macOS 现在推荐用launchctl bootstrap和launchctl bootout老的load/unload也能用但新系统上行为略有差异。# 1. 检查 plist 语法是否合法输出 OK 说明没问题 plutil -lint ~/Library/LaunchAgents/ai.openclaw.gateway.plist # 2. 创建日志目录 mkdir -p /tmp/openclaw # 3. 加载服务新写法gui/$(id -u) 表示当前用户会话 launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist # 如果之前加载过先卸载再加载 launchctl bootout gui/$(id -u)/ai.openclaw.gateway launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist加载后确认服务状态# 查看服务是否在运行能看到 PID 和退出码 launchctl print gui/$(id -u)/ai.openclaw.gateway # 只看关键行 launchctl list | grep openclawlaunchctl list输出里第一列是 PID第二列是上次退出码第三列是 Label。如果 PID 有数字、退出码是 0说明服务正常在跑。如果 PID 是-、退出码非 0说明启动失败这时候去看日志。日志在/tmp/openclaw/openclaw-gateway.logstdout 和 stderr 都写到这里。实时跟踪tail -f /tmp/openclaw/openclaw-gateway.log常见的启动失败日志包括找不到 node、找不到 openclaw.js、端口被占用、API Key 无效。对着日志改 plist改完bootout再bootstrap重新加载。服务起来后做一次冒烟测试验证 Gateway 存活。先确认 CLI 版本再直接调 health 接口# 确认 CLI 版本 openclaw --version # 手动前台跑一次确认参数没问题可选用于对比 OPENCLAW_SKIP_CHANNELS1 \ OPENCLAW_SKIP_CANVAS_HOST1 \ openclaw gateway --port 18999 --bind loopback # 另开一个终端调用 health 检查 openclaw gateway call health --url ws://127.0.0.1:18999 --timeout 3000如果 health 返回正常状态说明 Gateway 在ws://127.0.0.1:18999上活着。这时候再回到 OpenClaw.app它应该能连上这个本地 Gateway。注意 App 的行为如果配置端口上已经有 Gateway 在跑App 会直接连接它而不是再启动一个新实例。所以你先用 launchd 把 Gateway 拉起来App 打开后就是连现成的不会重复启动。「OpenClaw Active」这个开关控制的是 LaunchAgent 的启用和禁用。你退出 App 不会停止 Gateway因为 launchd 在托管它。想彻底停掉用launchctl bootout或者把 plist 从 LaunchAgents 目录移走再卸载。5. 本篇常见错误排查错误一launchctl bootstrap报 Input/output error 或 service already loaded。说明这个 Label 已经加载过了。先launchctl bootout gui/$(id -u)/ai.openclaw.gateway卸载再重新 bootstrap。如果 bootout 也报错用launchctl list | grep openclaw找到确切 Label注意旧版可能是com.openclaw.*Label 对不上就卸载不掉。错误二服务加载了但 PID 一直是-日志为空。多半是ProgramArguments里的路径不对launchd 根本没执行到写日志那一步。把 node 和 openclaw.js 的绝对路径复制出来在终端里手动执行一遍能跑通再写回 plist。nvm 用户尤其注意plist 里的 node 路径不会跟着 nvm 切换版本走。错误三日志里报EADDRINUSE端口被占用。说明 18999 已经被别的进程占了可能是你之前手动跑的 Gateway 没退干净或者另一个 LaunchAgent 也在用这个端口。用lsof -i :18999查占用进程杀掉或换端口。换端口后记得同步改 App 里的连接配置。错误四health 调用超时但服务在跑。检查--bind参数和 URL 是否一致。如果 Gateway 绑的是 loopbackURL 就必须是ws://127.0.0.1:18999不能用局域网 IP。另外--timeout 3000是 3 秒机器负载高时可能不够适当调大再试。错误五App 提示 gateway 版本不兼容。这是 CLI 版本和 App 版本不匹配。openclaw --version看当前版本更新全局 CLI 到与应用一致的版本然后重启 LaunchAgent。更新 CLI 后如果安装路径变了plist 里的openclaw.js路径也要跟着改。错误六改了 plist 但行为没变化。launchd 会缓存已加载的服务定义改完 plist 必须bootout再bootstrap光改文件不重新加载是不生效的。改完用plutil -lint再检查一遍语法避免 XML 写错导致加载失败。6. 把 Gateway 稳定跑在本地之后到这里OpenClaw Gateway 在 macOS 上就算稳定托管起来了。回顾一下关键动作装对 Node 22 和匹配版本的 openclaw CLI写好 LaunchAgent plist用launchctl bootstrap加载通过/tmp/openclaw/openclaw-gateway.log看日志用openclaw gateway call health验证存活。这一套跑通后App 退出、终端关闭、甚至重启登录Gateway 都能自己回来。如果你后面要长期跑编码类任务或 Agent 工作流可以考虑用 Coding Plan 来管理调用配额和模型切换入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。日常想快速验证某个模型是否可用直接用模型对话页面测一下最省事https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。需要管理多把 Key 或查看用量控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后留一个实用习惯把 plist 里的 API Key 抽到一个只有本机可读的配置文件里plist 只引用路径。这样你分享配置模板、备份 dotfiles 时都不用担心泄露。Gateway 这种常驻服务配置一次能管很久前期把路径、日志、版本这三样对齐后面基本不用再动它。
返回列表