@decentnetwork/lan 0.1.200 → 0.1.202
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/commands.js +4 -2
- package/dist/daemon/ipc.d.ts +9 -0
- package/dist/daemon/ipc.js +11 -0
- package/dist/ui/desktop/app.js +42 -5
- package/package.json +1 -1
package/dist/cli/commands.js
CHANGED
|
@@ -7,7 +7,7 @@ import { createConnection } from "net";
|
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
import { ConfigLoader, DEFAULT_DORAS, DEFAULT_EXITS } from "../config/loader.js";
|
|
10
|
-
import { ipcSocketPath } from "../daemon/ipc.js";
|
|
10
|
+
import { ipcSocketPath, ipcSocketSupportsFsExistenceCheck, } from "../daemon/ipc.js";
|
|
11
11
|
import { DaemonServer } from "../daemon/server.js";
|
|
12
12
|
import { Ipam } from "../ipam/ipam.js";
|
|
13
13
|
import { Policy } from "../acl/policy.js";
|
|
@@ -75,7 +75,9 @@ function assertDaemonNotRunning(config, commandName) {
|
|
|
75
75
|
*/
|
|
76
76
|
async function ipcCall(config, req, timeoutMs = 30_000) {
|
|
77
77
|
const sockPath = ipcSocketPath(config.carrier.dataDir);
|
|
78
|
-
|
|
78
|
+
// fs.existsSync works for Unix-domain sockets, but always reports false for
|
|
79
|
+
// Windows named pipes. On Windows, connect directly and trust net's error.
|
|
80
|
+
if (ipcSocketSupportsFsExistenceCheck() && !existsSync(sockPath)) {
|
|
79
81
|
throw new Error(`Daemon socket not found at ${sockPath} — is the daemon running?`);
|
|
80
82
|
}
|
|
81
83
|
return new Promise((resolve, reject) => {
|
package/dist/daemon/ipc.d.ts
CHANGED
|
@@ -154,3 +154,12 @@ export declare class IpcServer {
|
|
|
154
154
|
/** Derive the socket path that pairs with a given carrier data dir.
|
|
155
155
|
* Kept as a one-liner helper so daemon and client agree. */
|
|
156
156
|
export declare function ipcSocketPath(dataDir: string, platform?: NodeJS.Platform): string;
|
|
157
|
+
/** Whether the IPC endpoint can be probed with fs.existsSync().
|
|
158
|
+
*
|
|
159
|
+
* Unix-domain sockets have filesystem entries. Windows named pipes live in
|
|
160
|
+
* the pipe namespace instead: Node can connect to them with net.connect(),
|
|
161
|
+
* but fs.existsSync("\\\\.\\pipe\\...") reports false even while the server is
|
|
162
|
+
* listening. Clients must therefore skip the filesystem preflight on Windows
|
|
163
|
+
* and let net.connect() report a real connection error instead.
|
|
164
|
+
*/
|
|
165
|
+
export declare function ipcSocketSupportsFsExistenceCheck(platform?: NodeJS.Platform): boolean;
|
package/dist/daemon/ipc.js
CHANGED
|
@@ -285,3 +285,14 @@ export function ipcSocketPath(dataDir, platform = process.platform) {
|
|
|
285
285
|
// on macOS; <dataDir>/daemon.sock fits comfortably for sane homedirs.
|
|
286
286
|
return `${dataDir.replace(/\/+$/, "")}/daemon.sock`;
|
|
287
287
|
}
|
|
288
|
+
/** Whether the IPC endpoint can be probed with fs.existsSync().
|
|
289
|
+
*
|
|
290
|
+
* Unix-domain sockets have filesystem entries. Windows named pipes live in
|
|
291
|
+
* the pipe namespace instead: Node can connect to them with net.connect(),
|
|
292
|
+
* but fs.existsSync("\\\\.\\pipe\\...") reports false even while the server is
|
|
293
|
+
* listening. Clients must therefore skip the filesystem preflight on Windows
|
|
294
|
+
* and let net.connect() report a real connection error instead.
|
|
295
|
+
*/
|
|
296
|
+
export function ipcSocketSupportsFsExistenceCheck(platform = process.platform) {
|
|
297
|
+
return platform !== "win32";
|
|
298
|
+
}
|
package/dist/ui/desktop/app.js
CHANGED
|
@@ -1494,10 +1494,41 @@ function NetworkTab({ T, me, peers, exits, activeExit, reqCount, onSetExit, onOp
|
|
|
1494
1494
|
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1040, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 26 } }, /* @__PURE__ */ React.createElement(MyNode, { T, me, activeExit, peers, reqCount }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement(Section, { label: T.peerRouting, count: peers.length }), /* @__PURE__ */ React.createElement(PeerTable, { T, peers, onOpenChat })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement(Section, { label: T.exitNodes, trailing: /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6 } }, /* @__PURE__ */ React.createElement(Btn, { icon: "plus", size: "sm" }, T.addExit)) }), activeExit && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "11px 14px", borderRadius: 10, background: "color-mix(in oklab, var(--warn), transparent 90%)", border: "1px solid color-mix(in oklab, var(--warn), transparent 70%)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "route", size: 17, color: "var(--warn)", stroke: 2 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, T.egressVia), /* @__PURE__ */ React.createElement(Mono, { size: 13, copy: activeExit }, activeExit), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", icon: "unlink", size: "sm", onClick: () => onSetExit(null) }, T.stopRouting)), /* @__PURE__ */ React.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } }, exits.map((r) => /* @__PURE__ */ React.createElement(ExitCard, { key: r.region, T, region: r, activeExit, onSetExit }))))));
|
|
1495
1495
|
}
|
|
1496
1496
|
Object.assign(window, { NetworkTab });
|
|
1497
|
-
function
|
|
1498
|
-
|
|
1497
|
+
function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed", copyTitle = "Copy" }) {
|
|
1498
|
+
const [status, setStatus] = React.useState(null);
|
|
1499
|
+
const timer = React.useRef(null);
|
|
1500
|
+
React.useEffect(() => () => {
|
|
1501
|
+
if (timer.current) clearTimeout(timer.current);
|
|
1502
|
+
}, []);
|
|
1503
|
+
const onCopy = async () => {
|
|
1504
|
+
if (timer.current) clearTimeout(timer.current);
|
|
1505
|
+
setStatus("copied");
|
|
1506
|
+
timer.current = setTimeout(() => setStatus(null), 1400);
|
|
1507
|
+
const ok = await dkCopy(value);
|
|
1508
|
+
if (!ok) {
|
|
1509
|
+
setStatus("failed");
|
|
1510
|
+
if (timer.current) clearTimeout(timer.current);
|
|
1511
|
+
timer.current = setTimeout(() => setStatus(null), 1400);
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
const message = status === "copied" ? copiedText : copyFailedText;
|
|
1515
|
+
return /* @__PURE__ */ React.createElement("span", { "aria-live": "polite", style: { display: "inline-flex", flexShrink: 0 } }, /* @__PURE__ */ React.createElement(
|
|
1516
|
+
Btn,
|
|
1517
|
+
{
|
|
1518
|
+
icon: status === "copied" ? "check" : "copy",
|
|
1519
|
+
tone: status === "copied" ? "ok" : status === "failed" ? "danger" : "ghost",
|
|
1520
|
+
size: "sm",
|
|
1521
|
+
title: status ? message : copyTitle,
|
|
1522
|
+
onClick: onCopy,
|
|
1523
|
+
style: { minWidth: 82 }
|
|
1524
|
+
},
|
|
1525
|
+
status ? message : copyTitle
|
|
1526
|
+
));
|
|
1527
|
+
}
|
|
1528
|
+
function FieldRow({ T, label, value, mono = true, copy, qr, onQr, last }) {
|
|
1529
|
+
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) }));
|
|
1499
1530
|
}
|
|
1500
|
-
function DkQrModal({ value, label, onClose }) {
|
|
1531
|
+
function DkQrModal({ T, value, label, onClose }) {
|
|
1501
1532
|
const qr = React.useMemo(() => {
|
|
1502
1533
|
if (typeof qrcode === "undefined") return null;
|
|
1503
1534
|
try {
|
|
@@ -1517,7 +1548,7 @@ function DkQrModal({ value, label, onClose }) {
|
|
|
1517
1548
|
(_, r) => Array.from({ length: count }).map(
|
|
1518
1549
|
(__, c) => qr.isDark(r, c) ? /* @__PURE__ */ React.createElement("rect", { key: `${r}-${c}`, x: c + quiet, y: r + quiet, width: 1.04, height: 1.04, fill: "#000" }) : null
|
|
1519
1550
|
)
|
|
1520
|
-
)) : /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)", padding: 48 } }, "QR unavailable"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)", wordBreak: "break-all", textAlign: "center", maxWidth: 280, lineHeight: 1.5 } }, value), /* @__PURE__ */ React.createElement(
|
|
1551
|
+
)) : /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)", padding: 48 } }, "QR unavailable"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)", wordBreak: "break-all", textAlign: "center", maxWidth: 280, lineHeight: 1.5 } }, value), /* @__PURE__ */ React.createElement(CopyBtn, { value, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy })));
|
|
1521
1552
|
}
|
|
1522
1553
|
function Card({ label, children, trailing }) {
|
|
1523
1554
|
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 10 } }, /* @__PURE__ */ React.createElement(Section, { label, trailing }), /* @__PURE__ */ React.createElement("div", { style: { borderRadius: 11, border: "1px solid var(--line)", overflow: "hidden", background: "var(--panel)" } }, children));
|
|
@@ -1540,7 +1571,7 @@ function DkEditModal({ T, me, onClose, onSave }) {
|
|
|
1540
1571
|
function ProfileTab({ T, me, onEdit }) {
|
|
1541
1572
|
const [qr, setQr] = React.useState(null);
|
|
1542
1573
|
const [editing, setEditing] = React.useState(false);
|
|
1543
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 24 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 18, padding: "20px 22px", borderRadius: 14, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkIdenticon, { seed: me.userId, size: 68, radius: 16 }), /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", right: -3, bottom: -3, width: 18, height: 18, borderRadius: 999, background: "var(--online)", border: "3px solid var(--panel)" } })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 22, fontWeight: 700, letterSpacing: -0.5, color: "var(--text)" } }, me.name), /* @__PURE__ */ React.createElement(Tag, { tone: "ok" }, "online"), me.isExit && /* @__PURE__ */ React.createElement(Tag, { tone: "warn" }, "exit", me.exitRegion ? ` \xB7 ${me.exitRegion.toUpperCase()}` : "")), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13, color: "var(--dim)", marginTop: 4 } }, me.handle), me.description ? /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 3 } }, me.description) : null), /* @__PURE__ */ React.createElement(Btn, { icon: "edit", onClick: () => setEditing(true) }, T.editProfile)), /* @__PURE__ */ React.createElement(Card, { label: T.identity }, /* @__PURE__ */ React.createElement(FieldRow, { label: T.userId, value: me.userId, copy: true, qr: true, onQr: (v, l) => setQr({ value: v, label: l }) }), /* @__PURE__ */ React.createElement(FieldRow, { label: T.carrierAddr, value: me.carrier, copy: true, qr: true, onQr: (v, l) => setQr({ value: v, label: l }) }), /* @__PURE__ */ React.createElement(FieldRow, { label: T.netKey, value: me.netKey, copy: true, last: true })), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(FieldRow, { label: T.version, value: `lan ${me.lanVer} \xB7 peer ${me.peerVer} \xB7 ${me.channel}`, last: true })), /* @__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, { value: qr.value, label: qr.label, onClose: () => setQr(null) }), editing && /* @__PURE__ */ React.createElement(DkEditModal, { T, me, onClose: () => setEditing(false), onSave: (name, description) => {
|
|
1574
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 24 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 18, padding: "20px 22px", borderRadius: 14, background: "var(--panel)", border: "1px solid var(--line)" } }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkIdenticon, { seed: me.userId, size: 68, radius: 16 }), /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", right: -3, bottom: -3, width: 18, height: 18, borderRadius: 999, background: "var(--online)", border: "3px solid var(--panel)" } })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 22, fontWeight: 700, letterSpacing: -0.5, color: "var(--text)" } }, me.name), /* @__PURE__ */ React.createElement(Tag, { tone: "ok" }, "online"), me.isExit && /* @__PURE__ */ React.createElement(Tag, { tone: "warn" }, "exit", me.exitRegion ? ` \xB7 ${me.exitRegion.toUpperCase()}` : "")), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13, color: "var(--dim)", marginTop: 4 } }, me.handle), me.description ? /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 3 } }, me.description) : null), /* @__PURE__ */ React.createElement(Btn, { icon: "edit", onClick: () => setEditing(true) }, T.editProfile)), /* @__PURE__ */ React.createElement(Card, { label: T.identity }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.userId, value: me.userId, copy: true, qr: true, onQr: (v, l) => setQr({ value: v, label: l }) }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.carrierAddr, value: me.carrier, copy: true, qr: true, onQr: (v, l) => setQr({ value: v, label: l }) }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.netKey, value: me.netKey, copy: true, last: true })), /* @__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(FieldRow, { T, label: T.version, value: `lan ${me.lanVer} \xB7 peer ${me.peerVer} \xB7 ${me.channel}`, last: true })), /* @__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) => {
|
|
1544
1575
|
if (onEdit) onEdit(name, description);
|
|
1545
1576
|
setEditing(false);
|
|
1546
1577
|
} }));
|
|
@@ -1959,6 +1990,9 @@ const STR = {
|
|
|
1959
1990
|
virtualIp: "virtual ip",
|
|
1960
1991
|
version: "version",
|
|
1961
1992
|
editProfile: "edit",
|
|
1993
|
+
copy: "Copy",
|
|
1994
|
+
copied: "Copied",
|
|
1995
|
+
copyFailed: "Copy failed",
|
|
1962
1996
|
dangerZone: "Danger zone",
|
|
1963
1997
|
deleteNode: "Delete this node",
|
|
1964
1998
|
deleteSub: "Permanently remove identity & keys from this device",
|
|
@@ -2036,6 +2070,9 @@ const STR = {
|
|
|
2036
2070
|
virtualIp: "\u865A\u62DF IP",
|
|
2037
2071
|
version: "\u7248\u672C",
|
|
2038
2072
|
editProfile: "\u7F16\u8F91",
|
|
2073
|
+
copy: "\u590D\u5236",
|
|
2074
|
+
copied: "\u5DF2\u590D\u5236",
|
|
2075
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
2039
2076
|
dangerZone: "\u5371\u9669\u64CD\u4F5C",
|
|
2040
2077
|
deleteNode: "\u5220\u9664\u6B64\u8282\u70B9",
|
|
2041
2078
|
deleteSub: "\u4ECE\u672C\u8BBE\u5907\u6C38\u4E45\u79FB\u9664\u8EAB\u4EFD\u4E0E\u5BC6\u94A5",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.202",
|
|
4
4
|
"description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|