Handle errors and rate limits

Parse the response body defensively, branch on status, and distinguish transient rate/provider failures from exhausted monthly limits.

async function ivorleafRequest(url: string, init: RequestInit) {
  const response = await fetch(url, init);
  const text = await response.text();
  let body: unknown;
  try { body = text ? JSON.parse(text) : null; } catch { body = text; }

  if (response.status === 429) {
    // Read /v1/api/usage/status before deciding whether a retry is appropriate.
  }
  if (!response.ok) throw new Error(`Ivorleaf HTTP ${response.status}: ${JSON.stringify(body)}`);
  return body;
}

See Errors and Usage and Limits for verified current messages.