@decentnetwork/lan 0.1.295 → 0.1.297

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.
@@ -77,6 +77,18 @@ function assertDaemonNotRunning(config, commandName) {
77
77
  * if the socket isn't there / hangs up.
78
78
  */
79
79
  /** Who, if anyone, is holding the identity lock without serving IPC. */
80
+ class IdentityHeldByBeagle extends Error {
81
+ pid;
82
+ command;
83
+ port;
84
+ constructor(pid, command, port) {
85
+ super(`beagle (pid ${pid}) holds this identity on port ${port}`);
86
+ this.pid = pid;
87
+ this.command = command;
88
+ this.port = port;
89
+ this.name = "IdentityHeldByBeagle";
90
+ }
91
+ }
80
92
  function readIdentityHolder(dataDir) {
81
93
  try {
82
94
  const pid = Number.parseInt(readFileSync(join(dataDir, "daemon.pid"), "utf8").trim(), 10);
@@ -89,7 +101,14 @@ function readIdentityHolder(dataDir) {
89
101
  .trim().split("\n")[0]?.slice(0, 90) ?? command;
90
102
  }
91
103
  catch { /* ps is best-effort */ }
92
- return { pid, command };
104
+ let port;
105
+ try {
106
+ const p = Number.parseInt(readFileSync(join(dataDir, "holder.http-port"), "utf8").trim(), 10);
107
+ if (Number.isFinite(p) && p > 0)
108
+ port = p;
109
+ }
110
+ catch { /* older beagle, or none running */ }
111
+ return { pid, command, port };
93
112
  }
94
113
  catch {
95
114
  return undefined;
@@ -109,6 +128,15 @@ async function ipcCall(config, req, timeoutMs = 30_000) {
109
128
  // beagle had been holding the identity for six hours.
110
129
  const holder = readIdentityHolder(config.carrier.dataDir);
111
130
  if (holder) {
131
+ // beagle publishes the port of its own HTTP API next to the lock, so we
132
+ // can finish the job instead of only naming the obstacle. The documented
133
+ // upgrade is `npm i -g @decentnetwork/lan@latest && agentnet restart`,
134
+ // and on a machine where beagle holds the identity the second half had
135
+ // nothing to talk to: the person was left with a new package installed
136
+ // and a process still running the old one, twice in one evening.
137
+ if (holder.port) {
138
+ throw new IdentityHeldByBeagle(holder.pid, holder.command, holder.port);
139
+ }
112
140
  throw new Error(`No daemon here — pid ${holder.pid} holds this identity (${holder.command}). ` +
113
141
  `That is beagle running embedded; restart it from the app, or stop it and then \`agentnet up\`.`);
114
142
  }
@@ -2773,7 +2801,26 @@ export async function cmdRestart(args) {
2773
2801
  if (daemonPid(config) === null) {
2774
2802
  throw new Error("Daemon not running. Start it once with 'agentnet up' (needs sudo for TUN); after that 'agentnet restart' takes care of upgrades without further sudo prompts.");
2775
2803
  }
2776
- const res = await ipcCall(config, { op: "self-restart" });
2804
+ let res;
2805
+ try {
2806
+ res = await ipcCall(config, { op: "self-restart" });
2807
+ }
2808
+ catch (err) {
2809
+ // beagle holds the identity and runs its own HTTP API. Restart THAT — it is
2810
+ // what "restart" means on this machine, and refusing here left the person
2811
+ // with a freshly installed package and a process still running the old one.
2812
+ if (err instanceof IdentityHeldByBeagle) {
2813
+ const r = await fetch(`http://127.0.0.1:${err.port}/api/update-restart`, { method: "POST" })
2814
+ .catch((e) => { throw new Error(`beagle (pid ${err.pid}) is on port ${err.port} but did not answer: ${String(e)}`); });
2815
+ if (!r.ok)
2816
+ throw new Error(`beagle refused the restart: HTTP ${r.status}`);
2817
+ console.log(`No daemon here — beagle holds this identity (pid ${err.pid}). Asked it to restart.`);
2818
+ console.log(` ${err.command}`);
2819
+ console.log("A beagle under launchd/pm2 comes back on its own; one started by hand in a terminal just exits — start it again there.");
2820
+ return;
2821
+ }
2822
+ throw err;
2823
+ }
2777
2824
  if (!res.ok)
2778
2825
  throw new Error(`Daemon refused: ${res.error}`);
2779
2826
  const data = res.data;
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.295";
1
+ window.__DK_UI_VERSION="0.1.297";
2
2
  const ICON_PATHS = {
3
3
  // ---- tab bar (the four must feel like one set) ----
4
4
  users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.295",
3
+ "version": "0.1.297",
4
4
  "description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -84,7 +84,7 @@
84
84
  },
85
85
  "dependencies": {
86
86
  "@decentnetwork/dora": "^0.1.14",
87
- "@decentnetwork/peer": "^0.1.163",
87
+ "@decentnetwork/peer": "^0.1.164",
88
88
  "@decentnetwork/peer-webrtc": "^0.2.10",
89
89
  "ink": "^5.2.1",
90
90
  "js-yaml": "^4.1.0",