@decentnetwork/beagle 0.1.49 → 0.1.51
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/cli.js +44 -40
- package/dist/desktop/app.js +24 -2
- package/dist/embedded-host.d.ts +3 -0
- package/dist/embedded-host.js +4 -1
- package/dist/peer-host.d.ts +4 -0
- package/dist/peer-host.js +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +62 -7
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -50,6 +50,46 @@ const readJsonVer = (file) => {
|
|
|
50
50
|
return "";
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
54
|
+
// Version lookup for the "my node" panel.
|
|
55
|
+
//
|
|
56
|
+
// NOT via createRequire().resolve(): peer and lan declare `exports` with an
|
|
57
|
+
// "import" condition only, so CJS resolution throws
|
|
58
|
+
// ERR_PACKAGE_PATH_NOT_EXPORTED for both the bare specifier and
|
|
59
|
+
// "<pkg>/package.json". (decentlan's own UI has the same call and has been
|
|
60
|
+
// silently showing an empty peer version because of it.) import.meta.resolve
|
|
61
|
+
// uses ESM resolution, which honours that condition.
|
|
62
|
+
const resolveVer = (pkg) => {
|
|
63
|
+
const manifestFrom = (start) => {
|
|
64
|
+
let dir = start;
|
|
65
|
+
for (let depth = 0; depth < 6; depth++) {
|
|
66
|
+
const manifest = join(dir, "package.json");
|
|
67
|
+
if (existsSync(manifest)) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(readFileSync(manifest, "utf-8"));
|
|
70
|
+
if (parsed.name === pkg)
|
|
71
|
+
return parsed.version ?? "";
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// unreadable manifest — keep walking
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const up = dirname(dir);
|
|
78
|
+
if (up === dir)
|
|
79
|
+
break;
|
|
80
|
+
dir = up;
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
return manifestFrom(dirname(fileURLToPath(import.meta.resolve(pkg)))) ?? "";
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Fall back to a plain node_modules lookup beside this package, which
|
|
89
|
+
// also covers the case where the dependency is absent entirely.
|
|
90
|
+
return manifestFrom(join(moduleDir, "..", "node_modules", ...pkg.split("/"))) ?? "";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
53
93
|
async function main() {
|
|
54
94
|
const args = parseArgs(process.argv.slice(2));
|
|
55
95
|
// Bootstrap list: prefer the user's decentlan config so both stacks agree on
|
|
@@ -67,6 +107,7 @@ async function main() {
|
|
|
67
107
|
statusMessage,
|
|
68
108
|
autoAcceptFriends: autoAccept,
|
|
69
109
|
onAutoAcceptChange: (enabled) => saveAutoAccept(args.configDir, enabled),
|
|
110
|
+
peerVersion: resolveVer("@decentnetwork/peer"),
|
|
70
111
|
force: args.backend,
|
|
71
112
|
}));
|
|
72
113
|
}
|
|
@@ -94,46 +135,6 @@ async function main() {
|
|
|
94
135
|
makeDaemon: () => makeDaemonHost(decentlanCarrierDir(args.configDir)),
|
|
95
136
|
onChange: (state) => console.log(`[LAN] backend is now: ${state}`),
|
|
96
137
|
});
|
|
97
|
-
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
98
|
-
// Version lookup for the "my node" panel.
|
|
99
|
-
//
|
|
100
|
-
// NOT via createRequire().resolve(): peer and lan declare `exports` with an
|
|
101
|
-
// "import" condition only, so CJS resolution throws
|
|
102
|
-
// ERR_PACKAGE_PATH_NOT_EXPORTED for both the bare specifier and
|
|
103
|
-
// "<pkg>/package.json". (decentlan's own UI has the same call and has been
|
|
104
|
-
// silently showing an empty peer version because of it.) import.meta.resolve
|
|
105
|
-
// uses ESM resolution, which honours that condition.
|
|
106
|
-
const resolveVer = (pkg) => {
|
|
107
|
-
const manifestFrom = (start) => {
|
|
108
|
-
let dir = start;
|
|
109
|
-
for (let depth = 0; depth < 6; depth++) {
|
|
110
|
-
const manifest = join(dir, "package.json");
|
|
111
|
-
if (existsSync(manifest)) {
|
|
112
|
-
try {
|
|
113
|
-
const parsed = JSON.parse(readFileSync(manifest, "utf-8"));
|
|
114
|
-
if (parsed.name === pkg)
|
|
115
|
-
return parsed.version ?? "";
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
// unreadable manifest — keep walking
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
const up = dirname(dir);
|
|
122
|
-
if (up === dir)
|
|
123
|
-
break;
|
|
124
|
-
dir = up;
|
|
125
|
-
}
|
|
126
|
-
return undefined;
|
|
127
|
-
};
|
|
128
|
-
try {
|
|
129
|
-
return manifestFrom(dirname(fileURLToPath(import.meta.resolve(pkg)))) ?? "";
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
// Fall back to a plain node_modules lookup beside this package, which
|
|
133
|
-
// also covers the case where the dependency is absent entirely.
|
|
134
|
-
return manifestFrom(join(moduleDir, "..", "node_modules", ...pkg.split("/"))) ?? "";
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
138
|
// If this machine also runs a dora, surface its allocation table.
|
|
138
139
|
const doraCandidates = args.doraDir
|
|
139
140
|
? [resolve(args.doraDir, "roster.yaml")]
|
|
@@ -159,6 +160,9 @@ async function main() {
|
|
|
159
160
|
// node look like it was running lan 0.1.0.
|
|
160
161
|
lanVer: resolveVer("@decentnetwork/lan") || "(none)",
|
|
161
162
|
peerVer: resolveVer("@decentnetwork/peer"),
|
|
163
|
+
// The call engine compiled into the served vendor bundle — the version
|
|
164
|
+
// that decides whether a screen share fits the Carrier invite cap.
|
|
165
|
+
webrtcVer: resolveVer("@decentnetwork/peer-webrtc"),
|
|
162
166
|
wire: "163",
|
|
163
167
|
channel: "@next",
|
|
164
168
|
},
|
package/dist/desktop/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__DK_UI_VERSION="0.1.
|
|
1
|
+
window.__DK_UI_VERSION="0.1.51";
|
|
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"/>',
|
|
@@ -2983,6 +2983,20 @@ function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed",
|
|
|
2983
2983
|
function FieldRow({ T, label, value, mono = true, copy, qr, onQr, last }) {
|
|
2984
2984
|
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, padding: "13px 16px", borderBottom: last ? "none" : "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, label), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: mono ? "var(--mono)" : "var(--ui)", fontSize: 13.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, value), copy && /* @__PURE__ */ React.createElement(CopyBtn, { value, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }), qr && /* @__PURE__ */ React.createElement(Btn, { icon: "qr", size: "sm", onClick: () => onQr && onQr(value, label) }));
|
|
2985
2985
|
}
|
|
2986
|
+
function DkVersionRow({ T, me }) {
|
|
2987
|
+
const unsure = me.verFromBackend === false;
|
|
2988
|
+
const pill = (name, ver, mark) => /* @__PURE__ */ React.createElement("span", { key: name, style: { display: "inline-flex", alignItems: "baseline", gap: 5 } }, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)" } }, name), /* @__PURE__ */ React.createElement("span", { title: mark && unsure ? T.verInstalledHint : void 0 }, ver || "\u2014", mark && unsure && ver ? /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)" } }, " ?") : null));
|
|
2989
|
+
const line = (label, items) => /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 52, flexShrink: 0, fontSize: 11, color: "var(--faint)" } }, label), /* @__PURE__ */ React.createElement("span", { style: { display: "flex", gap: 12, flexWrap: "wrap" } }, items));
|
|
2990
|
+
const embedded = me.backend === "embedded";
|
|
2991
|
+
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "flex-start", gap: 14, padding: "13px 16px", borderBottom: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("span", { style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.version), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 6, fontFamily: "var(--mono)", fontSize: 13, color: "var(--text)" } }, line(T.verApp || "app", [
|
|
2992
|
+
pill("beagle", me.beagleVer),
|
|
2993
|
+
pill("peer-webrtc", me.webrtcVer),
|
|
2994
|
+
...embedded ? [pill("peer", me.peerVer)] : []
|
|
2995
|
+
]), !embedded && line(T.verDaemon || "daemon", [
|
|
2996
|
+
pill("lan", me.lanVer, true),
|
|
2997
|
+
pill("peer", me.peerVer, true)
|
|
2998
|
+
]), !embedded && unsure && /* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--faint)", fontFamily: "var(--ui)" } }, T.verInstalledHint), line(T.verChannel || "channel", [/* @__PURE__ */ React.createElement("span", { key: "ch" }, me.channel)])));
|
|
2999
|
+
}
|
|
2986
3000
|
function DkQrModal({ T, value, label, onClose }) {
|
|
2987
3001
|
const qr = React.useMemo(() => {
|
|
2988
3002
|
if (typeof qrcode === "undefined")
|
|
@@ -3472,7 +3486,7 @@ function ProfileTab({ T, me, onEdit }) {
|
|
|
3472
3486
|
},
|
|
3473
3487
|
style: { width: 16, height: 16, accentColor: "var(--accent)" }
|
|
3474
3488
|
}
|
|
3475
|
-
), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, T && T.autoAcceptLabel || "Auto-accept friend requests"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, T && T.autoAcceptSub || "Off: requests wait in the sidebar for your approval.")))), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(
|
|
3489
|
+
), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, T && T.autoAcceptLabel || "Auto-accept friend requests"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, T && T.autoAcceptSub || "Off: requests wait in the sidebar for your approval.")))), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(DkVersionRow, { T, me }), /* @__PURE__ */ React.createElement(DkUpdateCheck, { T })), /* @__PURE__ */ React.createElement(Card, { label: T.dangerZone }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 13, padding: "14px 16px" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 34, height: 34, borderRadius: 8, flexShrink: 0, background: "color-mix(in oklab, var(--danger), transparent 86%)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--danger)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "trash", size: 17, stroke: 2 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--danger)" } }, T.deleteNode), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)", marginTop: 1 } }, T.deleteSub)), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", size: "sm" }, T.delete)))), qr && /* @__PURE__ */ React.createElement(DkQrModal, { T, value: qr.value, label: qr.label, onClose: () => setQr(null) }), editing && /* @__PURE__ */ React.createElement(DkEditModal, { T, me, onClose: () => setEditing(false), onSave: (name, description) => {
|
|
3476
3490
|
if (onEdit)
|
|
3477
3491
|
onEdit(name, description);
|
|
3478
3492
|
setEditing(false);
|
|
@@ -4595,6 +4609,10 @@ const STR = {
|
|
|
4595
4609
|
virtualIp: "virtual ip",
|
|
4596
4610
|
version: "version",
|
|
4597
4611
|
editProfile: "edit",
|
|
4612
|
+
verApp: "app",
|
|
4613
|
+
verDaemon: "daemon",
|
|
4614
|
+
verChannel: "channel",
|
|
4615
|
+
verInstalledHint: "installed on disk \u2014 restart the daemon so it can report what it is actually running",
|
|
4598
4616
|
copy: "Copy",
|
|
4599
4617
|
copied: "Copied",
|
|
4600
4618
|
copyFailed: "Copy failed",
|
|
@@ -4764,6 +4782,10 @@ const STR = {
|
|
|
4764
4782
|
virtualIp: "\u865A\u62DF IP",
|
|
4765
4783
|
version: "\u7248\u672C",
|
|
4766
4784
|
editProfile: "\u7F16\u8F91",
|
|
4785
|
+
verApp: "\u5E94\u7528",
|
|
4786
|
+
verDaemon: "\u5B88\u62A4\u8FDB\u7A0B",
|
|
4787
|
+
verChannel: "\u901A\u9053",
|
|
4788
|
+
verInstalledHint: "\u8FD9\u662F\u78C1\u76D8\u4E0A\u5DF2\u5B89\u88C5\u7684\u7248\u672C\uFF1B\u91CD\u542F\u5B88\u62A4\u8FDB\u7A0B\u540E\u624D\u80FD\u663E\u793A\u5B83\u771F\u6B63\u8FD0\u884C\u7684\u7248\u672C",
|
|
4767
4789
|
copy: "\u590D\u5236",
|
|
4768
4790
|
copied: "\u5DF2\u590D\u5236",
|
|
4769
4791
|
copyFailed: "\u590D\u5236\u5931\u8D25",
|
package/dist/embedded-host.d.ts
CHANGED
|
@@ -24,6 +24,9 @@ export interface EmbeddedHostOptions {
|
|
|
24
24
|
description?: string;
|
|
25
25
|
}) => void;
|
|
26
26
|
onAutoAcceptChange?: (enabled: boolean) => void;
|
|
27
|
+
/** @decentnetwork/peer version backing this embedded node, surfaced by `ping`
|
|
28
|
+
* so the panel can name the code that is actually running. */
|
|
29
|
+
peerVersion?: string;
|
|
27
30
|
}
|
|
28
31
|
export declare class EmbeddedHost implements PeerHost {
|
|
29
32
|
#private;
|
package/dist/embedded-host.js
CHANGED
|
@@ -313,7 +313,10 @@ export class EmbeddedHost {
|
|
|
313
313
|
const uid = (req.userid ?? "");
|
|
314
314
|
switch (req.op) {
|
|
315
315
|
case "ping":
|
|
316
|
-
|
|
316
|
+
// Embedded backend: there is no daemon, so the versions the UI should
|
|
317
|
+
// show are this process's own — reported the same way the daemon
|
|
318
|
+
// reports its, so the panel needs no special case.
|
|
319
|
+
return { ok: true, backend: "embedded", peerVer: this.#opts.peerVersion ?? "" };
|
|
317
320
|
case "diag": {
|
|
318
321
|
const id = this.#node.identity();
|
|
319
322
|
return {
|
package/dist/peer-host.d.ts
CHANGED
|
@@ -37,6 +37,10 @@ export interface OpenPeerHostOptions {
|
|
|
37
37
|
nickname?: string;
|
|
38
38
|
statusMessage?: string;
|
|
39
39
|
autoAcceptFriends?: boolean;
|
|
40
|
+
/** @decentnetwork/peer version, forwarded to an embedded host so its `ping`
|
|
41
|
+
* can name the code actually running. Ignored for a daemon host — the
|
|
42
|
+
* daemon reports its own. */
|
|
43
|
+
peerVersion?: string;
|
|
40
44
|
/** Persist a runtime auto-accept toggle (embedded backend only). */
|
|
41
45
|
onAutoAcceptChange?: (enabled: boolean) => void;
|
|
42
46
|
/** Force a backend instead of auto-detecting. Mainly for testing the
|
package/dist/peer-host.js
CHANGED
|
@@ -98,6 +98,7 @@ export async function openPeerHost(opts) {
|
|
|
98
98
|
statusMessage: opts.statusMessage,
|
|
99
99
|
autoAcceptFriends: opts.autoAcceptFriends ?? true,
|
|
100
100
|
onAutoAcceptChange: opts.onAutoAcceptChange,
|
|
101
|
+
peerVersion: opts.peerVersion,
|
|
101
102
|
});
|
|
102
103
|
await host.start();
|
|
103
104
|
return {
|
package/dist/server.d.ts
CHANGED
package/dist/server.js
CHANGED
|
@@ -206,6 +206,35 @@ function beagleVersion() {
|
|
|
206
206
|
return "";
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Versions of the code the BACKEND process is running, straight from its `ping`.
|
|
211
|
+
*
|
|
212
|
+
* meExtra reads beagle's own node_modules, which describes the packages sitting
|
|
213
|
+
* beside beagle — not the daemon answering our calls. After `npm i -g` but
|
|
214
|
+
* before a daemon restart those disagree, and the panel confidently showed the
|
|
215
|
+
* new version while the old build was still serving every message. Ask the
|
|
216
|
+
* process itself; fall back to meExtra when it is too old to answer.
|
|
217
|
+
*/
|
|
218
|
+
const RUNNING_VER_TTL_MS = 30_000;
|
|
219
|
+
let runningVerCache = null;
|
|
220
|
+
async function runningVersions(call) {
|
|
221
|
+
if (runningVerCache && Date.now() - runningVerCache.at < RUNNING_VER_TTL_MS) {
|
|
222
|
+
return { lanVer: runningVerCache.lanVer, peerVer: runningVerCache.peerVer };
|
|
223
|
+
}
|
|
224
|
+
let lanVer = "";
|
|
225
|
+
let peerVer = "";
|
|
226
|
+
try {
|
|
227
|
+
const r = await call({ op: "ping" });
|
|
228
|
+
const data = (r.ok ? r.data : undefined) ?? {};
|
|
229
|
+
lanVer = String(data.lanVer ?? "");
|
|
230
|
+
peerVer = String(data.peerVer ?? "");
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// Backend down or too old to report — the caller falls back.
|
|
234
|
+
}
|
|
235
|
+
runningVerCache = { at: Date.now(), lanVer, peerVer };
|
|
236
|
+
return { lanVer, peerVer };
|
|
237
|
+
}
|
|
209
238
|
/** How this copy of beagle got onto disk — decides what "update" can do.
|
|
210
239
|
* npx cache paths contain _npx AND node_modules, so test _npx first. */
|
|
211
240
|
function installMode() {
|
|
@@ -326,6 +355,21 @@ async function pickWritableSaveDir(downloadsDir) {
|
|
|
326
355
|
return alt;
|
|
327
356
|
}
|
|
328
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Turn a bare permission error into the one command that fixes it.
|
|
360
|
+
*
|
|
361
|
+
* The daemon runs as root and the files it leaves under ~/.agentnet are
|
|
362
|
+
* root-owned, so this unprivileged process cannot write them — and "EACCES:
|
|
363
|
+
* permission denied, open '/Users/x/.agentnet/downloads/a.jpg'" tells the user
|
|
364
|
+
* nothing about what to do. Daemons from lan 0.1.278 repair this themselves at
|
|
365
|
+
* startup; older ones need the sweep run once by hand.
|
|
366
|
+
*/
|
|
367
|
+
function permissionHint(err) {
|
|
368
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
369
|
+
if (!/EACCES|EPERM|permission denied/i.test(msg))
|
|
370
|
+
return msg;
|
|
371
|
+
return `${msg}\n\nThis is the root-owned ~/.agentnet problem. Fix it once with:\n sudo agentnet fix-perms`;
|
|
372
|
+
}
|
|
329
373
|
/** Resolve a saved file by name across downloads/ and sent/, with a
|
|
330
374
|
* path-traversal guard. Returns null when absent from both. */
|
|
331
375
|
function findSavedFile(downloadsDir, name) {
|
|
@@ -1042,7 +1086,7 @@ export function startBeagleServer(opts) {
|
|
|
1042
1086
|
sendJson(res, 200, { ok: true, ...(r.data ?? {}) });
|
|
1043
1087
|
}
|
|
1044
1088
|
catch (e) {
|
|
1045
|
-
sendJson(res, 500, { ok: false, error:
|
|
1089
|
+
sendJson(res, 500, { ok: false, error: permissionHint(e) });
|
|
1046
1090
|
}
|
|
1047
1091
|
finally {
|
|
1048
1092
|
if (!keep)
|
|
@@ -1309,7 +1353,8 @@ export function startBeagleServer(opts) {
|
|
|
1309
1353
|
npmLatest("@decentnetwork/peer"),
|
|
1310
1354
|
npmLatest("@decentnetwork/lan"),
|
|
1311
1355
|
]);
|
|
1312
|
-
const
|
|
1356
|
+
const running = await runningVersions(opts.call);
|
|
1357
|
+
const curLan = running.lanVer || (opts.meExtra?.lanVer ?? "");
|
|
1313
1358
|
const row = (pkg, current, latest) => ({
|
|
1314
1359
|
pkg,
|
|
1315
1360
|
current,
|
|
@@ -1322,8 +1367,10 @@ export function startBeagleServer(opts) {
|
|
|
1322
1367
|
mode: installMode(),
|
|
1323
1368
|
updates: [
|
|
1324
1369
|
row(BEAGLE_PKG, beagleVersion(), beagleLatest),
|
|
1325
|
-
// peer
|
|
1326
|
-
|
|
1370
|
+
// The peer the BACKEND is running — a daemon that has been updated
|
|
1371
|
+
// on disk but not restarted must still read as behind, or the panel
|
|
1372
|
+
// reports a fix as live while the old build is serving.
|
|
1373
|
+
row("@decentnetwork/peer", running.peerVer || (opts.meExtra?.peerVer ?? ""), peerLatest),
|
|
1327
1374
|
...(curLan && curLan !== "(none)" ? [row("@decentnetwork/lan", curLan, lanLatest)] : []),
|
|
1328
1375
|
],
|
|
1329
1376
|
};
|
|
@@ -1403,11 +1450,12 @@ export function startBeagleServer(opts) {
|
|
|
1403
1450
|
return;
|
|
1404
1451
|
}
|
|
1405
1452
|
if (req.method === "GET" && url === "/api/desktop") {
|
|
1406
|
-
const [diag, pending, flist, ensPub] = await Promise.all([
|
|
1453
|
+
const [diag, pending, flist, ensPub, running] = await Promise.all([
|
|
1407
1454
|
opts.call({ op: "diag" }),
|
|
1408
1455
|
opts.call({ op: "friends-pending" }),
|
|
1409
1456
|
opts.call({ op: "friends-list" }),
|
|
1410
1457
|
ensPublicByUserid(),
|
|
1458
|
+
runningVersions(opts.call),
|
|
1411
1459
|
]);
|
|
1412
1460
|
const d = (diag.ok ? diag.data : {}) ?? {};
|
|
1413
1461
|
const identity = d.identity ?? {};
|
|
@@ -1449,8 +1497,15 @@ export function startBeagleServer(opts) {
|
|
|
1449
1497
|
ip: tun.ip ?? d.allocatedIp ?? "",
|
|
1450
1498
|
online: node.backend === "embedded" ? embeddedOnline : !!tun.ip,
|
|
1451
1499
|
lan: !!tun.ip,
|
|
1452
|
-
|
|
1453
|
-
|
|
1500
|
+
// Prefer what the backend says it is running over what sits in our
|
|
1501
|
+
// node_modules — see `runningVersions`.
|
|
1502
|
+
lanVer: running.lanVer || (opts.meExtra?.lanVer ?? ""),
|
|
1503
|
+
peerVer: running.peerVer || (opts.meExtra?.peerVer ?? ""),
|
|
1504
|
+
webrtcVer: opts.meExtra?.webrtcVer ?? "",
|
|
1505
|
+
backend: String(node.backend ?? "daemon"),
|
|
1506
|
+
// false = the backend is too old to report its own versions, so the
|
|
1507
|
+
// numbers above are what is INSTALLED, which is not the same claim.
|
|
1508
|
+
verFromBackend: !!running.peerVer,
|
|
1454
1509
|
channel: opts.meExtra?.channel ?? "@next",
|
|
1455
1510
|
wire: opts.meExtra?.wire ?? "163",
|
|
1456
1511
|
isExit: !!meExit,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/beagle",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
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",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@decentnetwork/chat-components": "^0.1.3",
|
|
29
|
-
"@decentnetwork/lan": "^0.1.
|
|
29
|
+
"@decentnetwork/lan": "^0.1.278",
|
|
30
30
|
"@decentnetwork/peer": "^0.1.139",
|
|
31
31
|
"@decentnetwork/peer-webrtc": "^0.2.16",
|
|
32
32
|
"js-yaml": "^4.1.0",
|