{"version":3,"file":"error-wiGHep5P.mjs","names":[],"sources":["../src/lib/error.ts"],"sourcesContent":["import type { Context } from \"hono\"\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\"\n\nimport consola from \"consola\"\n\nexport class HTTPError extends Error {\n  response: Response\n\n  constructor(message: string, response: Response) {\n    super(message)\n    this.response = response\n  }\n}\n\n/**\n * Anthropic API error envelope shape (per docs.anthropic.com/api/errors):\n *   { \"type\": \"error\", \"error\": { \"type\": \"<category>\", \"message\": \"...\" } }\n *\n * Error categories Claude Code recognizes (services/api/errors.ts):\n *   invalid_request_error  — 400 (incl. \"prompt is too long\" → reactive compact)\n *   authentication_error   — 401\n *   permission_error       — 403\n *   not_found_error        — 404\n *   request_too_large      — 413\n *   rate_limit_error       — 429\n *   api_error              — 500 / generic upstream\n *   overloaded_error       — 529\n */\n\ninterface AnthropicErrorBody {\n  type: \"error\"\n  error: { type: string; message: string }\n}\n\nfunction statusToErrorType(status: number): string {\n  switch (status) {\n    case 400: {\n      return \"invalid_request_error\"\n    }\n    case 401: {\n      return \"authentication_error\"\n    }\n    case 403: {\n      return \"permission_error\"\n    }\n    case 404: {\n      return \"not_found_error\"\n    }\n    case 413: {\n      return \"request_too_large\"\n    }\n    case 429: {\n      return \"rate_limit_error\"\n    }\n    case 529: {\n      return \"overloaded_error\"\n    }\n    default: {\n      return status >= 500 ? \"api_error\" : \"invalid_request_error\"\n    }\n  }\n}\n\n/** Detect bodies already shaped as Anthropic errors (don't double-wrap). */\nfunction isAnthropicErrorShape(value: unknown): value is AnthropicErrorBody {\n  if (typeof value !== \"object\" || value === null) return false\n  const obj = value as Record<string, unknown>\n  if (obj.type !== \"error\") return false\n  const err = obj.error\n  if (typeof err !== \"object\" || err === null) return false\n  const e = err as Record<string, unknown>\n  return typeof e.type === \"string\" && typeof e.message === \"string\"\n}\n\n/** Strip internal implementation details from leaked messages. */\nfunction sanitizeMessage(msg: string): string {\n  // Common internal-only strings we don't want to leak to clients.\n  if (/copilot token not found/i.test(msg)) {\n    return \"Upstream authentication unavailable\"\n  }\n  if (/copilot/i.test(msg) && /token/i.test(msg)) {\n    return \"Upstream authentication error\"\n  }\n  // Trim verbose stack-traces; keep only first line.\n  const firstLine = msg.split(\"\\n\", 1)[0]\n  return firstLine.length > 500 ? firstLine.slice(0, 500) + \"…\" : firstLine\n}\n\nexport async function forwardError(c: Context, error: unknown) {\n  consola.error(\"Error occurred:\", error)\n\n  if (error instanceof HTTPError) {\n    const errorText = await error.response.text()\n    const status = error.response.status as ContentfulStatusCode\n\n    let parsed: unknown\n    try {\n      parsed = JSON.parse(errorText)\n    } catch {\n      parsed = errorText\n    }\n    consola.error(\"HTTP error body:\", parsed)\n\n    // If upstream already shaped it as an Anthropic error, pass through verbatim.\n    if (isAnthropicErrorShape(parsed)) {\n      return c.json(parsed, status)\n    }\n\n    // Try to extract a useful message from upstream JSON ({error: {message}}, etc.).\n    let message: string\n    if (typeof parsed === \"string\") {\n      message = parsed\n    } else if (parsed && typeof parsed === \"object\") {\n      const obj = parsed as Record<string, unknown>\n      const err = obj.error as Record<string, unknown> | string | undefined\n      if (typeof err === \"string\") {\n        message = err\n      } else if (err && typeof err.message === \"string\") {\n        message = err.message\n      } else if (typeof obj.message === \"string\") {\n        message = obj.message\n      } else {\n        message = errorText.slice(0, 500)\n      }\n    } else {\n      message = errorText.slice(0, 500)\n    }\n\n    const body: AnthropicErrorBody = {\n      type: \"error\",\n      error: {\n        type: statusToErrorType(status),\n        message: sanitizeMessage(message),\n      },\n    }\n    return c.json(body, status)\n  }\n\n  const message =\n    error instanceof Error ? error.message : \"An unknown error occurred\"\n  const body: AnthropicErrorBody = {\n    type: \"error\",\n    error: {\n      type: \"api_error\",\n      message: sanitizeMessage(message),\n    },\n  }\n  return c.json(body, 500)\n}\n"],"mappings":"uBAKA,IAAa,EAAb,cAA+B,KAAM,CACnC,SAEA,YAAY,EAAiB,EAAoB,CAC/C,MAAM,EAAQ,CACd,KAAK,SAAW,IAwBpB,SAAS,EAAkB,EAAwB,CACjD,OAAQ,EAAR,CACE,IAAK,KACH,MAAO,wBAET,IAAK,KACH,MAAO,uBAET,IAAK,KACH,MAAO,mBAET,IAAK,KACH,MAAO,kBAET,IAAK,KACH,MAAO,oBAET,IAAK,KACH,MAAO,mBAET,IAAK,KACH,MAAO,mBAET,QACE,OAAO,GAAU,IAAM,YAAc,yBAM3C,SAAS,EAAsB,EAA6C,CAC1E,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAM,EACZ,GAAI,EAAI,OAAS,QAAS,MAAO,GACjC,IAAM,EAAM,EAAI,MAChB,GAAI,OAAO,GAAQ,WAAY,EAAc,MAAO,GACpD,IAAM,EAAI,EACV,OAAO,OAAO,EAAE,MAAS,UAAY,OAAO,EAAE,SAAY,SAI5D,SAAS,EAAgB,EAAqB,CAE5C,GAAI,2BAA2B,KAAK,EAAI,CACtC,MAAO,sCAET,GAAI,WAAW,KAAK,EAAI,EAAI,SAAS,KAAK,EAAI,CAC5C,MAAO,gCAGT,IAAM,EAAY,EAAI,MAAM;EAAM,EAAE,CAAC,GACrC,OAAO,EAAU,OAAS,IAAM,EAAU,MAAM,EAAG,IAAI,CAAG,IAAM,EAGlE,eAAsB,EAAa,EAAY,EAAgB,CAG7D,GAFA,EAAQ,MAAM,kBAAmB,EAAM,CAEnC,aAAiB,EAAW,CAC9B,IAAM,EAAY,MAAM,EAAM,SAAS,MAAM,CACvC,EAAS,EAAM,SAAS,OAE1B,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,EAAU,MACxB,CACN,EAAS,EAKX,GAHA,EAAQ,MAAM,mBAAoB,EAAO,CAGrC,EAAsB,EAAO,CAC/B,OAAO,EAAE,KAAK,EAAQ,EAAO,CAI/B,IAAI,EACJ,GAAI,OAAO,GAAW,SACpB,EAAU,UACD,GAAU,OAAO,GAAW,SAAU,CAC/C,IAAM,EAAM,EACN,EAAM,EAAI,MAChB,AAOE,EAPE,OAAO,GAAQ,SACP,EACD,GAAO,OAAO,EAAI,SAAY,SAC7B,EAAI,QACL,OAAO,EAAI,SAAY,SACtB,EAAI,QAEJ,EAAU,MAAM,EAAG,IAAI,MAGnC,EAAU,EAAU,MAAM,EAAG,IAAI,CAGnC,IAAM,EAA2B,CAC/B,KAAM,QACN,MAAO,CACL,KAAM,EAAkB,EAAO,CAC/B,QAAS,EAAgB,EAAQ,CAClC,CACF,CACD,OAAO,EAAE,KAAK,EAAM,EAAO,CAK7B,IAAM,EAA2B,CAC/B,KAAM,QACN,MAAO,CACL,KAAM,YACN,QAAS,EALX,aAAiB,MAAQ,EAAM,QAAU,4BAKN,CAClC,CACF,CACD,OAAO,EAAE,KAAK,EAAM,IAAI"}