@decentnetwork/beagle 0.1.27 → 0.1.28
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.
- package/dist/desktop/app.js +63 -9
- package/dist/server.js +53 -8
- package/package.json +1 -1
package/dist/desktop/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__DK_UI_VERSION="0.1.
|
|
1
|
+
window.__DK_UI_VERSION="0.1.27";
|
|
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 });
|
|
@@ -2960,7 +2965,25 @@ function DkPublicProfile({ T, userid, fallbackName, onClose }) {
|
|
|
2960
2965
|
},
|
|
2961
2966
|
k,
|
|
2962
2967
|
" \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))))))
|
|
2968
|
+
))), 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(
|
|
2969
|
+
"button",
|
|
2970
|
+
{
|
|
2971
|
+
onClick: () => addFriend(prof.address),
|
|
2972
|
+
disabled: addState === "busy",
|
|
2973
|
+
style: {
|
|
2974
|
+
padding: "7px 16px",
|
|
2975
|
+
borderRadius: 9,
|
|
2976
|
+
border: "none",
|
|
2977
|
+
cursor: addState === "busy" ? "default" : "pointer",
|
|
2978
|
+
background: "var(--accent)",
|
|
2979
|
+
color: "#fff",
|
|
2980
|
+
fontFamily: "var(--ui)",
|
|
2981
|
+
fontSize: 12.5,
|
|
2982
|
+
opacity: addState === "busy" ? 0.6 : 1
|
|
2983
|
+
}
|
|
2984
|
+
},
|
|
2985
|
+
addState === "busy" ? "\u2026" : T.dirAdd || "Add"
|
|
2986
|
+
)))));
|
|
2964
2987
|
}
|
|
2965
2988
|
function DkEnsCard({ T, me, onRecord }) {
|
|
2966
2989
|
const [st, setSt] = React.useState({ loading: true });
|
|
@@ -3309,7 +3332,17 @@ function DiscoverTab({ T, kind, peers, meId, onAdd, onOpenChat }) {
|
|
|
3309
3332
|
onOpenChat,
|
|
3310
3333
|
onOpenProfile: (row) => setProf({ userid: row.userid, name: row.name })
|
|
3311
3334
|
}
|
|
3312
|
-
))), prof && /* @__PURE__ */ React.createElement(
|
|
3335
|
+
))), prof && /* @__PURE__ */ React.createElement(
|
|
3336
|
+
DkPublicProfile,
|
|
3337
|
+
{
|
|
3338
|
+
T,
|
|
3339
|
+
userid: prof.userid,
|
|
3340
|
+
fallbackName: prof.name,
|
|
3341
|
+
onClose: () => setProf(null),
|
|
3342
|
+
isFriend: friendIds.has(prof.userid),
|
|
3343
|
+
isMe: !!meId && prof.userid === meId
|
|
3344
|
+
}
|
|
3345
|
+
));
|
|
3313
3346
|
}
|
|
3314
3347
|
const DK_FILE_RTC_KIND = "file";
|
|
3315
3348
|
const DK_FILE_RTC_CHUNK = 16 * 1024;
|
|
@@ -4254,10 +4287,13 @@ const STR = {
|
|
|
4254
4287
|
updRestarting: "Restarting\u2026 this page reloads when it\u2019s back. (If beagle was started by hand in a terminal, run it again.)",
|
|
4255
4288
|
updNpx: "Running via npx \u2014 restarting picks up the latest version automatically.",
|
|
4256
4289
|
updDev: "Running from a source checkout \u2014 update with:",
|
|
4257
|
-
updLanNote: "agentnet (lan)
|
|
4290
|
+
updLanNote: "agentnet (lan): one click reinstalls it and asks the running daemon to restart itself. Manual equivalent:",
|
|
4258
4291
|
updCheck: "check for updates",
|
|
4259
4292
|
updCheckLabel: "updates",
|
|
4260
4293
|
updLatest: "up to date \u2713",
|
|
4294
|
+
updLanNow: "Update lan",
|
|
4295
|
+
updLanDone: "lan updated \u2014 daemon restarted with the new version \u2713",
|
|
4296
|
+
updLanNoRestart: "lan updated, but the daemon was not running (or refused) \u2014 the new version applies on its next start.",
|
|
4261
4297
|
pubOpen: "View public profile",
|
|
4262
4298
|
pubNone: "No public profile \u2014 this user has not registered a beagles.eth name.",
|
|
4263
4299
|
ensNoEthWallet: "no Ethereum wallet extension found (MetaMask\u2026)",
|
|
@@ -4413,10 +4449,13 @@ const STR = {
|
|
|
4413
4449
|
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
4450
|
updNpx: "\u4F60\u662F\u7528 npx \u8FD0\u884C\u7684\u2014\u2014\u91CD\u542F\u5373\u81EA\u52A8\u83B7\u53D6\u6700\u65B0\u7248\u3002",
|
|
4415
4451
|
updDev: "\u5F53\u524D\u4ECE\u6E90\u7801\u76EE\u5F55\u8FD0\u884C,\u8BF7\u624B\u52A8\u66F4\u65B0:",
|
|
4416
|
-
updLanNote: "agentnet(lan)\
|
|
4452
|
+
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
4453
|
updCheck: "\u68C0\u67E5\u66F4\u65B0",
|
|
4418
4454
|
updCheckLabel: "\u66F4\u65B0",
|
|
4419
4455
|
updLatest: "\u5DF2\u662F\u6700\u65B0\u7248\u672C \u2713",
|
|
4456
|
+
updLanNow: "\u66F4\u65B0 lan",
|
|
4457
|
+
updLanDone: "lan \u5DF2\u66F4\u65B0\u2014\u2014daemon \u5DF2\u7528\u65B0\u7248\u91CD\u542F \u2713",
|
|
4458
|
+
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
4459
|
pubOpen: "\u67E5\u770B\u516C\u5F00\u8D44\u6599",
|
|
4421
4460
|
pubNone: "\u6682\u65E0\u516C\u5F00\u8D44\u6599\u2014\u2014\u5BF9\u65B9\u8FD8\u6CA1\u6709\u6CE8\u518C beagles.eth \u540D\u5B57\u3002",
|
|
4422
4461
|
ensNoEthWallet: "\u672A\u68C0\u6D4B\u5230\u4EE5\u592A\u574A\u94B1\u5305\u63D2\u4EF6(MetaMask \u7B49)",
|
|
@@ -4476,7 +4515,7 @@ function DkUpdateModal({ T, info, onClose }) {
|
|
|
4476
4515
|
const doUpdate = () => {
|
|
4477
4516
|
setPhase("running");
|
|
4478
4517
|
setErr(null);
|
|
4479
|
-
fetch("/api/update-run", { method: "POST" }).then((r) => r.json()).then((d) => {
|
|
4518
|
+
fetch("/api/update-run", { method: "POST", headers: { "content-type": "application/json" }, body: "{}" }).then((r) => r.json()).then((d) => {
|
|
4480
4519
|
if (d.ok)
|
|
4481
4520
|
setPhase("done");
|
|
4482
4521
|
else {
|
|
@@ -4488,6 +4527,21 @@ function DkUpdateModal({ T, info, onClose }) {
|
|
|
4488
4527
|
setErr(String(e && e.message || e));
|
|
4489
4528
|
});
|
|
4490
4529
|
};
|
|
4530
|
+
const doLanUpdate = () => {
|
|
4531
|
+
setPhase("running");
|
|
4532
|
+
setErr(null);
|
|
4533
|
+
fetch("/api/update-run", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ pkg: "lan" }) }).then((r) => r.json()).then((d) => {
|
|
4534
|
+
if (d.ok)
|
|
4535
|
+
setPhase(d.restarted ? "lan-done" : "lan-norestart");
|
|
4536
|
+
else {
|
|
4537
|
+
setPhase("error");
|
|
4538
|
+
setErr(d.error || "update failed");
|
|
4539
|
+
}
|
|
4540
|
+
}).catch((e) => {
|
|
4541
|
+
setPhase("error");
|
|
4542
|
+
setErr(String(e && e.message || e));
|
|
4543
|
+
});
|
|
4544
|
+
};
|
|
4491
4545
|
const doRestart = () => {
|
|
4492
4546
|
setPhase("restarting");
|
|
4493
4547
|
fetch("/api/update-restart", { method: "POST" }).catch(() => {
|
|
@@ -4512,7 +4566,7 @@ function DkUpdateModal({ T, info, onClose }) {
|
|
|
4512
4566
|
background: tone ? "var(--accent)" : "transparent",
|
|
4513
4567
|
color: tone ? "#fff" : "var(--text)"
|
|
4514
4568
|
} }, 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)
|
|
4569
|
+
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
4570
|
}
|
|
4517
4571
|
function DkApp() {
|
|
4518
4572
|
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
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
async function
|
|
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", `${
|
|
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
|
-
?
|
|
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:
|
|
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
|
|
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