@bulolo/hermes-link 0.2.3 → 0.2.7

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.
@@ -1529,33 +1529,51 @@ function discoverLanIpsFromInterfaces(interfaces) {
1529
1529
  }
1530
1530
  async function observePublicRoute(options) {
1531
1531
  const fetcher = options.fetchImpl ?? fetch;
1532
- const response = await fetcher(
1533
- `${options.relayBaseUrl.replace(/\/+$/u, "")}/api/v1/relay/public-route/observe`,
1534
- {
1535
- method: "POST",
1536
- headers: {
1537
- "content-type": "application/json",
1538
- ...options.relayBootstrapToken ? { authorization: `Bearer ${options.relayBootstrapToken}` } : {}
1539
- },
1540
- body: JSON.stringify({
1541
- install_id: options.installId,
1542
- link_id: options.linkId,
1543
- public_key_pem: options.publicKeyPem
1544
- })
1545
- }
1546
- );
1547
- const payload = await response.json().catch(() => null);
1548
- const record = typeof payload?.record === "object" && payload.record !== null ? payload.record : null;
1549
- const observed = typeof payload?.observed === "object" && payload.observed !== null ? payload.observed : null;
1550
- const values = [
1551
- readIpRecord(record?.ipv4),
1552
- readIpRecord(record?.ipv6),
1553
- typeof observed?.ip === "string" ? observed.ip : null
1554
- ].filter((v) => Boolean(v));
1555
- return {
1556
- publicIpv4s: unique(values.filter(isUsablePublicIpv4)),
1557
- publicIpv6s: unique(values.filter(isUsablePublicIpv6))
1558
- };
1532
+ const simpleIp = await fetchPublicIpSimple(fetcher).catch(() => null);
1533
+ try {
1534
+ const response = await fetcher(
1535
+ `${options.relayBaseUrl.replace(/\/+$/u, "")}/api/v1/relay/public-route/observe`,
1536
+ {
1537
+ method: "POST",
1538
+ headers: {
1539
+ "content-type": "application/json",
1540
+ ...options.relayBootstrapToken ? { authorization: `Bearer ${options.relayBootstrapToken}` } : {}
1541
+ },
1542
+ body: JSON.stringify({
1543
+ install_id: options.installId,
1544
+ link_id: options.linkId,
1545
+ public_key_pem: options.publicKeyPem
1546
+ }),
1547
+ signal: AbortSignal.timeout(5e3)
1548
+ }
1549
+ );
1550
+ const payload = await response.json().catch(() => null);
1551
+ const record = typeof payload?.record === "object" && payload.record !== null ? payload.record : null;
1552
+ const observed = typeof payload?.observed === "object" && payload.observed !== null ? payload.observed : null;
1553
+ const values = [
1554
+ simpleIp,
1555
+ readIpRecord(record?.ipv4),
1556
+ readIpRecord(record?.ipv6),
1557
+ typeof observed?.ip === "string" ? observed.ip : null
1558
+ ].filter((v) => Boolean(v));
1559
+ return {
1560
+ publicIpv4s: unique(values.filter(isUsablePublicIpv4)),
1561
+ publicIpv6s: unique(values.filter(isUsablePublicIpv6))
1562
+ };
1563
+ } catch {
1564
+ const values = [simpleIp].filter((v) => Boolean(v));
1565
+ return {
1566
+ publicIpv4s: unique(values.filter(isUsablePublicIpv4)),
1567
+ publicIpv6s: []
1568
+ };
1569
+ }
1570
+ }
1571
+ async function fetchPublicIpSimple(fetcher) {
1572
+ const res = await fetcher("https://api.ipify.org?format=json", {
1573
+ signal: AbortSignal.timeout(4e3)
1574
+ });
1575
+ const data = await res.json();
1576
+ return typeof data.ip === "string" && data.ip.trim() ? data.ip.trim() : null;
1559
1577
  }
1560
1578
  function readIpRecord(value) {
1561
1579
  if (typeof value !== "object" || value === null) return null;
@@ -3506,8 +3524,9 @@ async function startLinkService(options) {
3506
3524
  const statsRouter = createStatisticsRouter({ db, paths });
3507
3525
  app.use(statsRouter.routes());
3508
3526
  app.use(statsRouter.allowedMethods());
3527
+ const listenHost = process.env.HERMESLINK_LISTEN_HOST ?? "0.0.0.0";
3509
3528
  const server = await new Promise((resolve, reject) => {
3510
- const s = app.listen(config.port, "127.0.0.1", () => resolve(s));
3529
+ const s = app.listen(config.port, listenHost, () => resolve(s));
3511
3530
  s.once("error", reject);
3512
3531
  });
3513
3532
  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-YARHXGP4.js";
26
+ } from "../chunk-ZN3XW7FI.js";
27
27
 
28
28
  // src/cli/index.ts
29
29
  import { mkdir as mkdir3 } from "fs/promises";
@@ -262,28 +262,30 @@ async function runPairingPreflight(options) {
262
262
  };
263
263
  await mkdir2(options.paths.pairingDir, { recursive: true, mode: 448 }).catch(() => void 0);
264
264
  await writeJsonFile(pairingSessionPath(sessionId, options.paths), session);
265
+ const lanUrls = (routes?.lanIps ?? []).map((ip) => `http://${ip}:${options.config.port}`);
266
+ const publicUrls = (routes?.publicIpv4s ?? []).map((ip) => `http://${ip}:${options.config.port}`);
265
267
  const pairingUrl = buildPairingUrl({
266
268
  linkId: options.identity.link_id ?? "",
267
269
  installId: options.identity.install_id,
268
270
  connectToken: token.token,
269
271
  port: options.config.port,
270
- localApiUrl: bestUrl,
271
- preferredUrls: session.preferred_urls
272
+ lanUrls,
273
+ publicUrls
272
274
  });
273
275
  if (options.openBrowser !== false) {
274
276
  await openSystemBrowser(pairingUrl).catch(() => void 0);
275
277
  }
276
- return { pairingUrl, connectToken: token.token };
278
+ return { pairingUrl, connectToken: token.token, bestUrl };
277
279
  }
278
280
  function buildPairingUrl(params) {
279
281
  const qs = new URLSearchParams({
280
282
  link_id: params.linkId,
281
283
  install_id: params.installId,
282
284
  connect_token: params.connectToken,
283
- port: String(params.port),
284
- local_url: params.localApiUrl,
285
- preferred_urls: params.preferredUrls.join(",")
285
+ port: String(params.port)
286
286
  });
287
+ if (params.lanUrls.length > 0) qs.set("lan_urls", params.lanUrls.join(","));
288
+ if (params.publicUrls.length > 0) qs.set("public_urls", params.publicUrls.join(","));
287
289
  return `hermesapp://pair?${qs.toString()}`;
288
290
  }
289
291
 
@@ -441,10 +443,14 @@ async function cmdPair(paths) {
441
443
  return;
442
444
  }
443
445
  const result = await runPairingPreflight({ identity, config, paths });
446
+ const pageBase = result.bestUrl.replace(/\/+$/u, "");
447
+ const pageUrl = `${pageBase}/pair?connect_token=${encodeURIComponent(result.connectToken)}`;
444
448
  process.stdout.write("\n");
445
449
  qrcode.generate(result.pairingUrl, { small: true });
446
450
  process.stdout.write(`
447
451
  Pairing URL: ${result.pairingUrl}
452
+ `);
453
+ process.stdout.write(`Pairing page: ${pageUrl}
448
454
  `);
449
455
  process.stdout.write(`Connect token: ${result.connectToken}
450
456
  `);
package/dist/http/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startLinkService
3
- } from "../chunk-YARHXGP4.js";
3
+ } from "../chunk-ZN3XW7FI.js";
4
4
  export {
5
5
  startLinkService
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bulolo/hermes-link",
3
- "version": "0.2.3",
3
+ "version": "0.2.7",
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",