@cryptiklemur/lattice 5.4.3 → 5.6.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.
Files changed (91) hide show
  1. package/README.md +114 -0
  2. package/dist/client/assets/{angular-html-BMCIs4B9.js → angular-html-B_LrO4d_.js} +1 -1
  3. package/dist/client/assets/{angular-ts-Dl9U67Xi.js → angular-ts-DG2-AEn3.js} +1 -1
  4. package/dist/client/assets/{apl-Dc2Jp1EP.js → apl-D9AV-2KJ.js} +1 -1
  5. package/dist/client/assets/{astro-KOvSEh6g.js → astro-D55z-bYO.js} +1 -1
  6. package/dist/client/assets/{blade-Bp9mbJ5K.js → blade-Bs-tcMis.js} +1 -1
  7. package/dist/client/assets/{c-C0ETB1bm.js → c-CAs-oCwZ.js} +1 -1
  8. package/dist/client/assets/{cobol-DE5VmQJX.js → cobol-0B2WKsi0.js} +1 -1
  9. package/dist/client/assets/{coffee-COzJhfRU.js → coffee-BLi0y75x.js} +1 -1
  10. package/dist/client/assets/{cpp-jzhs79Ii.js → cpp-CTXpwK-o.js} +1 -1
  11. package/dist/client/assets/{crystal-Czf3Etrk.js → crystal-B_OPrNIv.js} +1 -1
  12. package/dist/client/assets/{css-Cf45eU4M.js → css-C9nlz4Z2.js} +1 -1
  13. package/dist/client/assets/{dist-Bk-EVR9B.js → dist-CzUlrFrL.js} +2 -2
  14. package/dist/client/assets/{edge-BmKSsYuX.js → edge-DE3SfmPJ.js} +1 -1
  15. package/dist/client/assets/{elixir-Ds7FEpTU.js → elixir-BS_1fIPf.js} +1 -1
  16. package/dist/client/assets/{elm-C_AmF7eK.js → elm-CrfkxZpb.js} +1 -1
  17. package/dist/client/assets/{erb-DevlC2ei.js → erb-B6Ab2Y07.js} +1 -1
  18. package/dist/client/assets/{git-rebase-CpJCeeHA.js → git-rebase-RkfMNUtm.js} +1 -1
  19. package/dist/client/assets/{glimmer-js-CFH-Kzxa.js → glimmer-js-CBA96B3V.js} +1 -1
  20. package/dist/client/assets/{glimmer-ts-BkiQ1tjj.js → glimmer-ts-WPYNrOak.js} +1 -1
  21. package/dist/client/assets/{glsl-DLuvkGQM.js → glsl-BVPbkPwC.js} +1 -1
  22. package/dist/client/assets/{graphql-CYjDXA-l.js → graphql-CplPyf-J.js} +1 -1
  23. package/dist/client/assets/{hack-CDfrJ_Sc.js → hack-Bqnr1D44.js} +1 -1
  24. package/dist/client/assets/{haml-C4YjrCNF.js → haml-Bk9H5oqC.js} +1 -1
  25. package/dist/client/assets/{handlebars-WIT13u08.js → handlebars-Dho-7JSJ.js} +1 -1
  26. package/dist/client/assets/{html-ntqTOVvn.js → html-Dx1dAJ5K.js} +1 -1
  27. package/dist/client/assets/{html-derivative-B3rc20Rp.js → html-derivative-DMEkbacz.js} +1 -1
  28. package/dist/client/assets/{http-jTU2ko3B.js → http-yzZRblhh.js} +1 -1
  29. package/dist/client/assets/{hurl-D2HM7b2B.js → hurl-DwA-puXE.js} +1 -1
  30. package/dist/client/assets/{index-CZNra7P3.js → index-BV9isded.js} +321 -88
  31. package/dist/client/assets/{index-DCKRhnC1.css → index-DcwXjiH0.css} +1 -1
  32. package/dist/client/assets/{java-BofMW2-d.js → java-da4cNL_S.js} +1 -1
  33. package/dist/client/assets/{javascript-DHlF2jGd.js → javascript-BRBtsTGZ.js} +1 -1
  34. package/dist/client/assets/{jinja-BXkUuA_p.js → jinja-KYkTqvLF.js} +1 -1
  35. package/dist/client/assets/{jison-CYSiwIU-.js → jison-CpJZ2qu3.js} +1 -1
  36. package/dist/client/assets/{json-5cpQWmWq.js → json-1ZKXsOcj.js} +1 -1
  37. package/dist/client/assets/{jsx-D8kfeKVb.js → jsx-Ci3IutCK.js} +1 -1
  38. package/dist/client/assets/{julia-BkW_r2Xd.js → julia-8BIB33ff.js} +1 -1
  39. package/dist/client/assets/{just-Dz61xACw.js → just-BlxIBqAl.js} +1 -1
  40. package/dist/client/assets/{latex-gqtZNh_M.js → latex-Dt6aIDIN.js} +1 -1
  41. package/dist/client/assets/{liquid-KcpeevnQ.js → liquid-rNU0f2fO.js} +1 -1
  42. package/dist/client/assets/{lua-C3Vw3Kyi.js → lua-9O59duWh.js} +1 -1
  43. package/dist/client/assets/{marko-DVaxNS7b.js → marko-cSK6Q4M9.js} +1 -1
  44. package/dist/client/assets/{mdc-DRm1pTBY.js → mdc-DD0DONjK.js} +1 -1
  45. package/dist/client/assets/{nginx-DYudX1m0.js → nginx-Bfzt7qPl.js} +1 -1
  46. package/dist/client/assets/{nim-DejMEf9z.js → nim-DOD72OhS.js} +1 -1
  47. package/dist/client/assets/{perl-D09qXnjW.js → perl-1cg42R6O.js} +1 -1
  48. package/dist/client/assets/{php-BrfQzrDQ.js → php-DfG0eJE0.js} +1 -1
  49. package/dist/client/assets/{pug-DPbk8XD2.js → pug-BJ4ktrjh.js} +1 -1
  50. package/dist/client/assets/{qml-BCh8MHc-.js → qml-BwGhenQ4.js} +1 -1
  51. package/dist/client/assets/{r-DCcyjvCV.js → r-DSKy_gZ9.js} +1 -1
  52. package/dist/client/assets/{razor-CHeljxcn.js → razor-morGoSMw.js} +1 -1
  53. package/dist/client/assets/{regexp-DpLv_uhV.js → regexp-DSQzd7Gw.js} +1 -1
  54. package/dist/client/assets/{rst-BmAGX4li.js → rst-CQ4DB61w.js} +1 -1
  55. package/dist/client/assets/{ruby-C28IDakj.js → ruby-dcUoNsdX.js} +1 -1
  56. package/dist/client/assets/{sas-Dq78Tt13.js → sas-BckOIwmR.js} +1 -1
  57. package/dist/client/assets/{scss-CCq8md_z.js → scss-CIDQomQQ.js} +1 -1
  58. package/dist/client/assets/{shellscript-Cl_slHLv.js → shellscript-DscXCB_p.js} +1 -1
  59. package/dist/client/assets/{shellsession-D50FTgCo.js → shellsession-C9EjZURj.js} +1 -1
  60. package/dist/client/assets/{soy-Da12pRro.js → soy-CVzyKHIE.js} +1 -1
  61. package/dist/client/assets/{sql-DRKhJcrs.js → sql-Ddk-vRG3.js} +1 -1
  62. package/dist/client/assets/{stata-E06HAz87.js → stata-BGl5cxb8.js} +1 -1
  63. package/dist/client/assets/{surrealql-CTaVrdiv.js → surrealql-3IZgL_Ze.js} +1 -1
  64. package/dist/client/assets/{svelte-j0H5d0BS.js → svelte-BRM0niF5.js} +1 -1
  65. package/dist/client/assets/{templ-BXfOHYWo.js → templ-qbgb30u-.js} +1 -1
  66. package/dist/client/assets/{tex-CGBWhBxV.js → tex-Ct_-hpJo.js} +1 -1
  67. package/dist/client/assets/{ts-tags-C-diQc0f.js → ts-tags-C85B12Rc.js} +1 -1
  68. package/dist/client/assets/{tsx-CUBPow91.js → tsx-d401eVG9.js} +1 -1
  69. package/dist/client/assets/{twig-BYoQufij.js → twig-CEvQ_MDP.js} +1 -1
  70. package/dist/client/assets/{typescript-CAiUPCi4.js → typescript-y2NNcyKH.js} +1 -1
  71. package/dist/client/assets/{vue-CEKqQRtx.js → vue-CiTZbygT.js} +1 -1
  72. package/dist/client/assets/{vue-html-DSRnbzbs.js → vue-html-B4nYUWKJ.js} +1 -1
  73. package/dist/client/assets/{vue-vine-PFaCZbOl.js → vue-vine-DdfbYtGV.js} +1 -1
  74. package/dist/client/assets/{xml-CpFeNxWK.js → xml-1B2HtnyO.js} +1 -1
  75. package/dist/client/assets/{xsl-DOlnqqgg.js → xsl-DoRYSDxo.js} +1 -1
  76. package/dist/client/assets/{yaml-Bv-3IQB_.js → yaml-DpbRFfH9.js} +1 -1
  77. package/dist/client/index.html +2 -2
  78. package/dist/client/sw.js +1 -1
  79. package/dist/server/daemon.js +75 -26
  80. package/dist/server/features/brainstorm.js +267 -0
  81. package/dist/server/handlers/attachment.js +66 -29
  82. package/dist/server/handlers/brainstorm.js +33 -0
  83. package/dist/server/handlers/fs.js +61 -72
  84. package/dist/server/handlers/memory.js +37 -52
  85. package/dist/server/handlers/mesh.js +4 -1
  86. package/dist/server/handlers/session.js +47 -58
  87. package/dist/server/mesh/connector.js +85 -1
  88. package/dist/server/project/file-browser.js +14 -14
  89. package/dist/server/project/session.js +98 -14
  90. package/dist/server/ws/broadcast.js +60 -0
  91. package/package.json +1 -1
@@ -1,19 +1,24 @@
1
1
  import { registerHandler } from "../ws/router.js";
2
- import { sendTo, broadcast } from "../ws/broadcast.js";
2
+ import { sendTo, subscribeClientToProject, broadcastToProject } from "../ws/broadcast.js";
3
3
  import { getProjectBySlug } from "../project/registry.js";
4
4
  import { listDirectory, readFile, writeFile } from "../project/file-browser.js";
5
- import { readdirSync, existsSync, readFileSync, statSync } from "node:fs";
5
+ import { readdir, readFile as fsReadFile, stat } from "node:fs/promises";
6
+ import { existsSync } from "node:fs";
6
7
  import { join } from "node:path";
7
8
  import { homedir } from "node:os";
8
9
  import { loadConfig } from "../config.js";
9
10
  var activeProjectByClient = new Map();
10
11
  export function setActiveProject(clientId, projectSlug) {
11
12
  activeProjectByClient.set(clientId, projectSlug);
13
+ subscribeClientToProject(clientId, projectSlug);
12
14
  }
13
15
  export function clearActiveProject(clientId) {
14
16
  activeProjectByClient.delete(clientId);
15
17
  }
16
- registerHandler("fs", function (clientId, message) {
18
+ export function getActiveProjectForClient(clientId) {
19
+ return activeProjectByClient.get(clientId);
20
+ }
21
+ registerHandler("fs", async function (clientId, message) {
17
22
  if (message.type === "fs:list") {
18
23
  var listMsg = message;
19
24
  var projectSlug = activeProjectByClient.get(clientId) || listMsg.projectSlug;
@@ -30,7 +35,7 @@ registerHandler("fs", function (clientId, message) {
30
35
  sendTo(clientId, { type: "chat:error", message: "Project not found: " + projectSlug });
31
36
  return;
32
37
  }
33
- var entries = listDirectory(project.path, listMsg.path);
38
+ var entries = await listDirectory(project.path, listMsg.path);
34
39
  sendTo(clientId, { type: "fs:list_result", path: listMsg.path, entries });
35
40
  return;
36
41
  }
@@ -50,7 +55,7 @@ registerHandler("fs", function (clientId, message) {
50
55
  sendTo(clientId, { type: "chat:error", message: "Project not found: " + projectSlugRead });
51
56
  return;
52
57
  }
53
- var content = readFile(projectRead.path, readMsg.path);
58
+ var content = await readFile(projectRead.path, readMsg.path);
54
59
  if (content === null) {
55
60
  sendTo(clientId, { type: "chat:error", message: "Cannot read file: " + readMsg.path });
56
61
  return;
@@ -70,12 +75,12 @@ registerHandler("fs", function (clientId, message) {
70
75
  sendTo(clientId, { type: "chat:error", message: "Project not found: " + projectSlugWrite });
71
76
  return;
72
77
  }
73
- var ok = writeFile(projectWrite.path, writeMsg.path, writeMsg.content);
78
+ var ok = await writeFile(projectWrite.path, writeMsg.path, writeMsg.content);
74
79
  if (!ok) {
75
80
  sendTo(clientId, { type: "chat:error", message: "Cannot write file: " + writeMsg.path });
76
81
  return;
77
82
  }
78
- broadcast({ type: "fs:changed", path: writeMsg.path });
83
+ broadcastToProject(projectSlugWrite, { type: "fs:changed", path: writeMsg.path });
79
84
  return;
80
85
  }
81
86
  });
@@ -86,59 +91,49 @@ function resolvePath(path) {
86
91
  return join(homedir(), path.slice(2));
87
92
  return path;
88
93
  }
89
- function detectProjectName(dirPath) {
94
+ async function detectProjectName(dirPath) {
90
95
  try {
91
96
  var pkgPath = join(dirPath, "package.json");
92
- if (existsSync(pkgPath)) {
93
- var pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
94
- if (pkg.name)
95
- return pkg.name;
96
- }
97
+ var pkg = JSON.parse(await fsReadFile(pkgPath, "utf-8"));
98
+ if (pkg.name)
99
+ return pkg.name;
97
100
  }
98
101
  catch { }
99
102
  try {
100
103
  var cargoPath = join(dirPath, "Cargo.toml");
101
- if (existsSync(cargoPath)) {
102
- var cargo = readFileSync(cargoPath, "utf-8");
103
- var cargoMatch = cargo.match(/\[package\][\s\S]*?name\s*=\s*"([^"]+)"/);
104
- if (cargoMatch)
105
- return cargoMatch[1];
106
- }
104
+ var cargo = await fsReadFile(cargoPath, "utf-8");
105
+ var cargoMatch = cargo.match(/\[package\][\s\S]*?name\s*=\s*"([^"]+)"/);
106
+ if (cargoMatch)
107
+ return cargoMatch[1];
107
108
  }
108
109
  catch { }
109
110
  try {
110
111
  var composerPath = join(dirPath, "composer.json");
111
- if (existsSync(composerPath)) {
112
- var composer = JSON.parse(readFileSync(composerPath, "utf-8"));
113
- if (composer.name)
114
- return composer.name;
115
- }
112
+ var composer = JSON.parse(await fsReadFile(composerPath, "utf-8"));
113
+ if (composer.name)
114
+ return composer.name;
116
115
  }
117
116
  catch { }
118
117
  try {
119
118
  var pyprojectPath = join(dirPath, "pyproject.toml");
120
- if (existsSync(pyprojectPath)) {
121
- var pyproject = readFileSync(pyprojectPath, "utf-8");
122
- var pyMatch = pyproject.match(/\[project\][\s\S]*?name\s*=\s*"([^"]+)"/);
123
- if (pyMatch)
124
- return pyMatch[1];
125
- }
119
+ var pyproject = await fsReadFile(pyprojectPath, "utf-8");
120
+ var pyMatch = pyproject.match(/\[project\][\s\S]*?name\s*=\s*"([^"]+)"/);
121
+ if (pyMatch)
122
+ return pyMatch[1];
126
123
  }
127
124
  catch { }
128
125
  try {
129
126
  var goModPath = join(dirPath, "go.mod");
130
- if (existsSync(goModPath)) {
131
- var goMod = readFileSync(goModPath, "utf-8");
132
- var goMatch = goMod.match(/^module\s+(\S+)/m);
133
- if (goMatch) {
134
- var parts = goMatch[1].split("/");
135
- return parts[parts.length - 1];
136
- }
127
+ var goMod = await fsReadFile(goModPath, "utf-8");
128
+ var goMatch = goMod.match(/^module\s+(\S+)/m);
129
+ if (goMatch) {
130
+ var parts = goMatch[1].split("/");
131
+ return parts[parts.length - 1];
137
132
  }
138
133
  }
139
134
  catch { }
140
135
  try {
141
- var entries = readdirSync(dirPath);
136
+ var entries = await readdir(dirPath);
142
137
  for (var i = 0; i < entries.length; i++) {
143
138
  if (entries[i].endsWith(".sln") || entries[i].endsWith(".csproj")) {
144
139
  return entries[i].replace(/\.[^.]+$/, "");
@@ -148,18 +143,14 @@ function detectProjectName(dirPath) {
148
143
  catch { }
149
144
  return null;
150
145
  }
151
- registerHandler("browse", function (clientId, message) {
146
+ registerHandler("browse", async function (clientId, message) {
152
147
  if (message.type === "browse:list") {
153
148
  var browseMsg = message;
154
149
  var resolvedPath = resolvePath(browseMsg.path);
155
150
  var home = homedir();
156
- if (!existsSync(resolvedPath)) {
157
- sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: [] });
158
- return;
159
- }
160
151
  try {
161
- var stat = statSync(resolvedPath);
162
- if (!stat.isDirectory()) {
152
+ var pathStat = await stat(resolvedPath);
153
+ if (!pathStat.isDirectory()) {
163
154
  sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: [] });
164
155
  return;
165
156
  }
@@ -169,7 +160,7 @@ registerHandler("browse", function (clientId, message) {
169
160
  return;
170
161
  }
171
162
  try {
172
- var dirEntries = readdirSync(resolvedPath, { withFileTypes: true });
163
+ var dirEntries = await readdir(resolvedPath, { withFileTypes: true });
173
164
  var results = [];
174
165
  for (var i = 0; i < dirEntries.length; i++) {
175
166
  var entry = dirEntries[i];
@@ -177,7 +168,7 @@ registerHandler("browse", function (clientId, message) {
177
168
  continue;
178
169
  var entryPath = join(resolvedPath, entry.name);
179
170
  var hasClaudeMd = existsSync(join(entryPath, "CLAUDE.md"));
180
- var projectName = detectProjectName(entryPath);
171
+ var projectName = await detectProjectName(entryPath);
181
172
  results.push({
182
173
  name: entry.name,
183
174
  path: entryPath,
@@ -198,35 +189,33 @@ registerHandler("browse", function (clientId, message) {
198
189
  var config = loadConfig();
199
190
  var existingPaths = new Set(config.projects.map(function (p) { return p.path; }));
200
191
  var suggestions = [];
201
- if (existsSync(claudeProjectsDir)) {
202
- try {
203
- var hashDirs = readdirSync(claudeProjectsDir);
204
- for (var i = 0; i < hashDirs.length; i++) {
205
- var hashDir = hashDirs[i];
206
- var candidatePath = "/" + hashDir.slice(1).replace(/-/g, "/");
207
- if (!existsSync(candidatePath))
208
- continue;
209
- if (existingPaths.has(candidatePath))
210
- continue;
211
- try {
212
- var stat = statSync(candidatePath);
213
- if (!stat.isDirectory())
214
- continue;
215
- }
216
- catch {
192
+ try {
193
+ var hashDirs = await readdir(claudeProjectsDir);
194
+ for (var i = 0; i < hashDirs.length; i++) {
195
+ var hashDir = hashDirs[i];
196
+ var candidatePath = "/" + hashDir.slice(1).replace(/-/g, "/");
197
+ if (!existsSync(candidatePath))
198
+ continue;
199
+ if (existingPaths.has(candidatePath))
200
+ continue;
201
+ try {
202
+ var candidateStat = await stat(candidatePath);
203
+ if (!candidateStat.isDirectory())
217
204
  continue;
218
- }
219
- var hasClaudeMd = existsSync(join(candidatePath, "CLAUDE.md"));
220
- var name = candidatePath.split("/").pop() || hashDir;
221
- suggestions.push({
222
- path: candidatePath,
223
- name: name,
224
- hasClaudeMd: hasClaudeMd,
225
- });
226
205
  }
206
+ catch {
207
+ continue;
208
+ }
209
+ var hasClaudeMd = existsSync(join(candidatePath, "CLAUDE.md"));
210
+ var name = candidatePath.split("/").pop() || hashDir;
211
+ suggestions.push({
212
+ path: candidatePath,
213
+ name: name,
214
+ hasClaudeMd: hasClaudeMd,
215
+ });
227
216
  }
228
- catch { }
229
217
  }
218
+ catch { }
230
219
  suggestions.sort(function (a, b) { return a.name.localeCompare(b.name); });
231
220
  sendTo(clientId, { type: "browse:suggestions_result", suggestions: suggestions });
232
221
  return;
@@ -1,4 +1,5 @@
1
- import { existsSync, readFileSync, writeFileSync, readdirSync, unlinkSync, mkdirSync } from "node:fs";
1
+ import { readdir, readFile, writeFile, unlink, mkdir } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
2
3
  import { join } from "node:path";
3
4
  import { homedir } from "node:os";
4
5
  import { registerHandler } from "../ws/router.js";
@@ -38,16 +39,16 @@ function parseFrontmatter(content) {
38
39
  }
39
40
  return { name, description, type };
40
41
  }
41
- function regenerateIndex(memoryDir) {
42
+ async function regenerateIndex(memoryDir) {
42
43
  if (!existsSync(memoryDir))
43
44
  return;
44
- var files = readdirSync(memoryDir).filter(function (f) {
45
+ var files = (await readdir(memoryDir)).filter(function (f) {
45
46
  return f.endsWith(".md") && f !== "MEMORY.md";
46
47
  });
47
48
  var grouped = {};
48
49
  for (var i = 0; i < files.length; i++) {
49
50
  try {
50
- var content = readFileSync(join(memoryDir, files[i]), "utf-8");
51
+ var content = await readFile(join(memoryDir, files[i]), "utf-8");
51
52
  var meta = parseFrontmatter(content);
52
53
  var type = meta.type || "other";
53
54
  if (!grouped[type])
@@ -67,9 +68,30 @@ function regenerateIndex(memoryDir) {
67
68
  }
68
69
  lines.push("");
69
70
  }
70
- writeFileSync(join(memoryDir, "MEMORY.md"), lines.join("\n"), "utf-8");
71
+ await writeFile(join(memoryDir, "MEMORY.md"), lines.join("\n"), "utf-8");
71
72
  }
72
- registerHandler("memory", function (clientId, message) {
73
+ async function listMemoryFiles(memDir) {
74
+ var files = (await readdir(memDir)).filter(function (f) {
75
+ return f.endsWith(".md") && f !== "MEMORY.md";
76
+ });
77
+ var memories = [];
78
+ for (var i = 0; i < files.length; i++) {
79
+ try {
80
+ var content = await readFile(join(memDir, files[i]), "utf-8");
81
+ var meta = parseFrontmatter(content);
82
+ memories.push({
83
+ filename: files[i],
84
+ name: meta.name || files[i].replace(/\.md$/, ""),
85
+ description: meta.description,
86
+ type: meta.type || "other",
87
+ });
88
+ }
89
+ catch { }
90
+ }
91
+ memories.sort(function (a, b) { return a.name.localeCompare(b.name); });
92
+ return memories;
93
+ }
94
+ registerHandler("memory", async function (clientId, message) {
73
95
  if (message.type === "memory:list") {
74
96
  var listMsg = message;
75
97
  var memDir = getMemoryDir(listMsg.projectSlug);
@@ -77,24 +99,7 @@ registerHandler("memory", function (clientId, message) {
77
99
  sendTo(clientId, { type: "memory:list_result", projectSlug: listMsg.projectSlug, memories: [] });
78
100
  return;
79
101
  }
80
- var files = readdirSync(memDir).filter(function (f) {
81
- return f.endsWith(".md") && f !== "MEMORY.md";
82
- });
83
- var memories = [];
84
- for (var i = 0; i < files.length; i++) {
85
- try {
86
- var content = readFileSync(join(memDir, files[i]), "utf-8");
87
- var meta = parseFrontmatter(content);
88
- memories.push({
89
- filename: files[i],
90
- name: meta.name || files[i].replace(/\.md$/, ""),
91
- description: meta.description,
92
- type: meta.type || "other",
93
- });
94
- }
95
- catch { }
96
- }
97
- memories.sort(function (a, b) { return a.name.localeCompare(b.name); });
102
+ var memories = await listMemoryFiles(memDir);
98
103
  sendTo(clientId, { type: "memory:list_result", projectSlug: listMsg.projectSlug, memories: memories });
99
104
  return;
100
105
  }
@@ -106,7 +111,7 @@ registerHandler("memory", function (clientId, message) {
106
111
  return;
107
112
  }
108
113
  try {
109
- var viewContent = readFileSync(join(viewDir, viewMsg.filename), "utf-8");
114
+ var viewContent = await readFile(join(viewDir, viewMsg.filename), "utf-8");
110
115
  sendTo(clientId, { type: "memory:view_result", filename: viewMsg.filename, content: viewContent });
111
116
  }
112
117
  catch {
@@ -122,21 +127,11 @@ registerHandler("memory", function (clientId, message) {
122
127
  return;
123
128
  }
124
129
  try {
125
- mkdirSync(saveDir, { recursive: true });
126
- writeFileSync(join(saveDir, saveMsg.filename), saveMsg.content, "utf-8");
127
- regenerateIndex(saveDir);
130
+ await mkdir(saveDir, { recursive: true });
131
+ await writeFile(join(saveDir, saveMsg.filename), saveMsg.content, "utf-8");
132
+ await regenerateIndex(saveDir);
128
133
  sendTo(clientId, { type: "memory:save_result", success: true });
129
- var updatedFiles = readdirSync(saveDir).filter(function (f) { return f.endsWith(".md") && f !== "MEMORY.md"; });
130
- var updatedMemories = [];
131
- for (var j = 0; j < updatedFiles.length; j++) {
132
- try {
133
- var c = readFileSync(join(saveDir, updatedFiles[j]), "utf-8");
134
- var m = parseFrontmatter(c);
135
- updatedMemories.push({ filename: updatedFiles[j], name: m.name || updatedFiles[j].replace(/\.md$/, ""), description: m.description, type: m.type || "other" });
136
- }
137
- catch { }
138
- }
139
- updatedMemories.sort(function (a, b) { return a.name.localeCompare(b.name); });
134
+ var updatedMemories = await listMemoryFiles(saveDir);
140
135
  sendTo(clientId, { type: "memory:list_result", projectSlug: saveMsg.projectSlug, memories: updatedMemories });
141
136
  }
142
137
  catch (err) {
@@ -157,20 +152,10 @@ registerHandler("memory", function (clientId, message) {
157
152
  sendTo(clientId, { type: "memory:delete_result", success: false, message: "Memory not found." });
158
153
  return;
159
154
  }
160
- unlinkSync(filePath);
161
- regenerateIndex(delDir);
155
+ await unlink(filePath);
156
+ await regenerateIndex(delDir);
162
157
  sendTo(clientId, { type: "memory:delete_result", success: true });
163
- var remainingFiles = readdirSync(delDir).filter(function (f) { return f.endsWith(".md") && f !== "MEMORY.md"; });
164
- var remainingMemories = [];
165
- for (var k = 0; k < remainingFiles.length; k++) {
166
- try {
167
- var rc = readFileSync(join(delDir, remainingFiles[k]), "utf-8");
168
- var rm = parseFrontmatter(rc);
169
- remainingMemories.push({ filename: remainingFiles[k], name: rm.name || remainingFiles[k].replace(/\.md$/, ""), description: rm.description, type: rm.type || "other" });
170
- }
171
- catch { }
172
- }
173
- remainingMemories.sort(function (a, b) { return a.name.localeCompare(b.name); });
158
+ var remainingMemories = await listMemoryFiles(delDir);
174
159
  sendTo(clientId, { type: "memory:list_result", projectSlug: delMsg.projectSlug, memories: remainingMemories });
175
160
  }
176
161
  catch (err) {
@@ -6,7 +6,7 @@ import { loadConfig } from "../config.js";
6
6
  import { loadOrCreateIdentity } from "../identity.js";
7
7
  import { generateInviteCode, parseInviteCode, validatePairingToken, consumePairingToken } from "../mesh/pairing.js";
8
8
  import { addPeer, removePeer, loadPeers, getPeer } from "../mesh/peers.js";
9
- import { getConnectedPeerIds, connectToPeer, reconnectPeer, getPeerConnection, disconnectPeer, getConnectedPeerProjects, registerInboundPeer } from "../mesh/connector.js";
9
+ import { getConnectedPeerIds, connectToPeer, reconnectPeer, getPeerConnection, disconnectPeer, getConnectedPeerProjects, registerInboundPeer, getPeerHealth } from "../mesh/connector.js";
10
10
  import { getClientWebSocket, registerVirtualClient, removeVirtualClient } from "../ws/broadcast.js";
11
11
  import { networkInterfaces } from "node:os";
12
12
  import { existsSync, readFileSync } from "node:fs";
@@ -97,6 +97,7 @@ export function buildNodesMessage() {
97
97
  };
98
98
  var remotes = peers.map(function (peer) {
99
99
  var peerProjects = getConnectedPeerProjects(peer.id);
100
+ var health = getPeerHealth(peer.id);
100
101
  return {
101
102
  id: peer.id,
102
103
  name: peer.name,
@@ -108,6 +109,8 @@ export function buildNodesMessage() {
108
109
  projects: peerProjects.map(function (p) {
109
110
  return { slug: p.slug, path: "", title: p.title, nodeId: peer.id };
110
111
  }),
112
+ latencyMs: health?.latencyMs,
113
+ healthy: health?.healthy,
111
114
  };
112
115
  });
113
116
  return [local, ...remotes];
@@ -1,13 +1,13 @@
1
1
  import { registerHandler } from "../ws/router.js";
2
- import { sendTo } from "../ws/broadcast.js";
2
+ import { sendTo, broadcastToProject } from "../ws/broadcast.js";
3
3
  import { loadConfig } from "../config.js";
4
- import { createSession, deleteSession, findProjectSlugForSession, getSessionPreview, getSessionTitle, getSessionUsage, listSessions, invalidateSessionCache, invalidateHistoryCache, getSessionHistoryPage, loadSessionHistory, renameSession, getSessionFileSizeBytes, } from "../project/session.js";
4
+ import { createSession, deleteSession, findProjectSlugForSession, getSessionPreview, getSessionTitle, getSessionUsage, listSessions, invalidateSessionCache, invalidateHistoryCache, getSessionHistoryPage, loadSessionHistory, renameSession, getSessionFileSizeBytes, updateSessionInIndex, removeSessionFromIndex, } from "../project/session.js";
5
5
  import { getContextBreakdown } from "../project/context-breakdown.js";
6
6
  import { setActiveSession, getActiveSession } from "./chat.js";
7
7
  import { setActiveProject } from "./fs.js";
8
8
  import { wasSessionInterrupted, clearInterruptedFlag } from "../project/sdk-bridge.js";
9
9
  import { log } from "../logger.js";
10
- registerHandler("session", function (clientId, message) {
10
+ registerHandler("session", async function (clientId, message) {
11
11
  if (message.type === "session:list_request") {
12
12
  var listReqMsg = message;
13
13
  var offset = listReqMsg.offset || 0;
@@ -71,7 +71,15 @@ registerHandler("session", function (clientId, message) {
71
71
  if (message.type === "session:create") {
72
72
  var createMsg = message;
73
73
  var session = createSession(createMsg.projectSlug);
74
+ updateSessionInIndex(createMsg.projectSlug, session);
74
75
  sendTo(clientId, { type: "session:created", session });
76
+ broadcastToProject(createMsg.projectSlug, {
77
+ type: "session:list",
78
+ projectSlug: createMsg.projectSlug,
79
+ sessions: [session],
80
+ totalCount: undefined,
81
+ offset: 0,
82
+ });
75
83
  return;
76
84
  }
77
85
  if (message.type === "session:activate") {
@@ -79,12 +87,20 @@ registerHandler("session", function (clientId, message) {
79
87
  setActiveSession(clientId, activateMsg.projectSlug, activateMsg.sessionId);
80
88
  setActiveProject(clientId, activateMsg.projectSlug);
81
89
  invalidateHistoryCache(activateMsg.sessionId);
82
- var fileSize = getSessionFileSizeBytes(activateMsg.projectSlug, activateMsg.sessionId);
90
+ var fileSize = await getSessionFileSizeBytes(activateMsg.projectSlug, activateMsg.sessionId);
83
91
  sendTo(clientId, { type: "session:loading_progress", sessionId: activateMsg.sessionId, fileSize });
84
92
  var activateT0 = Date.now();
85
- void loadSessionHistory(activateMsg.projectSlug, activateMsg.sessionId).then(function (historyResult) {
86
- log.session("session:activate history: %dms", Date.now() - activateT0);
87
- var title = null;
93
+ void Promise.all([
94
+ loadSessionHistory(activateMsg.projectSlug, activateMsg.sessionId),
95
+ getSessionTitle(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
96
+ getSessionUsage(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
97
+ getContextBreakdown(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
98
+ ]).then(function (results) {
99
+ var historyResult = results[0];
100
+ var sessionTitle = results[1];
101
+ var usage = results[2];
102
+ var breakdown = results[3];
103
+ log.session("session:activate: %dms", Date.now() - activateT0);
88
104
  var interrupted = wasSessionInterrupted(activateMsg.sessionId);
89
105
  if (interrupted) {
90
106
  clearInterruptedFlag(activateMsg.sessionId);
@@ -94,60 +110,33 @@ registerHandler("session", function (clientId, message) {
94
110
  projectSlug: activateMsg.projectSlug,
95
111
  sessionId: activateMsg.sessionId,
96
112
  messages: historyResult.messages,
97
- title: title,
113
+ title: sessionTitle,
98
114
  interrupted: interrupted || undefined,
99
115
  totalMessages: historyResult.totalMessages,
100
116
  hasMore: historyResult.hasMore,
101
117
  });
118
+ if (usage) {
119
+ sendTo(clientId, {
120
+ type: "chat:context_usage",
121
+ inputTokens: usage.inputTokens,
122
+ outputTokens: usage.outputTokens,
123
+ cacheReadTokens: usage.cacheReadTokens,
124
+ cacheCreationTokens: usage.cacheCreationTokens,
125
+ contextWindow: usage.contextWindow,
126
+ });
127
+ }
128
+ if (breakdown) {
129
+ sendTo(clientId, {
130
+ type: "chat:context_breakdown",
131
+ segments: breakdown.segments,
132
+ contextWindow: breakdown.contextWindow,
133
+ autocompactAt: breakdown.autocompactAt,
134
+ });
135
+ }
102
136
  }).catch(function (err) {
103
- log.session("Error sending session history: %O", err);
104
- sendTo(clientId, { type: "chat:error", message: "Failed to load session history" });
137
+ log.session("Failed to activate session: %O", err);
138
+ sendTo(clientId, { type: "chat:error", message: "Failed to load session" });
105
139
  });
106
- setTimeout(function () {
107
- void getSessionTitle(activateMsg.projectSlug, activateMsg.sessionId).then(function (sessionTitle) {
108
- if (sessionTitle) {
109
- sendTo(clientId, { type: "session:history", projectSlug: activateMsg.projectSlug, sessionId: activateMsg.sessionId, messages: [], title: sessionTitle });
110
- }
111
- }).catch(function () { });
112
- void Promise.all([
113
- getSessionUsage(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
114
- getContextBreakdown(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
115
- ]).then(function (results) {
116
- try {
117
- var usage = results[0];
118
- if (usage) {
119
- sendTo(clientId, {
120
- type: "chat:context_usage",
121
- inputTokens: usage.inputTokens,
122
- outputTokens: usage.outputTokens,
123
- cacheReadTokens: usage.cacheReadTokens,
124
- cacheCreationTokens: usage.cacheCreationTokens,
125
- contextWindow: usage.contextWindow,
126
- });
127
- }
128
- }
129
- catch (err) {
130
- log.session("Error sending context usage: %O", err);
131
- }
132
- try {
133
- var breakdown = results[1];
134
- if (breakdown) {
135
- sendTo(clientId, {
136
- type: "chat:context_breakdown",
137
- segments: breakdown.segments,
138
- contextWindow: breakdown.contextWindow,
139
- autocompactAt: breakdown.autocompactAt,
140
- });
141
- }
142
- }
143
- catch (err) {
144
- log.session("Error sending context breakdown: %O", err);
145
- }
146
- }).catch(function (err) {
147
- log.session("Failed to activate session: %O", err);
148
- sendTo(clientId, { type: "chat:error", message: "Failed to activate session" });
149
- });
150
- }, 50);
151
140
  return;
152
141
  }
153
142
  if (message.type === "session:rename") {
@@ -160,7 +149,7 @@ registerHandler("session", function (clientId, message) {
160
149
  void renameSession(projectSlug, renameMsg.sessionId, renameMsg.title).then(function () {
161
150
  invalidateSessionCache(projectSlug);
162
151
  void listSessions(projectSlug, { limit: 40 }).then(function (result) {
163
- sendTo(clientId, {
152
+ broadcastToProject(projectSlug, {
164
153
  type: "session:list",
165
154
  projectSlug,
166
155
  sessions: result.sessions,
@@ -179,9 +168,9 @@ registerHandler("session", function (clientId, message) {
179
168
  return;
180
169
  }
181
170
  void deleteSession(deleteProjectSlug, deleteMsg.sessionId).then(function () {
182
- invalidateSessionCache(deleteProjectSlug);
171
+ removeSessionFromIndex(deleteProjectSlug, deleteMsg.sessionId);
183
172
  void listSessions(deleteProjectSlug, { limit: 40 }).then(function (result) {
184
- sendTo(clientId, {
173
+ broadcastToProject(deleteProjectSlug, {
185
174
  type: "session:list",
186
175
  projectSlug: deleteProjectSlug,
187
176
  sessions: result.sessions,