| import { feedbackKey, utcSavedAt } from './extension_feedback.js'; |
|
|
| const ERROR_DETAIL_MAX = 500; |
| export const CUSTOM_DOMAIN = 'api.info-lens.app'; |
| export const AUTO_ERROR_TTL_SEC = 7 * 24 * 3600; |
| |
| export const UNPARSEABLE_RETRIES = 3; |
|
|
| export function isCustomDomain(request) { |
| if (!request) return false; |
| try { |
| const hostHeader = request.headers?.get?.('host') || ''; |
| const hostName = hostHeader.split(':')[0].trim().toLowerCase(); |
| if (hostName === CUSTOM_DOMAIN) return true; |
| const url = new URL(request.url); |
| return url.hostname.toLowerCase() === CUSTOM_DOMAIN; |
| } catch { |
| return false; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function publicRemoteError(err) { |
| const raw = err && err.message != null ? String(err.message) : String(err); |
| const error_detail = |
| raw.length <= ERROR_DETAIL_MAX ? raw : raw.slice(0, ERROR_DETAIL_MAX - 1) + '…'; |
|
|
| if ( |
| /^network:/i.test(raw) || |
| (typeof TypeError !== 'undefined' && err instanceof TypeError) || |
| /network|fetch failed|timed out|timeout|ECONNRESET|connection/i.test(raw) |
| ) { |
| return { kind: 'network', message: 'Network error', error_detail }; |
| } |
|
|
| const http = |
| raw.match(/OpenRouter HTTP (\d+)/) || |
| raw.match(/OpenRouter non-JSON response: HTTP (\d+)/); |
| if (http) { |
| const code = parseInt(http[1], 10); |
| if (code === 408 || code === 429 || code >= 500) { |
| return { |
| kind: 'inference', |
| message: `Inference API temporarily unavailable (${code})`, |
| error_detail, |
| }; |
| } |
| |
| return { |
| kind: 'internal', |
| message: `Unexpected analysis error (${code})`, |
| error_detail, |
| }; |
| } |
|
|
| |
| const streamErr = |
| raw.match(/OpenRouter stream error:/); |
| if (streamErr) { |
| const se = raw.match(/code=(\d+)/); |
| if (se) { |
| const code = parseInt(se[1], 10); |
| if (code === 408 || code === 429 || code >= 500) { |
| return { |
| kind: 'inference', |
| message: `Inference API temporarily unavailable (${code})`, |
| error_detail, |
| }; |
| } |
| } |
| |
| return { |
| kind: 'inference', |
| message: 'Inference API temporarily unavailable', |
| error_detail, |
| }; |
| } |
|
|
| |
| if (/^OpenRouter error:/i.test(raw)) { |
| return { |
| kind: 'inference', |
| message: 'Inference API temporarily unavailable', |
| error_detail, |
| }; |
| } |
|
|
| |
| |
| if ( |
| /^unparseable /i.test(raw) || |
| /^missing tool_calls/i.test(raw) |
| ) { |
| return { |
| kind: 'internal', |
| message: 'Unparsable model output', |
| error_detail, |
| }; |
| } |
|
|
| return { |
| kind: 'internal', |
| message: 'Unexpected analysis error', |
| error_detail, |
| }; |
| } |
|
|
| |
| |
| |
| export async function logRemoteFailure(event, err, pub, env, request, extra) { |
| const raw = err && err.message != null ? String(err.message) : String(err); |
| const extraFields = extra && typeof extra === 'object' ? extra : {}; |
| console.error({ |
| event, |
| kind: pub.kind, |
| message: pub.message, |
| |
| error_detail: raw.length <= 4000 ? raw : raw.slice(0, 3999) + '…', |
| ...extraFields, |
| }); |
|
|
| if (env && env.STATE && isCustomDomain(request)) { |
| try { |
| const id8 = ( |
| typeof globalThis.crypto?.randomUUID === 'function' |
| ? globalThis.crypto.randomUUID() |
| : Math.random().toString(36).slice(2, 10) |
| ) |
| .replace(/-/g, '') |
| .slice(0, 8); |
| const key = feedbackKey(id8); |
| const record = { |
| saved_at: utcSavedAt(), |
| source: 'facade_auto', |
| event, |
| kind: pub.kind, |
| message: pub.message, |
| error_detail: pub.error_detail, |
| domain: CUSTOM_DOMAIN, |
| ...extraFields, |
| }; |
| await env.STATE.put(key, JSON.stringify(record), { |
| expirationTtl: AUTO_ERROR_TTL_SEC, |
| }); |
| } catch (e) { |
| const msg = e && e.message ? String(e.message) : String(e); |
| console.error('[facade_auto] failed to record error in KV:', msg); |
| } |
| } |
| } |
|
|