rahmad7/hermes-openmodel
0
1/**2 * DNS fix preload script for HF Spaces (Node.js side).3 *4 * Patches Node.js dns.lookup to:5 * 1. Check pre-resolved domains from /tmp/dns-resolved.json (populated by dns-resolve.py)6 * 2. Fall back to DNS-over-HTTPS (Cloudflare) for any other unresolvable domain7 *8 * Loaded via: NODE_OPTIONS="--require /path/to/dns-fix.cjs"9 *10 * Hermes Agent is primarily Python, but Node processes (playwright, whatsapp-bridge,11 * web dashboard build) benefit from this preload when launched from bash.12 */13"use strict";14 15const dns = require("dns");16const https = require("https");17const fs = require("fs");18 19let preResolved = {};20try {21 const raw = fs.readFileSync("/tmp/dns-resolved.json", "utf8");22 preResolved = JSON.parse(raw);23 const count = Object.keys(preResolved).length;24 if (count > 0) {25 console.log(`[dns-fix] Loaded ${count} pre-resolved domains`);26 }27} catch {28 // File not found or parse error — proceed without pre-resolved cache29}30 31const runtimeCache = new Map();32 33function dohResolve(hostname, callback) {34 const cached = runtimeCache.get(hostname);35 if (cached && cached.expiry > Date.now()) {36 return callback(null, cached.ip);37 }38 39 const url = `https://1.1.1.1/dns-query?name=${encodeURIComponent(hostname)}&type=A`;40 const req = https.get(41 url,42 { headers: { Accept: "application/dns-json" }, timeout: 15000 },43 (res) => {44 let body = "";45 res.on("data", (c) => (body += c));46 res.on("end", () => {47 try {48 const data = JSON.parse(body);49 const aRecords = (data.Answer || []).filter((a) => a.type === 1);50 if (aRecords.length === 0) {51 return callback(new Error(`DoH: no A record for ${hostname}`));52 }53 const ip = aRecords[0].data;54 const ttl = Math.max((aRecords[0].TTL || 300) * 1000, 60000);55 runtimeCache.set(hostname, { ip, expiry: Date.now() + ttl });56 callback(null, ip);57 } catch (e) {58 callback(new Error(`DoH parse error: ${e.message}`));59 }60 });61 }62 );63 req.on("error", (e) => callback(new Error(`DoH request failed: ${e.message}`)));64 req.on("timeout", () => {65 req.destroy();66 callback(new Error("DoH request timed out"));67 });68}69 70const origLookup = dns.lookup;71 72dns.lookup = function patchedLookup(hostname, options, callback) {73 if (typeof options === "function") {74 callback = options;75 options = {};76 }77 if (typeof options === "number") {78 options = { family: options };79 }80 options = options || {};81 82 if (83 !hostname ||84 hostname === "localhost" ||85 hostname === "0.0.0.0" ||86 hostname === "127.0.0.1" ||87 hostname === "::1" ||88 /^\d+\.\d+\.\d+\.\d+$/.test(hostname) ||89 /^::/.test(hostname)90 ) {91 return origLookup.call(dns, hostname, options, callback);92 }93 94 if (preResolved[hostname]) {95 const ip = preResolved[hostname];96 if (options.all) {97 return process.nextTick(() => callback(null, [{ address: ip, family: 4 }]));98 }99 return process.nextTick(() => callback(null, ip, 4));100 }101 102 origLookup.call(dns, hostname, options, (err, address, family) => {103 if (!err && address) {104 return callback(null, address, family);105 }106 if (err && (err.code === "ENOTFOUND" || err.code === "EAI_AGAIN")) {107 dohResolve(hostname, (dohErr, ip) => {108 if (dohErr || !ip) {109 return callback(err);110 }111 if (options.all) {112 return callback(null, [{ address: ip, family: 4 }]);113 }114 callback(null, ip, 4);115 });116 } else {117 callback(err, address, family);118 }119 });120};121 