import { setTimeout as delay } from 'node:timers/promises';
import { readBody } from './http-client.mjs';
// http-client validates API_ROOT_URL and API_KEY on import. The root excludes /v1.
const url = `${process.env.API_ROOT_URL.replace(/\/+$/, '')}/v1/models`;
const started = performance.now();
const deadline = started + 10_000;
const signal = AbortSignal.timeout(10_000);
try {
for (let attempt = 1; attempt <= 3; attempt++) {
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.API_KEY}` },
redirect: 'error', signal,
});
const id = response.headers.get('x-gateway-request-id')
|| response.headers.get('x-oneapi-request-id');
console.error(JSON.stringify({
event: 'models_response', attempt, status: response.status,
elapsed_ms: Math.round(performance.now() - started),
request_id: id && /^[A-Za-z0-9._:-]{1,128}$/.test(id) ? id : null,
}));
if (!response.ok) {
await response.body?.cancel();
if (attempt === 3 || ![429, 502, 503, 504].includes(response.status)) {
throw new Error('Non-retryable status or attempt limit.');
}
const header = response.headers.get('retry-after')?.trim();
let retryAfter = 0;
if (header) {
const parsed = /^\d+$/.test(header) ? Number(header) * 1000 : Date.parse(header) - Date.now();
if (!Number.isNaN(parsed)) retryAfter = Math.max(0, parsed);
}
const backoff = Math.random() * 200 * 2 ** (attempt - 1);
const wait = Math.max(retryAfter, backoff);
// Never shorten a server-requested wait to fit the total deadline.
if (wait >= deadline - performance.now()) throw new Error('Retry exceeds deadline.');
await delay(wait, undefined, { signal });
continue;
}
if (!/^application\/json(?:\s*;|$)/i.test(response.headers.get('content-type') || '')) {
await response.body?.cancel();
throw new Error('Expected JSON.');
}
const result = JSON.parse((await readBody(response, 1024 * 1024)).toString('utf8'));
if (!result || result.error || result.success === false || result.object !== 'list'
|| !Array.isArray(result.data)
|| result.data.some((item) => !item || typeof item.id !== 'string' || !item.id.trim())) {
throw new Error('Invalid model list.');
}
console.log(JSON.stringify({ models_count: result.data.length }));
break;
}
} catch {
console.error('Model discovery failed. Check the status and request ID above. No model generation was attempted.');
process.exitCode = 1;
}