@decentnetwork/beagle 0.1.27 → 0.1.29

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.26";
1
+ window.__DK_UI_VERSION="0.1.28";
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"/>',
@@ -2593,7 +2593,7 @@ ${peer.address}`
2593
2593
  style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain", borderRadius: 6 }
2594
2594
  }
2595
2595
  ))),
2596
- pubProfile && /* @__PURE__ */ React.createElement(DkPublicProfile, { T, userid: peer.userId, fallbackName: peer.alias, onClose: () => setPubProfile(false) })
2596
+ pubProfile && /* @__PURE__ */ React.createElement(DkPublicProfile, { T, userid: peer.userId, fallbackName: peer.alias, isFriend: true, onClose: () => setPubProfile(false) })
2597
2597
  );
2598
2598
  }
2599
2599
  function MenuItem({ icon, label, onClick, danger }) {
@@ -2928,8 +2928,13 @@ function DkPunkPicker({ T, onPick, onClose }) {
2928
2928
  /* @__PURE__ */ React.createElement("img", { src: p.image, alt: `punk #${p.id}`, style: { width: "100%", height: "100%", imageRendering: "pixelated", display: "block" } })
2929
2929
  ))))));
2930
2930
  }
2931
- function DkPublicProfile({ T, userid, fallbackName, onClose }) {
2931
+ function DkPublicProfile({ T, userid, fallbackName, onClose, isFriend, isMe }) {
2932
2932
  const [st, setSt] = React.useState({ loading: true });
2933
+ const [addState, setAddState] = React.useState(null);
2934
+ const addFriend = (address) => {
2935
+ setAddState("busy");
2936
+ fetch("/api/add", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ address }) }).then((r) => r.json()).then((r) => setAddState(r.ok !== false ? "sent" : r.error || T.addFailed || "failed")).catch((e) => setAddState(String(e && e.message || e)));
2937
+ };
2933
2938
  React.useEffect(() => {
2934
2939
  let dead = false;
2935
2940
  setSt({ loading: true });
@@ -2949,7 +2954,9 @@ function DkPublicProfile({ T, userid, fallbackName, onClose }) {
2949
2954
  const links = prof ? Object.entries(prof.links || {}) : [];
2950
2955
  const lbl = { width: 110, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 };
2951
2956
  const rowS = { display: "flex", alignItems: "center", gap: 10, padding: "10px 0", borderTop: "1px solid var(--line)" };
2952
- return /* @__PURE__ */ React.createElement("div", { onClick: onClose, style: { position: "fixed", inset: 0, zIndex: 90, background: "color-mix(in oklab, #000, transparent 38%)", display: "flex", alignItems: "center", justifyContent: "center", padding: 24 } }, /* @__PURE__ */ React.createElement("div", { onClick: (e) => e.stopPropagation(), style: { width: 420, maxWidth: "94vw", background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: "20px 22px", display: "flex", flexDirection: "column", gap: 14 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14 } }, prof && prof.avatarUrl ? /* @__PURE__ */ React.createElement(DkImgAvatar, { url: prof.avatarUrl, seed: userid, size: 56, radius: 14 }) : prof && prof.punkId != null ? /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: prof.punkId, size: 56, radius: 14, fallbackSeed: userid }) : /* @__PURE__ */ React.createElement(DkIdenticon, { seed: userid, size: 56, radius: 14 }), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 17, fontWeight: 700, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, prof && prof.displayName || fallbackName || shortKey(userid, 10, 6)), prof && prof.ens && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--accent)", marginTop: 2 } }, prof.ens)), /* @__PURE__ */ React.createElement(Btn, { icon: "x", size: "sm", onClick: onClose })), st.loading ? /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.dirLoading || "loading\u2026") : !prof ? /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.pubNone || "No public profile \u2014 this user has not registered a beagles.eth name.") : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column" } }, prof.description && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--dim)", paddingBottom: 12, lineHeight: 1.5 } }, prof.description), links.length > 0 && /* @__PURE__ */ React.createElement("div", { style: { ...rowS, gap: 8, flexWrap: "wrap" } }, /* @__PURE__ */ React.createElement("span", { style: lbl }, T.linksCard || "links"), links.map(([k, h]) => /* @__PURE__ */ React.createElement(
2957
+ return /* @__PURE__ */ React.createElement("div", { onClick: onClose, style: { position: "fixed", inset: 0, zIndex: 90, background: "color-mix(in oklab, #000, transparent 38%)", display: "flex", alignItems: "center", justifyContent: "center", padding: 24 } }, /* @__PURE__ */ React.createElement("div", { onClick: (e) => e.stopPropagation(), style: { width: 420, maxWidth: "94vw", background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: "20px 22px", display: "flex", flexDirection: "column", gap: 14 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14 } }, prof && prof.avatarUrl ? /* @__PURE__ */ React.createElement(DkImgAvatar, { url: prof.avatarUrl, seed: userid, size: 56, radius: 14 }) : prof && prof.punkId != null ? /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: prof.punkId, size: 56, radius: 14, fallbackSeed: userid }) : /* @__PURE__ */ React.createElement(DkIdenticon, { seed: userid, size: 56, radius: 14 }), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 17, fontWeight: 700, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, prof && prof.displayName || fallbackName || shortKey(userid, 10, 6)), prof && prof.ens && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--accent)", marginTop: 2 } }, prof.ens)), /* @__PURE__ */ React.createElement(Btn, { icon: "x", size: "sm", onClick: onClose })), st.loading ? /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.dirLoading || "loading\u2026") : !prof ? /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.pubNone || "No public profile \u2014 this user has not registered a beagles.eth name.") : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column" } }, prof.description && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--dim)", paddingBottom: 12, lineHeight: 1.5 } }, prof.description), links.length > 0 && // Label keeps its column; pills wrap inside their OWN container so
2958
+ // an overflowing pill lands under the first pill, never under the label.
2959
+ /* @__PURE__ */ React.createElement("div", { style: { ...rowS, alignItems: "flex-start" } }, /* @__PURE__ */ React.createElement("span", { style: { ...lbl, paddingTop: 5 } }, T.linksCard || "links"), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, display: "flex", gap: 8, flexWrap: "wrap" } }, links.map(([k, h]) => /* @__PURE__ */ React.createElement(
2953
2960
  "a",
2954
2961
  {
2955
2962
  key: k,
@@ -2960,7 +2967,25 @@ function DkPublicProfile({ T, userid, fallbackName, onClose }) {
2960
2967
  },
2961
2968
  k,
2962
2969
  " \u2197"
2963
- ))), prof.eth && /* @__PURE__ */ React.createElement("div", { style: rowS }, /* @__PURE__ */ React.createElement("span", { style: lbl }, "ethereum"), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: prof.eth, title: prof.eth }, shortKey(prof.eth, 8, 6))), prof.sol && /* @__PURE__ */ React.createElement("div", { style: rowS }, /* @__PURE__ */ React.createElement("span", { style: lbl }, "solana"), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: prof.sol, title: prof.sol }, shortKey(prof.sol, 8, 6))), /* @__PURE__ */ React.createElement("div", { style: rowS }, /* @__PURE__ */ React.createElement("span", { style: lbl }, T.userId || "user id"), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: userid, title: userid }, shortKey(userid, 10, 6))))));
2970
+ )))), prof.eth && /* @__PURE__ */ React.createElement("div", { style: rowS }, /* @__PURE__ */ React.createElement("span", { style: lbl }, "ethereum"), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: prof.eth, title: prof.eth }, shortKey(prof.eth, 8, 6))), prof.sol && /* @__PURE__ */ React.createElement("div", { style: rowS }, /* @__PURE__ */ React.createElement("span", { style: lbl }, "solana"), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: prof.sol, title: prof.sol }, shortKey(prof.sol, 8, 6))), /* @__PURE__ */ React.createElement("div", { style: rowS }, /* @__PURE__ */ React.createElement("span", { style: lbl }, T.userId || "user id"), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: userid, title: userid }, shortKey(userid, 10, 6))), !isMe && !isFriend && prof.address && /* @__PURE__ */ React.createElement("div", { style: { ...rowS, justifyContent: "flex-end" } }, addState === "sent" ? /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, T.dirSent || "requested") : addState && addState !== "busy" ? /* @__PURE__ */ React.createElement("span", { title: addState, style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--warn, #f59e0b)" } }, T.addFailed || "failed") : /* @__PURE__ */ React.createElement(
2971
+ "button",
2972
+ {
2973
+ onClick: () => addFriend(prof.address),
2974
+ disabled: addState === "busy",
2975
+ style: {
2976
+ padding: "7px 16px",
2977
+ borderRadius: 9,
2978
+ border: "none",
2979
+ cursor: addState === "busy" ? "default" : "pointer",
2980
+ background: "var(--accent)",
2981
+ color: "#fff",
2982
+ fontFamily: "var(--ui)",
2983
+ fontSize: 12.5,
2984
+ opacity: addState === "busy" ? 0.6 : 1
2985
+ }
2986
+ },
2987
+ addState === "busy" ? "\u2026" : T.dirAdd || "Add"
2988
+ )))));
2964
2989
  }
2965
2990
  function DkEnsCard({ T, me, onRecord }) {
2966
2991
  const [st, setSt] = React.useState({ loading: true });
@@ -3309,7 +3334,17 @@ function DiscoverTab({ T, kind, peers, meId, onAdd, onOpenChat }) {
3309
3334
  onOpenChat,
3310
3335
  onOpenProfile: (row) => setProf({ userid: row.userid, name: row.name })
3311
3336
  }
3312
- ))), prof && /* @__PURE__ */ React.createElement(DkPublicProfile, { T, userid: prof.userid, fallbackName: prof.name, onClose: () => setProf(null) }));
3337
+ ))), prof && /* @__PURE__ */ React.createElement(
3338
+ DkPublicProfile,
3339
+ {
3340
+ T,
3341
+ userid: prof.userid,
3342
+ fallbackName: prof.name,
3343
+ onClose: () => setProf(null),
3344
+ isFriend: friendIds.has(prof.userid),
3345
+ isMe: !!meId && prof.userid === meId
3346
+ }
3347
+ ));
3313
3348
  }
3314
3349
  const DK_FILE_RTC_KIND = "file";
3315
3350
  const DK_FILE_RTC_CHUNK = 16 * 1024;
@@ -4254,10 +4289,13 @@ const STR = {
4254
4289
  updRestarting: "Restarting\u2026 this page reloads when it\u2019s back. (If beagle was started by hand in a terminal, run it again.)",
4255
4290
  updNpx: "Running via npx \u2014 restarting picks up the latest version automatically.",
4256
4291
  updDev: "Running from a source checkout \u2014 update with:",
4257
- updLanNote: "agentnet (lan) updates separately; the daemon restart is manual:",
4292
+ updLanNote: "agentnet (lan): one click reinstalls it and asks the running daemon to restart itself. Manual equivalent:",
4258
4293
  updCheck: "check for updates",
4259
4294
  updCheckLabel: "updates",
4260
4295
  updLatest: "up to date \u2713",
4296
+ updLanNow: "Update lan",
4297
+ updLanDone: "lan updated \u2014 daemon restarted with the new version \u2713",
4298
+ updLanNoRestart: "lan updated, but the daemon was not running (or refused) \u2014 the new version applies on its next start.",
4261
4299
  pubOpen: "View public profile",
4262
4300
  pubNone: "No public profile \u2014 this user has not registered a beagles.eth name.",
4263
4301
  ensNoEthWallet: "no Ethereum wallet extension found (MetaMask\u2026)",
@@ -4413,10 +4451,13 @@ const STR = {
4413
4451
  updRestarting: "\u6B63\u5728\u91CD\u542F\u2026\u5B8C\u6210\u540E\u672C\u9875\u4F1A\u81EA\u52A8\u5237\u65B0\u3002(\u5982\u679C beagle \u662F\u5728\u7EC8\u7AEF\u91CC\u624B\u52A8\u542F\u52A8\u7684,\u8BF7\u91CD\u65B0\u8FD0\u884C\u4E00\u6B21\u3002)",
4414
4452
  updNpx: "\u4F60\u662F\u7528 npx \u8FD0\u884C\u7684\u2014\u2014\u91CD\u542F\u5373\u81EA\u52A8\u83B7\u53D6\u6700\u65B0\u7248\u3002",
4415
4453
  updDev: "\u5F53\u524D\u4ECE\u6E90\u7801\u76EE\u5F55\u8FD0\u884C,\u8BF7\u624B\u52A8\u66F4\u65B0:",
4416
- updLanNote: "agentnet(lan)\u9700\u5355\u72EC\u5347\u7EA7,daemon \u91CD\u542F\u9700\u624B\u52A8\u6267\u884C:",
4454
+ updLanNote: "agentnet(lan):\u4E00\u952E\u91CD\u88C5\u5E76\u8BA9\u8FD0\u884C\u4E2D\u7684 daemon \u81EA\u6211\u91CD\u542F\u3002\u7B49\u4EF7\u624B\u52A8\u547D\u4EE4:",
4417
4455
  updCheck: "\u68C0\u67E5\u66F4\u65B0",
4418
4456
  updCheckLabel: "\u66F4\u65B0",
4419
4457
  updLatest: "\u5DF2\u662F\u6700\u65B0\u7248\u672C \u2713",
4458
+ updLanNow: "\u66F4\u65B0 lan",
4459
+ updLanDone: "lan \u5DF2\u66F4\u65B0\u2014\u2014daemon \u5DF2\u7528\u65B0\u7248\u91CD\u542F \u2713",
4460
+ updLanNoRestart: "lan \u5DF2\u66F4\u65B0,\u4F46 daemon \u672A\u5728\u8FD0\u884C(\u6216\u91CD\u542F\u88AB\u62D2)\u2014\u2014\u4E0B\u6B21\u542F\u52A8\u65F6\u751F\u6548\u3002",
4420
4461
  pubOpen: "\u67E5\u770B\u516C\u5F00\u8D44\u6599",
4421
4462
  pubNone: "\u6682\u65E0\u516C\u5F00\u8D44\u6599\u2014\u2014\u5BF9\u65B9\u8FD8\u6CA1\u6709\u6CE8\u518C beagles.eth \u540D\u5B57\u3002",
4422
4463
  ensNoEthWallet: "\u672A\u68C0\u6D4B\u5230\u4EE5\u592A\u574A\u94B1\u5305\u63D2\u4EF6(MetaMask \u7B49)",
@@ -4476,7 +4517,7 @@ function DkUpdateModal({ T, info, onClose }) {
4476
4517
  const doUpdate = () => {
4477
4518
  setPhase("running");
4478
4519
  setErr(null);
4479
- fetch("/api/update-run", { method: "POST" }).then((r) => r.json()).then((d) => {
4520
+ fetch("/api/update-run", { method: "POST", headers: { "content-type": "application/json" }, body: "{}" }).then((r) => r.json()).then((d) => {
4480
4521
  if (d.ok)
4481
4522
  setPhase("done");
4482
4523
  else {
@@ -4488,6 +4529,21 @@ function DkUpdateModal({ T, info, onClose }) {
4488
4529
  setErr(String(e && e.message || e));
4489
4530
  });
4490
4531
  };
4532
+ const doLanUpdate = () => {
4533
+ setPhase("running");
4534
+ setErr(null);
4535
+ fetch("/api/update-run", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ pkg: "lan" }) }).then((r) => r.json()).then((d) => {
4536
+ if (d.ok)
4537
+ setPhase(d.restarted ? "lan-done" : "lan-norestart");
4538
+ else {
4539
+ setPhase("error");
4540
+ setErr(d.error || "update failed");
4541
+ }
4542
+ }).catch((e) => {
4543
+ setPhase("error");
4544
+ setErr(String(e && e.message || e));
4545
+ });
4546
+ };
4491
4547
  const doRestart = () => {
4492
4548
  setPhase("restarting");
4493
4549
  fetch("/api/update-restart", { method: "POST" }).catch(() => {
@@ -4512,7 +4568,7 @@ function DkUpdateModal({ T, info, onClose }) {
4512
4568
  background: tone ? "var(--accent)" : "transparent",
4513
4569
  color: tone ? "#fff" : "var(--text)"
4514
4570
  } }, label);
4515
- return /* @__PURE__ */ React.createElement("div", { style: { position: "fixed", inset: 0, zIndex: 95, background: "color-mix(in oklab, #000, transparent 38%)", display: "flex", alignItems: "center", justifyContent: "center", padding: 24 } }, /* @__PURE__ */ React.createElement("div", { style: { width: 430, maxWidth: "94vw", background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: "20px 22px", display: "flex", flexDirection: "column", gap: 14 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 700, color: "var(--text)" } }, T.updTitle || "Update available"), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 6 } }, behind.map((u) => /* @__PURE__ */ React.createElement("div", { key: u.pkg, style: { display: "flex", alignItems: "center", gap: 10, fontFamily: "var(--mono)", fontSize: 12.5 } }, /* @__PURE__ */ React.createElement("span", { style: { width: 70, color: "var(--faint)" } }, short(u.pkg)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--dim)" } }, u.current), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)" } }, "\u2192"), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--accent)", fontWeight: 700 } }, u.latest)))), 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 === "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."), lanBehind && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--dim)", lineHeight: 1.5 } }, T.updLanNote || "agentnet (lan) updates separately; the daemon restart is manual:", /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("code", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", userSelect: "all" } }, "npm i -g @decentnetwork/lan@latest && agentnet restart")), 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" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--danger)", wordBreak: "break-word" } }, err), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 } }, (phase === "idle" || phase === "error") && btn(T.updLater || "Later", onClose), phase === "idle" && appBehind && info.mode === "global" && btn(T.updNow || "Update now", doUpdate, true), phase === "idle" && appBehind && info.mode === "npx" && btn(T.updRestart || "Restart", doRestart, true), phase === "done" && btn(T.updRestart || "Restart", doRestart, true))));
4571
+ return /* @__PURE__ */ React.createElement("div", { style: { position: "fixed", inset: 0, zIndex: 95, background: "color-mix(in oklab, #000, transparent 38%)", display: "flex", alignItems: "center", justifyContent: "center", padding: 24 } }, /* @__PURE__ */ React.createElement("div", { style: { width: 430, maxWidth: "94vw", background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: "20px 22px", display: "flex", flexDirection: "column", gap: 14 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 700, color: "var(--text)" } }, T.updTitle || "Update available"), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 6 } }, behind.map((u) => /* @__PURE__ */ React.createElement("div", { key: u.pkg, style: { display: "flex", alignItems: "center", gap: 10, fontFamily: "var(--mono)", fontSize: 12.5 } }, /* @__PURE__ */ React.createElement("span", { style: { width: 70, color: "var(--faint)" } }, short(u.pkg)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--dim)" } }, u.current), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)" } }, "\u2192"), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--accent)", fontWeight: 700 } }, u.latest)))), 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 === "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."), 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("br", null), /* @__PURE__ */ React.createElement("code", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--text)", userSelect: "all" } }, "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" && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--danger)", wordBreak: "break-word" } }, err), /* @__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" && btn(T.updNow || "Update now", doUpdate, true), phase === "idle" && appBehind && info.mode === "npx" && btn(T.updRestart || "Restart", doRestart, true), phase === "done" && btn(T.updRestart || "Restart", doRestart, true))));
4516
4572
  }
4517
4573
  function DkApp() {
4518
4574
  const [t, setTweak] = useTweaks(DK_DEFAULTS);
package/dist/server.js CHANGED
@@ -158,6 +158,8 @@ function ensPubFromEntry(ens, v) {
158
158
  const t = v?.texts ?? {};
159
159
  return {
160
160
  ens,
161
+ // Shareable Carrier address — lets the profile card offer add-friend.
162
+ address: t.carrierAddress || null,
161
163
  avatarUrl: t.avatar || null,
162
164
  punkId: v?.nft === "CryptoPunks" && typeof v?.nftid === "number" && v.nftid > 0 ? v.nftid : null,
163
165
  displayName: t.displayName || null,
@@ -241,12 +243,12 @@ function verLt(a, b) {
241
243
  }
242
244
  return false;
243
245
  }
244
- /** Run `npm i -g @decentnetwork/beagle@latest`. npm is found on PATH, else via
245
- * npm-cli.js next to the running node (covers pm2/cron environments whose
246
- * PATH lacks the nvm shims). */
247
- async function npmSelfUpdate() {
246
+ /** Run `npm i -g <pkg>@latest`. npm is found on PATH, else via npm-cli.js
247
+ * next to the running node (covers pm2/cron environments whose PATH lacks
248
+ * the nvm shims). */
249
+ async function npmGlobalInstall(pkg) {
248
250
  const { spawn } = await import("node:child_process");
249
- const args = ["i", "-g", `${BEAGLE_PKG}@latest`];
251
+ const args = ["i", "-g", `${pkg}@latest`];
250
252
  const npmCli = join(dirname(process.execPath), "..", "lib", "node_modules", "npm", "bin", "npm-cli.js");
251
253
  const attempts = [
252
254
  { cmd: "npm", args },
@@ -268,11 +270,40 @@ async function npmSelfUpdate() {
268
270
  if (r.code === 0)
269
271
  return { ok: true, log: tail };
270
272
  const hint = /EACCES|permission denied/i.test(r.out)
271
- ? " — no write access to the global npm dir; run `sudo npm i -g @decentnetwork/beagle@latest` manually"
273
+ ? ` — no write access to the global npm dir; run \`sudo npm i -g ${pkg}@latest\` manually`
272
274
  : "";
273
275
  return { ok: false, error: `npm exited ${r.code}${hint}`, log: tail };
274
276
  }
275
- return { ok: false, error: "npm not found — update manually: npm i -g @decentnetwork/beagle@latest" };
277
+ return { ok: false, error: `npm not found — update manually: npm i -g ${pkg}@latest` };
278
+ }
279
+ /** Ask the running agentnet daemon to re-exec itself so it picks up the
280
+ * just-installed lan version. Tries the PATH binary, then the global
281
+ * install's CLI entry directly (pm2 PATH often lacks the nvm shims). */
282
+ async function agentnetRestart() {
283
+ const { spawn } = await import("node:child_process");
284
+ const cliJs = join(dirname(process.execPath), "..", "lib", "node_modules", "@decentnetwork", "lan", "dist", "cli", "index.js");
285
+ const attempts = [
286
+ { cmd: "agentnet", args: ["restart"] },
287
+ ...(existsSync(cliJs) ? [{ cmd: process.execPath, args: [cliJs, "restart"] }] : []),
288
+ ];
289
+ let lastErr = "agentnet CLI not found";
290
+ for (const a of attempts) {
291
+ const r = await new Promise((resolveRun) => {
292
+ const ch = spawn(a.cmd, a.args, { shell: process.platform === "win32" });
293
+ let out = "";
294
+ const timer = setTimeout(() => ch.kill("SIGKILL"), 60_000);
295
+ ch.stdout?.on("data", (c) => (out += c));
296
+ ch.stderr?.on("data", (c) => (out += c));
297
+ ch.on("error", (e) => { clearTimeout(timer); resolveRun({ code: null, out, spawnErr: e.message }); });
298
+ ch.on("close", (code) => { clearTimeout(timer); resolveRun({ code, out }); });
299
+ });
300
+ if (r.spawnErr)
301
+ continue;
302
+ if (r.code === 0)
303
+ return { ok: true };
304
+ lastErr = r.out.slice(-400) || `agentnet restart exited ${r.code}`;
305
+ }
306
+ return { ok: false, error: lastErr };
276
307
  }
277
308
  /** True only when the request came from the local machine. Used to gate the
278
309
  * "Sign in with Decent" routes so binding the UI to a LAN IP can't expose
@@ -1251,6 +1282,20 @@ export function startBeagleServer(opts) {
1251
1282
  sendJson(res, 403, { ok: false, error: "update runs only from this machine (localhost)" });
1252
1283
  return;
1253
1284
  }
1285
+ const body = await readBody(req);
1286
+ // lan (the agentnet daemon) updates independently of beagle's own
1287
+ // install mode: global reinstall, then ask the daemon to re-exec.
1288
+ if (body.pkg === "lan") {
1289
+ const inst = await npmGlobalInstall("@decentnetwork/lan");
1290
+ if (!inst.ok) {
1291
+ sendJson(res, 502, inst);
1292
+ return;
1293
+ }
1294
+ const rr = await agentnetRestart();
1295
+ updateCache = null;
1296
+ sendJson(res, 200, { ok: true, restarted: rr.ok, ...(rr.ok ? {} : { note: rr.error }) });
1297
+ return;
1298
+ }
1254
1299
  const mode = installMode();
1255
1300
  if (mode === "dev") {
1256
1301
  sendJson(res, 400, { ok: false, error: "running from a source checkout — update with: git pull && npm run build" });
@@ -1261,7 +1306,7 @@ export function startBeagleServer(opts) {
1261
1306
  sendJson(res, 200, { ok: true, npx: true });
1262
1307
  return;
1263
1308
  }
1264
- const r = await npmSelfUpdate();
1309
+ const r = await npmGlobalInstall(BEAGLE_PKG);
1265
1310
  updateCache = null; // next check reflects the new install
1266
1311
  sendJson(res, r.ok ? 200 : 502, r);
1267
1312
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
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",