UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

122 lines • 4.48 kB
import { createHash, randomBytes } from 'node:crypto'; /** A random URL-safe ID. 16 bytes = 128 random bits, the floor the spec sets for `jti` (4.2). */ export function randomId(prefix = '', bytes = 16) { return prefix + randomBytes(bytes).toString('base64url'); } export function sha256b64url(input) { return createHash('sha256').update(input).digest('base64url'); } export function nowSeconds() { return Math.floor(Date.now() / 1000); } export function sleep(ms, signal) { return new Promise((resolve, reject) => { if (signal?.aborted) return reject(signal.reason ?? new Error('aborted')); const t = setTimeout(() => { signal?.removeEventListener('abort', onAbort); resolve(); }, ms); const onAbort = () => { clearTimeout(t); reject(signal?.reason ?? new Error('aborted')); }; signal?.addEventListener('abort', onAbort, { once: true }); }); } /** Hosts we treat as "local development" for messages and defaults. */ export function isLoopbackHost(hostname) { const h = hostname.replace(/^\[|\]$/g, '').toLowerCase(); return (h === 'localhost' || h.endsWith('.localhost') || h === '::1' || /^127(\.\d{1,3}){3}$/.test(h)); } /** * Parses `value` as an absolute URL and enforces the spec's HTTPS requirement. With * `insecureDev`, plain `http:` is tolerated too. */ export function requireSecureUrl(value, what, sec) { if (typeof value !== 'string' || value.length === 0) { throw new TypeError(`${what} must be a URL string`); } let url; try { url = new URL(value); } catch { throw new TypeError(`${what} is not a valid URL: ${value}`); } if (url.protocol === 'https:') return url; if (url.protocol === 'http:' && sec.insecureDev) return url; throw new TypeError(`${what} must be an https:// URL (got ${url.protocol}//${url.host})${url.protocol === 'http:' ? '. Pass --insecure-dev to allow http:// for local testing.' : ''}`); } /** Strip a single leading `www.` and lowercase, as the spec compares domains (3.1). */ export function normalizeDomain(domain) { return domain.toLowerCase().replace(/^www\./, ''); } /** `Retry-After` as seconds: either delta-seconds or an HTTP-date. */ export function parseRetryAfter(value) { if (!value) return undefined; const n = Number(value); if (Number.isFinite(n) && n >= 0) return n; const date = Date.parse(value); if (!Number.isNaN(date)) return Math.max(0, Math.ceil((date - Date.now()) / 1000)); return undefined; } /** * Minimal `WWW-Authenticate` parser: enough for `DPoP error="...", scope="..."` and * `Bearer error="..."` (RFC 6750 / RFC 9449). Handles several challenges in one header. */ export function parseWwwAuthenticate(header) { if (!header) return []; const out = []; let i = 0; let current; while (i < header.length) { while (header[i] === ' ' || header[i] === ',') i++; if (i >= header.length) break; // Try param first (token "=" ...), then scheme. const paramRe = /([A-Za-z0-9_-]+)\s*=\s*("((?:[^"\\]|\\.)*)"|[^,\s]+)/y; paramRe.lastIndex = i; const pm = paramRe.exec(header); if (pm && current) { current.params[(pm[1] ?? '').toLowerCase()] = pm[3] !== undefined ? pm[3].replace(/\\(.)/g, '$1') : (pm[2] ?? ''); i = paramRe.lastIndex; continue; } const schemeRe = /([A-Za-z][A-Za-z0-9!#$%&'*+.^_`|~-]*)/y; schemeRe.lastIndex = i; const sm = schemeRe.exec(header); if (!sm) break; current = { scheme: sm[1] ?? '', params: {} }; out.push(current); i = schemeRe.lastIndex; } return out; } /** The first `error` found in any challenge of a `WWW-Authenticate` header. */ export function wwwAuthError(header) { for (const c of parseWwwAuthenticate(header)) { if (c.params.error) { return { error: c.params.error, scope: c.params.scope, description: c.params.error_description, }; } } return {}; } /** A space-separated scope string as a set (OAuth scope syntax, RFC 6749 3.3). */ export function scopeSet(scope) { return new Set((scope ?? '').split(' ').filter(Boolean)); } //# sourceMappingURL=util.js.map