@cryptiklemur/lattice 1.29.2 → 1.30.0
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 () {
|
|
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 () {
|
|
@@ -77,7 +97,7 @@ export var PairingDialog = memo(function PairingDialog(props: PairingDialogProps
|
|
|
77
97
|
function handleGenerateInvite() {
|
|
78
98
|
clearInvite();
|
|
79
99
|
setGenerating(true);
|
|
80
|
-
|
|
100
|
+
ws.send({ type: "mesh:generate_invite", address: selectedAddress } as any);
|
|
81
101
|
}
|
|
82
102
|
|
|
83
103
|
function handlePair() {
|
|
@@ -172,9 +192,31 @@ export var PairingDialog = memo(function PairingDialog(props: PairingDialogProps
|
|
|
172
192
|
The code encodes this node's address and a one-time auth token.
|
|
173
193
|
</div>
|
|
174
194
|
|
|
195
|
+
{!mesh.inviteCode && !generating && addresses.length > 1 && (
|
|
196
|
+
<div className="mb-3">
|
|
197
|
+
<label className="text-[11px] font-mono text-base-content/35 uppercase tracking-wider mb-1.5 block">
|
|
198
|
+
Network interface
|
|
199
|
+
</label>
|
|
200
|
+
<select
|
|
201
|
+
value={selectedAddress}
|
|
202
|
+
onChange={function (e) { setSelectedAddress(e.target.value); }}
|
|
203
|
+
className="select select-bordered select-sm w-full bg-base-100 text-base-content text-[13px] font-mono"
|
|
204
|
+
>
|
|
205
|
+
{addresses.map(function (a) {
|
|
206
|
+
return (
|
|
207
|
+
<option key={a.address} value={a.address}>
|
|
208
|
+
{a.address} ({a.name})
|
|
209
|
+
</option>
|
|
210
|
+
);
|
|
211
|
+
})}
|
|
212
|
+
</select>
|
|
213
|
+
</div>
|
|
214
|
+
)}
|
|
215
|
+
|
|
175
216
|
{!mesh.inviteCode && !generating && (
|
|
176
217
|
<button
|
|
177
218
|
onClick={handleGenerateInvite}
|
|
219
|
+
disabled={!selectedAddress && addresses.length > 0}
|
|
178
220
|
className="btn btn-primary btn-sm"
|
|
179
221
|
>
|
|
180
222
|
Generate Invite Code
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
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
|
-
|
|
25
|
+
results.push({ name: keys[i], address: addrs[j].address });
|
|
20
26
|
}
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
|
-
return
|
|
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);
|