跳转至

ocn 定时执行方案(LaunchAgent)

状态: ✅ 已完成

创建日期: 2026-03-27 最后更新: 2026-03-27


背景

当前 ocn 脚本的核心动作是:

  1. security find-generic-password -s "Claude Code-credentials" -w 从 macOS Keychain 取出 Claude 凭证
  2. 生成或更新 ~/.claude/.credentials.json
  3. 同步到 ~/.local/share/opencode/auth.json

手动在终端前台执行时,脚本可以正常完成;但放进 crontab 后,会卡在或失败在下面这一步:

[INFO] Generating auth.json from Claude credentials...

原因判断

在当前 Mac 环境里,ocn 依赖 security 访问登录用户的 Keychain。

cron 的运行环境和交互式登录 shell 不同,通常缺少下面这些条件:

  • 登录图形会话上下文
  • 已解锁的登录钥匙串上下文
  • 某些需要用户授权的 Keychain 访问路径
  • 完整的用户环境变量和 PATH

因此,ocn 这种需要访问 Keychain 的脚本,不适合放到 crontab 里做长期定时任务。


推荐方案

使用 macOS 的 LaunchAgent,不要使用 crontab

原因:

  • LaunchAgent 运行在当前登录用户的会话中,更适合访问 Keychain
  • 可以设置固定时间间隔执行
  • 可以配置开机登录后自动加载
  • 可以直接把标准输出和错误输出写入日志文件

目标行为

建议把 ocn 配置成:

  • 用户登录后自动加载
  • 每 30 分钟执行一次
  • 每次执行都把日志写到 /tmp/ocn.out.log/tmp/ocn.err.log
  • 仍然保持脚本本身可手动执行

LaunchAgent 配置文件

建议创建文件:

~/Library/LaunchAgents/com.pengxin.ocn.plist

内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>com.pengxin.ocn</string>

    <key>ProgramArguments</key>
    <array>
      <string>/Users/pengxin/.sh/ocn</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>StartInterval</key>
    <integer>1800</integer>

    <key>StandardOutPath</key>
    <string>/tmp/ocn.out.log</string>

    <key>StandardErrorPath</key>
    <string>/tmp/ocn.err.log</string>
  </dict>
</plist>

安装步骤

1. 创建 LaunchAgents 目录

mkdir -p ~/Library/LaunchAgents

2. 写入 plist 文件

cat > ~/Library/LaunchAgents/com.pengxin.ocn.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>com.pengxin.ocn</string>

    <key>ProgramArguments</key>
    <array>
      <string>/Users/pengxin/.sh/ocn</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>StartInterval</key>
    <integer>1800</integer>

    <key>StandardOutPath</key>
    <string>/tmp/ocn.out.log</string>

    <key>StandardErrorPath</key>
    <string>/tmp/ocn.err.log</string>
  </dict>
</plist>
EOF

3. 重新加载配置

launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.pengxin.ocn.plist 2>/dev/null || true
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.pengxin.ocn.plist

4. 立即手动触发一次

launchctl kickstart -k gui/$(id -u)/com.pengxin.ocn

验证方法

查看 agent 是否已加载

launchctl print gui/$(id -u)/com.pengxin.ocn

查看标准输出和错误日志

tail -f /tmp/ocn.out.log /tmp/ocn.err.log

预期标准输出

正常情况下,/tmp/ocn.out.log 里应该能看到类似:

[INFO] Generating auth.json from Claude credentials...
[INFO] Updating anthropic auth in place...
[INFO] Done.
[INFO] Next refresh is needed in 1h 8m 19s (2026-03-27 18:00:53 PDT)

如果 expiresAt 未变化,则应该看到:

[INFO] expiresAt matches: 1774659653714
[INFO] Next refresh is needed in 1h 8m 19s (2026-03-27 18:00:53 PDT)
[INFO] Skipping update.

常见问题

为什么不用 crontab

因为当前脚本依赖 Keychain,而 cron 对这类访问不稳定。即使补全 PATH,也不能保证 securitycron 环境中稳定读取到登录用户的凭证。

为什么要用绝对路径

LaunchAgent 的环境通常比交互式 shell 更精简。当前 ocn 脚本已经足够简单,但如果后续新增依赖,优先使用绝对路径会更稳。

是否需要开机后自动执行

需要。RunAtLoadtrue 可以保证用户重新登录后,定时任务自动恢复。


后续可选优化

  • /tmp/ocn.out.log/tmp/ocn.err.log 改到固定目录,例如 ~/.local/share/ocn/
  • ocn 中增加失败重试和更明确的错误日志
  • ocn 成功更新时追加一条简短的时间戳日志,便于追踪最近一次成功同步时间