@bulolo/hermes-link 0.2.6 → 0.2.8
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.
|
@@ -1462,29 +1462,20 @@ function normalizeLogLevel(level) {
|
|
|
1462
1462
|
return defaultLinkConfig.logLevel;
|
|
1463
1463
|
}
|
|
1464
1464
|
function normalizeLanHost(value) {
|
|
1465
|
-
if (value === null || value === void 0)
|
|
1466
|
-
|
|
1467
|
-
}
|
|
1468
|
-
if (typeof value !== "string") {
|
|
1469
|
-
return null;
|
|
1470
|
-
}
|
|
1465
|
+
if (value === null || value === void 0) return null;
|
|
1466
|
+
if (typeof value !== "string") return null;
|
|
1471
1467
|
const host = value.trim().replace(/^\[/u, "").replace(/\]$/u, "");
|
|
1472
|
-
if (!host)
|
|
1473
|
-
|
|
1474
|
-
}
|
|
1475
|
-
if (!isUsableLanIpv4(host)) {
|
|
1476
|
-
return null;
|
|
1477
|
-
}
|
|
1468
|
+
if (!host) return null;
|
|
1469
|
+
if (!isValidHostIpv4(host)) return null;
|
|
1478
1470
|
return host;
|
|
1479
1471
|
}
|
|
1480
|
-
function
|
|
1472
|
+
function isValidHostIpv4(value) {
|
|
1481
1473
|
const parts = value.split(".").map((part) => Number.parseInt(part, 10));
|
|
1482
1474
|
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
|
|
1483
1475
|
return false;
|
|
1484
1476
|
}
|
|
1485
|
-
const [
|
|
1486
|
-
|
|
1487
|
-
return privateRange && fourth !== 0 && fourth !== 255;
|
|
1477
|
+
const [, , , fourth] = parts;
|
|
1478
|
+
return fourth !== 0 && fourth !== 255;
|
|
1488
1479
|
}
|
|
1489
1480
|
|
|
1490
1481
|
// src/network/topology.ts
|
|
@@ -1517,7 +1508,7 @@ function discoverLanIpsFromInterfaces(interfaces) {
|
|
|
1517
1508
|
for (const [name, items] of Object.entries(interfaces)) {
|
|
1518
1509
|
if (shouldIgnoreInterface(name)) continue;
|
|
1519
1510
|
for (const item of items ?? []) {
|
|
1520
|
-
if (!item.internal && item.address && item.family === "IPv4" &&
|
|
1511
|
+
if (!item.internal && item.address && item.family === "IPv4" && isUsableLanIpv4(item.address, item.netmask)) {
|
|
1521
1512
|
candidates.push({ name, address: item.address });
|
|
1522
1513
|
}
|
|
1523
1514
|
}
|
|
@@ -1529,33 +1520,51 @@ function discoverLanIpsFromInterfaces(interfaces) {
|
|
|
1529
1520
|
}
|
|
1530
1521
|
async function observePublicRoute(options) {
|
|
1531
1522
|
const fetcher = options.fetchImpl ?? fetch;
|
|
1532
|
-
const
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1523
|
+
const simpleIp = await fetchPublicIpSimple(fetcher).catch(() => null);
|
|
1524
|
+
try {
|
|
1525
|
+
const response = await fetcher(
|
|
1526
|
+
`${options.relayBaseUrl.replace(/\/+$/u, "")}/api/v1/relay/public-route/observe`,
|
|
1527
|
+
{
|
|
1528
|
+
method: "POST",
|
|
1529
|
+
headers: {
|
|
1530
|
+
"content-type": "application/json",
|
|
1531
|
+
...options.relayBootstrapToken ? { authorization: `Bearer ${options.relayBootstrapToken}` } : {}
|
|
1532
|
+
},
|
|
1533
|
+
body: JSON.stringify({
|
|
1534
|
+
install_id: options.installId,
|
|
1535
|
+
link_id: options.linkId,
|
|
1536
|
+
public_key_pem: options.publicKeyPem
|
|
1537
|
+
}),
|
|
1538
|
+
signal: AbortSignal.timeout(5e3)
|
|
1539
|
+
}
|
|
1540
|
+
);
|
|
1541
|
+
const payload = await response.json().catch(() => null);
|
|
1542
|
+
const record = typeof payload?.record === "object" && payload.record !== null ? payload.record : null;
|
|
1543
|
+
const observed = typeof payload?.observed === "object" && payload.observed !== null ? payload.observed : null;
|
|
1544
|
+
const values = [
|
|
1545
|
+
simpleIp,
|
|
1546
|
+
readIpRecord(record?.ipv4),
|
|
1547
|
+
readIpRecord(record?.ipv6),
|
|
1548
|
+
typeof observed?.ip === "string" ? observed.ip : null
|
|
1549
|
+
].filter((v) => Boolean(v));
|
|
1550
|
+
return {
|
|
1551
|
+
publicIpv4s: unique(values.filter(isUsablePublicIpv4)),
|
|
1552
|
+
publicIpv6s: unique(values.filter(isUsablePublicIpv6))
|
|
1553
|
+
};
|
|
1554
|
+
} catch {
|
|
1555
|
+
const values = [simpleIp].filter((v) => Boolean(v));
|
|
1556
|
+
return {
|
|
1557
|
+
publicIpv4s: unique(values.filter(isUsablePublicIpv4)),
|
|
1558
|
+
publicIpv6s: []
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
async function fetchPublicIpSimple(fetcher) {
|
|
1563
|
+
const res = await fetcher("https://api.ipify.org?format=json", {
|
|
1564
|
+
signal: AbortSignal.timeout(4e3)
|
|
1565
|
+
});
|
|
1566
|
+
const data = await res.json();
|
|
1567
|
+
return typeof data.ip === "string" && data.ip.trim() ? data.ip.trim() : null;
|
|
1559
1568
|
}
|
|
1560
1569
|
function readIpRecord(value) {
|
|
1561
1570
|
if (typeof value !== "object" || value === null) return null;
|
|
@@ -1575,7 +1584,7 @@ function compareLanCandidate(left, right) {
|
|
|
1575
1584
|
function interfacePriority(name) {
|
|
1576
1585
|
return /^(en|eth|wlan|wi-fi|wifi)/iu.test(name) ? 0 : 1;
|
|
1577
1586
|
}
|
|
1578
|
-
function
|
|
1587
|
+
function isUsableLanIpv4(address, netmask) {
|
|
1579
1588
|
return isPrivateIpv4(address) && !isNetworkOrBroadcastIpv4Address(address, netmask);
|
|
1580
1589
|
}
|
|
1581
1590
|
function isUsablePublicIpv4(address) {
|
|
@@ -3506,8 +3515,9 @@ async function startLinkService(options) {
|
|
|
3506
3515
|
const statsRouter = createStatisticsRouter({ db, paths });
|
|
3507
3516
|
app.use(statsRouter.routes());
|
|
3508
3517
|
app.use(statsRouter.allowedMethods());
|
|
3518
|
+
const listenHost = process.env.HERMESLINK_LISTEN_HOST ?? "0.0.0.0";
|
|
3509
3519
|
const server = await new Promise((resolve, reject) => {
|
|
3510
|
-
const s = app.listen(config.port,
|
|
3520
|
+
const s = app.listen(config.port, listenHost, () => resolve(s));
|
|
3511
3521
|
s.once("error", reject);
|
|
3512
3522
|
});
|
|
3513
3523
|
const relayClient = new RelayClient({
|
package/dist/cli/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
saveConfig,
|
|
24
24
|
startLinkService,
|
|
25
25
|
writeJsonFile
|
|
26
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-Q546CZFY.js";
|
|
27
27
|
|
|
28
28
|
// src/cli/index.ts
|
|
29
29
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
@@ -275,7 +275,7 @@ async function runPairingPreflight(options) {
|
|
|
275
275
|
if (options.openBrowser !== false) {
|
|
276
276
|
await openSystemBrowser(pairingUrl).catch(() => void 0);
|
|
277
277
|
}
|
|
278
|
-
return { pairingUrl, connectToken: token.token };
|
|
278
|
+
return { pairingUrl, connectToken: token.token, bestUrl };
|
|
279
279
|
}
|
|
280
280
|
function buildPairingUrl(params) {
|
|
281
281
|
const qs = new URLSearchParams({
|
|
@@ -288,10 +288,6 @@ function buildPairingUrl(params) {
|
|
|
288
288
|
if (params.publicUrls.length > 0) qs.set("public_urls", params.publicUrls.join(","));
|
|
289
289
|
return `hermesapp://pair?${qs.toString()}`;
|
|
290
290
|
}
|
|
291
|
-
function buildLocalPairingPageUrl(port, connectToken) {
|
|
292
|
-
const qs = new URLSearchParams({ connect_token: connectToken });
|
|
293
|
-
return `http://127.0.0.1:${port}/pair?${qs.toString()}`;
|
|
294
|
-
}
|
|
295
291
|
|
|
296
292
|
// src/cli/index.ts
|
|
297
293
|
var args = process.argv.slice(2);
|
|
@@ -447,13 +443,14 @@ async function cmdPair(paths) {
|
|
|
447
443
|
return;
|
|
448
444
|
}
|
|
449
445
|
const result = await runPairingPreflight({ identity, config, paths });
|
|
450
|
-
const
|
|
446
|
+
const pageBase = result.bestUrl.replace(/\/+$/u, "");
|
|
447
|
+
const pageUrl = `${pageBase}/pair?connect_token=${encodeURIComponent(result.connectToken)}`;
|
|
451
448
|
process.stdout.write("\n");
|
|
452
449
|
qrcode.generate(result.pairingUrl, { small: true });
|
|
453
450
|
process.stdout.write(`
|
|
454
451
|
Pairing URL: ${result.pairingUrl}
|
|
455
452
|
`);
|
|
456
|
-
process.stdout.write(`Pairing page: ${
|
|
453
|
+
process.stdout.write(`Pairing page: ${pageUrl}
|
|
457
454
|
`);
|
|
458
455
|
process.stdout.write(`Connect token: ${result.connectToken}
|
|
459
456
|
`);
|
package/dist/http/app.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bulolo/hermes-link",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Hermes Link companion service and CLI for connecting hermes-agent through zhiji",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"prepack": "npm run build",
|
|
37
37
|
"start": "node ./dist/cli/index.js",
|
|
38
38
|
"test": "vitest",
|
|
39
|
-
"publish:npm": "npm publish --access public"
|
|
39
|
+
"publish:npm": "npm publish --access public --registry https://registry.npmjs.org"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@koa/cors": "^5.0.0",
|