关于 earn-hunter
earn-hunter 是用于监控 OKX 闪赚、定期赚币和活期赚币机会的 Agent 技能,可按配置筛选并发送提醒。它还支持定时或手动扫描,以及暂停和配置管理。
主要功能
- 01监控三类 Earn 机会
- 02按币种和 APY 筛选
- 03定时或手动扫描
- 04Telegram、Lark 或会话通知
- 05管理配置与监控状态
适用场景
- 01发现符合条件的 Earn 产品
- 02定期接收收益机会提醒
- 03调整币种和 APY 监控条件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ okx/ agent-skills --skill earn-hunter安装后在 Claude Code 中输入/earn-hunter确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
config/claude-code.default.jsondefault.jsonopenclaw.default.jsonreferences/config-reference.mdnotify-channels.mdpurchase-guide.mdscan-logic.mdscheduler-setup.mdscripts/scan.shtemplates/activation.mderror-alert.mdfixed-earn.mdflash-earn.mdflexible-earn.mdmixed-notify.md详细介绍
Earn Hunter
用于自动监控 OKX 闪赚、定期赚币和灵活赚币(简单赚币)机会。
{baseDir} = 包含此 SKILL.md 文件的目录。所有相对路径(references/、templates/、config/)均以此目录为基准解析。
预检
-
验证是否已安装
okxCLI:which okx。如果未安装,请通过npm install -g @okx_ai/okx-trade-cli安装。 在 OpenClaw 上,还要验证 Agent 工具列表中是否提供会话内的cron工具(用于安排计划任务,而不是openclawCLI)。 -
检查可选依赖技能:
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,后续可手动安装。” - 无论技能安装结果如何,均继续预检
-
检测认证模式 — 依次执行两项检查,采用第一个匹配结果:
okx config show --json→ 存在非空的api_key字段 → API Key 模式。所有命令都添加--profile live。- 没有 API Key,且
okx auth status --json→"status":"logged_in"→ OAuth 模式。无需--profile标志。 - 两者都不符合 → 停止。加载
okx-cex-auth技能并按照登录步骤操作。
-
初始化配置和状态:
- 如果
~/.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):
扫描脚本会读取此文件来解析工具路径,即使在 cron 的精简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 SNAPPATH=/usr/bin:/bin环境下也能正常运行。
Agent 对配置、状态和平台 JSON 执行的读写操作仅用于激活和配置管理。定期扫描本身完全由
scripts/scan.sh执行(shell + jq)——扫描需要jq。通过which jq验证;如果未安装,请安装(brew install jq/apt-get install jq)。 - 如果
平台与渠道检测
三个独立维度:平台(Agent 运行的位置)、调度器(触发扫描的方式)、通知渠道(发送提醒的位置)。
平台检测(主动探测 + 用户确认)
首次运行(不存在 platform.json):
- 探测环境线索:
- 是否存在
OPENCLAW_HOME环境变量?→ 提示:OpenClaw - Agent 工具列表是否包含
cron/delivery工具?→ 提示:OpenClaw - 是否存在
HERMES_HOME环境变量,或which hermes是否执行成功?→ 提示:Hermes Agent - 是否在 Claude Code 会话中运行?→ 提示:Claude Code
- 以上均不匹配 → 提示:Generic
- 是否存在
- 显示检测结果并请用户确认:
- “检测到你正在使用 {detected_platform},是否正确?”
- 用户确认 → 继续
- 用户否认 → 询问:“你使用的是哪个平台?1) OpenClaw 2) Claude Code 3) Hermes Agent 4) 其他”
- 初始化平台配置:
- 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"
- OpenClaw / Claude Code → 将
- 将结果写入
~/.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"— 通过会话内的cronAgent 工具进行定时调度(不使用 OS crontab,也不使用 CLI 命令)。任务以隔离的轻量上下文 Agent 轮次运行,并通过 cronannounce投递将输出发送回对话渠道。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):
- Telegram —
$TELEGRAM_BOT_TOKEN和$TELEGRAM_CHAT_ID均已设置 → TG 就绪 - Lark —
platform.notify.lark_webhook非空 → Lark 就绪 - 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 发送到对话中。按以下顺序检测(逐一检查,并报告所有渠道的状态):
- 检查
$TELEGRAM_BOT_TOKEN和$TELEGRAM_CHAT_ID环境变量:- 两者都已设置 → TG 就绪
- 已设置 Token,但缺少 chat_id → 警告:"Telegram 配置不完整(缺少 TELEGRAM_CHAT_ID),跳过 TG" → 继续检查下一个渠道
- 两者都未设置 → TG 不可用
- 检查
platform.notify.lark_webhook或 Lark MCP 工具:- 已设置且有效的 Webhook(以
https://开头并包含/hook/)→ Lark 就绪 - 已设置但格式无效的 Webhook(不以
https://开头或缺少/hook/)→ 警告:"Lark webhook 格式无效,跳过 Lark" → 继续检查下一个渠道 - 未配置且没有 Lark MCP → Lark 不可用
- 已设置且有效的 Webhook(以
始终询问用户确认通知渠道,绝不可默认使用会话。 对于监控工具,通知至关重要;默认使用会话意味着用户不在对话中时会错过提醒。
如果检测到一个或多个外部渠道:
"检测到以下推送渠道可用:
- {检测到的渠道列表,例如 Telegram / Lark}
你希望通知发到哪里?
- {检测到的渠道 1}
- {检测到的渠道 2,如有}
- 仅在当前会话显示(离线收不到)"
如果未检测到外部渠道:
"有新机会时才能把消息推送给你。你希望通知发到哪里?
- Telegram — 需要提供 Bot Token 和 Chat ID(通过环境变量)
- Lark/飞书 — 需要提供 Webhook URL
- 仅在当前会话显示(⚠ 离线收不到通知)
建议配置 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 设置如何,冒烟测试始终会发送通知。
- 立即运行一个扫描周期,并强制开启
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) - 如果发现新机会 → 脚本发送常规通知(由模板渲染)
- 如果没有发现机会 → 在强制开启
verboseLog的情况下,脚本发送简要状态。也可以附加激活确认消息: “Earn Hunter 已激活,当前暂无新机会,将在下一轮自动扫描。” (使用{baseDir}/templates/activation.md作为基础,并附加无机会说明) - TG 或 Lark 渠道 → 询问:“已向 {channel} 发送测试消息,请确认是否收到?”
- 用户确认收到 → 继续步骤 5
- 未收到 → 排查问题(参见
notify-channels.md) - 5 分钟无响应 → 提醒一次
- 会话渠道 → 跳过确认
**注意:**冒烟测试会忽略 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运行okxCLI,不依赖 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)
- 读取
~/.okx/earn-hunter/config.json - 运行扫描命令(依据
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
- Flash:
- 进行筛选(两层 APY 门槛、期限筛选、货币筛选;Flexible 使用门槛穿越模型)
- 与
~/.okx/earn-hunter/state.json去重(state.flash["<id>:<status>"]、state.fixed["<ccy>:<term>:<rate>"]、state.flexible["<ccy>"]) - 如果有新机会 → 渲染对应模板(flash / fixed / flexible / mixed),并通过检测到的渠道发送(TG → Lark → 会话),同时记录到
notify.log - 更新
state.json(写入新键;清理 Flash ID 级差异;清理 Fixed 键级差异;清理 Flexible 门槛穿越差异;7 天 TTL;失败计数器) - 如果没有新机会:
verboseLog=true→ 显示简要状态;verboseLog=false→ 静默退出 0,不输出,也不发送任何内容 - 错误处理:连续失败计数器(失败 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 了解完整流程。
摘要:
- 并行检查余额(资金账户 + 交易账户 + Flexible Earn)
- 比较固定 APR 与 Flexible 的 lendingRate
- 计算建议金额:
min(idle + movable_simple_earn, lendQuota) - 提供建议并附上比较提示
- 用户确认后 → 重新检查产品是否可申购(soldOut 防护)→ 移交给
okx-cex-earn
purchase-guide.md 涵盖的边界情况:
- 余额 < minLend → 显示缺额
- 金额 > lendQuota → 自动按上限调整并提示
- 赎回成功但申购失败 → 提醒用户,资金已在资金账户中
- 通知与申购之间产品已售罄 → 告知用户
**重要:**earn-hunter 不会直接执行写操作,而是将控制权移交给 okx-cex-earn。
配置管理
阅读 {baseDir}/references/config-reference.md 了解字段定义和自然语言示例。
用户想要更改设置时:
- 解析意图 → 映射到配置字段(字段映射见
config-reference.md) - 读取目标 JSON 文件(
config.json或platform.json)→ 修改字段 → 写回 - 读取更新后的文件 → 向用户确认变更
**例外:**无法通过自然语言更改 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”时:
- 停止调度器(与暂停操作相同)。对于 LaunchAgent,还要删除 plist:
launchctl unload ~/Library/LaunchAgents/com.okx.earn-hunter.plist && rm -f ~/Library/LaunchAgents/com.okx.earn-hunter.plist - 询问:“是否保留配置和历史数据?”
- 是 → 仅移除调度器
- 否 → 也移除
~/.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
- 执行完整扫描周期 — 脚本执行相同的扫描,但
EH_TEST_NAMESPACE=1会隔离状态写入 - 强制发送通知 — 临时设置
config.verboseLog=true,使脚本无论是否找到机会都会发送输出(之后恢复) - 将去重写入测试命名空间 —
EH_TEST_NAMESPACE=1会为去重键添加test:前缀(例如test:flash:12345:100);这些键不受差异清理影响(仅 TTL 会移除它们),因此测试运行不会污染生产状态 - 扫描完成后输出诊断信息:
- okx 身份验证状态(已登录 / 已过期 / 未配置)
- 扫描命令结果(Flash 项目数量 + 固定产品数量 + 灵活利率数量)
- 后置筛选结果(每种类型中通过筛选的数量)
- 通知渠道状态(配置了哪个渠道、发送结果)
- 调度器状态(OS crontab 平台:crontab 条目是否存在?/ OpenClaw:
cron工具的action: "list"是否显示earn-hunter-hourly任务?) ~/.okx/earn-hunter/notify.log的最后 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 的所有更改:
- **cron PATH ≠ 用户 shell PATH。**任何由 cron 触发的脚本都必须独立解析工具路径,绝不能假设 cron 会继承用户的登录 PATH。脚本使用
env.snapshot+resolve_bin回退。 - **先进行健全性检查。**运行任何 CLI 命令前,先确认二进制文件存在。工具缺失时立即退出并显示明确错误,不能静默失败。
- **绝不丢弃 stderr。**对于 CLI 调用,使用
2>&1(捕获到变量中),而不是2>/dev/null。否则错误消息会消失,last_error也会为空。 - **冒烟测试必须模拟生产环境。**激活冒烟测试运行两次:一次在用户 shell 中运行(验证凭据和网络),一次在最小化环境
env -i PATH=/usr/bin:/bin中运行(验证 cron 能够执行)。如果 cron 冒烟测试失败,不要通过步骤 5。 - **告警模板必须处理空字段。**如果
last_error是空字符串,则追加“检查~/.okx/earn-hunter/cron.log;错误为空通常意味着 PATH 问题”的指引。 - **工具路径使用快照 + 回退,而不是全局 PATH。**激活时将确切路径写入
env.snapshot;resolve_bin依次尝试快照 →command -v→ 常见硬编码路径。这样可适应nvm switch/ Homebrew 升级。