@cryptiklemur/lattice 1.43.2 → 1.43.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.
|
@@ -175,10 +175,25 @@ function PreviewPopover(props: { preview: SessionPreview | null; anchorRect: DOM
|
|
|
175
175
|
return createPortal(content, document.body);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
function loadCachedSessions(slug: string | null): SessionSummary[] {
|
|
179
|
+
if (!slug) return [];
|
|
180
|
+
try {
|
|
181
|
+
var raw = localStorage.getItem("lattice:sessions:" + slug);
|
|
182
|
+
if (raw) return JSON.parse(raw);
|
|
183
|
+
} catch {}
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function cacheSessions(slug: string, sessions: SessionSummary[]): void {
|
|
188
|
+
try {
|
|
189
|
+
localStorage.setItem("lattice:sessions:" + slug, JSON.stringify(sessions.slice(0, 100)));
|
|
190
|
+
} catch {}
|
|
191
|
+
}
|
|
192
|
+
|
|
178
193
|
export function SessionList(props: SessionListProps) {
|
|
179
194
|
useTimeTick();
|
|
180
195
|
var ws = useWebSocket();
|
|
181
|
-
var [sessions, setSessions] = useState<SessionSummary[]>(
|
|
196
|
+
var [sessions, setSessions] = useState<SessionSummary[]>(function () { return loadCachedSessions(props.projectSlug); });
|
|
182
197
|
var [loading, setLoading] = useState<boolean>(false);
|
|
183
198
|
var [loadingMore, setLoadingMore] = useState<boolean>(false);
|
|
184
199
|
var [totalCount, setTotalCount] = useState<number>(0);
|
|
@@ -240,6 +255,9 @@ export function SessionList(props: SessionListProps) {
|
|
|
240
255
|
|
|
241
256
|
setTotalCount(listTotal);
|
|
242
257
|
offsetRef.current = listOffset + incoming.length;
|
|
258
|
+
if (props.projectSlug && listOffset === 0) {
|
|
259
|
+
cacheSessions(props.projectSlug, incoming);
|
|
260
|
+
}
|
|
243
261
|
hasMoreRef.current = listOffset + incoming.length < listTotal;
|
|
244
262
|
}
|
|
245
263
|
} else if (msg.type === "session:created") {
|
|
@@ -286,8 +304,9 @@ export function SessionList(props: SessionListProps) {
|
|
|
286
304
|
|
|
287
305
|
useEffect(function () {
|
|
288
306
|
if (props.projectSlug && ws.status === "connected") {
|
|
289
|
-
|
|
290
|
-
|
|
307
|
+
var cached = loadCachedSessions(props.projectSlug);
|
|
308
|
+
setSessions(cached);
|
|
309
|
+
setLoading(cached.length === 0);
|
|
291
310
|
offsetRef.current = 0;
|
|
292
311
|
hasMoreRef.current = true;
|
|
293
312
|
sendRef.current({ type: "session:list_request", projectSlug: props.projectSlug, offset: 0, limit: PAGE_SIZE });
|
|
@@ -12,9 +12,23 @@ export interface UseProjectsResult {
|
|
|
12
12
|
setActiveProject: (project: ProjectInfo | null) => void;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function loadCachedProjects(): ProjectInfo[] {
|
|
16
|
+
try {
|
|
17
|
+
var raw = localStorage.getItem("lattice:projects");
|
|
18
|
+
if (raw) return JSON.parse(raw);
|
|
19
|
+
} catch {}
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cacheProjects(projects: ProjectInfo[]): void {
|
|
24
|
+
try {
|
|
25
|
+
localStorage.setItem("lattice:projects", JSON.stringify(projects));
|
|
26
|
+
} catch {}
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
export function useProjects(): UseProjectsResult {
|
|
16
30
|
var ws = useWebSocket();
|
|
17
|
-
var [projects, setProjects] = useState<ProjectInfo[]>(
|
|
31
|
+
var [projects, setProjects] = useState<ProjectInfo[]>(loadCachedProjects);
|
|
18
32
|
var activeProjectSlug = useStore(getSidebarStore(), function (state) { return state.activeProjectSlug; });
|
|
19
33
|
|
|
20
34
|
var handleRef = useRef<(msg: ServerMessage) => void>(function () {});
|
|
@@ -32,7 +46,9 @@ export function useProjects(): UseProjectsResult {
|
|
|
32
46
|
for (var i = 0; i < kept.length; i++) {
|
|
33
47
|
(kept[i] as any).online = false;
|
|
34
48
|
}
|
|
35
|
-
|
|
49
|
+
var merged = incoming.concat(kept);
|
|
50
|
+
cacheProjects(merged);
|
|
51
|
+
return merged;
|
|
36
52
|
});
|
|
37
53
|
var storeState = getSidebarStore().state;
|
|
38
54
|
var currentSlug = storeState.activeProjectSlug;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.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>",
|
|
@@ -255,7 +255,11 @@ function refreshCliDetection(): void {
|
|
|
255
255
|
var cmdline = cliPids[j].cmdline;
|
|
256
256
|
var resumeIdx = cmdline.indexOf("--resume");
|
|
257
257
|
if (resumeIdx !== -1 && resumeIdx + 1 < cmdline.length) {
|
|
258
|
-
|
|
258
|
+
var sessionName = cmdline[resumeIdx + 1];
|
|
259
|
+
found = resolveSessionName(projectPath, sessionName);
|
|
260
|
+
if (!found) {
|
|
261
|
+
resolveSessionNameAsync(projectPath, sessionName);
|
|
262
|
+
}
|
|
259
263
|
} else {
|
|
260
264
|
found = findMostRecentSession(projectPath);
|
|
261
265
|
}
|
|
@@ -303,37 +307,42 @@ function resolveSessionName(projectPath: string, name: string): string | null {
|
|
|
303
307
|
var cached = sessionNameCache.get(cacheKey);
|
|
304
308
|
if (cached) return cached;
|
|
305
309
|
|
|
306
|
-
var hash = projectPath.replace(/\//g, "-");
|
|
307
|
-
var dir = join(homedir(), ".claude", "projects", hash);
|
|
308
|
-
if (!existsSync(dir)) return null;
|
|
309
|
-
|
|
310
310
|
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(name)) {
|
|
311
|
-
|
|
311
|
+
var hash = projectPath.replace(/\//g, "-");
|
|
312
|
+
if (existsSync(join(homedir(), ".claude", "projects", hash, name + ".jsonl"))) {
|
|
312
313
|
sessionNameCache.set(cacheKey, name);
|
|
313
314
|
return name;
|
|
314
315
|
}
|
|
315
316
|
}
|
|
316
317
|
|
|
317
|
-
var entries = readdirSync(dir).filter(function (f) { return f.endsWith(".jsonl"); });
|
|
318
|
-
for (var e = 0; e < entries.length; e++) {
|
|
319
|
-
try {
|
|
320
|
-
var content = readFileSync(join(dir, entries[e]), "utf-8");
|
|
321
|
-
var titleIdx = content.indexOf('"custom-title"');
|
|
322
|
-
if (titleIdx === -1) continue;
|
|
323
|
-
var lineStart = content.lastIndexOf("\n", titleIdx) + 1;
|
|
324
|
-
var lineEnd = content.indexOf("\n", titleIdx);
|
|
325
|
-
var line = content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd);
|
|
326
|
-
var parsed = JSON.parse(line);
|
|
327
|
-
if (parsed.type === "custom-title" && parsed.customTitle === name) {
|
|
328
|
-
var id = entries[e].replace(".jsonl", "");
|
|
329
|
-
sessionNameCache.set(cacheKey, id);
|
|
330
|
-
return id;
|
|
331
|
-
}
|
|
332
|
-
} catch {}
|
|
333
|
-
}
|
|
334
318
|
return null;
|
|
335
319
|
}
|
|
336
320
|
|
|
321
|
+
function resolveSessionNameAsync(projectPath: string, name: string): void {
|
|
322
|
+
var cacheKey = projectPath + ":" + name;
|
|
323
|
+
if (sessionNameCache.has(cacheKey)) return;
|
|
324
|
+
|
|
325
|
+
var hash = projectPath.replace(/\//g, "-");
|
|
326
|
+
var dir = join(homedir(), ".claude", "projects", hash);
|
|
327
|
+
if (!existsSync(dir)) return;
|
|
328
|
+
|
|
329
|
+
var proc = Bun.spawn(["grep", "-rl", "--include=*.jsonl", "-m", "1", name, dir], {
|
|
330
|
+
stdout: "pipe", stderr: "ignore",
|
|
331
|
+
});
|
|
332
|
+
void proc.exited.then(function () {
|
|
333
|
+
var output = new Response(proc.stdout).text();
|
|
334
|
+
return output;
|
|
335
|
+
}).then(function (text) {
|
|
336
|
+
var files = text.trim().split("\n").filter(Boolean);
|
|
337
|
+
if (files.length > 0) {
|
|
338
|
+
var match = files[0].match(/([0-9a-f-]{36})\.jsonl$/);
|
|
339
|
+
if (match) {
|
|
340
|
+
sessionNameCache.set(cacheKey, match[1]);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}).catch(function () {});
|
|
344
|
+
}
|
|
345
|
+
|
|
337
346
|
function findMostRecentSession(projectPath: string): string | null {
|
|
338
347
|
var hash = projectPath.replace(/\//g, "-");
|
|
339
348
|
var dir = join(homedir(), ".claude", "projects", hash);
|