@cryptiklemur/lattice 1.36.0 → 1.36.2
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.
|
@@ -254,6 +254,18 @@ export function ProjectRail(props: ProjectRailProps) {
|
|
|
254
254
|
);
|
|
255
255
|
})}
|
|
256
256
|
|
|
257
|
+
{props.nodes.filter(function (n) {
|
|
258
|
+
return !n.isLocal && n.online && n.projects.length === 0;
|
|
259
|
+
}).map(function (n) {
|
|
260
|
+
return (
|
|
261
|
+
<div
|
|
262
|
+
key={"skeleton-" + n.id}
|
|
263
|
+
className="w-[42px] h-[42px] rounded-full bg-base-200 animate-pulse flex-shrink-0"
|
|
264
|
+
title={"Loading projects from " + n.name + "..."}
|
|
265
|
+
/>
|
|
266
|
+
);
|
|
267
|
+
})}
|
|
268
|
+
|
|
257
269
|
{groups.length > 0 && (
|
|
258
270
|
<div className="w-6 h-px bg-base-300 my-0.5 flex-shrink-0" />
|
|
259
271
|
)}
|
|
@@ -351,17 +351,20 @@ export function Sidebar({ onSessionSelect }: { onSessionSelect?: () => void }) {
|
|
|
351
351
|
|
|
352
352
|
<div className="flex flex-col gap-0.5 mx-3 mt-1">
|
|
353
353
|
{[
|
|
354
|
-
{ type: "files" as const, icon: FolderOpen, label: "Files" },
|
|
355
|
-
{ type: "terminal" as const, icon: TerminalSquare, label: "Terminal" },
|
|
356
|
-
{ type: "notes" as const, icon: StickyNote, label: "Notes" },
|
|
357
|
-
{ type: "tasks" as const, icon: Calendar, label: "Tasks" },
|
|
358
|
-
{ type: "bookmarks" as const, icon: Bookmark, label: "Bookmarks" },
|
|
354
|
+
{ type: "files" as const, icon: FolderOpen, label: "Files", localOnly: true },
|
|
355
|
+
{ type: "terminal" as const, icon: TerminalSquare, label: "Terminal", localOnly: true },
|
|
356
|
+
{ type: "notes" as const, icon: StickyNote, label: "Notes", localOnly: false },
|
|
357
|
+
{ type: "tasks" as const, icon: Calendar, label: "Tasks", localOnly: false },
|
|
358
|
+
{ type: "bookmarks" as const, icon: Bookmark, label: "Bookmarks", localOnly: false },
|
|
359
359
|
].map(function (item) {
|
|
360
|
+
var isDisabled = item.localOnly && activeProject?.isRemote;
|
|
360
361
|
return (
|
|
361
362
|
<button
|
|
362
363
|
key={item.type}
|
|
363
364
|
type="button"
|
|
365
|
+
disabled={!!isDisabled}
|
|
364
366
|
onClick={function () {
|
|
367
|
+
if (isDisabled) return;
|
|
365
368
|
openTab(item.type);
|
|
366
369
|
var state = getSidebarStore().state;
|
|
367
370
|
if (state.activeView.type !== "chat") {
|
|
@@ -370,7 +373,13 @@ export function Sidebar({ onSessionSelect }: { onSessionSelect?: () => void }) {
|
|
|
370
373
|
});
|
|
371
374
|
}
|
|
372
375
|
}}
|
|
373
|
-
className=
|
|
376
|
+
className={
|
|
377
|
+
"flex items-center gap-2 px-2 py-1.5 rounded-lg text-[11px] transition-colors " +
|
|
378
|
+
(isDisabled
|
|
379
|
+
? "text-base-content/15 cursor-not-allowed"
|
|
380
|
+
: "text-base-content/40 hover:text-base-content/70 hover:bg-base-300/30")
|
|
381
|
+
}
|
|
382
|
+
title={isDisabled ? "Not available for remote projects" : undefined}
|
|
374
383
|
>
|
|
375
384
|
<item.icon size={12} />
|
|
376
385
|
<span className="font-mono tracking-wide">{item.label}</span>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.2",
|
|
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>",
|
package/server/src/mesh/proxy.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import type { ClientMessage, MeshProxyRequestMessage, MeshProxyResponseMessage, ServerMessage } from "@lattice/shared";
|
|
3
3
|
import { getPeerConnection } from "./connector";
|
|
4
|
-
import { sendTo, broadcast } from "../ws/broadcast";
|
|
4
|
+
import { sendTo, broadcast, registerVirtualClient, removeVirtualClient } from "../ws/broadcast";
|
|
5
5
|
import { routeMessage } from "../ws/router";
|
|
6
6
|
|
|
7
7
|
var pendingRequests = new Map<string, string>();
|
|
@@ -30,29 +30,26 @@ export function proxyToRemoteNode(nodeId: string, projectSlug: string, clientId:
|
|
|
30
30
|
export function handleProxyRequest(sourceNodeId: string, msg: MeshProxyRequestMessage): void {
|
|
31
31
|
var proxyClientId = "mesh-proxy:" + sourceNodeId + ":" + msg.requestId;
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (!ws) {
|
|
40
|
-
console.warn("[mesh/proxy] Cannot send response, no connection to: " + sourceNodeId);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
var envelope: MeshProxyResponseMessage = {
|
|
45
|
-
type: "mesh:proxy_response",
|
|
46
|
-
projectSlug: msg.projectSlug,
|
|
47
|
-
requestId: msg.requestId,
|
|
48
|
-
payload: response as ServerMessage,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
ws.send(JSON.stringify(envelope));
|
|
33
|
+
registerVirtualClient(proxyClientId, function (response: object) {
|
|
34
|
+
var ws = getPeerConnection(sourceNodeId);
|
|
35
|
+
if (!ws) {
|
|
36
|
+
console.warn("[mesh/proxy] Cannot send response, no connection to: " + sourceNodeId);
|
|
37
|
+
removeVirtualClient(proxyClientId);
|
|
38
|
+
return;
|
|
52
39
|
}
|
|
53
|
-
};
|
|
54
40
|
|
|
55
|
-
|
|
41
|
+
var envelope: MeshProxyResponseMessage = {
|
|
42
|
+
type: "mesh:proxy_response",
|
|
43
|
+
projectSlug: msg.projectSlug,
|
|
44
|
+
requestId: msg.requestId,
|
|
45
|
+
payload: response as ServerMessage,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
ws.send(JSON.stringify(envelope));
|
|
49
|
+
removeVirtualClient(proxyClientId);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
routeMessage(proxyClientId, msg.payload);
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
export function handleProxyResponse(msg: MeshProxyResponseMessage): void {
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { ServerWebSocket } from "bun";
|
|
2
2
|
|
|
3
3
|
var clients = new Map<string, ServerWebSocket<{ id: string }>>();
|
|
4
|
+
var virtualSendHandlers = new Map<string, (message: object) => void>();
|
|
5
|
+
|
|
6
|
+
export function registerVirtualClient(id: string, handler: (message: object) => void): void {
|
|
7
|
+
virtualSendHandlers.set(id, handler);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function removeVirtualClient(id: string): void {
|
|
11
|
+
virtualSendHandlers.delete(id);
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
export function addClient(ws: ServerWebSocket<{ id: string }>): void {
|
|
6
15
|
clients.set(ws.data.id, ws);
|
|
@@ -23,6 +32,11 @@ export function sendTo(id: string, message: object): void {
|
|
|
23
32
|
var ws = clients.get(id);
|
|
24
33
|
if (ws) {
|
|
25
34
|
ws.send(JSON.stringify(message));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
var virtualHandler = virtualSendHandlers.get(id);
|
|
38
|
+
if (virtualHandler) {
|
|
39
|
+
virtualHandler(message);
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
42
|
|
package/server/src/ws/router.ts
CHANGED
|
@@ -56,17 +56,18 @@ export function routeMessage(clientId: string, message: ClientMessage): void {
|
|
|
56
56
|
if (PROXIED_PREFIXES.has(prefix)) {
|
|
57
57
|
var remote = clientRemoteNode.get(clientId);
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
var msgSlug = (message as any).projectSlug as string | undefined;
|
|
60
|
+
|
|
61
|
+
if (msgSlug) {
|
|
62
|
+
var localProject = getLocalProject(msgSlug);
|
|
62
63
|
if (!localProject) {
|
|
63
|
-
var remoteEntry = getRemoteNodeForProject(
|
|
64
|
+
var remoteEntry = getRemoteNodeForProject(msgSlug);
|
|
64
65
|
if (remoteEntry) {
|
|
65
|
-
setClientRemoteNode(clientId, remoteEntry.nodeId,
|
|
66
|
-
proxyMessage(clientId, remoteEntry.nodeId,
|
|
66
|
+
setClientRemoteNode(clientId, remoteEntry.nodeId, msgSlug);
|
|
67
|
+
proxyMessage(clientId, remoteEntry.nodeId, msgSlug, message);
|
|
67
68
|
return;
|
|
68
69
|
}
|
|
69
|
-
} else {
|
|
70
|
+
} else if (message.type === "session:activate" || message.type === "session:list_request") {
|
|
70
71
|
clearClientRemoteNode(clientId);
|
|
71
72
|
}
|
|
72
73
|
} else if (remote) {
|