earn-hunter について
earn-hunterは、OKX Flash Earn、Fixed Earn、Flexible Earnの機会を監視するエージェントスキルです。通貨やAPYの条件で絞り込み、定期実行または手動スキャンを通知付きで行えます。
主な機能
- 013種類の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 Flash Earn、Fixed Earn、Flexible Earn(Simple Earn)の機会を自動で監視します。
{baseDir} = この SKILL.md ファイルが格納されているディレクトリです。すべての相対パス(references/、templates/、config/)はここを基準に解決されます。
事前確認
-
okxCLI がインストールされていることを確認します:which okx。見つからない場合は、npm install -g @okx_ai/okx-trade-cliでインストールします。 OpenClaw では、セッション内で使用できる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}のインストールに失敗しました。スキャンと通知機能には影響しません。申込ガイドと認証の復旧にはこのスキルが必要です。後で手動でインストールできます。」 - スキルのインストール結果にかかわらず、事前確認を続行します
-
認証モードの検出 — 両方を実行し、最初に一致したものを採用します:
okx config show --json→ 空でないapi_keyフィールドがある → API Key モード。すべてのコマンドに--profile liveを追加します。- API キーがなく、
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 # 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環境でもツールのパスを解決します。
エージェントによる設定/状態/プラットフォームの JSON の読み書きは、アクティベーション/設定管理にのみ使用します。定期的なスキャン自体はすべて
scripts/scan.sh(シェル+jq)で実行されます。スキャンにはjqが必要です。which jqで確認し、見つからない場合はインストールします(brew install jq/apt-get install jq)。
プラットフォームとチャネルの検出
独立した3つの要素があります:プラットフォーム(エージェントの実行場所)、スケジューラー(スキャンを開始するもの)、通知チャネル(アラートの送信先)。
プラットフォームの検出(アクティブなプローブ+ユーザーによる確認)
初回実行時(platform.json が存在しない場合):
- 環境の手がかりを調べます:
OPENCLAW_HOME環境変数が存在するか? → ヒント:OpenClaw- エージェントのツール一覧に
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に書き込まれます。以降の実行では検出をスキップします。
2 回目以降の実行時(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エージェントツールを使ってスケジュールします(OS の crontab も CLI コマンドも使用しません)。ジョブは独立した軽量コンテキストのエージェントターンとして実行され、cron のannounce配信を介して出力が会話チャンネルに返されます。notify.channel のデフォルトは"session"です。これにより、スキャン結果は stdout に出力され、announceが配信します(二重送信を防ぎます)。
Claude Code / Hermes / Generic(claude-code.default.json):
- scheduler.type =
"cron"— OS の crontab →scripts/scan.shを使ってスケジュールします(LLM トークンのコストはかかりません)。通知はスクリプト自体から TG / Lark の curl を介して送信されます。
通知チャンネル(プラットフォームとは独立)
優先順位に従って検出します(PRD の要件:TG を優先):
- Telegram —
$TELEGRAM_BOT_TOKENと$TELEGRAM_CHAT_IDの両方が設定されている場合 → TG を利用可能 - Lark —
platform.notify.lark_webhookが空でない場合 → Lark を利用可能 - Session — フォールバック。インタラクティブモードでのみ動作します
TG と Lark は独立したプッシュチャンネルであり、エージェントのクライアントが開いているかどうかに関係なく動作します。OS の crontab を使うプラットフォームでは、スケジュールされたスキャンが直接 curl を介して通知を送信します。OpenClaw では、スケジュールされたスキャンは独立した cron エージェントターンで実行され、cron の announce を介して会話チャンネルに配信されます(チャンネル = "session")。ユーザーが明示的にチャンネルを切り替えない限り、TG/Lark の curl は使用されません。
スキルのルーティング
| ユーザーの意図 | ルート |
|---|---|
| 「有闪赚通知我」/「monitor earn」/「帮我监控赚币」/「活期年化高了通知我」 | → 有効化フロー |
| 「改 APY 阈值」/「只看 USDT」/「change config」/「活期加上 BTC」 | → 設定管理 |
| 「申购 USDT 定期 7D」/「subscribe」/「我要买」 | → 購入ガイド |
| 「执行 earn-hunter 扫描」(cron または対話形式) | → スキャンサイクル — 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 は利用可能
- トークンは設定済みだが 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 が設定済みで有効(
通知チャネルは必ずユーザーに確認し、セッションを黙ってデフォルトにしてはいけません。 監視ツールにとって通知は重要です。セッションをデフォルトにすると、ユーザーが会話に参加していない間はアラートが届きません。
外部チャネルが 1 つ以上検出された場合:
「以下の通知チャネルが利用可能です:
- {検出されたチャネルの一覧。例: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 に書き込みます。
Step 3 — スキャン設定の確認(デフォルト値を含む3ステップ)
デフォルト設定を提示し、確認するかカスタマイズするかをユーザーに尋ねます。各ステップにはデフォルト値があり、ユーザーは Enter キーを押すとその値を適用できます。
Step 1/3 — スキャン範囲: 「スキャン範囲(複数選択可): [1] Flash Earn(闪赚) [2] Fixed Earn(定期赚币) [3] Flexible Earn(活期赚币) デフォルト:すべて選択」
- デフォルト:3つすべてを有効化
- ユーザーが特定の項目を選んだ場合 → それ以外を無効化
- ユーザーが [1] のみ選んだ場合 →
config.fixed.enabled = false、config.flexible.enabled = falseに設定し、Step 2/3 と 3/3 をスキップ - ユーザーが [3] のみ選んだ場合 →
config.flash.enabled = false、config.fixed.enabled = falseに設定し、Flexible 専用設定に進む(Step 2/3 で Flexible の通貨を尋ね、Step 3/3 で Flexible の APY しきい値を尋ねる)
Step 2/3 — 監視する通貨: Fixed Earn の場合:「定期監視通貨:すべて(デフォルト、Enter キーを押す)または指定通貨を入力(例:USDT, SOL)」
- デフォルト:
"all"(すべての通貨) - ユーザーが指定した場合 →
config.currenciesを配列に設定(例:["USDT", "SOL"])
Flexible Earn の場合:「活期監視通貨:USDT, USDC(デフォルト、Enter キーを押す)または指定通貨を入力」
- デフォルト:
["USDT", "USDC"] - ユーザーが指定した場合 →
config.flexible.currenciesを配列に設定 - 注:Flexible は通貨ごとに API を呼び出す必要があるため、リストを少数にすることをおすすめします
Step 3/3 — APY しきい値: Fixed Earn の場合:「定期の最低 APY しきい値:制限なし(デフォルト、Enter キーを押す)またはパーセンテージを入力(例:8)」
- デフォルト:
0(制限なし) - ユーザーが指定した場合 →
config.fixed.globalMinApyをvalue / 100に設定(例:8 →0.08)
Flexible Earn の場合:「活期の最低 APY しきい値:8%(デフォルト、Enter キーを押す)またはパーセンテージを入力」
- デフォルト:
0.08(8%) - ユーザーが指定した場合 →
config.flexible.globalMinApyをvalue / 100に設定
会話から言語を自動検出し、config.json の notify.language に書き込みます。
設定を ~/.okx/earn-hunter/config.json に書き込みます。
{baseDir}/templates/activation.md テンプレートを使い、サマリーを(ユーザーの言語で)表示します。
Step 4 — スモークテストと配信確認
スモークテストでは、verboseLog の設定にかかわらず、必ず通知を送信します。
verboseLogを一時的に有効化してスキャンを1サイクル実行し、機会がない場合でも必ず出力されるようにします。verboseLogを一時的に切り替えてスクリプトを実行した後、元に戻します。また、スモークテストの重複排除キーがtest:プレフィックス配下に保存され、本番環境の状態に影響しないよう、EH_TEST_NAMESPACE=1を使用します: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} にテストメッセージを送信しました。受信できたか確認してください」
- ユーザーが確認した場合 → Step 5 に進む
- 受信できていない場合 → トラブルシューティングを行う(
notify-channels.mdを参照) - 5 分間応答がない場合 → 1 回だけ再度確認する
- セッションチャネルの場合 → 確認をスキップ
注: スモークテストは verboseLog の設定を無視し、パイプライン全体がエンドツーエンドで機能することを確認するため、必ず出力を生成します。
Step 4b — Cron 環境のスモークテスト(OS の crontab を使用するプラットフォームのみ)
重要: ユーザーの対話型シェルには完全な PATH がありますが、cron にはありません。cron の最小環境をシミュレートする2つ目のスモークテストを実行し、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 は動作します。Step 5 に進みます。
- 終了コードが 127 または「FATAL: 'okx' not found」の場合 →
env.snapshotが不完全か、存在しません。Preflight ステップを再実行して再生成します。Step 5 に進まないでください — cron ジョブはエラーが表面化しないまま失敗します。 - ユーザーに次のように表示します:「cron の最小環境で正常に実行できることを確認しました。このステップが失敗した場合、env.snapshot 内のツールのパスに誤りがあります。」
Step 5 — スケジューラーの設定
スケジュールの仕組みは platform.json の .scheduler.type によって異なります。プラットフォームに応じて分岐します。
OpenClaw(scheduler.type = "openclaw-cron")
OpenClaw では、スケジュール設定はセッション内の cron エージェントツールを呼び出して会話内で行います。OS コマンドや openclaw cron CLI は決して使用しないでください(このコンテキストでは CLI 経由だと権限の問題が発生します)。このチャット内で設定するようユーザーに促してください。作成した cron ジョブは現在のセッションのチャンネルを引き継ぐため、スキャン結果はこの会話に直接送信されます。
action: "add" を指定して cron ツールを呼び出し、次の形式の 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 エージェントが okx MCP サーバーを読み込む設定になっていなければ、通常のツール(exec/read/write)のみが使用され、低コストで実行できます。読み込まれるツールは、このジョブではなくエージェントの設定によって決まります。
delivery:{ "mode": "announce" }— ジョブを作成した会話チャンネルに、ターンの出力を送信します。
ジョブが実行されると、分離されたエージェントがプロンプト "执行 earn-hunter 扫描" を実行します → スキャンサイクル(チャンネル session/stdout を指定して scripts/scan.sh を実行)→ 結果を中継 → announce によってここに配信されます。したがって、新たな機会があれば自動的に送信されます。
会話内では、シェルコマンドや 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 を試し、cron デーモンが実行されていない場合は、macOS では 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
platform.json に scheduler.type = "launchagent" を書き込み、ユーザーに次のように伝えます。
「macOS の cron サービスが実行されていないため、LaunchAgent スケジューラーに自動で切り替えました(sudo 不要、再起動後も自動的に復旧します)。」
注記:
- OAuth モード → plist の
EnvironmentVariablesからOKX_PROFILEを省略するか、空の値を設定します。 - LaunchAgent plist のパスは絶対パスで指定する必要があります(
~は使用不可)。有効化フローでは、生成時に$HOMEを展開します。 RunAtLoad: trueにすると、読み込み後すぐに最初のスキャンが実行されます。- スクリプトは
config.json/platform.jsonを読み込み、state.json/notify.logに書き込み、TG Bot API または Lark Webhook への curl で通知を送信します。エージェントが実行時に関与する必要はありません。 - 新しい機会がなく、
verboseLog=falseの場合、スクリプトは 0 を返して出力を一切行いません。これは意図されたサイレント動作です。
重要:OS スケジューラープラットフォームでは、エージェントプラットフォームの /loop / Routines を使用しないでください(Claude Code の /loop、クラウド Routines)。これらは実行のたびに LLM セッションを起動し、外部通知を確実に送信できません。(例外は前述の OpenClaw です。設計上、セッション内の cron ツールと announce 配信を使用します。)
スキャンサイクル
スキャンサイクル全体は scripts/scan.sh(純粋なシェル + jq、LLM コストなし)で実装されています。 OS crontab、OpenClaw の分離 cron エージェントターン、または対話セッションでのユーザー(「earn-hunter スキャンを実行」)のいずれが起動しても、サイクルは同じです。スクリプトを実行して、その出力をそのまま伝えます。スキャン手順を自然言語で再実装しないでください。スクリプトが唯一の信頼できる情報源です。
OpenClaw の分離 cron ターン: ジョブのプロンプトはここに処理を委ねます。scripts/scan.sh を実行し(platform.json の notify.channel = "session" の場合、スクリプトは通知を標準出力に出力します)、その標準出力をターンの応答としてそのまま伝えます。cron ジョブの announce 配信によって、会話チャネルに通知が送られます。このターンから TG/Lark に curl しないでください。配信は announce が処理します。
エージェントによるスキャンの実行方法(対話型トリガー)
# Profile: pass OKX_PROFILE only in API Key mode (omit for OAuth mode).
OKX_PROFILE=live ~/.okx/earn-hunter/scan.sh
次に、スクリプトの標準出力をそのままユーザーに伝えます。
- スクリプトが通知(Flash / Fixed / 混合 / 詳細ステータス)を出力した場合は、それを表示します。
- スクリプトが何も出力しない場合(新しい機会がなく、
verboseLog=falseのため、何も出力せずに 0 で終了した場合)は、ユーザーに「今回のスキャンが完了しました。新しい機会はありません」と伝えます。チャネルに「スキャン完了」メッセージを捏造して投稿しないでください。何も出力しないのは意図された動作です。
スクリプトの動作(実装仕様については 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:
- フィルタリングします(2 層の 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 → session)。
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)→ session(stdout)です。platform.json の notify.channel を telegram/lark/session に設定すると、そのチャネルが強制的に使用されます。
認証/プロファイル: スクリプトが認証情報を読み取ったり表示したりすることはありません。認証処理はすべて okx CLI に委任されます(~/.okx/config.toml を読み込みます)。プロファイルは OKX_PROFILE 環境変数を通じてのみ設定されます。
Flexible Earn の重複排除モデル: 特定の機会ごとに重複排除する Flash/Fixed とは異なり、Flexible はしきい値超過モデルを使用します。キーは <ccy> のみです。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")。 - 再開:
{ "enabled": true }を指定してaction: "update"を実行します(または有効化手順のステップ 5 と同様に再作成します)。 - ジョブ ID を確認するには、
action: "list"を使用します。
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 の認証状態(ログイン済み/期限切れ/未設定)
- スキャンコマンドの結果(フラッシュプロジェクト数+固定商品の数+フレキシブル金利の数)
- フィルター適用後の結果(タイプごとのフィルター通過数)
- 通知チャネルの状態(設定されているチャネル、送信結果)
- スケジューラーの状態(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 を読み込みます。 |
| ネットワークエラー/タイムアウト | サイレントで 1 回再試行します。それでも失敗した場合は、このサイクルをスキップします。 |
| スキャンが 3 回連続で失敗 | アラートを送信します(连续失败テンプレート)。カウンターは state.consecutive_failures に保存され、アラート送信後にリセットされます。 |
state.json が破損 | {"flash":{},"fixed":{},"consecutive_failures":0,"last_error":""} を書き込んでリセットします。重複通知が 1 回発生する場合があります。 |
| 通知の送信に失敗 | notify.log に記録し、スキャンを続行します。重複排除キーは追加されないため、次のサイクルで再試行されます。 |
実行時に config.json が存在しない | 次のアラートを送信します:「earn-hunter が設定されていません。初回有効化の手順を実行してください。」 |
| 2 つのクライアントの利用が疑われる(ユーザーが両方のプラットフォームに言及) | 次の警告を表示します:「重複通知を避けるため、earn-hunter は 1 つのクライアントでのみ実行することをおすすめします。」 |
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 トークンは環境変数経由でのみ指定してください。OKX の認証には、ユーザーに
okx config initを案内してください。 - 出力: すべての okx コマンドで
--jsonを使用してください。結果は Markdown テーブルで表示してください。 - ログ記録: すべての通知送信結果は
~/.okx/earn-hunter/notify.logに記録されます。 - 対象範囲: Flash Earn、Simple Earn Fixed、Simple Earn Flexible(活期)が対象です。DCD、オンチェーン、オートアーンは対象外です。
- モード: ライブ取引のみです。
config.simulatedTradingは常にfalseです。
防御的な設計原則
以下の制約は、earn-hunter に対するすべての変更に適用されます。
- cron の PATH とユーザーのシェルの PATH は異なります。 cron から実行されるスクリプトは、ツールのパスを独自に解決する必要があります。cron がユーザーのログイン PATH を引き継ぐと決めつけないでください。スクリプトでは
env.snapshotとresolve_binのフォールバックを使用します。 - 最初に整合性を確認します。 CLI コマンドを実行する前に、バイナリの存在を確認してください。ツールが見つからない場合は、黙って失敗させず、明確なエラーを表示して直ちに終了してください。
- 標準エラー出力を決して破棄しないでください。 CLI 呼び出しでは
2>/dev/nullではなく2>&1を使用して、変数に取り込んでください。そうしないとエラーメッセージが消え、last_errorが空になります。 - スモークテストでは本番環境を再現する必要があります。 有効化時のスモークテストは 2 回実行します。1 回目はユーザーのシェルで実行して認証情報とネットワークを確認し、2 回目は最小構成の
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 のアップグレード後も動作します。