@decentnetwork/beagle 0.1.86 → 0.1.87

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.
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.85";
1
+ window.__DK_UI_VERSION="0.1.86";
2
2
  "use strict";
3
3
  (() => {
4
4
  // packages/beagle-ui/dist/beagle-ui.js
@@ -4317,8 +4317,9 @@ ${peer.address}`
4317
4317
  Object.assign(window, { useTweaks });
4318
4318
  function ownershipFix(info) {
4319
4319
  const r = String(info && info.selfUpdateBlocked || "");
4320
- const at = r.indexOf("sudo chown");
4321
- return at >= 0 ? r.slice(at) : 'sudo chown -R $(id -u):$(id -g) "$(npm prefix -g)/lib/node_modules/@decentnetwork"';
4320
+ const at = r.indexOf("sudo npm i -g");
4321
+ const end = at >= 0 ? r.indexOf(".", at) : -1;
4322
+ return at >= 0 ? r.slice(at, end > at ? end : void 0) : "sudo npm i -g @decentnetwork/beagle@latest";
4322
4323
  }
4323
4324
  function dkParseDeepLinkAddress() {
4324
4325
  try {
@@ -4860,7 +4861,7 @@ ${peer.address}`
4860
4861
  lineHeight: 1.6,
4861
4862
  borderLeft: "2px solid var(--danger)",
4862
4863
  paddingLeft: 10
4863
- } }, T.updNoPerm || "Automatic updates cannot write the global npm directory \u2014 this is what a first install with sudo leaves behind. Run this once and it never comes back:", /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6, alignItems: "center", marginTop: 6 } }, /* @__PURE__ */ React.createElement("code", { style: {
4864
+ } }, T.updNoPerm || "This install lives under a system-owned prefix, so updating it is a privileged operation and this button cannot do it. Run it in a terminal:", /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6, alignItems: "center", marginTop: 6 } }, /* @__PURE__ */ React.createElement("code", { style: {
4864
4865
  flex: 1,
4865
4866
  minWidth: 0,
4866
4867
  fontFamily: "var(--mono)",
@@ -4872,7 +4873,7 @@ ${peer.address}`
4872
4873
  padding: "8px 10px",
4873
4874
  userSelect: "all",
4874
4875
  wordBreak: "break-all"
4875
- } }, ownershipFix(info)), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy(ownershipFix(info)) })), /* @__PURE__ */ React.createElement("div", { style: { marginTop: 6, fontSize: 11.5, color: "var(--faint)" } }, T.updNoPermAlt || "It touches only the directory, not the programs inside it. If you would rather not change anything under /usr/local, the alternative is to move npm out of it: `npm config set prefix ~/.npm-global`, then put `~/.npm-global/bin` first on your PATH.")), phase === "idle" && appBehind && info.mode === "dev" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updDev || "Running from a source checkout \u2014 update with:", /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("code", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)" } }, "git pull && npm run build")), phase === "idle" && appBehind && info.mode === "unknown" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updUnknown || "Update now runs the global install. If that is not how this was installed, run:", /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 } }, /* @__PURE__ */ React.createElement("code", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "8px 10px", userSelect: "all", wordBreak: "break-all" } }, "npm i -g @decentnetwork/beagle@latest"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy("npm i -g @decentnetwork/beagle@latest") }))), phase === "idle" && appBehind && info.mode === "npx" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updNpx || "Running via npx \u2014 restarting picks up the latest version automatically."), daemonPeerBehind && phase === "idle" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updPeerDaemon || "The peer version belongs to the running agentnet daemon, not to this app. Update it with:", /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 } }, /* @__PURE__ */ React.createElement("code", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "8px 10px", userSelect: "all", wordBreak: "break-all" } }, "npm i -g @decentnetwork/lan@latest && agentnet restart"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy("npm i -g @decentnetwork/lan@latest && agentnet restart") }))), lanBehind && phase === "idle" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updLanNote || "agentnet (lan): one click reinstalls it and asks the running daemon to restart itself. Manual equivalent:", /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 } }, /* @__PURE__ */ React.createElement("code", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "8px 10px", userSelect: "all", wordBreak: "break-all" } }, "npm i -g @decentnetwork/lan@latest && agentnet restart"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy("npm i -g @decentnetwork/lan@latest && agentnet restart") }))), phase === "lan-done" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--online)" } }, T.updLanDone || "lan updated \u2014 daemon restarted with the new version \u2713"), phase === "lan-norestart" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--warn, #f59e0b)" } }, T.updLanNoRestart || "lan updated, but the daemon was not running (or refused) \u2014 the new version applies on its next start."), phase === "running" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--dim)" } }, T.updBusy || "Updating\u2026 (about a minute)"), phase === "done" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--online)" } }, T.updDone || "Updated \u2014 restart to apply."), phase === "restarting" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--dim)" } }, T.updRestarting || "Restarting\u2026 this page reloads when it\u2019s back. (If beagle was started by hand in a terminal, run it again.)"), phase === "error" && (() => {
4876
+ } }, ownershipFix(info)), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy(ownershipFix(info)) })), /* @__PURE__ */ React.createElement("div", { style: { marginTop: 6, fontSize: 11.5, color: "var(--faint)" } }, T.updNoPermAlt || "Do not widen permissions there to make this button work \u2014 a group-writable bin directory lets any admin user replace programs that root later runs. For one-click updates, move npm out of the system prefix instead: `npm config set prefix ~/.npm-global`, then put `~/.npm-global/bin` first on your PATH.")), phase === "idle" && appBehind && info.mode === "dev" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updDev || "Running from a source checkout \u2014 update with:", /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("code", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)" } }, "git pull && npm run build")), phase === "idle" && appBehind && info.mode === "unknown" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updUnknown || "Update now runs the global install. If that is not how this was installed, run:", /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 } }, /* @__PURE__ */ React.createElement("code", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "8px 10px", userSelect: "all", wordBreak: "break-all" } }, "npm i -g @decentnetwork/beagle@latest"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy("npm i -g @decentnetwork/beagle@latest") }))), phase === "idle" && appBehind && info.mode === "npx" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updNpx || "Running via npx \u2014 restarting picks up the latest version automatically."), daemonPeerBehind && phase === "idle" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updPeerDaemon || "The peer version belongs to the running agentnet daemon, not to this app. Update it with:", /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 } }, /* @__PURE__ */ React.createElement("code", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "8px 10px", userSelect: "all", wordBreak: "break-all" } }, "npm i -g @decentnetwork/lan@latest && agentnet restart"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy("npm i -g @decentnetwork/lan@latest && agentnet restart") }))), lanBehind && phase === "idle" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updLanNote || "agentnet (lan): one click reinstalls it and asks the running daemon to restart itself. Manual equivalent:", /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 } }, /* @__PURE__ */ React.createElement("code", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "8px 10px", userSelect: "all", wordBreak: "break-all" } }, "npm i -g @decentnetwork/lan@latest && agentnet restart"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy("npm i -g @decentnetwork/lan@latest && agentnet restart") }))), phase === "lan-done" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--online)" } }, T.updLanDone || "lan updated \u2014 daemon restarted with the new version \u2713"), phase === "lan-norestart" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--warn, #f59e0b)" } }, T.updLanNoRestart || "lan updated, but the daemon was not running (or refused) \u2014 the new version applies on its next start."), phase === "running" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--dim)" } }, T.updBusy || "Updating\u2026 (about a minute)"), phase === "done" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--online)" } }, T.updDone || "Updated \u2014 restart to apply."), phase === "restarting" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--dim)" } }, T.updRestarting || "Restarting\u2026 this page reloads when it\u2019s back. (If beagle was started by hand in a terminal, run it again.)"), phase === "error" && (() => {
4876
4877
  const m = /`([^`]+)`/.exec(err || "");
4877
4878
  return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--danger)", wordBreak: "break-word" } }, m ? err.replace("`" + m[1] + "`", "") : err), m && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement("code", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "8px 10px", userSelect: "all", wordBreak: "break-all" } }, m[1]), /* @__PURE__ */ React.createElement(Btn, { size: "sm", icon: "copy", title: T.copy || "Copy", onClick: () => dkCopy(m[1]) })), m && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)" } }, T.updManualHint || "Run it in a terminal, then click Restart to apply."));
4878
4879
  })(), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 } }, (phase === "idle" || phase === "error" || phase === "lan-done" || phase === "lan-norestart") && btn(phase === "idle" ? T.updLater || "Later" : T.receivedClose || "Close", onClose), phase === "idle" && lanBehind && btn(T.updLanNow || "Update lan", doLanUpdate, !appBehind), phase === "idle" && appBehind && (info.mode === "global" || info.mode === "unknown") && btn(T.updNow || "Update now", doUpdate, true), phase === "idle" && appBehind && info.mode === "npx" && btn(T.updRestart || "Restart", doRestart, true), phase === "error" && btn(T.updRestart || "Restart", doRestart, true), phase === "done" && btn(T.updRestart || "Restart", doRestart, true))));
package/dist/server.js CHANGED
@@ -350,11 +350,14 @@ async function npmGlobalInstall(pkg) {
350
350
  return { ok: false, error: `npm not found — update manually: npm i -g ${pkg}@latest` };
351
351
  }
352
352
  /**
353
- * Whether this process could write the global npm scope directory.
353
+ * Whether this process could perform a global update at all.
354
354
  *
355
- * Checked up front so the update card can say "you need to run this once"
356
- * rather than offering a button that fails. The check is a real access() on
357
- * the directory npm would have to rename, not a guess from the uid.
355
+ * Checked up front so the card can say what is actually true instead of
356
+ * offering a button that fails. A real access() on each directory npm has to
357
+ * write, not a guess from the uid: the package dir it rewrites, the bin dir it
358
+ * re-links through, and the cache it downloads into. Checking only the first
359
+ * reported "you can update" on a machine that then failed on the bin links,
360
+ * and then again on the cache.
358
361
  */
359
362
  async function globalNpmWritable() {
360
363
  try {
@@ -366,15 +369,8 @@ async function globalNpmWritable() {
366
369
  return { ok: true }; // nothing installed there yet
367
370
  const { access } = await import("node:fs/promises");
368
371
  const { constants } = await import("node:fs");
369
- // BOTH, and both as directories. npm rewrites the package directory and
370
- // re-links the binaries, and either one being unwritable stops the update.
371
- // Checking only the scope reported "you can update" on a machine that then
372
- // failed on the bin links.
373
372
  await access(scope, constants.W_OK);
374
373
  await access(join(prefix, "bin"), constants.W_OK);
375
- // And the cache. A root-owned ~/.npm fails the install just as hard, and
376
- // reporting "you can update" because the other two were fine is the same
377
- // false promise this check exists to stop making.
378
374
  const cache = await npmCacheDir();
379
375
  if (cache && existsSync(cache))
380
376
  await access(cache, constants.W_OK);
@@ -385,49 +381,40 @@ async function globalNpmWritable() {
385
381
  }
386
382
  }
387
383
  /**
388
- * The one-time fix for a global npm dir this user cannot write.
384
+ * What to tell someone whose global npm directory this process cannot write.
389
385
  *
390
- * Almost always the result of a first install done with `sudo npm i -g`: the
391
- * scope directory ends up root-owned, and every later update — including this
392
- * app's own — dies renaming it. Telling people to sudo again works once and
393
- * rebuilds the trap. Hand them the command that ends it instead, with the real
394
- * paths for THIS machine.
386
+ * NOT a permission change. Two earlier versions of this were.
387
+ *
388
+ * The first said `sudo npm i -g`, which works and leaves the package directory
389
+ * root-owned, so the next unprivileged update fails identically — the advice
390
+ * was what kept people in the loop.
391
+ *
392
+ * The second said `chgrp -R admin` plus `chmod -R g+w` on the bin directory.
393
+ * On the machine it was run on that made node, npm, docker and kubectl
394
+ * writable by every admin user, while the daemon runs as root out of that same
395
+ * /usr/local/bin/node — a root escalation traded for a convenience button. The
396
+ * user stopped it before it did any harm, and was right to.
397
+ *
398
+ * A root-owned prefix is not a misconfiguration to repair. It is the machine
399
+ * saying installs there are privileged. So: say the button cannot do it, give
400
+ * the command to run by hand, and name the one change that removes the problem
401
+ * without weakening anything — moving npm's prefix into $HOME.
395
402
  */
396
403
  async function globalNpmOwnershipHint(pkg) {
397
404
  const prefix = await npmGlobalPrefix();
398
405
  if (!prefix) {
399
- return `no write access to the global npm dir. \`sudo npm i -g ${pkg}@latest\` would work but leaves it root-owned, so the next update fails the same way — fix the ownership once instead.`;
406
+ return `no write access to the global npm directory — update in a terminal with \`sudo npm i -g ${pkg}@latest\`.`;
400
407
  }
401
408
  const scope = join(prefix, "lib", "node_modules", "@decentnetwork");
402
- const bin = join(prefix, "bin");
403
- // Both DIRECTORIES, not the files inside them. Replacing a package directory
404
- // or a bin symlink is a rename, and rename needs write permission on the
405
- // CONTAINING directory — naming the symlinks themselves does nothing when
406
- // their parent is root-owned. An earlier version of this hint listed
407
- // bin/agentnet and bin/beagle and left people exactly as stuck, one error
408
- // further along: the scope chown succeeded and npm then failed on the bin
409
- // links instead.
410
409
  const cache = await npmCacheDir();
411
- // Three places, because a sudo install poisons three: the package directory
412
- // it rewrites, the bin directory it re-links through, and the cache it
413
- // downloads into. Fixing them one at a time just moves the error along —
414
- // watched exactly that happen on one machine, three rounds, each ending in a
415
- // different npm message.
416
- //
417
- // The bin line is deliberately narrow, and an earlier version of it was not.
418
- // It said `chgrp -R admin` plus `chmod -R g+w`, which hands every admin user
419
- // write access to every binary in a shared directory — a way to replace
420
- // something another user, or root, will run. Creating or replacing a symlink
421
- // needs write on the CONTAINING directory and nothing else, so: no -R, no
422
- // group bit, no touching the files inside. Mode stays 755 and only this user
423
- // gains write.
424
- //
425
- // The cleaner answer for anyone willing to change their PATH is not to touch
426
- // /usr/local at all — `npm config set prefix ~/.npm-global` and put its bin
427
- // first. That needs more than one line, so the card says it in prose.
428
- return (`no write access to ${scope}. This is what a first install with sudo leaves behind, and ` +
429
- `\`sudo npm i -g\` would rebuild it — run this ONCE and updates never need sudo again: ` +
430
- `sudo chown -R $(id -u):$(id -g) ${scope}${cache ? ` ${cache}` : ""} && sudo chown $(id -u) ${bin}`);
410
+ const cacheNote = cache
411
+ ? ` If npm also complains about its cache, that one IS yours to fix: sudo chown -R $(id -u):$(id -g) ${cache}.`
412
+ : "";
413
+ return (`${scope} belongs to root, so updates here are privileged and this button cannot perform them. ` +
414
+ `Update in a terminal: sudo npm i -g ${pkg}@latest.${cacheNote} ` +
415
+ `For one-click updates instead, move npm out of the system prefix — ` +
416
+ `\`npm config set prefix ~/.npm-global\`, then put ~/.npm-global/bin first on PATH. ` +
417
+ `Do not widen permissions under ${prefix} to make this button work.`);
431
418
  }
432
419
  /** `npm config get cache`, or undefined if npm cannot be reached. */
433
420
  async function npmCacheDir() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.86",
3
+ "version": "0.1.87",
4
4
  "description": "Beagle — P2P chat, file transfer and calls for regular users, on the Decent Network. No admin privilege required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,7 +34,7 @@
34
34
  "yargs": "^17.7.2"
35
35
  },
36
36
  "devDependencies": {
37
- "@decentnetwork/beagle-ui": "^0.2.6",
37
+ "@decentnetwork/beagle-ui": "^0.2.7",
38
38
  "@types/js-yaml": "^4.0.9",
39
39
  "@types/node": "^20.11.0",
40
40
  "@types/yargs": "^17.0.32",