@decentnetwork/lan 0.1.224 → 0.1.226

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.
@@ -41,6 +41,9 @@ export interface IpcHandlers {
41
41
  friendsReject: (userid: string) => Promise<void>;
42
42
  /** Send a chat message (Carrier text, packet 64) to a friend and log it. */
43
43
  chatSend: (userid: string, text: string) => Promise<void>;
44
+ /** Append a local-only chat history entry. Used for browser-local events such
45
+ * as WebRTC file receive; does not transmit anything to the peer. */
46
+ chatLogLocal: (userid: string, dir: "in" | "out", text: string) => Promise<void>;
44
47
  /** Return persisted chat history. With `peer`, returns just that thread and
45
48
  * honours `before`/`limit` pagination; without, every thread (legacy shape). */
46
49
  chatHistory: (peer?: string, before?: number, limit?: number) => Promise<Record<string, unknown>>;
@@ -115,11 +118,12 @@ export interface IpcHandlers {
115
118
  selfRestart: () => Promise<Record<string, unknown>>;
116
119
  }
117
120
  export interface IpcRequest {
118
- op: "friend-request" | "ping" | "diag" | "friends-pending" | "friends-accept" | "friends-reject" | "chat-send" | "chat-history" | "friends-list" | "friend-remove" | "friend-set-alias" | "friends-autoaccept" | "set-profile" | "file-send" | "file-delete" | "file-cancel" | "file-retry" | "chat-mark-read" | "subscribe" | "sign" | "call-signal" | "call-poll" | "proxy-reload" | "proxy-access" | "self-restart";
121
+ op: "friend-request" | "ping" | "diag" | "friends-pending" | "friends-accept" | "friends-reject" | "chat-send" | "chat-log-local" | "chat-history" | "friends-list" | "friend-remove" | "friend-set-alias" | "friends-autoaccept" | "set-profile" | "file-send" | "file-delete" | "file-cancel" | "file-retry" | "chat-mark-read" | "subscribe" | "sign" | "call-signal" | "call-poll" | "proxy-reload" | "proxy-access" | "self-restart";
119
122
  address?: string;
120
123
  hello?: string;
121
124
  userid?: string;
122
125
  text?: string;
126
+ dir?: "in" | "out";
123
127
  alias?: string;
124
128
  name?: string;
125
129
  description?: string;
@@ -196,6 +196,12 @@ export class IpcServer {
196
196
  await this.handlers.chatSend(req.userid, req.text ?? "");
197
197
  return;
198
198
  }
199
+ case "chat-log-local": {
200
+ if (!req.userid)
201
+ throw new Error("userid is required");
202
+ await this.handlers.chatLogLocal(req.userid, req.dir === "out" ? "out" : "in", req.text ?? "");
203
+ return;
204
+ }
199
205
  case "chat-history":
200
206
  return await this.handlers.chatHistory(req.userid, req.before, req.limit);
201
207
  case "friends-list":
@@ -381,6 +381,11 @@ export class DaemonServer {
381
381
  this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
382
382
  this.logger.info(`Queued text for offline ${userid.slice(0, 8)} (delivers on reconnect)`);
383
383
  },
384
+ chatLogLocal: async (userid, dir, text) => {
385
+ if (!text)
386
+ return;
387
+ this.logChat(userid, dir, text);
388
+ },
384
389
  chatHistory: async (peer, before, limit) => {
385
390
  return { chats: this.messageStore?.history(peer, { before, limit }) ?? {} };
386
391
  },
@@ -220,6 +220,7 @@ function dkCopyLegacy(s) {
220
220
  }
221
221
  const dkApi = {
222
222
  send: (userid, text) => dkPost("/api/chat-send", { userid, text }),
223
+ logLocal: (userid, dir, text) => dkPost("/api/chat-log-local", { userid, dir, text }),
223
224
  add: (address) => dkPost("/api/add", { address }),
224
225
  accept: (userid) => dkPost("/api/accept", { userid }),
225
226
  reject: (userid) => dkPost("/api/reject", { userid }),
@@ -1949,6 +1950,10 @@ function dkFileDownload(blob, name) {
1949
1950
  function useRtcFileController(selfId, onReceivedText) {
1950
1951
  const [incoming, setIncoming] = React.useState([]);
1951
1952
  const peersRef = React.useRef(/* @__PURE__ */ new Map());
1953
+ const onReceivedTextRef = React.useRef(onReceivedText);
1954
+ React.useEffect(() => {
1955
+ onReceivedTextRef.current = onReceivedText;
1956
+ }, [onReceivedText]);
1952
1957
  React.useEffect(() => {
1953
1958
  if (!selfId || !window.RTCPeerConnection || !window.dkRtcSignalBus) return;
1954
1959
  const bus = dkRtcSignalBus();
@@ -1988,7 +1993,7 @@ function useRtcFileController(selfId, onReceivedText) {
1988
1993
  const blob = new Blob(sess.chunks, { type: meta.mime || "application/octet-stream" });
1989
1994
  const dl = dkFileDownload(blob, meta.name || "agentnet-file");
1990
1995
  setIncoming((arr) => [{ id: fileId, peerId, name: dl.name, size: blob.size, url: dl.url }].concat(arr).slice(0, 8));
1991
- if (onReceivedText) onReceivedText(peerId, "received file via WebRTC: " + dl.name + " (" + dkFileSize(blob.size) + ")");
1996
+ if (onReceivedTextRef.current) onReceivedTextRef.current(peerId, "received file via WebRTC: " + dl.name + " (" + dkFileSize(blob.size) + ")");
1992
1997
  cleanup(fileId);
1993
1998
  } else if (msg.type === "cancel") {
1994
1999
  cleanup(fileId);
@@ -2070,7 +2075,7 @@ function useRtcFileController(selfId, onReceivedText) {
2070
2075
  off();
2071
2076
  for (const id of Array.from(peersRef.current.keys())) cleanup(id);
2072
2077
  };
2073
- }, [selfId, onReceivedText]);
2078
+ }, [selfId]);
2074
2079
  const sendFile = React.useCallback(async (peerId, file, onProgress) => {
2075
2080
  if (!peerId || !file || !window.RTCPeerConnection || !window.dkRtcSignalBus) {
2076
2081
  return { ok: false, fallback: true, error: "WebRTC unavailable" };
@@ -2683,7 +2688,12 @@ function DkApp() {
2683
2688
  const rowPad = t.density === "comfortable" ? "11px 12px" : "7px 10px";
2684
2689
  const callCtl = useCallController(me.userId);
2685
2690
  const onCall = (peerId, video) => callCtl.start(peerId, !!video);
2686
- const rtcFileCtl = useRtcFileController(me.userId);
2691
+ const rtcFileCtl = useRtcFileController(me.userId, (peerId, text) => {
2692
+ dkApi.logLocal(peerId, "in", text).then(() => {
2693
+ data.loadThread(peerId);
2694
+ data.refresh();
2695
+ });
2696
+ });
2687
2697
  React.useEffect(() => {
2688
2698
  if (!activeId && peers.length) setActiveId(peers[0].id);
2689
2699
  }, [peers, activeId]);
package/dist/ui/server.js CHANGED
@@ -650,6 +650,12 @@ export function startFriendUi(opts) {
650
650
  sendJson(res, r.ok ? 200 : 400, r);
651
651
  return;
652
652
  }
653
+ if (req.method === "POST" && url === "/api/chat-log-local") {
654
+ const { userid, dir, text } = await readBody(req);
655
+ const r = await opts.call({ op: "chat-log-local", userid, dir: dir === "out" ? "out" : "in", text });
656
+ sendJson(res, r.ok ? 200 : 400, r);
657
+ return;
658
+ }
653
659
  if (req.method === "POST" && url === "/api/chat-mark-read") {
654
660
  const { userid } = await readBody(req);
655
661
  const r = await opts.call({ op: "chat-mark-read", userid });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.224",
3
+ "version": "0.1.226",
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",