refactor: unify channel plugin resolution, family ordering, and changelog entry tooling

This commit is contained in:
Peter Steinberger
2026-02-24 15:15:11 +00:00
parent 878b4e0ed7
commit d18ae2256f
7 changed files with 310 additions and 156 deletions

View File

@@ -255,6 +255,24 @@ export type PinnedHostname = {
lookup: typeof dnsLookupCb;
};
function dedupeAndPreferIpv4(results: readonly LookupAddress[]): string[] {
const seen = new Set<string>();
const ipv4: string[] = [];
const otherFamilies: string[] = [];
for (const entry of results) {
if (seen.has(entry.address)) {
continue;
}
seen.add(entry.address);
if (entry.family === 4) {
ipv4.push(entry.address);
continue;
}
otherFamilies.push(entry.address);
}
return [...ipv4, ...otherFamilies];
}
export async function resolvePinnedHostnameWithPolicy(
hostname: string,
params: { lookupFn?: LookupFn; policy?: SsrFPolicy } = {},
@@ -290,18 +308,9 @@ export async function resolvePinnedHostnameWithPolicy(
assertAllowedResolvedAddressesOrThrow(results, params.policy);
}
// Sort IPv4 addresses before IPv6 so that Happy Eyeballs (autoSelectFamily) and
// round-robin pinned lookups try IPv4 first. This avoids connection failures on
// hosts where IPv6 is configured but not routed (common on cloud VMs and WSL2).
// See: https://github.com/openclaw/openclaw/issues/23975
const addresses = Array.from(new Set(results.map((entry) => entry.address))).toSorted((a, b) => {
const aIsV6 = a.includes(":");
const bIsV6 = b.includes(":");
if (aIsV6 === bIsV6) {
return 0;
}
return aIsV6 ? 1 : -1;
});
// Prefer addresses returned as IPv4 by DNS family metadata before other
// families so Happy Eyeballs and pinned round-robin both attempt IPv4 first.
const addresses = dedupeAndPreferIpv4(results);
if (addresses.length === 0) {
throw new Error(`Unable to resolve hostname: ${hostname}`);
}