@cryptiklemur/lattice 1.28.2 → 1.28.3

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.
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useCallback, useRef } from "react";
1
+ import { useState, useEffect, useCallback, useRef, memo } from "react";
2
2
  import { X, Copy, Check, Loader2 } from "lucide-react";
3
3
  import { useWebSocket } from "../../hooks/useWebSocket";
4
4
  import { useMesh } from "../../hooks/useMesh";
@@ -13,7 +13,7 @@ interface PairingDialogProps {
13
13
  onClose: () => void;
14
14
  }
15
15
 
16
- export function PairingDialog(props: PairingDialogProps) {
16
+ export var PairingDialog = memo(function PairingDialog(props: PairingDialogProps) {
17
17
  var ws = useWebSocket();
18
18
  var mesh = useMesh();
19
19
  var [tab, setTab] = useState<Tab>("generate");
@@ -198,17 +198,6 @@ export function PairingDialog(props: PairingDialogProps) {
198
198
  </button>
199
199
  </div>
200
200
 
201
- {mesh.inviteQr && (
202
- <div className="flex justify-center mb-4">
203
- <img
204
- src={mesh.inviteQr}
205
- alt="QR code for invite"
206
- className="w-40 h-40 rounded border border-base-300"
207
- style={{ imageRendering: "pixelated" }}
208
- />
209
- </div>
210
- )}
211
-
212
201
  <button
213
202
  onClick={handleGenerateInvite}
214
203
  className="text-[12px] text-base-content/40 underline cursor-pointer"
@@ -287,4 +276,4 @@ export function PairingDialog(props: PairingDialogProps) {
287
276
  </div>
288
277
  </div>
289
278
  );
290
- }
279
+ });
@@ -1,4 +1,4 @@
1
- import { useState } from "react";
1
+ import { useState, useCallback, memo } from "react";
2
2
  import { Plus, CircleDot, Circle } from "lucide-react";
3
3
  import { useWebSocket } from "../../hooks/useWebSocket";
4
4
  import { useMesh } from "../../hooks/useMesh";
@@ -83,6 +83,8 @@ export function MeshStatus() {
83
83
  var { nodes } = useMesh();
84
84
  var [pairingOpen, setPairingOpen] = useState(false);
85
85
 
86
+ var handleClosePairing = useCallback(function () { setPairingOpen(false); }, []);
87
+
86
88
  function handleUnpair(nodeId: string) {
87
89
  ws.send({ type: "mesh:unpair", nodeId });
88
90
  }
@@ -138,7 +140,7 @@ export function MeshStatus() {
138
140
 
139
141
  <PairingDialog
140
142
  isOpen={pairingOpen}
141
- onClose={function () { setPairingOpen(false); }}
143
+ onClose={handleClosePairing}
142
144
  />
143
145
  </div>
144
146
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "1.28.2",
3
+ "version": "1.28.3",
4
4
  "description": "Multi-machine agentic dashboard for Claude Code. Monitor sessions, manage MCP servers and skills, orchestrate across mesh-networked nodes.",
5
5
  "license": "MIT",
6
6
  "author": "Aaron Scherer <me@aaronscherer.me>",
@@ -1,7 +1,7 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import QRCode from "qrcode";
3
2
 
4
3
  var BASE62_CHARS = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz";
4
+ var CODE_LENGTH = 16;
5
5
 
6
6
  var PAIRING_TOKEN_TTL = 300000;
7
7
  var CLEANUP_INTERVAL = 60000;
@@ -16,7 +16,10 @@ function base62Encode(buf: Buffer): string {
16
16
  result = BASE62_CHARS[Number(n % base)] + result;
17
17
  n = n / base;
18
18
  }
19
- return result || BASE62_CHARS[0];
19
+ while (result.length < CODE_LENGTH) {
20
+ result = BASE62_CHARS[0] + result;
21
+ }
22
+ return result;
20
23
  }
21
24
 
22
25
  function base62Decode(s: string): Buffer {
@@ -36,11 +39,29 @@ function base62Decode(s: string): Buffer {
36
39
  return Buffer.from(hex, "hex");
37
40
  }
38
41
 
42
+ function packPayload(address: string, port: number, token: Buffer): Buffer {
43
+ var parts = address.split(".");
44
+ var buf = Buffer.alloc(4 + 2 + token.length);
45
+ for (var i = 0; i < 4; i++) {
46
+ buf[i] = parseInt(parts[i] || "0", 10);
47
+ }
48
+ buf.writeUInt16BE(port, 4);
49
+ token.copy(buf, 6);
50
+ return buf;
51
+ }
52
+
53
+ function unpackPayload(buf: Buffer): { address: string; port: number; token: string } | null {
54
+ if (buf.length < 8) return null;
55
+ var address = buf[0] + "." + buf[1] + "." + buf[2] + "." + buf[3];
56
+ var port = buf.readUInt16BE(4);
57
+ var token = buf.subarray(6).toString("hex");
58
+ return { address, port, token };
59
+ }
60
+
39
61
  function formatCode(raw: string): string {
40
- var upper = raw.toUpperCase();
41
62
  var chunks: string[] = [];
42
- for (var i = 0; i < upper.length; i += 4) {
43
- chunks.push(upper.slice(i, i + 4));
63
+ for (var i = 0; i < raw.length; i += 4) {
64
+ chunks.push(raw.slice(i, i + 4));
44
65
  }
45
66
  return "LTCE-" + chunks.join("-");
46
67
  }
@@ -53,17 +74,15 @@ export async function generateInviteCode(
53
74
  address: string,
54
75
  port: number
55
76
  ): Promise<{ code: string; token: string; qrDataUrl: string }> {
56
- var token = randomBytes(8).toString("hex");
57
- var payload = Buffer.from(address + ":" + port + ":" + token, "utf-8");
77
+ var tokenBuf = randomBytes(4);
78
+ var token = tokenBuf.toString("hex");
79
+ var payload = packPayload(address, port, tokenBuf);
58
80
  var encoded = base62Encode(payload);
59
81
  var code = formatCode(encoded);
60
82
 
61
83
  pendingTokens.set(token, Date.now());
62
84
 
63
- var qrSvg = await QRCode.toString(code, { type: "svg" });
64
- var qrDataUrl = "data:image/svg+xml;base64," + Buffer.from(qrSvg).toString("base64");
65
-
66
- return { code, token, qrDataUrl };
85
+ return { code, token, qrDataUrl: "" };
67
86
  }
68
87
 
69
88
  export function parseInviteCode(
@@ -71,19 +90,8 @@ export function parseInviteCode(
71
90
  ): { address: string; port: number; token: string } | null {
72
91
  try {
73
92
  var stripped = stripCode(code);
74
- var decoded = base62Decode(stripped).toString("utf-8");
75
- var parts = decoded.split(":");
76
- if (parts.length < 3) {
77
- return null;
78
- }
79
- var token = parts[parts.length - 1];
80
- var portStr = parts[parts.length - 2];
81
- var address = parts.slice(0, parts.length - 2).join(":");
82
- var port = parseInt(portStr, 10);
83
- if (isNaN(port)) {
84
- return null;
85
- }
86
- return { address, port, token };
93
+ var decoded = base62Decode(stripped);
94
+ return unpackPayload(decoded);
87
95
  } catch {
88
96
  return null;
89
97
  }