/** * flow-captcha.ts — shared captcha-retry resolution for the in-repo Google Flow * (useapi.net) submit paths: POST /videos (R2V + omni-flash V2V), POST /images, * POST /voices, and POST /videos/upscale. * * The useapi Flow API auto-solves the Google reCAPTCHA when a submit body carries * `captchaRetry` (1-10): it "cycles through configured providers in priority * order" and draws on the free CapSolver credits granted with the first account * (300 since 2026-06-29, was 100) plus any provider keys registered via * POST /accounts/captcha-providers. * Passing it is the SUPPORTED way to ride through the intermittent * `403 PUBLIC_ERROR_UNUSUAL_ACTIVITY` / reCAPTCHA blocks — without it the submit * just fails and the caller is left to cool down and resubmit. * * The count comes from VCLAW_FLOW_CAPTCHA_RETRY (default 5), clamped to [1,10]; * an explicit `0`/negative opts out (no field emitted), and garbage falls back to * the default. `captchaToken`/`captchaOrder` are intentionally NOT set — the spec * marks the three mutually exclusive, and `captchaRetry` is the auto-solve knob. */ /** * Default retries. useapi raised the SERVER-side default (used when a body sends * no captcha field at all) from 3 to 5 on 2026-06-29. Ours was 3, which meant we * were explicitly asking for FEWER attempts than sending nothing would have got — * so this tracks the server default rather than sitting below it. */ export const FLOW_CAPTCHA_RETRY_DEFAULT = 5; export const FLOW_CAPTCHA_RETRY_MAX = 10; /** * Resolve the captcha-retry count for a Flow submit body from the environment. * Returns 0 (omit the field — explicit opt-out) when VCLAW_FLOW_CAPTCHA_RETRY is * `0` or negative; the default when unset or unparseable; otherwise the clamped * value. */ export function resolveFlowCaptchaRetry(env: NodeJS.ProcessEnv = process.env): number { const raw = (env.VCLAW_FLOW_CAPTCHA_RETRY ?? '').trim(); if (raw === '') return FLOW_CAPTCHA_RETRY_DEFAULT; const n = Number.parseInt(raw, 10); if (!Number.isFinite(n)) return FLOW_CAPTCHA_RETRY_DEFAULT; if (n <= 0) return 0; return Math.min(n, FLOW_CAPTCHA_RETRY_MAX); } /** * Set `captchaRetry` on a Flow submit body when `retry > 0` (mutates and returns * the body for chaining). A retry of 0 leaves the body untouched (opt-out). */ export function applyFlowCaptcha>(body: T, retry: number): T { if (retry > 0) (body as Record).captchaRetry = retry; return body; }