UNPKG

balena-cli

Version:

The official balena Command Line Interface

88 lines (78 loc) 2.36 kB
import Bonjour from 'bonjour-service'; import type { Service } from 'bonjour-service'; import * as os from 'os'; interface LocalBalenaOsDevice { address: string; host: string; osVariant?: string; port: number; } const avahiBalenaSshConfig = { type: 'ssh', name: '_resin-device._sub', protocol: 'tcp' as const, }; const avahiBalenaSshSubtype = 'resin-device'; export async function discoverLocalBalenaOsDevices( timeout = 4000, ): Promise<LocalBalenaOsDevice[]> { // search over all network interfaces const networks = os.networkInterfaces(); const validNics: os.NetworkInterfaceInfo[] = []; for (const networkName of Object.keys(networks)) { for (const iface of networks[networkName]!) { if (isIPv4(iface.family) && !iface.internal) { validNics.push(iface); } } } const allServices = await Promise.all( validNics.map((iface) => searchBalenaDevicesOnInterface(iface, timeout)), ); // dedupe services in case the same device is found on multiple interfaces const services = Array.from( new Map(allServices.flat().map((item) => [item.fqdn, item])).values(), ); return services .filter( ({ subtypes, referer }) => subtypes?.includes(avahiBalenaSshSubtype) && referer != null, ) .map(({ referer, host, port }) => ({ // We ensure referer is not null on the filter above address: referer!.address, host, port, })); } async function searchBalenaDevicesOnInterface( iface: os.NetworkInterfaceInfo, timeout: number, ): Promise<Service[]> { return await new Promise<Service[]>((resolve) => { const bonjour = new Bonjour( { // @ts-expect-error bonjour-service types are incorrect https://github.com/onlxltd/bonjour-service/issues/10 interface: iface.address, // binds to receive from any incoming interface // see: https://github.com/mafintosh/multicast-dns/issues/53#issuecomment-638365104 bind: '0.0.0.0', }, async (err: string | Error) => { await (await import('../errors')).handleError(err); }, ); const resinSshServices: Service[] = []; const browser = bonjour.find(avahiBalenaSshConfig, (service) => resinSshServices.push(service), ); setTimeout(() => { browser.stop(); bonjour.destroy(); resolve(resinSshServices); }, timeout); }); } function isIPv4(family: string | number) { return family === 4 || family === 'IPv4'; }