Fix web inspector: endianness (LE), ECONNRESET, persistent discovery

- protocol.mjs: all reads/writes switched to LE to match serial.h
- node_client.mjs: persistent error handler prevents ECONNRESET crash
- discovery.mjs: remove unnecessary SO_REUSEPORT
- server.mjs: discovery runs at startup (not per SSE open); uses
  EventEmitter + known_peers Map so SSE replays existing peers on connect

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 01:45:33 +00:00
parent 62c25247ef
commit e1151410ad
4 changed files with 112 additions and 86 deletions

View File

@@ -26,25 +26,29 @@ const ANN_FIXED_SIZE = 8;
* Returns a stop() function. Call it when done (e.g. user closed the picker
* or selected a node). Safe to call multiple times.
*/
export function start_discovery(on_peer) {
export function start_discovery(on_peer, on_error) {
const sock = dgram.createSocket({ type: 'udp4', reuseAddr: true });
const seen = new Set();
let closed = false;
sock.on('error', () => stop());
sock.on('error', err => {
console.error('discovery socket error:', err.message);
if (on_error) { on_error(err); }
stop();
});
sock.on('message', (msg, rinfo) => {
if (msg.length < HEADER_SIZE) { return; }
const msg_type = msg.readUInt16BE(0);
const payload_len = msg.readUInt32BE(2);
const msg_type = msg.readUInt16LE(0);
const payload_len = msg.readUInt32LE(2);
if (msg_type !== ANNOUNCE_TYPE) { return; }
if (msg.length < HEADER_SIZE + payload_len) { return; }
if (payload_len < ANN_FIXED_SIZE) { return; }
const p = msg.slice(HEADER_SIZE);
const site_id = p.readUInt16BE(1);
const tcp_port = p.readUInt16BE(3);
const func_flags = p.readUInt16BE(5);
const site_id = p.readUInt16LE(1);
const tcp_port = p.readUInt16LE(3);
const func_flags = p.readUInt16LE(5);
const name_len = p.readUInt8(7);
if (payload_len < ANN_FIXED_SIZE + name_len) { return; }
const name = p.toString('utf8', 8, 8 + name_len);
@@ -74,12 +78,12 @@ function send_announce(sock) {
const name = Buffer.from('web-inspector', 'utf8');
const payload_len = 8 + name.length;
const buf = Buffer.allocUnsafe(6 + payload_len);
buf.writeUInt16BE(ANNOUNCE_TYPE, 0);
buf.writeUInt32BE(payload_len, 2);
buf.writeUInt16LE(ANNOUNCE_TYPE, 0);
buf.writeUInt32LE(payload_len, 2);
buf.writeUInt8(1, 6); /* protocol_version */
buf.writeUInt16BE(0, 7); /* site_id */
buf.writeUInt16BE(0, 9); /* tcp_port (0 = no server) */
buf.writeUInt16BE(0, 11); /* function_flags */
buf.writeUInt16LE(0, 7); /* site_id */
buf.writeUInt16LE(0, 9); /* tcp_port (0 = no server) */
buf.writeUInt16LE(0, 11); /* function_flags */
buf.writeUInt8(name.length, 13);
name.copy(buf, 14);
sock.send(buf, 0, buf.length, DISCOVERY_PORT, MULTICAST_GROUP);