症状
logs/*.logの API 失敗行(例igApiFail)が 1日数百件・全部 status 429、bodyHead は「ログイン済み HTML の殻」=Cookie は生きている- 既存の防御が プロセス内変数の streak かレーン別 state で、run のたびにゼロから撃ち直す
- 複数アカウントが同じ日に順次 429(同一IP でまとめて絞られている)
手順(実装30分・6ファイル程度)
src/<api>-ratelimit.jsを新規:{blockedUntil, openedAt, lastStatus, lastUrl, source, hits, lastNotifyAt}を tmp→rename で原子的に読み書き。export はisXCooldownActive(now)/openXCooldown({source,status,url}, now)(初回90分・窓内再検知で残り×1.5・上限6h)/describeXCooldown()(既存の『rate-limit cooldown中 -> ISO まで…』文言に揃える)/xRateLimitError()(e.code='<api>-rate-limit')。state パスは env で差し替え可にし、呼び出し時に env を読む(ESM の import hoist 対策)。- 低レベルの HTTP 経路すべて(page.evaluate 版と request.get 版の両方)で「先頭で active なら撃たない」「429 を見たら open」。
res.status()を必ず見る(本文が JSON でないだけの判定は 429 を取りこぼす)。 - 各エントリポイントは Chrome 起動前に gate → exit 0(launchd を赤にしない)。致命エラー catch で
e.codeを見て exit 0 に分岐。Discord はlastNotifyAtで24hに1回。 - テストの落とし穴: 429 をモックする既存テストが本番 state へ書き 90 分本番を止める。既存テストの先頭でも env を tmp へ向け、
node --test後にls state/<file>で「存在しない」ことを確認する。test/index.jsに import 登録しないとnode --test test/に拾われない。 - 検証は
launchctl kickstart -k→last exit code = 0と新規ログ行の grep(完了 or cooldown中)で実測。
関連
- (窓が長すぎて陳腐化する逆パターン。上限6h と期限切れ後の再オープンで両立)