@cryptiklemur/lattice 1.29.2 → 1.30.1

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.
@@ -22,6 +22,8 @@ export var PairingDialog = memo(function PairingDialog(props: PairingDialogProps
22
22
  var [pairError, setPairError] = useState<string | null>(null);
23
23
  var [copied, setCopied] = useState(false);
24
24
  var [generating, setGenerating] = useState(false);
25
+ var [addresses, setAddresses] = useState<Array<{ name: string; address: string }>>([]);
26
+ var [selectedAddress, setSelectedAddress] = useState("");
25
27
  var modalRef = useRef<HTMLDivElement>(null);
26
28
  var inputRef = useRef<HTMLInputElement>(null);
27
29
 
@@ -33,14 +35,32 @@ export var PairingDialog = memo(function PairingDialog(props: PairingDialogProps
33
35
  setPairError(null);
34
36
  setCopied(false);
35
37
  setGenerating(false);
38
+ setAddresses([]);
39
+ setSelectedAddress("");
36
40
  setTab("generate");
37
41
  return;
38
42
  }
43
+
44
+ function handleAddresses(msg: ServerMessage) {
45
+ if ((msg as any).type !== "mesh:addresses_result") return;
46
+ var data = msg as any as { addresses: Array<{ name: string; address: string }> };
47
+ setAddresses(data.addresses);
48
+ if (data.addresses.length > 0) {
49
+ setSelectedAddress(data.addresses[0].address);
50
+ }
51
+ }
52
+
53
+ ws.subscribe("mesh:addresses_result", handleAddresses);
54
+ ws.send({ type: "mesh:addresses" } as any);
55
+
39
56
  function handleKeyDown(e: KeyboardEvent) {
40
57
  if (e.key === "Escape") props.onClose();
41
58
  }
42
59
  document.addEventListener("keydown", handleKeyDown);
43
- return function () { document.removeEventListener("keydown", handleKeyDown); };
60
+ return function () {
61
+ document.removeEventListener("keydown", handleKeyDown);
62
+ ws.unsubscribe("mesh:addresses_result", handleAddresses);
63
+ };
44
64
  }, [props.isOpen]);
45
65
 
46
66
  useEffect(function () {
@@ -54,6 +74,7 @@ export var PairingDialog = memo(function PairingDialog(props: PairingDialogProps
54
74
  if (msg.type === "mesh:paired") {
55
75
  setPairStatus("paired");
56
76
  setPairError(null);
77
+ setTimeout(function () { props.onClose(); }, 3000);
57
78
  }
58
79
  }
59
80
 
@@ -77,7 +98,7 @@ export var PairingDialog = memo(function PairingDialog(props: PairingDialogProps
77
98
  function handleGenerateInvite() {
78
99
  clearInvite();
79
100
  setGenerating(true);
80
- mesh.generateInvite();
101
+ ws.send({ type: "mesh:generate_invite", address: selectedAddress } as any);
81
102
  }
82
103
 
83
104
  function handlePair() {
@@ -172,9 +193,31 @@ export var PairingDialog = memo(function PairingDialog(props: PairingDialogProps
172
193
  The code encodes this node&apos;s address and a one-time auth token.
173
194
  </div>
174
195
 
196
+ {!mesh.inviteCode && !generating && addresses.length > 1 && (
197
+ <div className="mb-3">
198
+ <label className="text-[11px] font-mono text-base-content/35 uppercase tracking-wider mb-1.5 block">
199
+ Network interface
200
+ </label>
201
+ <select
202
+ value={selectedAddress}
203
+ onChange={function (e) { setSelectedAddress(e.target.value); }}
204
+ className="select select-bordered select-sm w-full bg-base-100 text-base-content text-[13px] font-mono"
205
+ >
206
+ {addresses.map(function (a) {
207
+ return (
208
+ <option key={a.address} value={a.address}>
209
+ {a.address} ({a.name})
210
+ </option>
211
+ );
212
+ })}
213
+ </select>
214
+ </div>
215
+ )}
216
+
175
217
  {!mesh.inviteCode && !generating && (
176
218
  <button
177
219
  onClick={handleGenerateInvite}
220
+ disabled={!selectedAddress && addresses.length > 0}
178
221
  className="btn btn-primary btn-sm"
179
222
  >
180
223
  Generate Invite Code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "1.29.2",
3
+ "version": "1.30.1",
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>",
@@ -9,18 +9,24 @@ import type { PeerInfo } from "@lattice/shared";
9
9
  import { networkInterfaces } from "node:os";
10
10
 
11
11
  function getLocalAddress(): string {
12
+ var all = getAllAddresses();
13
+ return all.length > 0 ? all[0].address : "localhost";
14
+ }
15
+
16
+ function getAllAddresses(): Array<{ name: string; address: string }> {
12
17
  var interfaces = networkInterfaces();
13
18
  var keys = Object.keys(interfaces);
19
+ var results: Array<{ name: string; address: string }> = [];
14
20
  for (var i = 0; i < keys.length; i++) {
15
21
  var addrs = interfaces[keys[i]];
16
22
  if (!addrs) continue;
17
23
  for (var j = 0; j < addrs.length; j++) {
18
24
  if (!addrs[j].internal && addrs[j].family === "IPv4") {
19
- return addrs[j].address;
25
+ results.push({ name: keys[i], address: addrs[j].address });
20
26
  }
21
27
  }
22
28
  }
23
- return "localhost";
29
+ return results;
24
30
  }
25
31
 
26
32
  export function buildNodesMessage(): NodeInfo[] {
@@ -57,8 +63,9 @@ export function buildNodesMessage(): NodeInfo[] {
57
63
 
58
64
  registerHandler("mesh", function (clientId: string, message: ClientMessage) {
59
65
  if (message.type === "mesh:generate_invite") {
66
+ var genMsg = message as any as { type: "mesh:generate_invite"; address?: string };
60
67
  var config = loadConfig();
61
- var address = getLocalAddress();
68
+ var address = genMsg.address || getLocalAddress();
62
69
  generateInviteCode(address, config.port).then(function (result) {
63
70
  sendTo(clientId, {
64
71
  type: "mesh:invite_code",
@@ -71,6 +78,12 @@ registerHandler("mesh", function (clientId: string, message: ClientMessage) {
71
78
  return;
72
79
  }
73
80
 
81
+ if ((message as any).type === "mesh:addresses") {
82
+ var addresses = getAllAddresses();
83
+ sendTo(clientId, { type: "mesh:addresses_result" as any, addresses: addresses });
84
+ return;
85
+ }
86
+
74
87
  if (message.type === "mesh:pair") {
75
88
  var pairMsg = message as MeshPairMessage;
76
89
  var parsed = parseInviteCode(pairMsg.code);