okx/agent-skills

earn-hunter

Automatically monitors OKX Flash Earn, Fixed Earn and Flexible Earn opportunities, sends push notifications, and guides subscription.

Ver código-fonte

Como usar

  1. Copie o comando e verifique o pacote antes de instalar no seu ambiente de agente.
  2. Consulte no original as ferramentas, credenciais e ambientes necessários.
  3. Descreva os dados de entrada e a entrega esperada; confira a saída com as fontes.
npx skills add https://github.com/okx/agent-skills --skill earn-hunter

Antes de começar

  • A instalação não inclui assinatura de modelo, franquia API ou acesso aos seus arquivos.
  • Estrelas e instalações indicam atividade, não um teste independente de qualidade ou segurança.
Documento original do Skill

Renderizado do repositório de origem, preservando títulos, exemplos, código, tabelas, links e imagens.

Earn Hunter

Automated monitor for OKX Flash Earn, Fixed Earn, and Flexible Earn (Simple Earn) opportunities.

`{baseDir}` = the directory containing this SKILL.md file. All relative paths (references/, templates/, config/) are resolved from here.

Preflight

  1. Verify okx CLI installed: which okx. If missing, install via npm install -g @okx_ai/okx-trade-cli.

On OpenClaw, also verify the in-session cron tool is available in the agent tool list (used for scheduling — not the openclaw CLI).

  1. Check optional dependent skills:
bash
   okx skill list --json

Optional skills (not required for scanning/notifications):

  • okx-cex-earn — needed for purchase guide (subscription execution)
  • okx-cex-auth — needed for authentication recovery

If either is missing, attempt to install but do not block if installation fails:

bash
   okx skill add okx-cex-earn
   okx skill add okx-cex-auth
  • Install succeeds → continue
  • Install fails (network error, marketplace unavailable, etc.) → warn and continue:

"⚠ {skill_name} 安装失败,扫描和通知功能不受影响。申购引导和认证恢复需要该 skill,后续可手动安装。"

  • Preflight continues regardless of skill installation result
  1. Auth mode detection — run both, first match wins:
  • okx config show --json → has non-empty api_key field → API Key mode. Add --profile live to all commands.
  • No API key + okx auth status --json → "status":"logged_in" → OAuth mode. No --profile flag needed.
  • Neither → stop. Load okx-cex-auth skill and follow login steps.
  1. Init config and state:
  • If ~/.okx/earn-hunter/ directory does not exist → mkdir -p ~/.okx/earn-hunter
  • If ~/.okx/earn-hunter/config.json does not exist → copy {baseDir}/config/default.json to it
  • If ~/.okx/earn-hunter/state.json does not exist → write {"flash":{},"fixed":{},"flexible":{},"consecutive_failures":0,"last_error":""}
  • If ~/.okx/earn-hunter/platform.json does not exist → run Platform Detection
  • Always (re)install the scan script: cp {baseDir}/scripts/scan.sh ~/.okx/earn-hunter/scan.sh && chmod +x ~/.okx/earn-hunter/scan.sh. This is the script cron and interactive scans both call.
  • Write ~/.okx/earn-hunter/env.snapshot with resolved tool paths (cron cannot rely on the user's login PATH):
bash
     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

The scan script sources this file to resolve tool paths even under cron's minimal PATH=/usr/bin:/bin.

Config/state/platform JSON read-write done by the agent is only for activation/config management. The recurring scan itself is performed entirely by `scripts/scan.sh` (shell + jq) — jq is required for scanning. Verify with which jq; if missing, install (brew install jq / apt-get install jq).

Platform & Channel Detection

Three independent dimensions: platform (where the agent runs), scheduler (what triggers scans), notification channel (where alerts go).

Platform Detection (active probe + user confirmation)

First run (no `platform.json` exists):

  1. Probe environment clues:
  • OPENCLAW_HOME env var exists? → hint: OpenClaw
  • Agent tool list contains cron / delivery tools? → hint: OpenClaw
  • HERMES_HOME env var exists or which hermes succeeds? → hint: Hermes Agent
  • Running inside Claude Code session? → hint: Claude Code
  • None of the above matched → hint: Generic
  1. Present detection result and ask user to confirm:
  • "检测到你正在使用 {detected_platform},是否正确?"
  • User confirms → proceed
  • User says no → ask: "你使用的是哪个平台?1) OpenClaw 2) Claude Code 3) Hermes Agent 4) 其他"
  1. Initialize platform config:
  • OpenClaw / Claude Code → copy {baseDir}/config/<confirmed_platform>.default.json to ~/.okx/earn-hunter/platform.json
  • Hermes Agent → copy {baseDir}/config/claude-code.default.json as base, set .platform to "hermes", .scheduler.type to "cron"
  • Generic → copy {baseDir}/config/claude-code.default.json as base, set .platform to "generic", .scheduler.type to "manual"
  1. Result written to ~/.okx/earn-hunter/platform.json, subsequent runs skip detection.

Subsequent runs (platform.json exists): Read ~/.okx/earn-hunter/platform.json and extract the .platform field (returns "openclaw", "claude-code", "hermes", or "generic").

No scheduler available on detected platform (only applies to platforms that should have one but don't) → error: "当前客户端不支持定时任务,请升级到最新版本。" Generic platform → no automatic scheduler. Inform: "当前平台不支持自动调度,你可以手动说'执行 earn-hunter 扫描'来触发。"

Configuration Files

FileScopeContent
config.jsonSharedScan scope (flash/fixed/flexible), currencies, APY thresholds, terms, language, verboseLog
platform.jsonPlatform-specificScheduler type/interval, notification channel, TG/Lark credentials
state.jsonSharedDedup state

Core config (config.json) is identical across platforms. Platform config (platform.json) differs — the scheduler.type field determines how scans are triggered:

OpenClaw (`openclaw.default.json`):

  • scheduler.type = "openclaw-cron" — scheduled via the in-session `cron` agent tool (no OS crontab, no CLI commands). The job runs as an isolated, light-context agent turn and delivers its output back to the conversation channel via cron `announce` delivery. notify.channel defaults to "session" so the scan prints to stdout for announce to push (avoids double-send).

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

  • scheduler.type = "cron" — scheduled via OS crontab → `scripts/scan.sh` (zero LLM token cost), notification via TG / Lark curl from the script itself.

Notification Channels (independent of platform)

Detect in priority order (PRD requirement: TG first):

  1. Telegram — $TELEGRAM_BOT_TOKEN and $TELEGRAM_CHAT_ID both set → TG ready
  2. Lark — platform.notify.lark_webhook non-empty → Lark ready
  3. Session — fallback, only works in interactive mode

TG and Lark are standalone push channels — they work regardless of whether the agent client is open. On OS-crontab platforms, scheduled scans send notifications via direct curl. On OpenClaw, the scheduled scan runs in an isolated cron agent turn and delivers via cron `announce` to the conversation channel (channel = "session"); TG/Lark curl is not used unless the user explicitly switches the channel.


Skill Routing

User intentRoute
"有闪赚通知我" / "monitor earn" / "帮我监控赚币" / "活期年化高了通知我"→ Activation Flow
"改 APY 阈值" / "只看 USDT" / "change config" / "活期加上 BTC"→ Config Management
"申购 USDT 定期 7D" / "subscribe" / "我要买"→ Purchase Guide
"执行 earn-hunter 扫描" (cron OR interactive)→ Scan Cycle — run scripts/scan.sh and relay its output
"停止监控" / "暂停" / "stop"→ Pause/Resume
"卸载 earn-hunter" / "uninstall"→ Uninstall
"测试 earn-hunter" / "smoke test" / "测试定时任务"→ Test Mode

Activation Flow

First-time setup. Only confirm platform — everything else uses smart defaults.

Step 1 — Platform Detection & Confirmation

See Platform Detection. Probe environment → ask user to confirm → write platform.json.

Step 2 — Detect Notification Channel & Confirm

Must actively check available channels before proceeding. Do NOT silently fall back to session.

On OS-crontab platforms, scheduled notifications go out via direct curl; on OpenClaw they go out via cron announce to the conversation. Detection order (check each, report status for all):

  1. Check $TELEGRAM_BOT_TOKEN and $TELEGRAM_CHAT_ID env vars:
  • Both set → TG ready
  • Token set but chatid missing → warn: "Telegram 配置不完整(缺少 TELEGRAMCHAT_ID),跳过 TG" → continue to next channel
  • Neither set → TG not available
  1. Check platform.notify.lark_webhook or Lark MCP tools:
  • Webhook set and valid (starts with https:// and contains /hook/) → Lark ready
  • Webhook set but format invalid (does not start with https:// or missing /hook/) → warn: "Lark webhook 格式无效,跳过 Lark" → continue to next channel
  • Not configured and no Lark MCP → Lark not available

Always ask the user to confirm notification channel — never silently default to session. For a monitoring tool, notification is critical; defaulting to session means alerts are lost when the user is not in the conversation.

If one or more external channels detected:

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

  • {list of detected channels, e.g. Telegram / Lark}

你希望通知发到哪里?

  1. {detected channel 1}
  2. {detected channel 2, if any}
  3. 仅在当前会话显示(离线收不到)"

If no external channel detected:

"新机会才能推送到你手上。你希望通知发到哪里?

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

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

  • If user picks Telegram → guide setting TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID env vars
  • If user picks Lark → ask for webhook URL, validate format (starts with https://, contains /hook/), write to platform.notify.lark_webhook
  • If user picks session → write "session" and warn: "⚠ 离线状态下不会收到通知,建议后续配置外部渠道。"

Write confirmed channel to platform.json notify.channel.

Step 3 — Confirm Scan Config (3-step with defaults)

Present default config and ask user to confirm or customize. Each step offers a default — user can press enter to accept.

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

  • Default: all three enabled
  • If user picks specific items → disable the others
  • If user only picks [1] → set config.fixed.enabled = false, config.flexible.enabled = false; skip Step 2/3 and 3/3
  • If user only picks [3] → set config.flash.enabled = false, config.fixed.enabled = false; go to flexible-specific config (Step 2/3 asks flexible currencies, Step 3/3 asks flexible APY threshold)

Step 2/3 — 监控币种: For Fixed Earn: "定期监控币种:全部(默认,按回车)或输入指定币种(如 USDT, SOL)"

  • Default: "all" (all currencies)
  • If user specifies → set config.currencies to array (e.g. ["USDT", "SOL"])

For Flexible Earn: "活期监控币种:USDT, USDC(默认,按回车)或输入指定币种"

  • Default: ["USDT", "USDC"]
  • If user specifies → set config.flexible.currencies to array
  • Note: flexible requires per-currency API calls, so recommend keeping the list small

Step 3/3 — APY 阈值: For Fixed Earn: "定期最低 APY 阈值:不限(默认,按回车)或输入百分比(如 8)"

  • Default: 0 (no limit)
  • If user specifies → set config.fixed.globalMinApy to value / 100 (e.g. 8 → 0.08)

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

  • Default: 0.08 (8%)
  • If user specifies → set config.flexible.globalMinApy to value / 100

Auto-detect language from conversation and write to config.json notify.language.

Write config to ~/.okx/earn-hunter/config.json.

Display summary using {baseDir}/templates/activation.md template (in user's language).

Step 4 — Smoke Test & Delivery Confirmation

Smoke test always sends a notification, regardless of `verboseLog` setting.

  1. Run one scan cycle immediately with verboseLog forced on so output is always produced, even when there are no opportunities. Temporarily flip verboseLog, run the script, then restore it — and use EH_TEST_NAMESPACE=1 so smoke-test dedup keys go under the test: prefix and don't pollute production state:
bash
   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)
  1. If new opportunities found → the script sends the normal notification (rendered from templates)
  2. If no opportunities found → with verboseLog forced on, the script sends the brief status. Optionally append the activation confirmation message:

"Earn Hunter 已激活,当前暂无新机会,将在下一轮自动扫描。" (Use {baseDir}/templates/activation.md as base, append the no-opportunity note)

  1. TG or Lark channel → ask: "已向 {channel} 发送测试消息,请确认是否收到?"
  2. User confirms → proceed to Step 5
  3. Not received → troubleshoot (see notify-channels.md)
  4. 5 min no response → ping once
  5. Session channel → skip confirmation

Note: The smoke test ignores verboseLog setting — it always produces output to verify the full pipeline works end-to-end.

Step 4b — Cron Environment Smoke Test (OS-crontab platforms only)

Critical: The user's interactive shell has a full PATH, but cron does not. Run a second smoke test simulating cron's minimal environment to verify env.snapshot works:

bash
env -i HOME="$HOME" PATH=/usr/bin:/bin \
  EH_TEST_NAMESPACE=1 OKX_PROFILE=live \
  bash ~/.okx/earn-hunter/scan.sh
  • If exit 0 → cron will work. Proceed to Step 5.
  • If exit 127 / "FATAL: 'okx' not found" → env.snapshot is incomplete or missing. Re-run Preflight step to regenerate it. Do NOT proceed to Step 5 — the cron job will fail silently.
  • Show the user: "已验证 cron 最小环境下可正常执行。如果此步失败,说明 env.snapshot 中的工具路径有误。"

Step 5 — Set Up Scheduler

The scheduling mechanism depends on platform.json .scheduler.type. Branch on the platform.

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

On OpenClaw, scheduling is done inside the conversation by calling the in-session `cron` agent tool — never an OS command or openclaw cron CLI (the CLI path has permission issues in this context). Encourage the user to set it up right here in the chat: the cron job you create inherits the current session's channel, so its scan output is delivered straight back to this conversation.

Call the cron tool with action: "add" and a job shaped like this (read .scheduler.interval from platform.json for the frequency):

  • name: "earn-hunter-hourly"
  • schedule: { "kind": "every", "everyMs": 3600000 } — derive everyMs from scheduler.interval ("1h" → 3600000, "30m" → 1800000, "2h" → 7200000)
  • sessionTarget: "isolated" — run in an isolated session, not the main one
  • payload: { "kind": "agentTurn", "message": "执行 earn-hunter 扫描", "lightContext": true }
  • lightContext: true runs the turn with a lightweight bootstrap context (skips workspace bootstrap files) → lower token cost per tick.
  • Token budget: OpenClaw cron jobs have no per-job tool-whitelist field (the old --tools exec,read,write flag no longer exists). The scan stays cheap because it runs the okx CLI through exec and does not depend on the 160+ okx MCP tools — so as long as the isolated cron agent isn't configured to load the okx MCP server, only the regular tools (exec/read/write) are in play. Which tools load is governed by the agent's config, not by this job.
  • delivery: { "mode": "announce" } — pushes the turn's output back to the conversation channel that created the job.

When it fires, the isolated agent runs the prompt "执行 earn-hunter 扫描" → Scan Cycle (which runs scripts/scan.sh with channel session/stdout) → relays the result → announce delivers it here. Any new opportunity is therefore sent automatically.

Do NOT emit any shell/openclaw cron CLI command in the conversation — drive scheduling only through the cron tool.

OS-crontab platforms (scheduler.type = "cron" or "launchagent" — Claude Code / Hermes / Generic)

These use OS scheduler → `scripts/scan.sh`. The script does everything (CLI calls, filter, dedup, render, curl notifications) with zero LLM cost.

Install the script — copy the skill's scripts/scan.sh into the state dir so the scheduler has a stable path:

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

Set up scheduler — try crontab first, fallback to LaunchAgent on macOS if cron daemon is not running:

bash
# 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: -)

Step A: Try crontab + verify cron daemon (macOS)

bash
(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 -

On macOS (uname -s == Darwin), immediately check if the cron daemon is running:

bash
if [[ "$(uname -s)" == "Darwin" ]] && ! launchctl list com.vix.cron >/dev/null 2>&1; then
  # cron daemon not running — fallback to LaunchAgent
fi
  • If cron daemon is running → done, scheduler.type = "cron".
  • If cron daemon is not running → remove the crontab entry and proceed to Step B.
  • On Linux → skip the check (cron is always available), scheduler.type = "cron".

Step B: macOS LaunchAgent fallback (scheduler.type = "launchagent")

Generate ~/Library/LaunchAgents/com.okx.earn-hunter.plist with the resolved paths:

bash
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

Write scheduler.type = "launchagent" to platform.json. Inform user:

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

Notes:

  • OAuth mode → omit OKX_PROFILE from the plist EnvironmentVariables (or set to empty).
  • LaunchAgent plist paths must be absolute (no ~). The activation flow expands $HOME at generation time.
  • RunAtLoad: true means the first scan runs immediately after loading.
  • The script reads config.json / platform.json, writes state.json / notify.log, and sends notifications via curl to TG Bot API or Lark Webhook itself. No agent involvement needed at tick time.
  • The script exits 0 and produces no output when there are no new opportunities and verboseLog=false — this is the intended silent behavior.

IMPORTANT: On OS-scheduler platforms, do NOT use agent-platform `/loop` / Routines (Claude Code /loop, cloud Routines). These spawn LLM sessions per tick and cannot reliably push external notifications. (OpenClaw is the exception above — it uses its in-session cron tool with announce delivery by design.)


Scan Cycle

The entire Scan Cycle is implemented by `scripts/scan.sh` (pure shell + jq, zero LLM cost). Whether triggered by OS crontab, by an OpenClaw isolated cron agent turn, or by a user in an interactive session ("执行 earn-hunter 扫描"), the cycle is the same: run the script and relay its output. Do NOT re-implement the scan steps in natural language — the script is the single source of truth.

OpenClaw isolated cron turn: the job's prompt routes here. Run scripts/scan.sh (with platform.json notify.channel = "session", the script prints any notification to stdout); relay that stdout as the turn's response. The cron job's announce delivery then pushes it to the conversation channel. Do not curl TG/Lark from this turn — delivery is handled by announce.

How the agent runs a scan (interactive trigger)

bash
# Profile: pass OKX_PROFILE only in API Key mode (omit for OAuth mode).
OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh

Then relay the script's stdout verbatim to the user:

  • If the script prints a notification (Flash / Fixed / mixed / verbose status), show it.
  • If the script prints nothing (silent exit 0, the no-new-opportunity + verboseLog=false case), tell the user "本轮扫描完成,无新机会" — do NOT fabricate a "scan complete" message into a channel; the silence is intentional.

What the script does (see references/scan-logic.md for the spec it implements)

  1. Reads ~/.okx/earn-hunter/config.json
  2. Runs scan commands (based on 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 (auto-fallback to rate-history + fixedOffers on CLI <1.3.3)
  • Flexible: for each currency in flexible.currencies, okx [--profile live] earn savings rate-history --ccy <ccy> --limit 1 --json
  1. Filters (two-layer APY threshold, terms filter, currency filter; flexible uses threshold-crossing model)
  2. Dedups against ~/.okx/earn-hunter/state.json (state.flash["<id>:<status>"], state.fixed["<ccy>:<term>:<rate>"], state.flexible["<ccy>"])
  3. If new opportunities → renders the matching template (flash / fixed / flexible / mixed) and sends via the detected channel (TG → Lark → session), logging to notify.log
  4. Updates state.json (write new keys; flash ID-level diff cleanup; fixed key-level diff cleanup; flexible threshold-crossing diff cleanup; 7-day TTL; failure counter)
  5. If no new opportunities: verboseLog=true → brief status; verboseLog=false → silent exit 0, no output, nothing sent
  6. Error handling: consecutive-failure counter (alert at 3, then reset); 401/session-expired → credential alert + stop

Channel routing inside the script: detection order TG ($TELEGRAM_BOT_TOKEN+$TELEGRAM_CHAT_ID) → Lark (platform.json .notify.lark_webhook) → session (stdout). A notify.channel of telegram/lark/session in platform.json forces that channel.

Auth / profile: the script never reads or prints credentials. It delegates all auth to the okx CLI (which reads ~/.okx/config.toml). Profile is injected only via the OKX_PROFILE env var.

Flexible Earn dedup model: Unlike Flash/Fixed which dedup by specific opportunity, Flexible uses a threshold-crossing model — key is just <ccy>. Notifies once when APY crosses above threshold; stays silent while it remains above; resets when APY drops below threshold (diff cleanup removes the key). This avoids frequent notifications from rate fluctuations.


Purchase Guide

When user wants to subscribe to a Fixed Earn product after receiving a notification.

Read {baseDir}/references/purchase-guide.md for the complete flow.

Summary:

  1. Parallel balance check (funding + trading + flexible earn)
  2. Compare fixed APR vs flexible lendingRate
  3. Calculate recommended amount: min(idle + movable_simple_earn, lendQuota)
  4. Present recommendation with comparison hint
  5. User confirms → re-check offer availability (soldOut guard) → hand off to okx-cex-earn

Edge cases covered in purchase-guide.md:

  • Balance < minLend → show deficit
  • Amount > lendQuota → auto-cap with notice
  • Redeem succeeded but subscribe failed → warn user, funds are in funding account
  • Offer sold out between notification and subscription → inform user

Important: earn-hunter does NOT execute write operations directly. It transfers control to okx-cex-earn.


Config Management

Read {baseDir}/references/config-reference.md for field definitions and natural language examples.

When user wants to change settings:

  1. Parse intent → map to config field (see config-reference.md for field mapping)
  2. Read the target JSON file (config.json or platform.json) → modify the field → write back
  3. Read the updated file → confirm the change to user

Exception: TG credentials cannot be changed via natural language. Tell user to set environment variables directly.


Pause/Resume

Branch on platform.json .scheduler.type:

OpenClaw (`openclaw-cron`) — manage via the in-session cron tool (no CLI):

  • Pause: call cron with action: "update", targeting the earn-hunter-hourly job, patch { "enabled": false } (or action: "remove" to delete it).
  • Resume: action: "update" with { "enabled": true } (or re-create as in Activation Step 5).
  • Use action: "list" to find the job id.

OS-crontab platforms (`cron`):

  • Pause: crontab -l | grep -v 'earn-hunter' | crontab -
  • Resume: Re-add the crontab entry (same as Activation Step 5).

macOS LaunchAgent (`launchagent`):

  • Pause: launchctl unload ~/Library/LaunchAgents/com.okx.earn-hunter.plist
  • Resume: launchctl load ~/Library/LaunchAgents/com.okx.earn-hunter.plist

Config and state are preserved — resuming picks up where it left off.


Uninstall

When user says "卸载" / "uninstall":

  1. Stop the scheduler (same as Pause). For LaunchAgent, also remove the plist:

launchctl unload ~/Library/LaunchAgents/com.okx.earn-hunter.plist && rm -f ~/Library/LaunchAgents/com.okx.earn-hunter.plist

  1. Ask: "是否保留配置和历史数据?"
  • Yes → only remove scheduler
  • No → also remove ~/.okx/earn-hunter/ directory

Test Mode

Trigger phrases: "测试 earn-hunter" / "earn-hunter smoke test" / "测试定时任务触发"

Behavior — run the script with the Test Mode hooks (no separate logic needed):

bash
# 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. Execute a full Scan Cycle — the script runs the same scan, but EH_TEST_NAMESPACE=1 isolates state writes
  2. Force-send notification — temporarily set config.verboseLog=true so the script always sends output regardless of whether opportunities are found (restore afterwards)
  3. Dedup writes to test namespace — EH_TEST_NAMESPACE=1 prefixes dedup keys with test: (e.g. test:flash:12345:100); these keys are immune to diff cleanup (only TTL removes them), so test runs do not pollute production state
  4. Output diagnostics after scan completes:
  • okx auth status (logged in / expired / not configured)
  • Scan command results (flash project count + fixed product count + flexible rate count)
  • Post-filter results (how many passed filters per type)
  • Notification channel status (which channel is configured, send result)
  • Scheduler status (OS-crontab platforms: crontab entry exists? / OpenClaw: cron tool action: "list" shows the earn-hunter-hourly job?)
  • Last 5 lines of ~/.okx/earn-hunter/notify.log
  1. Completion message: "测试完成。test: 前缀的 state 不影响正式去重,正式扫描不受影响。"

Error Handling

Read {baseDir}/templates/error-alert.md for exact alert message templates.

ErrorAction
okx 401 / "Session expired"Stop scan. Send alert (凭证失效 template). Load okx-cex-auth if interactive.
Network error / timeoutRetry once silently. If still fails, skip this cycle.
3 consecutive scan failuresSend alert (连续失败 template). Counter stored in state.consecutive_failures, reset after alert.
state.json corruptedReset by writing {"flash":{},"fixed":{},"consecutive_failures":0,"last_error":""}. May cause one round of duplicate notifications.
Notification send failsLog to notify.log, continue scan. Dedup key NOT added (next cycle retries).
config.json missing at runtimeSend alert: "earn-hunter 未配置,请运行首次激活流程。"
Dual-client suspected (user mentions both platforms)Warn: "建议仅在一个客户端运行 earn-hunter,避免重复通知。"
verboseLog = true + no hitsSend brief status (not silent).

i18n

  • All notifications rendered in user's language (detected at activation, stored in config.notify.language)
  • Locked terms (never translate): Flash Earn, Fixed Earn, Simple Earn, DCD, APY, APR, OKX, earn-hunter, Telegram, project names, currency codes (USDT, BTC, etc.)
  • Fallback: If LLM rendering fails, send Chinese template + append (translation unavailable, sent in zh-CN)

Global Notes

  • Security: Never accept credentials in chat. TG token only via env vars. Guide users to okx config init for OKX auth.
  • Output: Use --json for all okx commands. Render results as markdown tables.
  • Logging: All notification send results logged to ~/.okx/earn-hunter/notify.log.
  • Scope: Covers Flash Earn, Simple Earn Fixed, and Simple Earn Flexible (活期). DCD, on-chain, auto-earn are out of scope.
  • Mode: Live trading only. config.simulatedTrading is always false.

Defensive Design Principles

These constraints apply to all changes to earn-hunter:

  1. cron PATH ≠ user shell PATH. Any cron-triggered script must resolve tool paths independently — never assume cron inherits the user's login PATH. The script uses env.snapshot + resolve_bin fallback.
  2. Sanity check first. Before running any CLI command, verify the binary exists. Missing tool → immediate exit with clear error, not silent failure.
  3. Never swallow stderr. Use 2>&1 (capture into variable) instead of 2>/dev/null for CLI calls. Otherwise error messages vanish and last_error is empty.
  4. Smoke test must simulate production. Activation smoke test runs twice: once in user shell (verify credentials/network), once in minimal env -i PATH=/usr/bin:/bin (verify cron can run). Don't pass Step 5 if cron smoke fails.
  5. Alert templates must handle empty fields. If last_error is empty string, append "check ~/.okx/earn-hunter/cron.log; empty error usually means PATH issue" guidance.
  6. Tool paths use snapshot + fallback, not global PATH. env.snapshot written at activation with exact paths; resolve_bin tries snapshot → command -v → hardcoded common paths. Survives nvm switch / Homebrew upgrades.

Relacionados por tarefa

do mesmo repositório

Mais Skills

Todos os Skills
okx
Comunidade

okx-cex-bot

Manage Grid bots (spot/contract/coin-margined), DCA Martingale bots (Spot DCA 现货马丁 / Contract DCA 合约马丁), and Smart Portfolio bots (智能组合, auto-rebalancing multi-asset portfolio) on OKX. Covers create, stop, amend, monitor P&L, TP/SL, margin/investment adjustment, AI-recommended parameters, and recommended zone backtest. Requires API credentials. Not for regular orders (okx-cex-trade), market data (okx-cex-market), or account info (okx-cex-portfolio).

instalações
1
GitHub Stars
193
Atualizado
19 de out.
okx
Comunidade

okx-cex-earn

Manages OKX Simple Earn (flexible savings/lending), Flash Earn, On-chain Earn (staking/DeFi), Dual Investment (DCD/双币赢), and AutoEarn (自动赚币) via the okx CLI. Use this skill whenever the user wants to check earn balances, browse flash-earn projects, subscribe or redeem earn products, view or set lending rates, monitor on-chain staking orders, interact with dual investment structured products, or manage auto-earn — even if phrased casually as 活期赚币, 定期赚币, 闪赚, 赚币, 申购, 赎回, 链上赚币, 质押, 理财, 双币赢, 双币理财, 双币

instalações
1
GitHub Stars
193
Atualizado
19 de out.
okx
Comunidade

okx-cex-portfolio

This skill should be used when the user asks about 'account balance', 'how much USDT do I have', 'my funding account', 'show my positions', 'open positions', 'position P&L', 'unrealized PnL', 'closed positions', 'position history', 'realized PnL', 'account bills', 'transaction history', 'trading fees', 'fee tier', 'account config', 'max order size', 'how much can I buy', 'withdrawable amount', 'transfer funds', 'move USDT to trading account', or 'switch position mode'. Also use for '总资产', 'full

instalações
1
GitHub Stars
193
Atualizado
19 de out.
okx
Comunidade

okx-cex-skill-mp

Use this skill when the user asks to: 'find a trading skill', 'search for skills', 'install a skill', 'add a skill', 'download a skill', 'browse skill marketplace', 'what skills are available', 'update a skill', 'check for skill updates', 'remove a skill', 'uninstall a skill', 'list installed skills', 'show my skills', 'skill categories', 'verify skill signature', 'verify installed skill', 're-verify a skill', 'check skill integrity', or any request to discover, install, update, verify, or manag

instalações
1
GitHub Stars
193
Atualizado
19 de out.