@decentnetwork/lan 0.1.97 → 0.1.99
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/bin/tun-helper-darwin-amd64 +0 -0
- package/bin/tun-helper-darwin-arm64 +0 -0
- package/bin/tun-helper-linux-amd64 +0 -0
- package/bin/tun-helper-linux-arm64 +0 -0
- package/dist/carrier/peer-manager.d.ts +11 -0
- package/dist/carrier/peer-manager.js +13 -0
- package/dist/cli/commands.d.ts +10 -0
- package/dist/cli/commands.js +12 -0
- package/dist/cli/index.js +4 -1
- package/dist/console/console.js +1011 -0
- package/dist/daemon/ipc.d.ts +8 -1
- package/dist/daemon/ipc.js +4 -0
- package/dist/daemon/server.js +14 -1
- package/dist/types.d.ts +2 -0
- package/dist/ui/desktop/app.js +32 -6
- package/dist/ui/server.js +9 -1
- package/package.json +7 -5
package/dist/daemon/ipc.d.ts
CHANGED
|
@@ -51,6 +51,11 @@ export interface IpcHandlers {
|
|
|
51
51
|
friendRemove: (userid: string) => Promise<void>;
|
|
52
52
|
/** Set/clear a local display alias for a friend. */
|
|
53
53
|
friendSetAlias: (userid: string, alias?: string) => Promise<void>;
|
|
54
|
+
/** Update our own profile (display name + description); persists + re-pushes. */
|
|
55
|
+
setProfile: (info: {
|
|
56
|
+
name?: string;
|
|
57
|
+
description?: string;
|
|
58
|
+
}) => Promise<void>;
|
|
54
59
|
/** Mark a conversation read up to `ts` (defaults to now) — clears unread. */
|
|
55
60
|
chatMarkRead: (userid: string, ts?: number) => Promise<void>;
|
|
56
61
|
/** Re-read proxy allowlist from config and apply it to the running
|
|
@@ -69,12 +74,14 @@ export interface IpcHandlers {
|
|
|
69
74
|
selfRestart: () => Promise<Record<string, unknown>>;
|
|
70
75
|
}
|
|
71
76
|
export interface IpcRequest {
|
|
72
|
-
op: "friend-request" | "ping" | "diag" | "friends-pending" | "friends-accept" | "friends-reject" | "chat-send" | "chat-history" | "friends-list" | "friend-remove" | "friend-set-alias" | "chat-mark-read" | "proxy-reload" | "self-restart";
|
|
77
|
+
op: "friend-request" | "ping" | "diag" | "friends-pending" | "friends-accept" | "friends-reject" | "chat-send" | "chat-history" | "friends-list" | "friend-remove" | "friend-set-alias" | "set-profile" | "chat-mark-read" | "proxy-reload" | "self-restart";
|
|
73
78
|
address?: string;
|
|
74
79
|
hello?: string;
|
|
75
80
|
userid?: string;
|
|
76
81
|
text?: string;
|
|
77
82
|
alias?: string;
|
|
83
|
+
name?: string;
|
|
84
|
+
description?: string;
|
|
78
85
|
before?: number;
|
|
79
86
|
limit?: number;
|
|
80
87
|
ts?: number;
|
package/dist/daemon/ipc.js
CHANGED
|
@@ -166,6 +166,10 @@ export class IpcServer {
|
|
|
166
166
|
await this.handlers.friendSetAlias(req.userid, req.alias);
|
|
167
167
|
return;
|
|
168
168
|
}
|
|
169
|
+
case "set-profile": {
|
|
170
|
+
await this.handlers.setProfile({ name: req.name, description: req.description });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
169
173
|
case "chat-mark-read": {
|
|
170
174
|
if (!req.userid)
|
|
171
175
|
throw new Error("userid is required");
|
package/dist/daemon/server.js
CHANGED
|
@@ -194,6 +194,7 @@ export class DaemonServer {
|
|
|
194
194
|
// Advertise this node's name so friends see "cn"/"tokyo"/"mac-dev"
|
|
195
195
|
// instead of the generic "@decentnetwork/peer".
|
|
196
196
|
nickname: this.config.node.name,
|
|
197
|
+
statusMessage: this.config.node.statusMessage,
|
|
197
198
|
});
|
|
198
199
|
await this.peerManager.start();
|
|
199
200
|
this.logger.info(`Identity: ${this.peerManager.getAddress()}`);
|
|
@@ -282,6 +283,18 @@ export class DaemonServer {
|
|
|
282
283
|
friendSetAlias: async (userid, alias) => {
|
|
283
284
|
this.friendMeta?.setAlias(userid, alias);
|
|
284
285
|
},
|
|
286
|
+
setProfile: async ({ name, description }) => {
|
|
287
|
+
// Re-push to friends over Carrier (live) …
|
|
288
|
+
this.peerManager?.setUserInfo({ name, description });
|
|
289
|
+
// … and persist so it survives a daemon restart.
|
|
290
|
+
if (typeof name === "string" && name.trim())
|
|
291
|
+
this.config.node.name = name.trim();
|
|
292
|
+
if (typeof description === "string")
|
|
293
|
+
this.config.node.statusMessage = description;
|
|
294
|
+
await ConfigLoader.save(this.config, resolve(this.configDir, "config.yaml")).catch((e) => {
|
|
295
|
+
this.logger.warn(`Failed to persist profile: ${e.message}`);
|
|
296
|
+
});
|
|
297
|
+
},
|
|
285
298
|
chatMarkRead: async (userid, ts) => {
|
|
286
299
|
this.friendMeta?.markRead(userid, ts);
|
|
287
300
|
},
|
|
@@ -365,7 +378,7 @@ export class DaemonServer {
|
|
|
365
378
|
}));
|
|
366
379
|
return {
|
|
367
380
|
identity: this.peerManager?.getIdentity(),
|
|
368
|
-
node: { name: this.config.node.name },
|
|
381
|
+
node: { name: this.config.node.name, statusMessage: this.config.node.statusMessage ?? "" },
|
|
369
382
|
tun: this.tunDevice?.getConfig(),
|
|
370
383
|
allocatedIp: this.doraIntegration?.getAllocatedIp() ?? this.config.network.ip,
|
|
371
384
|
dht: this.peerManager?.getDhtHealth() ?? null,
|
package/dist/types.d.ts
CHANGED
|
@@ -102,6 +102,8 @@ export interface FrameEncodingOptions {
|
|
|
102
102
|
export interface NodeConfig {
|
|
103
103
|
name: string;
|
|
104
104
|
namespace: string;
|
|
105
|
+
/** Optional status-message / short bio shown to friends (Carrier USERINFO descr). */
|
|
106
|
+
statusMessage?: string;
|
|
105
107
|
}
|
|
106
108
|
export interface BootstrapNode {
|
|
107
109
|
host: string;
|
package/dist/ui/desktop/app.js
CHANGED
|
@@ -185,7 +185,8 @@ const dkApi = {
|
|
|
185
185
|
reject: (userid) => dkPost("/api/reject", { userid }),
|
|
186
186
|
remove: (userid) => dkPost("/api/friend-remove", { userid }),
|
|
187
187
|
alias: (userid, alias) => dkPost("/api/friend-alias", { userid, alias }),
|
|
188
|
-
markRead: (userid) => dkPost("/api/chat-mark-read", { userid })
|
|
188
|
+
markRead: (userid) => dkPost("/api/chat-mark-read", { userid }),
|
|
189
|
+
setProfile: (name, description) => dkPost("/api/set-profile", { name, description })
|
|
189
190
|
};
|
|
190
191
|
const DK_ME_FALLBACK = {
|
|
191
192
|
name: "\u2026",
|
|
@@ -1013,7 +1014,7 @@ function Conversation({ T, peer, lang, thread: threadProp, onSend, onAlias, onRe
|
|
|
1013
1014
|
setDraft("");
|
|
1014
1015
|
}
|
|
1015
1016
|
};
|
|
1016
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { height: 60, flexShrink: 0, borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 12, padding: "0 16px", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 34, radius: 8 }), /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: peer.alias ? "var(--ui)" : "var(--mono)", fontSize: 15, fontWeight: 700, color: "var(--text)" } }, peer.alias || shortKey(peer.userId, 10, 6)), peer.agent && /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, "agent"), /* @__PURE__ */ React.createElement(RouteTag, { peer })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), style: { background: "none", border: "none", cursor: "pointer", padding: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 }), " ", peer.ip))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(Btn, { icon: "more", onClick: () => setMenu((v) => !v) }), menu && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { onClick: () => setMenu(false), style: { position: "fixed", inset: 0, zIndex: 40 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", right: 0, top: 36, zIndex: 50, width: 180, background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 9, padding: 6, boxShadow: "0 14px 40px rgba(0,0,0,0.4)" } }, /* @__PURE__ */ React.createElement(MenuItem, { icon: "hash", label: T.alias, onClick: () => {
|
|
1017
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, minHeight: 0, display: "flex", flexDirection: "column", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { height: 60, flexShrink: 0, borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 12, padding: "0 16px", background: "var(--panel)" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer, size: 34, radius: 8 }), /* @__PURE__ */ React.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: peer.alias ? "var(--ui)" : "var(--mono)", fontSize: 15, fontWeight: 700, color: "var(--text)" } }, peer.alias || shortKey(peer.userId, 10, 6)), peer.agent && /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, "agent"), /* @__PURE__ */ React.createElement(RouteTag, { peer })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), style: { background: "none", border: "none", cursor: "pointer", padding: 0, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 }), " ", peer.ip))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(Btn, { icon: "more", onClick: () => setMenu((v) => !v) }), menu && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { onClick: () => setMenu(false), style: { position: "fixed", inset: 0, zIndex: 40 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "absolute", right: 0, top: 36, zIndex: 50, width: 180, background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 9, padding: 6, boxShadow: "0 14px 40px rgba(0,0,0,0.4)" } }, /* @__PURE__ */ React.createElement(MenuItem, { icon: "hash", label: T.alias, onClick: () => {
|
|
1017
1018
|
setMenu(false);
|
|
1018
1019
|
onAlias(peer);
|
|
1019
1020
|
} }), /* @__PURE__ */ React.createElement("div", { style: { height: 1, background: "var(--line)", margin: "5px 4px" } }), /* @__PURE__ */ React.createElement(MenuItem, { icon: "trash", label: T.remove, danger: true, onClick: () => {
|
|
@@ -1058,7 +1059,7 @@ function ChatEmpty({ T }) {
|
|
|
1058
1059
|
}
|
|
1059
1060
|
function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onAlias, onRemove, onOpenNet }) {
|
|
1060
1061
|
const peer = peers.find((p) => p.id === activeId);
|
|
1061
|
-
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", minWidth: 0 } }, /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, thread, onSend, onAlias, onRemove, onOpenNet }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
1062
|
+
return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", minWidth: 0, minHeight: 0 } }, /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, thread, onSend, onAlias, onRemove, onOpenNet }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
|
|
1062
1063
|
}
|
|
1063
1064
|
Object.assign(window, { ChatTab });
|
|
1064
1065
|
function StatTile({ label, value, sub, tone }) {
|
|
@@ -1117,9 +1118,28 @@ function DkQrModal({ value, label, onClose }) {
|
|
|
1117
1118
|
function Card({ label, children, trailing }) {
|
|
1118
1119
|
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));
|
|
1119
1120
|
}
|
|
1120
|
-
function
|
|
1121
|
+
function DkEditModal({ T, me, onClose, onSave }) {
|
|
1122
|
+
const [name, setName] = React.useState(me.name || "");
|
|
1123
|
+
const [desc, setDesc] = React.useState(me.description || "");
|
|
1124
|
+
const save = () => {
|
|
1125
|
+
const n = name.trim();
|
|
1126
|
+
if (n) onSave(n, desc);
|
|
1127
|
+
};
|
|
1128
|
+
const field = { height: 38, borderRadius: 9, border: "1px solid var(--line)", background: "var(--panel-2)", color: "var(--text)", fontFamily: "var(--ui)", fontSize: 13.5, padding: "0 12px", outline: "none" };
|
|
1129
|
+
const lbl = { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 };
|
|
1130
|
+
const onKey = (e) => {
|
|
1131
|
+
if (e.key === "Enter") save();
|
|
1132
|
+
if (e.key === "Escape") onClose();
|
|
1133
|
+
};
|
|
1134
|
+
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: 440, maxWidth: "92vw", background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: 22, display: "flex", flexDirection: "column", gap: 14 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 13, fontWeight: 700, color: "var(--text)" } }, T.editProfile), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Btn, { icon: "x", size: "sm", onClick: onClose })), /* @__PURE__ */ React.createElement("label", { style: { display: "flex", flexDirection: "column", gap: 6 } }, /* @__PURE__ */ React.createElement("span", { style: lbl }, "display name"), /* @__PURE__ */ React.createElement("input", { value: name, onChange: (e) => setName(e.target.value), onKeyDown: onKey, autoFocus: true, maxLength: 48, style: field })), /* @__PURE__ */ React.createElement("label", { style: { display: "flex", flexDirection: "column", gap: 6 } }, /* @__PURE__ */ React.createElement("span", { style: lbl }, "status message"), /* @__PURE__ */ React.createElement("input", { value: desc, onChange: (e) => setDesc(e.target.value), onKeyDown: onKey, maxLength: 120, placeholder: "optional \u2014 a short bio friends will see", style: field })), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)" } }, "Your userid (the unique identity) can't change \u2014 only the display name + status."), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Btn, { size: "sm", onClick: onClose }, T.cancel || "cancel"), /* @__PURE__ */ React.createElement(Btn, { tone: "accent", size: "sm", onClick: save }, T.save || "save"))));
|
|
1135
|
+
}
|
|
1136
|
+
function ProfileTab({ T, me, onEdit }) {
|
|
1121
1137
|
const [qr, setQr] = React.useState(null);
|
|
1122
|
-
|
|
1138
|
+
const [editing, setEditing] = React.useState(false);
|
|
1139
|
+
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) => {
|
|
1140
|
+
if (onEdit) onEdit(name, description);
|
|
1141
|
+
setEditing(false);
|
|
1142
|
+
} }));
|
|
1123
1143
|
}
|
|
1124
1144
|
Object.assign(window, { ProfileTab });
|
|
1125
1145
|
const DK_DEFAULTS = (
|
|
@@ -1360,6 +1380,11 @@ function DkApp() {
|
|
|
1360
1380
|
if (!activeId) return;
|
|
1361
1381
|
data.loadThread(activeId);
|
|
1362
1382
|
dkApi.markRead(activeId).then(data.refresh);
|
|
1383
|
+
const iv = setInterval(() => {
|
|
1384
|
+
data.loadThread(activeId);
|
|
1385
|
+
dkApi.markRead(activeId);
|
|
1386
|
+
}, 2500);
|
|
1387
|
+
return () => clearInterval(iv);
|
|
1363
1388
|
}, [activeId]);
|
|
1364
1389
|
const onSelect = (id) => setActiveId(id);
|
|
1365
1390
|
const onAct = (id, kind) => {
|
|
@@ -1382,6 +1407,7 @@ function DkApp() {
|
|
|
1382
1407
|
const a = window.prompt("Set alias for this peer (empty to clear):", peer.alias || "");
|
|
1383
1408
|
if (a !== null) dkApi.alias(peer.id, a).then(data.refresh);
|
|
1384
1409
|
};
|
|
1410
|
+
const onEdit = (name, description) => dkApi.setProfile(name, description).then(data.refresh);
|
|
1385
1411
|
const onSetExit = () => {
|
|
1386
1412
|
};
|
|
1387
1413
|
const onOpenChat = (id) => {
|
|
@@ -1394,7 +1420,7 @@ function DkApp() {
|
|
|
1394
1420
|
{ id: "network", icon: "network", label: T.network },
|
|
1395
1421
|
{ id: "profile", icon: "userRound", label: T.profile }
|
|
1396
1422
|
];
|
|
1397
|
-
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(RailBtn, { key: n.id, icon: n.icon, label: n.label, active: tab === n.id, soon: n.soon, onClick: () => setTab(n.id) })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 36, radius: 9 }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement("div", { style: { height: 46, flexShrink: 0, borderBottom: "1px solid var(--line)", background: "var(--panel)", display: "flex", alignItems: "center", gap: 12, padding: "0 16px" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700, letterSpacing: -0.3, color: "var(--text)" } }, "decentlan"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "\xB7 ", nav.find((n) => n.id === tab).label.toLowerCase()), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, me.channel, " \xB7 lan ", me.lanVer), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 7, padding: "0 4px" } }, /* @__PURE__ */ React.createElement(StatusDot, { online: me.online }), /* @__PURE__ */ React.createElement(Mono, { size: 12.5, copy: me.ip }, me.ip)), /* @__PURE__ */ React.createElement("span", { style: { width: 1, height: 22, background: "var(--line)" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 26, radius: 7 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, fontWeight: 600, color: "var(--text)" } }, me.name))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, peers, requests, activeId, thread: data.threads[activeId], onSelect, onAct, onAdd, onSend, onAlias, onRemove, onOpenNet }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me })), /* @__PURE__ */ React.createElement(TweaksPanel, null, /* @__PURE__ */ React.createElement(TweakSection, { label: t.lang === "zh" ? "\u5916\u89C2" : "Appearance" }), /* @__PURE__ */ React.createElement(
|
|
1423
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(RailBtn, { key: n.id, icon: n.icon, label: n.label, active: tab === n.id, soon: n.soon, onClick: () => setTab(n.id) })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 36, radius: 9 }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0, minHeight: 0, display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement("div", { style: { height: 46, flexShrink: 0, borderBottom: "1px solid var(--line)", background: "var(--panel)", display: "flex", alignItems: "center", gap: 12, padding: "0 16px" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700, letterSpacing: -0.3, color: "var(--text)" } }, "decentlan"), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "\xB7 ", nav.find((n) => n.id === tab).label.toLowerCase()), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement(Tag, { tone: "accent" }, me.channel, " \xB7 lan ", me.lanVer), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 7, padding: "0 4px" } }, /* @__PURE__ */ React.createElement(StatusDot, { online: me.online }), /* @__PURE__ */ React.createElement(Mono, { size: 12.5, copy: me.ip }, me.ip)), /* @__PURE__ */ React.createElement("span", { style: { width: 1, height: 22, background: "var(--line)" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 26, radius: 7 }), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, fontWeight: 600, color: "var(--text)" } }, me.name))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, peers, requests, activeId, thread: data.threads[activeId], onSelect, onAct, onAdd, onSend, onAlias, onRemove, onOpenNet }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me, onEdit })), /* @__PURE__ */ React.createElement(TweaksPanel, null, /* @__PURE__ */ React.createElement(TweakSection, { label: t.lang === "zh" ? "\u5916\u89C2" : "Appearance" }), /* @__PURE__ */ React.createElement(
|
|
1398
1424
|
TweakRadio,
|
|
1399
1425
|
{
|
|
1400
1426
|
label: t.lang === "zh" ? "\u4E3B\u9898" : "Theme",
|
package/dist/ui/server.js
CHANGED
|
@@ -155,7 +155,9 @@ export function startFriendUi(opts) {
|
|
|
155
155
|
const meExit = DEFAULT_EXITS.find((e) => e.userid && e.userid === identity.userid);
|
|
156
156
|
const me = {
|
|
157
157
|
name: node.name || (identity.userid ?? "").slice(0, 8),
|
|
158
|
-
|
|
158
|
+
// Handle reads as a network address: <name>@decentnetwork.
|
|
159
|
+
handle: `${node.name || "peer"}@decentnetwork`,
|
|
160
|
+
description: node.statusMessage ?? "",
|
|
159
161
|
userId: identity.userid ?? "",
|
|
160
162
|
carrier: identity.address ?? "",
|
|
161
163
|
netKey: identity.userid ?? "",
|
|
@@ -293,6 +295,12 @@ export function startFriendUi(opts) {
|
|
|
293
295
|
sendJson(res, r.ok ? 200 : 400, r);
|
|
294
296
|
return;
|
|
295
297
|
}
|
|
298
|
+
if (req.method === "POST" && url === "/api/set-profile") {
|
|
299
|
+
const { name, description } = await readBody(req);
|
|
300
|
+
const r = await opts.call({ op: "set-profile", name, description });
|
|
301
|
+
sendJson(res, r.ok ? 200 : 400, r);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
296
304
|
if (req.method === "GET" && url === "/api/routes") {
|
|
297
305
|
let routes = { regions: [], default: "direct" };
|
|
298
306
|
if (existsSync(opts.routesPath)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.99",
|
|
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",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
|
-
"build": "tsc -p tsconfig.json && chmod +x dist/cli/index.js && node scripts/build-ui.mjs",
|
|
61
|
+
"build": "tsc -p tsconfig.json && chmod +x dist/cli/index.js && node scripts/build-ui.mjs && node scripts/build-console.mjs",
|
|
62
62
|
"build:ui": "node scripts/build-ui.mjs",
|
|
63
63
|
"build:helper": "cd helper/tun-helper && go build -o ../../bin/tun-helper-$(go env GOOS)-$(go env GOARCH) .",
|
|
64
64
|
"build:helper:linux-amd64": "cd helper/tun-helper && GOOS=linux GOARCH=amd64 go build -o ../../bin/tun-helper-linux-amd64 .",
|
|
@@ -74,12 +74,15 @@
|
|
|
74
74
|
"test:coverage": "vitest --coverage",
|
|
75
75
|
"typecheck": "tsc --noEmit",
|
|
76
76
|
"lint": "eslint src --ext .ts",
|
|
77
|
-
"prepublishOnly": "rm -rf dist && npm run build && npm run typecheck && npm run build:helpers:all"
|
|
77
|
+
"prepublishOnly": "rm -rf dist && npm run build && npm run typecheck && npm run build:helpers:all",
|
|
78
|
+
"build:console": "node scripts/build-console.mjs"
|
|
78
79
|
},
|
|
79
80
|
"dependencies": {
|
|
80
81
|
"@decentnetwork/dora": "^0.1.6",
|
|
81
|
-
"@decentnetwork/peer": "^0.1.
|
|
82
|
+
"@decentnetwork/peer": "^0.1.42",
|
|
83
|
+
"ink": "^5.2.1",
|
|
82
84
|
"js-yaml": "^4.1.0",
|
|
85
|
+
"react": "^18.3.1",
|
|
83
86
|
"yargs": "^17.7.2"
|
|
84
87
|
},
|
|
85
88
|
"devDependencies": {
|
|
@@ -90,7 +93,6 @@
|
|
|
90
93
|
"@typescript-eslint/parser": "^7.10.0",
|
|
91
94
|
"@vitest/coverage-v8": "^1.6.0",
|
|
92
95
|
"eslint": "^8.57.0",
|
|
93
|
-
"react": "^18.3.1",
|
|
94
96
|
"react-dom": "^18.3.1",
|
|
95
97
|
"ts-node": "^10.9.2",
|
|
96
98
|
"typescript": "^5.4.5",
|