@decentnetwork/peer 0.1.104 → 0.1.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compat/filetransfer.d.ts +6 -0
- package/dist/compat/filetransfer.js +67 -9
- package/dist/peer.d.ts +5 -0
- package/dist/peer.js +78 -2
- package/package.json +1 -1
|
@@ -57,6 +57,12 @@ export declare class FileTransferManager {
|
|
|
57
57
|
accept(friendId: string, fileNumber: number): void;
|
|
58
58
|
cancel(friendId: string, fileNumber: number, isSending: boolean): void;
|
|
59
59
|
cancelByFileId(friendId: string, fileIdHex: string, isSending: boolean): boolean;
|
|
60
|
+
/** Cancel in-flight sends matching both name and size.
|
|
61
|
+
* This is a conservative registry-desync fallback after fileId/hash lookup. */
|
|
62
|
+
cancelSendsMatching(friendId: string, match: {
|
|
63
|
+
name?: string;
|
|
64
|
+
size: number;
|
|
65
|
+
}): string[];
|
|
60
66
|
handlePacket(friendId: string, packetId: number, payload: Uint8Array): boolean;
|
|
61
67
|
clearFriend(friendId: string): void;
|
|
62
68
|
}
|
|
@@ -239,17 +239,23 @@ export class FileTransferManager {
|
|
|
239
239
|
}
|
|
240
240
|
return inner;
|
|
241
241
|
}
|
|
242
|
-
#freeNumber(friendId) {
|
|
242
|
+
#freeNumber(friendId, preferred = 0) {
|
|
243
243
|
const inner = this.#map(this.#sending, friendId);
|
|
244
|
-
|
|
244
|
+
// Start at a content-derived slot. Older receivers retain an incomplete
|
|
245
|
+
// receive slot in memory until it finishes or is killed. After the sender
|
|
246
|
+
// restarts, always reusing slot 0 makes a DIFFERENT new file look like a
|
|
247
|
+
// retransmitted offer for that stale slot, so the receiver silently ACKs
|
|
248
|
+
// the old file and the new UI stays at 0%. The same content keeps the same
|
|
249
|
+
// preferred slot (resume), while different content naturally avoids stale
|
|
250
|
+
// collisions; circular search still supports concurrent transfers.
|
|
251
|
+
for (let n = 0; n < MAX_CONCURRENT_FILE_PIPES; n++) {
|
|
252
|
+
const i = (preferred + n) % MAX_CONCURRENT_FILE_PIPES;
|
|
245
253
|
if (!inner.has(i))
|
|
246
254
|
return i;
|
|
255
|
+
}
|
|
247
256
|
return -1;
|
|
248
257
|
}
|
|
249
258
|
sendFile(friendId, data, opts) {
|
|
250
|
-
const fileNumber = this.#freeNumber(friendId);
|
|
251
|
-
if (fileNumber < 0)
|
|
252
|
-
return null;
|
|
253
259
|
// Content-addressed fileId (sha256): re-sending the SAME file after a
|
|
254
260
|
// restart or a dropped peer yields the SAME id, so the receiver can match
|
|
255
261
|
// it against its persisted partial and resume from where it left off — the
|
|
@@ -257,6 +263,9 @@ export class FileTransferManager {
|
|
|
257
263
|
const fileId = data.length > 0
|
|
258
264
|
? new Uint8Array(createHash("sha256").update(data).digest()).slice(0, FILE_ID_LENGTH)
|
|
259
265
|
: randomBytes(FILE_ID_LENGTH);
|
|
266
|
+
const fileNumber = this.#freeNumber(friendId, fileId[0] ?? 0);
|
|
267
|
+
if (fileNumber < 0)
|
|
268
|
+
return null;
|
|
260
269
|
const req = encodeFileSendRequest(fileNumber, opts.kind ?? FILEKIND.DATA, BigInt(data.length), fileId, opts.name);
|
|
261
270
|
const lanPath = this.isLanPath(friendId);
|
|
262
271
|
const st = {
|
|
@@ -271,6 +280,8 @@ export class FileTransferManager {
|
|
|
271
280
|
btlBwBps: 0, bwSamples: [], fullBwRounds: 0, startupQueueRounds: 0, bbrPhase: 0, roundCount: 0,
|
|
272
281
|
rateMarkMs: nowMs(), rateMarkAcked: 0, lowRttCount: 0, lowRttMinMs: Infinity, lastCcLogMs: 0,
|
|
273
282
|
lanPath,
|
|
283
|
+
dbgLanNow: lanPath ? 1 : 0, dbgSampleMs: 0, dbgRoundRate: 0, dbgMeasuredRate: 0,
|
|
284
|
+
dbgRawQueue: 0, dbgUnder: 0, dbgOvershot: 0,
|
|
274
285
|
};
|
|
275
286
|
this.#map(this.#sending, friendId).set(fileNumber, st);
|
|
276
287
|
void this.send(friendId, PACKET_ID_FILE_SENDREQUEST, req).catch(() => undefined);
|
|
@@ -321,27 +332,54 @@ export class FileTransferManager {
|
|
|
321
332
|
}
|
|
322
333
|
}
|
|
323
334
|
cancel(friendId, fileNumber, isSending) {
|
|
335
|
+
const inner = (isSending ? this.#sending : this.#receiving).get(friendId);
|
|
336
|
+
const st = inner?.get(fileNumber);
|
|
337
|
+
const fileId = st ? hex(st.fileId) : undefined;
|
|
324
338
|
const sr = isSending ? 0 : 1;
|
|
325
339
|
void this.send(friendId, PACKET_ID_FILE_CONTROL, encodeFileControl(sr, fileNumber, FILECONTROL.KILL)).catch(() => undefined);
|
|
326
340
|
if (isSending)
|
|
327
341
|
this.#endSend(friendId, fileNumber);
|
|
328
342
|
else
|
|
329
343
|
this.#endRecv(friendId, fileNumber);
|
|
344
|
+
// Local cancel used to tear down silently. The daemon tracks sends in
|
|
345
|
+
// activeSends and needs this event when the registry entry is already gone
|
|
346
|
+
// (or cancel raced with a progress tick) so the UI chip cannot stay "sending".
|
|
347
|
+
if (fileId)
|
|
348
|
+
this.emit("file-cancel", { friendId, fileId, sending: isSending, reason: "local" });
|
|
330
349
|
}
|
|
331
350
|
// Cancel by content fileId (hex) — the daemon/UI tracks transfers by fileId,
|
|
332
351
|
// not the internal fileNumber, so it can stop a specific in-flight transfer.
|
|
333
352
|
cancelByFileId(friendId, fileIdHex, isSending) {
|
|
353
|
+
const want = fileIdHex.trim().toLowerCase();
|
|
334
354
|
const inner = (isSending ? this.#sending : this.#receiving).get(friendId);
|
|
335
355
|
if (!inner)
|
|
336
356
|
return false;
|
|
337
357
|
for (const [fileNumber, st] of inner) {
|
|
338
|
-
if (hex(st.fileId) ===
|
|
358
|
+
if (hex(st.fileId) === want) {
|
|
339
359
|
this.cancel(friendId, fileNumber, isSending);
|
|
340
360
|
return true;
|
|
341
361
|
}
|
|
342
362
|
}
|
|
343
363
|
return false;
|
|
344
364
|
}
|
|
365
|
+
/** Cancel in-flight sends matching both name and size.
|
|
366
|
+
* This is a conservative registry-desync fallback after fileId/hash lookup. */
|
|
367
|
+
cancelSendsMatching(friendId, match) {
|
|
368
|
+
const inner = this.#sending.get(friendId);
|
|
369
|
+
if (!inner)
|
|
370
|
+
return [];
|
|
371
|
+
const candidates = [...inner].filter(([, st]) => st.size === match.size);
|
|
372
|
+
const named = match.name
|
|
373
|
+
? candidates.filter(([, st]) => st.name === match.name)
|
|
374
|
+
: [];
|
|
375
|
+
const picked = named;
|
|
376
|
+
const killed = [];
|
|
377
|
+
for (const [fileNumber, st] of picked) {
|
|
378
|
+
killed.push(hex(st.fileId));
|
|
379
|
+
this.cancel(friendId, fileNumber, true);
|
|
380
|
+
}
|
|
381
|
+
return killed;
|
|
382
|
+
}
|
|
345
383
|
handlePacket(friendId, packetId, payload) {
|
|
346
384
|
if (packetId === PACKET_ID_FILE_SENDREQUEST) {
|
|
347
385
|
this.#onSendRequest(friendId, payload);
|
|
@@ -789,6 +827,12 @@ export class FileTransferManager {
|
|
|
789
827
|
const measuredRate = lanNow
|
|
790
828
|
? Math.min(roundRate, Math.max(PACE_INIT_BPS, st.paceRateBps * LAN_SAMPLE_MAX_GAIN))
|
|
791
829
|
: roundRate;
|
|
830
|
+
st.dbgLanNow = lanNow ? 1 : 0;
|
|
831
|
+
st.dbgSampleMs = Math.round(sampleMs);
|
|
832
|
+
st.dbgRoundRate = Math.round(roundRate);
|
|
833
|
+
st.dbgMeasuredRate = Math.round(measuredRate);
|
|
834
|
+
st.dbgUnder = 0;
|
|
835
|
+
st.dbgOvershot = 0;
|
|
792
836
|
if (measuredRate > 0) {
|
|
793
837
|
st.bwSamples.push(measuredRate);
|
|
794
838
|
if (st.bwSamples.length > PACE_BW_WINDOW)
|
|
@@ -813,6 +857,8 @@ export class FileTransferManager {
|
|
|
813
857
|
st.cwnd = Math.max(st.cwnd, LAN_CWND_INIT_CHUNKS);
|
|
814
858
|
}
|
|
815
859
|
st.lanPath = lanNow;
|
|
860
|
+
const rawQueue = st.minRttMs !== Infinity ? acceptedRtt - st.minRttMs : 0;
|
|
861
|
+
st.dbgRawQueue = Math.round(rawQueue);
|
|
816
862
|
if (!lanNow) {
|
|
817
863
|
// Relay/public-NAT file traffic must remain conservative. A short
|
|
818
864
|
// ACK burst can wildly overstate bandwidth there; allowing LAN's
|
|
@@ -840,7 +886,6 @@ export class FileTransferManager {
|
|
|
840
886
|
else {
|
|
841
887
|
st.fullBwRounds++;
|
|
842
888
|
}
|
|
843
|
-
const rawQueue = st.minRttMs !== Infinity ? acceptedRtt - st.minRttMs : 0;
|
|
844
889
|
if (rawQueue > LAN_STARTUP_QUEUE_MS) {
|
|
845
890
|
st.startupQueueRounds++;
|
|
846
891
|
}
|
|
@@ -855,6 +900,8 @@ export class FileTransferManager {
|
|
|
855
900
|
const startupOvershot = rawQueue > LAN_STARTUP_HARD_QUEUE_MS ||
|
|
856
901
|
st.startupQueueRounds >= LAN_STARTUP_QUEUE_ROUNDS;
|
|
857
902
|
const startupUnderDelivering = st.fullBwRounds >= LAN_STARTUP_SLOW_ROUNDS;
|
|
903
|
+
st.dbgUnder = startupUnderDelivering ? 1 : 0;
|
|
904
|
+
st.dbgOvershot = startupOvershot ? 1 : 0;
|
|
858
905
|
if (startupUnderDelivering || startupOvershot) {
|
|
859
906
|
st.bbrPhase = 2;
|
|
860
907
|
}
|
|
@@ -895,15 +942,26 @@ export class FileTransferManager {
|
|
|
895
942
|
}
|
|
896
943
|
if (process.env.DECENT_DEBUG && nowT - st.lastCcLogMs >= 1000) {
|
|
897
944
|
st.lastCcLogMs = nowT;
|
|
945
|
+
const windowEnd = Math.min(st.acked + st.cwnd * MAX_FILE_DATA_SIZE, st.size);
|
|
898
946
|
console.error(`[file-cc] friend=${friendId.slice(0, 8)} ack=${st.acked}/${st.size}` +
|
|
899
947
|
` rtt=${rtt}ms srtt=${st.srttMs.toFixed(1)}ms low=${st.lowRttCount}` +
|
|
900
948
|
` accepted=${(!lowSample || promoteLowPath) ? 1 : 0}` +
|
|
901
949
|
` pace=${Math.round(st.paceRateBps)}Bps bw=${Math.round(st.btlBwBps)}Bps` +
|
|
902
950
|
` cwnd=${st.cwnd} phase=${st.bbrPhase} slow=${st.fullBwRounds}` +
|
|
903
|
-
` loss=${(st.lossEwma * 100).toFixed(1)}% rounds=${st.roundCount}`
|
|
951
|
+
` loss=${(st.lossEwma * 100).toFixed(1)}% rounds=${st.roundCount}` +
|
|
952
|
+
` lanNow=${st.dbgLanNow} lanPath=${st.lanPath ? 1 : 0}` +
|
|
953
|
+
` sampleMs=${st.dbgSampleMs} roundRate=${st.dbgRoundRate}` +
|
|
954
|
+
` measuredRate=${st.dbgMeasuredRate} rawQueue=${st.dbgRawQueue}` +
|
|
955
|
+
` startupQueueRounds=${st.startupQueueRounds}` +
|
|
956
|
+
` under=${st.dbgUnder} overshot=${st.dbgOvershot}` +
|
|
957
|
+
` rateMarkAcked=${st.rateMarkAcked} nextSend=${st.nextSend}` +
|
|
958
|
+
` windowEnd=${windowEnd} tokens=${Math.round(st.paceTokens)}`);
|
|
904
959
|
}
|
|
905
960
|
}
|
|
906
|
-
|
|
961
|
+
// Small files (UI screenshots etc.) used to sit at 0% until the final
|
|
962
|
+
// byte because progress only fired every 256 KB. Emit at least every
|
|
963
|
+
// 16 KB or on completion so a 100–200 KB send shows movement.
|
|
964
|
+
if (st.acked - st.lastProgressAcked >= 16 * 1024 || st.acked >= st.size) {
|
|
907
965
|
st.lastProgressAcked = st.acked;
|
|
908
966
|
this.emit("file-progress", { friendId, fileId: hex(st.fileId), received: st.acked, total: st.size, sending: true });
|
|
909
967
|
}
|
package/dist/peer.d.ts
CHANGED
|
@@ -164,6 +164,11 @@ export declare class Peer {
|
|
|
164
164
|
cancelFile(userid: string, fileNumber: number, isSending?: boolean): void;
|
|
165
165
|
/** Cancel an in-flight transfer by its content fileId (hex). Returns true if found. */
|
|
166
166
|
cancelFileById(userid: string, fileId: string, isSending?: boolean): boolean;
|
|
167
|
+
/** Cancel in-flight sends that match size/name (registry-desync fallback). */
|
|
168
|
+
cancelSendsMatching(userid: string, match: {
|
|
169
|
+
name?: string;
|
|
170
|
+
size: number;
|
|
171
|
+
}): string[];
|
|
167
172
|
/** Incoming file offer: { friendId, fileNumber, fileId, name, size, kind }. */
|
|
168
173
|
onFile(cb: (offer: {
|
|
169
174
|
friendId: string;
|
package/dist/peer.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
2
4
|
import { mkdir, readFile, writeFile, rename } from "node:fs/promises";
|
|
3
5
|
import { networkInterfaces } from "node:os";
|
|
4
6
|
import { dirname, join } from "node:path";
|
|
@@ -1348,6 +1350,10 @@ export class Peer {
|
|
|
1348
1350
|
cancelFile(userid, fileNumber, isSending = false) { this.#fileTransfer.cancel(userid, fileNumber, isSending); }
|
|
1349
1351
|
/** Cancel an in-flight transfer by its content fileId (hex). Returns true if found. */
|
|
1350
1352
|
cancelFileById(userid, fileId, isSending = false) { return this.#fileTransfer.cancelByFileId(userid, fileId, isSending); }
|
|
1353
|
+
/** Cancel in-flight sends that match size/name (registry-desync fallback). */
|
|
1354
|
+
cancelSendsMatching(userid, match) {
|
|
1355
|
+
return this.#fileTransfer.cancelSendsMatching(userid, match);
|
|
1356
|
+
}
|
|
1351
1357
|
/** Incoming file offer: { friendId, fileNumber, fileId, name, size, kind }. */
|
|
1352
1358
|
onFile(cb) { this.#events.on("file-offer", cb); }
|
|
1353
1359
|
/** Transfer progress: { friendId, fileId, received, total, sending? }. */
|
|
@@ -2163,6 +2169,21 @@ export class Peer {
|
|
|
2163
2169
|
// syscall (that was the CCTV CPU regression) — safe at CCTV packet
|
|
2164
2170
|
// rates.
|
|
2165
2171
|
this.#adoptRemote(state, remote.address, remote.port);
|
|
2172
|
+
// A successfully decrypted packet arriving from a private address in
|
|
2173
|
+
// one of our physical subnets is stronger LAN proof than an endpoint
|
|
2174
|
+
// hint: it demonstrates that this exact host:port already reaches us.
|
|
2175
|
+
// Lock it immediately. Without this, duplicate public/relay copies
|
|
2176
|
+
// arriving a moment later overwrite `remote`, so file DATA travels on
|
|
2177
|
+
// the LAN but ACKs hairpin through the public Internet (observed as
|
|
2178
|
+
// ~38KB/s between mac-dev and WSL on the same 10.0.0.0/24).
|
|
2179
|
+
if (!state.lanRemoteHost &&
|
|
2180
|
+
isPrivateAddress(remote.address) &&
|
|
2181
|
+
!isCgnatAddress(remote.address) &&
|
|
2182
|
+
getPhysicalLanSubnets().some((s) => isInIpv4Subnet(remote.address, s))) {
|
|
2183
|
+
state.lanRemoteHost = remote.address;
|
|
2184
|
+
this.#rememberEndpointCandidate(state, remote.address, remote.port);
|
|
2185
|
+
this.#debugLog(`lan_lock_confirmed friend=${friendId} host=${remote.address}:${remote.port} (decrypted UDP source)`);
|
|
2186
|
+
}
|
|
2166
2187
|
if (!state.lastUdpRecvMs) {
|
|
2167
2188
|
this.#debugLog(`udp_confirmed friend=${friendId} via=${remote.address}:${remote.port} (direct UDP path live)`);
|
|
2168
2189
|
}
|
|
@@ -4474,7 +4495,11 @@ export class Peer {
|
|
|
4474
4495
|
// office silently fall back to the TURN relay (~260ms for a <1ms hop).
|
|
4475
4496
|
const lanIp = getPhysicalLanAddresses().find((ip) => isPrivateAddress(ip));
|
|
4476
4497
|
const lanOctets = lanIp ? lanIp.split(".").map((s) => parseInt(s, 10)) : undefined;
|
|
4477
|
-
|
|
4498
|
+
// WSL packets are NATed by Windows, so the physical host NIC must be paired
|
|
4499
|
+
// with the observed server-reflexive port, not WSL's internal socket port.
|
|
4500
|
+
const lanPort = lanIp && _wslHostAddrsCache.has(lanIp)
|
|
4501
|
+
? srflx.port
|
|
4502
|
+
: (this.#udp.localPort() ?? 0);
|
|
4478
4503
|
const lanValid = !!lanOctets && lanOctets.length === 4 && !lanOctets.some((o) => Number.isNaN(o)) && lanPort > 0;
|
|
4479
4504
|
const size = lanValid ? 18 : relayValid ? 12 : 6;
|
|
4480
4505
|
const payload = new Uint8Array(size);
|
|
@@ -4559,10 +4584,17 @@ export class Peer {
|
|
|
4559
4584
|
if (payload.length >= 18) {
|
|
4560
4585
|
const lanHost = `${payload[12]}.${payload[13]}.${payload[14]}.${payload[15]}`;
|
|
4561
4586
|
const lanPort = ((payload[16] << 8) | payload[17]) >>> 0;
|
|
4587
|
+
// WSL sees only its 172.x virtual NIC, not the Windows host's physical
|
|
4588
|
+
// 10.x subnet. In that topology a peer on the same real LAN is still
|
|
4589
|
+
// safely identifiable because its server-reflexive address equals ours.
|
|
4590
|
+
// Permit the advertised private candidate when both peers share the same
|
|
4591
|
+
// public NAT; if it is a CGNAT/private collision and never validates, the
|
|
4592
|
+
// normal 12s stale-LAN-lock breaker restores the public/relay path.
|
|
4593
|
+
const samePublicNat = !!this.#srflxCache?.addr.host && this.#srflxCache.addr.host === host;
|
|
4562
4594
|
const sameLan = lanPort !== 0 &&
|
|
4563
4595
|
isPrivateAddress(lanHost) &&
|
|
4564
4596
|
!isCgnatAddress(lanHost) &&
|
|
4565
|
-
getPhysicalLanSubnets().some((s) => isInIpv4Subnet(lanHost, s)) &&
|
|
4597
|
+
(getPhysicalLanSubnets().some((s) => isInIpv4Subnet(lanHost, s)) || samePublicNat) &&
|
|
4566
4598
|
!(getPhysicalLanAddresses().includes(lanHost) && this.#udp.localPort() === lanPort);
|
|
4567
4599
|
if (sameLan) {
|
|
4568
4600
|
this.#rememberEndpointCandidate(session, lanHost, lanPort);
|
|
@@ -6305,6 +6337,45 @@ let _lanAddrsCache = [];
|
|
|
6305
6337
|
let _lanSubnetsCache = [];
|
|
6306
6338
|
let _allOwnAddrsCache = new Set();
|
|
6307
6339
|
let _ownVirtualAddrsCache = new Set();
|
|
6340
|
+
let _wslHostAddrsCache = new Set();
|
|
6341
|
+
let _wslHostCacheMs = 0;
|
|
6342
|
+
/** Discover the Windows host's physical NIC from WSL2. WSL itself only sees
|
|
6343
|
+
* its 172.x Hyper-V NIC, which is unreachable from another LAN machine. */
|
|
6344
|
+
function getWslWindowsHostAddresses(localSubnets) {
|
|
6345
|
+
const now = Date.now();
|
|
6346
|
+
if (now - _wslHostCacheMs < 60_000)
|
|
6347
|
+
return [..._wslHostAddrsCache];
|
|
6348
|
+
_wslHostCacheMs = now;
|
|
6349
|
+
const found = new Set();
|
|
6350
|
+
const ipconfig = "/mnt/c/Windows/System32/ipconfig.exe";
|
|
6351
|
+
if (process.platform !== "linux" || !existsSync(ipconfig)) {
|
|
6352
|
+
_wslHostAddrsCache = found;
|
|
6353
|
+
return [];
|
|
6354
|
+
}
|
|
6355
|
+
try {
|
|
6356
|
+
const output = execFileSync(ipconfig, [], {
|
|
6357
|
+
encoding: "utf8",
|
|
6358
|
+
timeout: 2000,
|
|
6359
|
+
windowsHide: true,
|
|
6360
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
6361
|
+
});
|
|
6362
|
+
// "IPv4" remains in localized ipconfig output (including Chinese).
|
|
6363
|
+
for (const match of output.matchAll(/IPv4[^:\r\n]*:\s*(\d+\.\d+\.\d+\.\d+)/gi)) {
|
|
6364
|
+
const host = match[1];
|
|
6365
|
+
if (!isPrivateAddress(host) || isCgnatAddress(host) || host.startsWith("127.") || host.startsWith("169.254."))
|
|
6366
|
+
continue;
|
|
6367
|
+
// Exclude WSL/Hyper-V addresses already visible inside Linux.
|
|
6368
|
+
if (localSubnets.some((s) => isInIpv4Subnet(host, s)))
|
|
6369
|
+
continue;
|
|
6370
|
+
found.add(host);
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
catch {
|
|
6374
|
+
// WSL interop can be disabled; ordinary interface discovery remains valid.
|
|
6375
|
+
}
|
|
6376
|
+
_wslHostAddrsCache = found;
|
|
6377
|
+
return [...found];
|
|
6378
|
+
}
|
|
6308
6379
|
function refreshLanIfaceCache() {
|
|
6309
6380
|
const now = Date.now();
|
|
6310
6381
|
if (_lanIfaceCacheMs !== 0 && now - _lanIfaceCacheMs < 5000)
|
|
@@ -6341,6 +6412,11 @@ function refreshLanIfaceCache() {
|
|
|
6341
6412
|
catch {
|
|
6342
6413
|
// best-effort
|
|
6343
6414
|
}
|
|
6415
|
+
// Prefer the physical Windows host NIC over WSL's internal eth0 address.
|
|
6416
|
+
const wslHostAddrs = getWslWindowsHostAddresses(subnets);
|
|
6417
|
+
for (const host of wslHostAddrs)
|
|
6418
|
+
allOwn.add(host);
|
|
6419
|
+
addrs.unshift(...wslHostAddrs);
|
|
6344
6420
|
_lanAddrsCache = addrs;
|
|
6345
6421
|
_lanSubnetsCache = subnets;
|
|
6346
6422
|
_allOwnAddrsCache = allOwn;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.107",
|
|
4
4
|
"description": "Pure TypeScript port of Elastos Carrier (toxcore-derived) P2P messaging. DHT, onion routing, TCP relay, FlatBuffers app payloads, Express offline relay. Wire-compatible with iOS Beagle and the Carrier C SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|