mcpField
E
earn-hunter

earn-hunter

github.com/okx其他1973.2k 次安装今天更新
代码仓库

关于 earn-hunter

earn-hunter 是用于监控 OKX 闪赚、定期赚币和活期赚币机会的 Agent 技能,可按配置筛选并发送提醒。它还支持定时或手动扫描,以及暂停和配置管理。

主要功能

  1. 01监控三类 Earn 机会
  2. 02按币种和 APY 筛选
  3. 03定时或手动扫描
  4. 04Telegram、Lark 或会话通知
  5. 05管理配置与监控状态

适用场景

  1. 01发现符合条件的 Earn 产品
  2. 02定期接收收益机会提醒
  3. 03调整币种和 APY 监控条件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/okx/agent-skills --skill earn-hunter

安装后在 Claude Code 中输入/earn-hunter确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

config/
claude-code.default.json
default.json
openclaw.default.json
references/
config-reference.md
notify-channels.md
purchase-guide.md
scan-logic.md
scheduler-setup.md
scripts/
scan.sh
templates/
activation.md
error-alert.md
fixed-earn.md
flash-earn.md
flexible-earn.md
mixed-notify.md

详细介绍

Earn Hunter

用于自动监控 OKX 闪赚、定期赚币和灵活赚币(简单赚币)机会。

{baseDir} = 包含此 SKILL.md 文件的目录。所有相对路径(references/、templates/、config/)均以此目录为基准解析。

预检

  1. 验证是否已安装 okx CLI:which okx。如果未安装,请通过 npm install -g @okx_ai/okx-trade-cli 安装。 在 OpenClaw 上,还要验证 Agent 工具列表中是否提供会话内的 cron 工具(用于安排计划任务,而不是 openclaw CLI)。

  2. 检查可选依赖技能:

    okx skill list --json
    

    可选技能(扫描和通知不需要):

    • okx-cex-earn — 申购指南(执行申购)需要
    • okx-cex-auth — 认证恢复需要

    如果缺少其中任一技能,请尝试安装,但如果安装失败,不要阻止后续流程:

    okx skill add okx-cex-earn
    okx skill add okx-cex-auth
    
    • 安装成功 → 继续
    • 安装失败(网络错误、市场不可用等)→ 警告并继续: “⚠ {skill_name} 安装失败,扫描和通知功能不受影响。申购引导和认证恢复需要该 skill,后续可手动安装。”
    • 无论技能安装结果如何,均继续预检
  3. 检测认证模式 — 依次执行两项检查,采用第一个匹配结果:

    • okx config show --json → 存在非空的 api_key 字段 → API Key 模式。所有命令都添加 --profile live。
    • 没有 API Key,且 okx auth status --json → "status":"logged_in" → OAuth 模式。无需 --profile 标志。
    • 两者都不符合 → 停止。加载 okx-cex-auth 技能并按照登录步骤操作。
  4. 初始化配置和状态:

    • 如果 ~/.okx/earn-hunter/ 目录不存在 → mkdir -p ~/.okx/earn-hunter
    • 如果 ~/.okx/earn-hunter/config.json 不存在 → 将 {baseDir}/config/default.json 复制到该位置
    • 如果 ~/.okx/earn-hunter/state.json 不存在 → 写入 {"flash":{},"fixed":{},"flexible":{},"consecutive_failures":0,"last_error":""}
    • 如果 ~/.okx/earn-hunter/platform.json 不存在 → 运行平台检测
    • 始终(重新)安装扫描脚本:cp {baseDir}/scripts/scan.sh ~/.okx/earn-hunter/scan.sh && chmod +x ~/.okx/earn-hunter/scan.sh。cron 和交互式扫描都会调用此脚本。
    • 将已解析的工具路径写入 ~/.okx/earn-hunter/env.snapshot(cron 不能依赖用户的登录 PATH):
      cat > ~/.okx/earn-hunter/env.snapshot << SNAP
      # auto-generated by earn-hunter activation — $(date -Iseconds)
      OKX_BIN=$(command -v okx)
      NODE_BIN=$(command -v node)
      JQ_BIN=$(command -v jq)
      ACTIVATION_PATH=$PATH
      SNAP
      
      扫描脚本会读取此文件来解析工具路径,即使在 cron 的精简 PATH=/usr/bin:/bin 环境下也能正常运行。

    Agent 对配置、状态和平台 JSON 执行的读写操作仅用于激活和配置管理。定期扫描本身完全由 scripts/scan.sh 执行(shell + jq)——扫描需要 jq。通过 which jq 验证;如果未安装,请安装(brew install jq / apt-get install jq)。

平台与渠道检测

三个独立维度:平台(Agent 运行的位置)、调度器(触发扫描的方式)、通知渠道(发送提醒的位置)。

平台检测(主动探测 + 用户确认)

首次运行(不存在 platform.json):

  1. 探测环境线索:
    • 是否存在 OPENCLAW_HOME 环境变量?→ 提示:OpenClaw
    • Agent 工具列表是否包含 cron / delivery 工具?→ 提示:OpenClaw
    • 是否存在 HERMES_HOME 环境变量,或 which hermes 是否执行成功?→ 提示:Hermes Agent
    • 是否在 Claude Code 会话中运行?→ 提示:Claude Code
    • 以上均不匹配 → 提示:Generic
  2. 显示检测结果并请用户确认:
    • “检测到你正在使用 {detected_platform},是否正确?”
    • 用户确认 → 继续
    • 用户否认 → 询问:“你使用的是哪个平台?1) OpenClaw 2) Claude Code 3) Hermes Agent 4) 其他”
  3. 初始化平台配置:
    • OpenClaw / Claude Code → 将 {baseDir}/config/<confirmed_platform>.default.json 复制到 ~/.okx/earn-hunter/platform.json
    • Hermes Agent → 将 {baseDir}/config/claude-code.default.json 复制为基础配置,将 .platform 设为 "hermes",将 .scheduler.type 设为 "cron"
    • Generic → 将 {baseDir}/config/claude-code.default.json 复制为基础配置,将 .platform 设为 "generic",将 .scheduler.type 设为 "manual"
  4. 将结果写入 ~/.okx/earn-hunter/platform.json,后续运行时跳过检测。

后续运行(已存在 platform.json): 读取 ~/.okx/earn-hunter/platform.json 并提取 .platform 字段(返回 "openclaw"、"claude-code"、"hermes" 或 "generic")。

检测到的平台不支持调度器(仅适用于本应支持但实际不支持的平台)→ 错误:“当前客户端不支持定时任务,请升级到最新版本。” Generic 平台 → 不支持自动调度。提示:“当前平台不支持自动调度,你可以手动说‘执行 earn-hunter 扫描’来触发。”

配置文件
文件适用范围内容
config.json共享扫描范围(flash/fixed/flexible)、币种、APY 阈值、期限、语言、verboseLog
platform.json特定平台调度器类型/间隔、通知渠道、TG/Lark 凭据
state.json共享去重状态

核心配置(config.json)在各个平台间保持一致。平台配置(platform.json)各不相同——scheduler.type 字段决定如何触发扫描:

OpenClaw(openclaw.default.json):

  • scheduler.type = "openclaw-cron" — 通过会话内的 cron Agent 工具进行定时调度(不使用 OS crontab,也不使用 CLI 命令)。任务以隔离的轻量上下文 Agent 轮次运行,并通过 cron announce 投递将输出发送回对话渠道。notify.channel 默认为 "session",因此扫描结果会输出到 stdout,供 announce 推送(避免重复发送)。

Claude Code / Hermes / Generic(claude-code.default.json):

  • scheduler.type = "cron" — 通过 OS crontab → scripts/scan.sh 进行定时调度(不消耗 LLM Token),通知由脚本本身通过 TG / Lark curl 发送。
通知渠道(独立于平台)

按优先级检测(PRD 要求:优先使用 TG):

  1. Telegram — $TELEGRAM_BOT_TOKEN 和 $TELEGRAM_CHAT_ID 均已设置 → TG 就绪
  2. Lark — platform.notify.lark_webhook 非空 → Lark 就绪
  3. Session — 回退选项,仅在交互模式下有效

TG 和 Lark 是独立的推送渠道——无论 Agent 客户端是否打开,它们都能正常工作。在使用 OS crontab 的平台上,定时扫描会通过直接调用 curl 发送通知。在 OpenClaw 上,定时扫描会在隔离的 cron Agent 轮次中运行,并通过 cron announce 将结果投递到对话渠道(渠道 = "session");除非用户主动切换渠道,否则不会使用 TG/Lark curl。


技能路由

用户意图路由
"有闪赚通知我" / "monitor earn" / "帮我监控赚币" / "活期年化高了通知我"→ 激活流程
"改 APY 阈值" / "只看 USDT" / "change config" / "活期加上 BTC"→ 配置管理
"申购 USDT 定期 7D" / "subscribe" / "我要买"→ 购买指南
"执行 earn-hunter 扫描" (cron OR interactive)→ 扫描周期 — 运行 scripts/scan.sh 并转达其输出
"停止监控" / "暂停" / "stop"→ 暂停/恢复
"卸载 earn-hunter" / "uninstall"→ 卸载
"测试 earn-hunter" / "smoke test" / "测试定时任务"→ 测试模式

激活流程

首次设置。只需确认平台,其余均使用智能默认值。

步骤 1 — 平台检测与确认

参见平台检测。探测环境 → 请用户确认 → 写入 platform.json。

步骤 2 — 检测通知渠道并确认

继续之前必须主动检查可用渠道。 不要悄悄回退到会话。

在 OS-crontab 平台上,定时通知通过直接 curl 发送;在 OpenClaw 上,通知通过 cron announce 发送到对话中。按以下顺序检测(逐一检查,并报告所有渠道的状态):

  1. 检查 $TELEGRAM_BOT_TOKEN 和 $TELEGRAM_CHAT_ID 环境变量:
    • 两者都已设置 → TG 就绪
    • 已设置 Token,但缺少 chat_id → 警告:"Telegram 配置不完整(缺少 TELEGRAM_CHAT_ID),跳过 TG" → 继续检查下一个渠道
    • 两者都未设置 → TG 不可用
  2. 检查 platform.notify.lark_webhook 或 Lark MCP 工具:
    • 已设置且有效的 Webhook(以 https:// 开头并包含 /hook/)→ Lark 就绪
    • 已设置但格式无效的 Webhook(不以 https:// 开头或缺少 /hook/)→ 警告:"Lark webhook 格式无效,跳过 Lark" → 继续检查下一个渠道
    • 未配置且没有 Lark MCP → Lark 不可用

始终询问用户确认通知渠道,绝不可默认使用会话。 对于监控工具,通知至关重要;默认使用会话意味着用户不在对话中时会错过提醒。

如果检测到一个或多个外部渠道:

"检测到以下推送渠道可用:

  • {检测到的渠道列表,例如 Telegram / Lark}

你希望通知发到哪里?

  1. {检测到的渠道 1}
  2. {检测到的渠道 2,如有}
  3. 仅在当前会话显示(离线收不到)"

如果未检测到外部渠道:

"有新机会时才能把消息推送给你。你希望通知发到哪里?

  1. Telegram — 需要提供 Bot Token 和 Chat ID(通过环境变量)
  2. Lark/飞书 — 需要提供 Webhook URL
  3. 仅在当前会话显示(⚠ 离线收不到通知)

建议配置 Telegram 或 Lark,这样即使不在对话中也能收到提醒。"

  • 如果用户选择 Telegram → 指引设置 TELEGRAM_BOT_TOKEN 和 TELEGRAM_CHAT_ID 环境变量
  • 如果用户选择 Lark → 询问 Webhook URL,验证格式(以 https:// 开头,包含 /hook/),写入 platform.notify.lark_webhook
  • 如果用户选择会话 → 写入 "session",并警告:"⚠ 离线状态下不会收到通知,建议后续配置外部渠道。"

将确认的渠道写入 platform.json 的 notify.channel。

步骤 3 — 确认扫描配置(3 步,含默认值)

显示默认配置并请用户确认或自定义。每一步都提供默认值,用户按回车即可接受。

步骤 1/3 — 扫描范围: “扫描范围(可多选): [1] Flash Earn(闪赚) [2] Fixed Earn(定期赚币) [3] Flexible Earn(活期赚币) 默认:全选”

  • 默认:启用全部三项
  • 如果用户选择了特定项目 → 禁用其他项目
  • 如果用户仅选择 [1] → 设置 config.fixed.enabled = false、config.flexible.enabled = false;跳过步骤 2/3 和步骤 3/3
  • 如果用户仅选择 [3] → 设置 config.flash.enabled = false、config.fixed.enabled = false;进入活期专属配置(步骤 2/3 询问活期币种,步骤 3/3 询问活期 APY 阈值)

步骤 2/3 — 监控币种: 对于 Fixed Earn:“定期监控币种:全部(默认,按回车)或输入指定币种(如 USDT、SOL)”

  • 默认:"all"(全部币种)
  • 如果用户指定币种 → 将 config.currencies 设置为数组(例如 ["USDT", "SOL"])

对于 Flexible Earn:“活期监控币种:USDT、USDC(默认,按回车)或输入指定币种”

  • 默认:["USDT", "USDC"]
  • 如果用户指定币种 → 将 config.flexible.currencies 设置为数组
  • 注意:活期需要按币种调用 API,因此建议保持列表较短

步骤 3/3 — APY 阈值: 对于 Fixed Earn:“定期最低 APY 阈值:不限(默认,按回车)或输入百分比(如 8)”

  • 默认:0(不设限)
  • 如果用户指定 → 将 config.fixed.globalMinApy 设置为 value / 100(例如 8 → 0.08)

对于 Flexible Earn:“活期最低 APY 阈值:8%(默认,按回车)或输入百分比”

  • 默认:0.08(8%)
  • 如果用户指定 → 将 config.flexible.globalMinApy 设置为 value / 100

根据对话自动检测语言,并写入 config.json 的 notify.language。

将配置写入 ~/.okx/earn-hunter/config.json。

使用 {baseDir}/templates/activation.md 模板,以用户所用语言显示摘要。

步骤 4 — 冒烟测试与交付确认

无论 verboseLog 设置如何,冒烟测试始终会发送通知。

  1. 立即运行一个扫描周期,并强制开启 verboseLog,以确保始终生成输出,即使没有任何机会也如此。临时切换 verboseLog,运行脚本,然后恢复原设置;同时使用 EH_TEST_NAMESPACE=1,使冒烟测试的去重键使用 test: 前缀,避免污染生产状态:
    jq '.verboseLog=true' ~/.okx/earn-hunter/config.json > ~/.okx/earn-hunter/config.tmp \
      && mv ~/.okx/earn-hunter/config.tmp ~/.okx/earn-hunter/config.json
    EH_TEST_NAMESPACE=1 OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh
    # then restore verboseLog to the user's original value (e.g. false)
    
  2. 如果发现新机会 → 脚本发送常规通知(由模板渲染)
  3. 如果没有发现机会 → 在强制开启 verboseLog 的情况下,脚本发送简要状态。也可以附加激活确认消息: “Earn Hunter 已激活,当前暂无新机会,将在下一轮自动扫描。” (使用 {baseDir}/templates/activation.md 作为基础,并附加无机会说明)
  4. TG 或 Lark 渠道 → 询问:“已向 {channel} 发送测试消息,请确认是否收到?”
  5. 用户确认收到 → 继续步骤 5
  6. 未收到 → 排查问题(参见 notify-channels.md)
  7. 5 分钟无响应 → 提醒一次
  8. 会话渠道 → 跳过确认

**注意:**冒烟测试会忽略 verboseLog 设置,始终生成输出,以验证整个流程端到端正常运行。

步骤 4b — Cron 环境冒烟测试(仅限使用 OS-crontab 的平台)

**重要:**用户的交互式 shell 拥有完整的 PATH,但 cron 没有。运行第二次冒烟测试,模拟 cron 的最小环境,以验证 env.snapshot 是否正常工作:

env -i HOME="$HOME" PATH=/usr/bin:/bin \
  EH_TEST_NAMESPACE=1 OKX_PROFILE=live \
  bash ~/.okx/earn-hunter/scan.sh
  • 如果退出码为 0 → cron 将正常工作。继续步骤 5。
  • 如果退出码为 127 / “FATAL: 'okx' not found” → env.snapshot 不完整或缺失。重新运行 Preflight 步骤以重新生成它。不要继续步骤 5 — cron 作业将静默失败。
  • 向用户显示:“已验证 cron 最小环境下可正常执行。如果此步失败,说明 env.snapshot 中的工具路径有误。”
步骤 5 — 设置调度程序

调度机制取决于 platform.json 中的 .scheduler.type。根据平台选择分支。

OpenClaw(scheduler.type = "openclaw-cron")

在 OpenClaw 上,计划任务通过在对话中调用会话内的 cron Agent 工具来设置,绝不能使用操作系统命令或 openclaw cron CLI(在此情境下,CLI 路径存在权限问题)。建议用户就在当前聊天中设置:你创建的 cron 任务会继承当前会话的频道,因此扫描结果会直接发送回此对话。

调用 cron 工具,设置 action: "add",并使用如下格式的 job(从 platform.json 读取 .scheduler.interval,以确定频率):

  • name:"earn-hunter-hourly"
  • schedule:{ "kind": "every", "everyMs": 3600000 } — 根据 scheduler.interval 推导 everyMs("1h" → 3600000,"30m" → 1800000,"2h" → 7200000)
  • sessionTarget:"isolated" — 在隔离会话中运行,而不是在主会话中运行
  • payload:{ "kind": "agentTurn", "message": "执行 earn-hunter 扫描", "lightContext": true }
    • lightContext: true 会以轻量级引导上下文运行此轮任务(跳过工作区引导文件)→ 降低每次运行的令牌成本。
    • 令牌预算: OpenClaw cron 任务没有按任务设置工具白名单的字段(旧的 --tools exec,read,write 标志已不存在)。扫描成本低,因为它通过 exec 运行 okx CLI,不依赖 160 多个 okx MCP 工具——因此,只要隔离的 cron Agent 未配置为加载 okx MCP 服务,就只会使用常规工具(exec/read/write)。加载哪些工具由 Agent 的配置决定,而非此任务。
  • delivery:{ "mode": "announce" } — 将此轮任务的输出推送回创建任务的对话频道。

触发时,隔离 Agent 会运行提示词 "执行 earn-hunter 扫描" → 扫描周期(使用频道 session/stdout 运行 scripts/scan.sh)→ 转发结果 → announce 将结果发送到此处。因此,任何新机会都会自动发送过来。

切勿在对话中输出任何 shell/openclaw cron CLI 命令——只能通过 cron 工具设置计划任务。

OS crontab 平台(scheduler.type = "cron" 或 "launchagent" — Claude Code / Hermes / Generic)

这些平台使用 OS 调度器 → scripts/scan.sh。脚本负责完成所有工作(CLI 调用、筛选、去重、渲染、curl 通知),LLM 成本为零。

安装脚本 — 将技能的 scripts/scan.sh 复制到状态目录,确保调度器使用稳定路径:

mkdir -p ~/.okx/earn-hunter
cp {baseDir}/scripts/scan.sh ~/.okx/earn-hunter/scan.sh
chmod +x ~/.okx/earn-hunter/scan.sh

设置调度器 — 首先尝试 crontab;如果 macOS 上 cron 守护进程未运行,则回退到 LaunchAgent:

# Resolve tool directories from the current shell
NODE_DIR=$(dirname "$(command -v node)")
OKX_DIR=$(dirname "$(command -v okx)")
JQ_DIR=$(dirname "$(command -v jq)")
CRON_PATH=$(printf '%s\n' "$NODE_DIR" "$OKX_DIR" "$JQ_DIR" /usr/bin /bin | awk '!seen[$0]++' | paste -sd: -)

步骤 A:尝试 crontab 并验证 cron 守护进程(macOS)

(crontab -l 2>/dev/null; echo "0 * * * * PATH=$CRON_PATH OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh >> ~/.okx/earn-hunter/cron.log 2>&1") | crontab -

在 macOS 上(uname -s == Darwin),立即检查 cron 守护进程是否正在运行:

if [[ "$(uname -s)" == "Darwin" ]] && ! launchctl list com.vix.cron >/dev/null 2>&1; then
  # cron daemon not running — fallback to LaunchAgent
fi
  • 如果 cron 守护进程正在运行 → 完成,scheduler.type = "cron"。
  • 如果 cron 守护进程未运行 → 删除 crontab 条目并继续步骤 B。
  • 在 Linux 上 → 跳过检查(cron 始终可用),scheduler.type = "cron"。

步骤 B:macOS LaunchAgent 回退方案(scheduler.type = "launchagent")

使用解析后的路径生成 ~/Library/LaunchAgents/com.okx.earn-hunter.plist:

SCAN_SCRIPT="$HOME/.okx/earn-hunter/scan.sh"
LOG_FILE="$HOME/.okx/earn-hunter/cron.log"
INTERVAL=3600  # derive from scheduler.interval: "1h"→3600, "30m"→1800, "10m"→600

cat > ~/Library/LaunchAgents/com.okx.earn-hunter.plist << 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.okx.earn-hunter</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>${SCAN_SCRIPT}</string>
    </array>
    <key>StartInterval</key>
    <integer>${INTERVAL}</integer>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>${CRON_PATH}</string>
        <key>OKX_PROFILE</key>
        <string>live</string>
        <key>HOME</key>
        <string>${HOME}</string>
    </dict>
    <key>StandardOutPath</key>
    <string>${LOG_FILE}</string>
    <key>StandardErrorPath</key>
    <string>${LOG_FILE}</string>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>
PLIST

launchctl load ~/Library/LaunchAgents/com.okx.earn-hunter.plist

将 scheduler.type = "launchagent" 写入 platform.json。告知用户:

“macOS cron 服务未运行,已自动切换为 LaunchAgent 调度(无需 sudo,重启自动恢复)。”

注意:

  • OAuth 模式 → 从 plist 的 EnvironmentVariables 中省略 OKX_PROFILE(或将其设为空)。
  • LaunchAgent plist 路径必须是绝对路径(不能使用 ~)。激活流程会在生成时展开 $HOME。
  • RunAtLoad: true 表示加载后立即运行首次扫描。
  • 脚本会读取 config.json / platform.json,写入 state.json / notify.log,并自行通过 curl 向 TG Bot API 或 Lark Webhook 发送通知。定时触发时无需 Agent 参与。
  • 如果没有新机会且 verboseLog=false,脚本会以 0 退出且不输出任何内容——这是预期的静默行为。

重要:在 OS 调度器平台上,不要使用 Agent 平台的 /loop / Routines(Claude Code /loop、云端 Routines)。这些功能会在每次触发时启动 LLM 会话,且无法可靠推送外部通知。(OpenClaw 是上述例外——按设计,它会使用会话内的 cron 工具和 announce 投递。)


扫描周期

整个扫描周期由 scripts/scan.sh 实现(纯 shell + jq,LLM 成本为零)。 无论扫描由 OS crontab 触发、由 OpenClaw 隔离 cron Agent 回合触发,还是由用户在交互会话中触发(“执行 earn-hunter 扫描”),周期都相同:运行脚本并转发其输出。不要用自然语言重新实现扫描步骤——脚本是唯一可信来源。

**OpenClaw 隔离 cron 回合:**任务的提示词会将请求路由到此处。运行 scripts/scan.sh(当 platform.json 中的 notify.channel = "session" 时,脚本会将通知打印到 stdout);将该 stdout 作为回合响应转发。随后,cron 任务的 announce 投递会将其推送到对话频道。不要在此回合中通过 curl 向 TG/Lark 发送通知——投递由 announce 处理。

Agent 如何运行扫描(交互式触发)
# Profile: pass OKX_PROFILE only in API Key mode (omit for OAuth mode).
OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh

然后将脚本的 stdout 原样转发给用户:

  • 如果脚本打印了通知(Flash / Fixed / 混合 / 详细状态),请显示该通知。
  • 如果脚本没有打印任何内容(静默退出 0,即没有新机会且 verboseLog=false),告知用户“本轮扫描完成,无新机会”——不要向频道编造“扫描完成”消息;静默是有意设计的。
脚本的功能(实现规范见 references/scan-logic.md)
  1. 读取 ~/.okx/earn-hunter/config.json
  2. 运行扫描命令(依据 flash.enabled / fixed.enabled / flexible.enabled):
    • Flash:okx [--profile live] earn flash-earn projects --status 0,100 --json
    • Fixed:okx [--profile live] earn savings fixed-products --json(CLI <1.3.3 时自动回退到 rate-history + fixedOffers)
    • Flexible:针对 flexible.currencies 中的每种货币,运行 okx [--profile live] earn savings rate-history --ccy <ccy> --limit 1 --json
  3. 进行筛选(两层 APY 门槛、期限筛选、货币筛选;Flexible 使用门槛穿越模型)
  4. 与 ~/.okx/earn-hunter/state.json 去重(state.flash["<id>:<status>"]、state.fixed["<ccy>:<term>:<rate>"]、state.flexible["<ccy>"])
  5. 如果有新机会 → 渲染对应模板(flash / fixed / flexible / mixed),并通过检测到的渠道发送(TG → Lark → 会话),同时记录到 notify.log
  6. 更新 state.json(写入新键;清理 Flash ID 级差异;清理 Fixed 键级差异;清理 Flexible 门槛穿越差异;7 天 TTL;失败计数器)
  7. 如果没有新机会:verboseLog=true → 显示简要状态;verboseLog=false → 静默退出 0,不输出,也不发送任何内容
  8. 错误处理:连续失败计数器(失败 3 次时告警,然后重置);401 / 会话过期 → 凭证告警并停止

**脚本内的渠道路由:**检测顺序为 TG($TELEGRAM_BOT_TOKEN+$TELEGRAM_CHAT_ID)→ Lark(platform.json .notify.lark_webhook)→ 会话(标准输出)。在 platform.json 中将 notify.channel 设为 telegram/lark/session 可强制使用对应渠道。

**身份验证 / 配置文件:**脚本从不读取或打印凭证。所有身份验证均交由 okx CLI 处理(该 CLI 会读取 ~/.okx/config.toml)。仅通过 OKX_PROFILE 环境变量注入配置文件。

Flexible Earn 去重模型:与按具体机会去重的 Flash / Fixed 不同,Flexible 使用门槛穿越模型——键仅为 <ccy>。当 APY 上穿门槛时通知一次;只要 APY 仍高于门槛就保持静默;APY 跌至门槛以下时重置(差异清理会移除该键)。这样可以避免因利率波动而频繁通知。


购买指南

用户收到通知后想要申购 Fixed Earn 产品时。

阅读 {baseDir}/references/purchase-guide.md 了解完整流程。

摘要:

  1. 并行检查余额(资金账户 + 交易账户 + Flexible Earn)
  2. 比较固定 APR 与 Flexible 的 lendingRate
  3. 计算建议金额:min(idle + movable_simple_earn, lendQuota)
  4. 提供建议并附上比较提示
  5. 用户确认后 → 重新检查产品是否可申购(soldOut 防护)→ 移交给 okx-cex-earn

purchase-guide.md 涵盖的边界情况:

  • 余额 < minLend → 显示缺额
  • 金额 > lendQuota → 自动按上限调整并提示
  • 赎回成功但申购失败 → 提醒用户,资金已在资金账户中
  • 通知与申购之间产品已售罄 → 告知用户

**重要:**earn-hunter 不会直接执行写操作,而是将控制权移交给 okx-cex-earn。


配置管理

阅读 {baseDir}/references/config-reference.md 了解字段定义和自然语言示例。

用户想要更改设置时:

  1. 解析意图 → 映射到配置字段(字段映射见 config-reference.md)
  2. 读取目标 JSON 文件(config.json 或 platform.json)→ 修改字段 → 写回
  3. 读取更新后的文件 → 向用户确认变更

**例外:**无法通过自然语言更改 TG 凭证。告知用户直接设置环境变量。


暂停/恢复

根据 platform.json 的 .scheduler.type 分支:

OpenClaw(openclaw-cron) — 通过会话内的 cron 工具管理(无需 CLI):

  • 暂停: 调用 cron,设置 action: "update",目标为 earn-hunter-hourly 任务,补丁为 { "enabled": false }(或设置 action: "remove" 将其删除)。
  • 恢复: 设置 action: "update",并使用 { "enabled": true }(或按照激活步骤 5 重新创建)。
  • 使用 action: "list" 查找任务 ID。

OS crontab 平台(cron):

  • 暂停: crontab -l | grep -v 'earn-hunter' | crontab -
  • 恢复: 重新添加 crontab 条目(与激活步骤 5 相同)。

macOS LaunchAgent(launchagent):

  • 暂停: launchctl unload ~/Library/LaunchAgents/com.okx.earn-hunter.plist
  • 恢复: launchctl load ~/Library/LaunchAgents/com.okx.earn-hunter.plist

配置和状态会保留 — 恢复后会从上次中断处继续。


卸载

当用户说“卸载” / “uninstall”时:

  1. 停止调度器(与暂停操作相同)。对于 LaunchAgent,还要删除 plist: launchctl unload ~/Library/LaunchAgents/com.okx.earn-hunter.plist && rm -f ~/Library/LaunchAgents/com.okx.earn-hunter.plist
  2. 询问:“是否保留配置和历史数据?”
    • 是 → 仅移除调度器
    • 否 → 也移除 ~/.okx/earn-hunter/ 目录

测试模式

触发语句:“测试 earn-hunter” / “earn-hunter smoke test” / “测试定时任务触发”

行为 — 使用测试模式钩子运行脚本(无需单独的逻辑):

# Force verbose so output is always produced; write dedup keys under test: prefix.
EH_TEST_NAMESPACE=1 OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh   # with config.verboseLog temporarily set to true
  1. 执行完整扫描周期 — 脚本执行相同的扫描,但 EH_TEST_NAMESPACE=1 会隔离状态写入
  2. 强制发送通知 — 临时设置 config.verboseLog=true,使脚本无论是否找到机会都会发送输出(之后恢复)
  3. 将去重写入测试命名空间 — EH_TEST_NAMESPACE=1 会为去重键添加 test: 前缀(例如 test:flash:12345:100);这些键不受差异清理影响(仅 TTL 会移除它们),因此测试运行不会污染生产状态
  4. 扫描完成后输出诊断信息:
    • okx 身份验证状态(已登录 / 已过期 / 未配置)
    • 扫描命令结果(Flash 项目数量 + 固定产品数量 + 灵活利率数量)
    • 后置筛选结果(每种类型中通过筛选的数量)
    • 通知渠道状态(配置了哪个渠道、发送结果)
    • 调度器状态(OS crontab 平台:crontab 条目是否存在?/ OpenClaw:cron 工具的 action: "list" 是否显示 earn-hunter-hourly 任务?)
    • ~/.okx/earn-hunter/notify.log 的最后 5 行
  5. 完成消息:“测试完成。test: 前缀的 state 不影响正式去重,正式扫描不受影响。”

错误处理

读取 {baseDir}/templates/error-alert.md,获取准确的告警消息模板。

错误操作
okx 401 / "Session expired"停止扫描。发送告警(凭证失效模板)。如果处于交互模式,加载 okx-cex-auth。
网络错误 / 超时静默重试一次。如果仍然失败,则跳过本周期。
连续 3 次扫描失败发送告警(连续失败模板)。计数器存储在 state.consecutive_failures 中,告警后重置。
state.json 损坏写入 {"flash":{},"fixed":{},"consecutive_failures":0,"last_error":""} 进行重置。可能导致一轮重复通知。
通知发送失败记录到 notify.log,继续扫描。不添加去重键(下个周期会重试)。
运行时缺少 config.json发送告警:“earn-hunter 未配置,请运行首次激活流程。”
怀疑双客户端运行(用户提到两个平台)警告:“建议仅在一个客户端运行 earn-hunter,避免重复通知。”
verboseLog = true 且无命中发送简要状态(不静默)。

i18n

  • 所有通知均以用户的语言呈现(在激活时检测,并存储在 config.notify.language 中)
  • 锁定术语(绝不翻译): Flash Earn、Fixed Earn、Simple Earn、DCD、APY、APR、OKX、earn-hunter、Telegram、项目名称、货币代码(USDT、BTC 等)
  • 回退: 如果 LLM 渲染失败,则发送中文模板,并附加 (translation unavailable, sent in zh-CN)

全局说明

  • **安全性:**切勿在聊天中接收凭据。TG token 只能通过环境变量提供。引导用户使用 okx config init 设置 OKX 身份验证。
  • **输出:**所有 okx 命令都使用 --json。将结果呈现为 Markdown 表格。
  • **日志记录:**所有通知发送结果都记录在 ~/.okx/earn-hunter/notify.log 中。
  • **范围:**涵盖 Flash Earn、Simple Earn Fixed 和 Simple Earn Flexible(活期)。DCD、链上功能和 auto-earn 不在范围内。
  • **模式:**仅限实盘交易。config.simulatedTrading 始终为 false。

防御性设计原则

以下约束适用于对 earn-hunter 的所有更改:

  1. **cron PATH ≠ 用户 shell PATH。**任何由 cron 触发的脚本都必须独立解析工具路径,绝不能假设 cron 会继承用户的登录 PATH。脚本使用 env.snapshot + resolve_bin 回退。
  2. **先进行健全性检查。**运行任何 CLI 命令前,先确认二进制文件存在。工具缺失时立即退出并显示明确错误,不能静默失败。
  3. **绝不丢弃 stderr。**对于 CLI 调用,使用 2>&1(捕获到变量中),而不是 2>/dev/null。否则错误消息会消失,last_error 也会为空。
  4. **冒烟测试必须模拟生产环境。**激活冒烟测试运行两次:一次在用户 shell 中运行(验证凭据和网络),一次在最小化环境 env -i PATH=/usr/bin:/bin 中运行(验证 cron 能够执行)。如果 cron 冒烟测试失败,不要通过步骤 5。
  5. **告警模板必须处理空字段。**如果 last_error 是空字符串,则追加“检查 ~/.okx/earn-hunter/cron.log;错误为空通常意味着 PATH 问题”的指引。
  6. **工具路径使用快照 + 回退,而不是全局 PATH。**激活时将确切路径写入 env.snapshot;resolve_bin 依次尝试快照 → command -v → 常见硬编码路径。这样可适应 nvm switch / Homebrew 升级。

评论

支持 Markdown