@cryptiklemur/lattice 5.5.0 → 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.
- package/dist/client/assets/{angular-html-Bmjy4gNc.js → angular-html-B_LrO4d_.js} +1 -1
- package/dist/client/assets/{angular-ts-10l13C4C.js → angular-ts-DG2-AEn3.js} +1 -1
- package/dist/client/assets/{apl--Bn7yH0T.js → apl-D9AV-2KJ.js} +1 -1
- package/dist/client/assets/{astro-C8P9TQkP.js → astro-D55z-bYO.js} +1 -1
- package/dist/client/assets/{blade-DQiHYnIS.js → blade-Bs-tcMis.js} +1 -1
- package/dist/client/assets/{c-BFny-WnE.js → c-CAs-oCwZ.js} +1 -1
- package/dist/client/assets/{cobol-hGEexMYH.js → cobol-0B2WKsi0.js} +1 -1
- package/dist/client/assets/{coffee-j3W4ZALV.js → coffee-BLi0y75x.js} +1 -1
- package/dist/client/assets/{cpp-DquWJNps.js → cpp-CTXpwK-o.js} +1 -1
- package/dist/client/assets/{crystal-CWL31dIV.js → crystal-B_OPrNIv.js} +1 -1
- package/dist/client/assets/{css-Di4GSq8V.js → css-C9nlz4Z2.js} +1 -1
- package/dist/client/assets/{dist-CaJdL3Va.js → dist-CzUlrFrL.js} +2 -2
- package/dist/client/assets/{edge-rK4d050l.js → edge-DE3SfmPJ.js} +1 -1
- package/dist/client/assets/{elixir-BMFKKu5z.js → elixir-BS_1fIPf.js} +1 -1
- package/dist/client/assets/{elm-B0QVlW3-.js → elm-CrfkxZpb.js} +1 -1
- package/dist/client/assets/{erb-C9w9QbhL.js → erb-B6Ab2Y07.js} +1 -1
- package/dist/client/assets/{git-rebase-FaylS7Vl.js → git-rebase-RkfMNUtm.js} +1 -1
- package/dist/client/assets/{glimmer-js-Bi2XnbHi.js → glimmer-js-CBA96B3V.js} +1 -1
- package/dist/client/assets/{glimmer-ts-DOdxK6E6.js → glimmer-ts-WPYNrOak.js} +1 -1
- package/dist/client/assets/{glsl-BiGDIGbZ.js → glsl-BVPbkPwC.js} +1 -1
- package/dist/client/assets/{graphql-CZJB8KIP.js → graphql-CplPyf-J.js} +1 -1
- package/dist/client/assets/{hack-CNGYBFqH.js → hack-Bqnr1D44.js} +1 -1
- package/dist/client/assets/{haml-pf3Zh5MH.js → haml-Bk9H5oqC.js} +1 -1
- package/dist/client/assets/{handlebars-BsQwsvPC.js → handlebars-Dho-7JSJ.js} +1 -1
- package/dist/client/assets/{html-Dj4Hvv69.js → html-Dx1dAJ5K.js} +1 -1
- package/dist/client/assets/{html-derivative-CE7dQ_mI.js → html-derivative-DMEkbacz.js} +1 -1
- package/dist/client/assets/{http-DjOuMI1u.js → http-yzZRblhh.js} +1 -1
- package/dist/client/assets/{hurl-BH_MQrQo.js → hurl-DwA-puXE.js} +1 -1
- package/dist/client/assets/{index-DA8ZBSYW.js → index-BV9isded.js} +2 -2
- package/dist/client/assets/{java-CnpykAva.js → java-da4cNL_S.js} +1 -1
- package/dist/client/assets/{javascript-BbqQ1sQM.js → javascript-BRBtsTGZ.js} +1 -1
- package/dist/client/assets/{jinja-DddpvWwk.js → jinja-KYkTqvLF.js} +1 -1
- package/dist/client/assets/{jison-Bd3G2n4x.js → jison-CpJZ2qu3.js} +1 -1
- package/dist/client/assets/{json-F0eTFQKP.js → json-1ZKXsOcj.js} +1 -1
- package/dist/client/assets/{jsx-B-wB5nUs.js → jsx-Ci3IutCK.js} +1 -1
- package/dist/client/assets/{julia-9Lfz4b5s.js → julia-8BIB33ff.js} +1 -1
- package/dist/client/assets/{just-BVE1sBJx.js → just-BlxIBqAl.js} +1 -1
- package/dist/client/assets/{latex-SF3xMSWG.js → latex-Dt6aIDIN.js} +1 -1
- package/dist/client/assets/{liquid-CLkbZ0ZV.js → liquid-rNU0f2fO.js} +1 -1
- package/dist/client/assets/{lua-DNph6TL8.js → lua-9O59duWh.js} +1 -1
- package/dist/client/assets/{marko-CGqdEtbF.js → marko-cSK6Q4M9.js} +1 -1
- package/dist/client/assets/{mdc-BLpzcq82.js → mdc-DD0DONjK.js} +1 -1
- package/dist/client/assets/{nginx-s7ciyZD1.js → nginx-Bfzt7qPl.js} +1 -1
- package/dist/client/assets/{nim-C4FeZI3R.js → nim-DOD72OhS.js} +1 -1
- package/dist/client/assets/{perl-GDfD5AIi.js → perl-1cg42R6O.js} +1 -1
- package/dist/client/assets/{php-CusAuy6y.js → php-DfG0eJE0.js} +1 -1
- package/dist/client/assets/{pug-DCKuXO7x.js → pug-BJ4ktrjh.js} +1 -1
- package/dist/client/assets/{qml-rp1ZHoeo.js → qml-BwGhenQ4.js} +1 -1
- package/dist/client/assets/{r-CwNbYaVb.js → r-DSKy_gZ9.js} +1 -1
- package/dist/client/assets/{razor-CinY5yf4.js → razor-morGoSMw.js} +1 -1
- package/dist/client/assets/{regexp-CxVI4rKV.js → regexp-DSQzd7Gw.js} +1 -1
- package/dist/client/assets/{rst-BrBmBgUr.js → rst-CQ4DB61w.js} +1 -1
- package/dist/client/assets/{ruby-BzNCpRio.js → ruby-dcUoNsdX.js} +1 -1
- package/dist/client/assets/{sas-B4IAap7m.js → sas-BckOIwmR.js} +1 -1
- package/dist/client/assets/{scss-eNAsUEA1.js → scss-CIDQomQQ.js} +1 -1
- package/dist/client/assets/{shellscript-BwH0aChW.js → shellscript-DscXCB_p.js} +1 -1
- package/dist/client/assets/{shellsession-B5ibJBbh.js → shellsession-C9EjZURj.js} +1 -1
- package/dist/client/assets/{soy-BrqReQP3.js → soy-CVzyKHIE.js} +1 -1
- package/dist/client/assets/{sql-KIDgTieS.js → sql-Ddk-vRG3.js} +1 -1
- package/dist/client/assets/{stata-D6yim7Rr.js → stata-BGl5cxb8.js} +1 -1
- package/dist/client/assets/{surrealql-C51iMPEA.js → surrealql-3IZgL_Ze.js} +1 -1
- package/dist/client/assets/{svelte-DYs5QSVt.js → svelte-BRM0niF5.js} +1 -1
- package/dist/client/assets/{templ-CLDdaEXl.js → templ-qbgb30u-.js} +1 -1
- package/dist/client/assets/{tex-D16HBCoj.js → tex-Ct_-hpJo.js} +1 -1
- package/dist/client/assets/{ts-tags-DTcB2bBd.js → ts-tags-C85B12Rc.js} +1 -1
- package/dist/client/assets/{tsx-BxGfVt8a.js → tsx-d401eVG9.js} +1 -1
- package/dist/client/assets/{twig-BUC08jwe.js → twig-CEvQ_MDP.js} +1 -1
- package/dist/client/assets/{typescript-CuPGT2MR.js → typescript-y2NNcyKH.js} +1 -1
- package/dist/client/assets/{vue-_ip8-SIk.js → vue-CiTZbygT.js} +1 -1
- package/dist/client/assets/{vue-html-D-I9-U-x.js → vue-html-B4nYUWKJ.js} +1 -1
- package/dist/client/assets/{vue-vine-7AONB1tt.js → vue-vine-DdfbYtGV.js} +1 -1
- package/dist/client/assets/{xml-D2ZUDsPU.js → xml-1B2HtnyO.js} +1 -1
- package/dist/client/assets/{xsl-GlFKKlLy.js → xsl-DoRYSDxo.js} +1 -1
- package/dist/client/assets/{yaml-BnuTxWck.js → yaml-DpbRFfH9.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/sw.js +1 -1
- package/dist/server/daemon.js +60 -27
- package/dist/server/handlers/attachment.js +66 -29
- package/dist/server/handlers/fs.js +58 -72
- package/dist/server/handlers/memory.js +37 -52
- package/dist/server/handlers/mesh.js +4 -1
- package/dist/server/handlers/session.js +38 -57
- package/dist/server/mesh/connector.js +85 -1
- package/dist/server/project/file-browser.js +14 -14
- package/dist/server/project/session.js +7 -4
- package/dist/server/ws/broadcast.js +60 -0
- package/package.json +1 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { registerHandler } from "../ws/router.js";
|
|
2
|
-
import { sendTo,
|
|
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 {
|
|
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);
|
|
@@ -16,7 +18,7 @@ export function clearActiveProject(clientId) {
|
|
|
16
18
|
export function getActiveProjectForClient(clientId) {
|
|
17
19
|
return activeProjectByClient.get(clientId);
|
|
18
20
|
}
|
|
19
|
-
registerHandler("fs", function (clientId, message) {
|
|
21
|
+
registerHandler("fs", async function (clientId, message) {
|
|
20
22
|
if (message.type === "fs:list") {
|
|
21
23
|
var listMsg = message;
|
|
22
24
|
var projectSlug = activeProjectByClient.get(clientId) || listMsg.projectSlug;
|
|
@@ -33,7 +35,7 @@ registerHandler("fs", function (clientId, message) {
|
|
|
33
35
|
sendTo(clientId, { type: "chat:error", message: "Project not found: " + projectSlug });
|
|
34
36
|
return;
|
|
35
37
|
}
|
|
36
|
-
var entries = listDirectory(project.path, listMsg.path);
|
|
38
|
+
var entries = await listDirectory(project.path, listMsg.path);
|
|
37
39
|
sendTo(clientId, { type: "fs:list_result", path: listMsg.path, entries });
|
|
38
40
|
return;
|
|
39
41
|
}
|
|
@@ -53,7 +55,7 @@ registerHandler("fs", function (clientId, message) {
|
|
|
53
55
|
sendTo(clientId, { type: "chat:error", message: "Project not found: " + projectSlugRead });
|
|
54
56
|
return;
|
|
55
57
|
}
|
|
56
|
-
var content = readFile(projectRead.path, readMsg.path);
|
|
58
|
+
var content = await readFile(projectRead.path, readMsg.path);
|
|
57
59
|
if (content === null) {
|
|
58
60
|
sendTo(clientId, { type: "chat:error", message: "Cannot read file: " + readMsg.path });
|
|
59
61
|
return;
|
|
@@ -73,12 +75,12 @@ registerHandler("fs", function (clientId, message) {
|
|
|
73
75
|
sendTo(clientId, { type: "chat:error", message: "Project not found: " + projectSlugWrite });
|
|
74
76
|
return;
|
|
75
77
|
}
|
|
76
|
-
var ok = writeFile(projectWrite.path, writeMsg.path, writeMsg.content);
|
|
78
|
+
var ok = await writeFile(projectWrite.path, writeMsg.path, writeMsg.content);
|
|
77
79
|
if (!ok) {
|
|
78
80
|
sendTo(clientId, { type: "chat:error", message: "Cannot write file: " + writeMsg.path });
|
|
79
81
|
return;
|
|
80
82
|
}
|
|
81
|
-
|
|
83
|
+
broadcastToProject(projectSlugWrite, { type: "fs:changed", path: writeMsg.path });
|
|
82
84
|
return;
|
|
83
85
|
}
|
|
84
86
|
});
|
|
@@ -89,59 +91,49 @@ function resolvePath(path) {
|
|
|
89
91
|
return join(homedir(), path.slice(2));
|
|
90
92
|
return path;
|
|
91
93
|
}
|
|
92
|
-
function detectProjectName(dirPath) {
|
|
94
|
+
async function detectProjectName(dirPath) {
|
|
93
95
|
try {
|
|
94
96
|
var pkgPath = join(dirPath, "package.json");
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return pkg.name;
|
|
99
|
-
}
|
|
97
|
+
var pkg = JSON.parse(await fsReadFile(pkgPath, "utf-8"));
|
|
98
|
+
if (pkg.name)
|
|
99
|
+
return pkg.name;
|
|
100
100
|
}
|
|
101
101
|
catch { }
|
|
102
102
|
try {
|
|
103
103
|
var cargoPath = join(dirPath, "Cargo.toml");
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return cargoMatch[1];
|
|
109
|
-
}
|
|
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];
|
|
110
108
|
}
|
|
111
109
|
catch { }
|
|
112
110
|
try {
|
|
113
111
|
var composerPath = join(dirPath, "composer.json");
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return composer.name;
|
|
118
|
-
}
|
|
112
|
+
var composer = JSON.parse(await fsReadFile(composerPath, "utf-8"));
|
|
113
|
+
if (composer.name)
|
|
114
|
+
return composer.name;
|
|
119
115
|
}
|
|
120
116
|
catch { }
|
|
121
117
|
try {
|
|
122
118
|
var pyprojectPath = join(dirPath, "pyproject.toml");
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return pyMatch[1];
|
|
128
|
-
}
|
|
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];
|
|
129
123
|
}
|
|
130
124
|
catch { }
|
|
131
125
|
try {
|
|
132
126
|
var goModPath = join(dirPath, "go.mod");
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return parts[parts.length - 1];
|
|
139
|
-
}
|
|
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];
|
|
140
132
|
}
|
|
141
133
|
}
|
|
142
134
|
catch { }
|
|
143
135
|
try {
|
|
144
|
-
var entries =
|
|
136
|
+
var entries = await readdir(dirPath);
|
|
145
137
|
for (var i = 0; i < entries.length; i++) {
|
|
146
138
|
if (entries[i].endsWith(".sln") || entries[i].endsWith(".csproj")) {
|
|
147
139
|
return entries[i].replace(/\.[^.]+$/, "");
|
|
@@ -151,18 +143,14 @@ function detectProjectName(dirPath) {
|
|
|
151
143
|
catch { }
|
|
152
144
|
return null;
|
|
153
145
|
}
|
|
154
|
-
registerHandler("browse", function (clientId, message) {
|
|
146
|
+
registerHandler("browse", async function (clientId, message) {
|
|
155
147
|
if (message.type === "browse:list") {
|
|
156
148
|
var browseMsg = message;
|
|
157
149
|
var resolvedPath = resolvePath(browseMsg.path);
|
|
158
150
|
var home = homedir();
|
|
159
|
-
if (!existsSync(resolvedPath)) {
|
|
160
|
-
sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: [] });
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
151
|
try {
|
|
164
|
-
var
|
|
165
|
-
if (!
|
|
152
|
+
var pathStat = await stat(resolvedPath);
|
|
153
|
+
if (!pathStat.isDirectory()) {
|
|
166
154
|
sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: [] });
|
|
167
155
|
return;
|
|
168
156
|
}
|
|
@@ -172,7 +160,7 @@ registerHandler("browse", function (clientId, message) {
|
|
|
172
160
|
return;
|
|
173
161
|
}
|
|
174
162
|
try {
|
|
175
|
-
var dirEntries =
|
|
163
|
+
var dirEntries = await readdir(resolvedPath, { withFileTypes: true });
|
|
176
164
|
var results = [];
|
|
177
165
|
for (var i = 0; i < dirEntries.length; i++) {
|
|
178
166
|
var entry = dirEntries[i];
|
|
@@ -180,7 +168,7 @@ registerHandler("browse", function (clientId, message) {
|
|
|
180
168
|
continue;
|
|
181
169
|
var entryPath = join(resolvedPath, entry.name);
|
|
182
170
|
var hasClaudeMd = existsSync(join(entryPath, "CLAUDE.md"));
|
|
183
|
-
var projectName = detectProjectName(entryPath);
|
|
171
|
+
var projectName = await detectProjectName(entryPath);
|
|
184
172
|
results.push({
|
|
185
173
|
name: entry.name,
|
|
186
174
|
path: entryPath,
|
|
@@ -201,35 +189,33 @@ registerHandler("browse", function (clientId, message) {
|
|
|
201
189
|
var config = loadConfig();
|
|
202
190
|
var existingPaths = new Set(config.projects.map(function (p) { return p.path; }));
|
|
203
191
|
var suggestions = [];
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (!stat.isDirectory())
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
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())
|
|
220
204
|
continue;
|
|
221
|
-
}
|
|
222
|
-
var hasClaudeMd = existsSync(join(candidatePath, "CLAUDE.md"));
|
|
223
|
-
var name = candidatePath.split("/").pop() || hashDir;
|
|
224
|
-
suggestions.push({
|
|
225
|
-
path: candidatePath,
|
|
226
|
-
name: name,
|
|
227
|
-
hasClaudeMd: hasClaudeMd,
|
|
228
|
-
});
|
|
229
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
|
+
});
|
|
230
216
|
}
|
|
231
|
-
catch { }
|
|
232
217
|
}
|
|
218
|
+
catch { }
|
|
233
219
|
suggestions.sort(function (a, b) { return a.name.localeCompare(b.name); });
|
|
234
220
|
sendTo(clientId, { type: "browse:suggestions_result", suggestions: suggestions });
|
|
235
221
|
return;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
71
|
+
await writeFile(join(memoryDir, "MEMORY.md"), lines.join("\n"), "utf-8");
|
|
71
72
|
}
|
|
72
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
126
|
-
|
|
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
|
|
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
|
-
|
|
161
|
-
regenerateIndex(delDir);
|
|
155
|
+
await unlink(filePath);
|
|
156
|
+
await regenerateIndex(delDir);
|
|
162
157
|
sendTo(clientId, { type: "memory:delete_result", success: true });
|
|
163
|
-
var
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import { registerHandler } from "../ws/router.js";
|
|
2
|
-
import { sendTo,
|
|
2
|
+
import { sendTo, broadcastToProject } from "../ws/broadcast.js";
|
|
3
3
|
import { loadConfig } from "../config.js";
|
|
4
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";
|
|
@@ -7,7 +7,7 @@ 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;
|
|
@@ -73,7 +73,7 @@ registerHandler("session", function (clientId, message) {
|
|
|
73
73
|
var session = createSession(createMsg.projectSlug);
|
|
74
74
|
updateSessionInIndex(createMsg.projectSlug, session);
|
|
75
75
|
sendTo(clientId, { type: "session:created", session });
|
|
76
|
-
|
|
76
|
+
broadcastToProject(createMsg.projectSlug, {
|
|
77
77
|
type: "session:list",
|
|
78
78
|
projectSlug: createMsg.projectSlug,
|
|
79
79
|
sessions: [session],
|
|
@@ -87,12 +87,20 @@ registerHandler("session", function (clientId, message) {
|
|
|
87
87
|
setActiveSession(clientId, activateMsg.projectSlug, activateMsg.sessionId);
|
|
88
88
|
setActiveProject(clientId, activateMsg.projectSlug);
|
|
89
89
|
invalidateHistoryCache(activateMsg.sessionId);
|
|
90
|
-
var fileSize = getSessionFileSizeBytes(activateMsg.projectSlug, activateMsg.sessionId);
|
|
90
|
+
var fileSize = await getSessionFileSizeBytes(activateMsg.projectSlug, activateMsg.sessionId);
|
|
91
91
|
sendTo(clientId, { type: "session:loading_progress", sessionId: activateMsg.sessionId, fileSize });
|
|
92
92
|
var activateT0 = Date.now();
|
|
93
|
-
void
|
|
94
|
-
|
|
95
|
-
|
|
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);
|
|
96
104
|
var interrupted = wasSessionInterrupted(activateMsg.sessionId);
|
|
97
105
|
if (interrupted) {
|
|
98
106
|
clearInterruptedFlag(activateMsg.sessionId);
|
|
@@ -102,60 +110,33 @@ registerHandler("session", function (clientId, message) {
|
|
|
102
110
|
projectSlug: activateMsg.projectSlug,
|
|
103
111
|
sessionId: activateMsg.sessionId,
|
|
104
112
|
messages: historyResult.messages,
|
|
105
|
-
title:
|
|
113
|
+
title: sessionTitle,
|
|
106
114
|
interrupted: interrupted || undefined,
|
|
107
115
|
totalMessages: historyResult.totalMessages,
|
|
108
116
|
hasMore: historyResult.hasMore,
|
|
109
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
|
+
}
|
|
110
136
|
}).catch(function (err) {
|
|
111
|
-
log.session("
|
|
112
|
-
sendTo(clientId, { type: "chat:error", message: "Failed to load session
|
|
137
|
+
log.session("Failed to activate session: %O", err);
|
|
138
|
+
sendTo(clientId, { type: "chat:error", message: "Failed to load session" });
|
|
113
139
|
});
|
|
114
|
-
setTimeout(function () {
|
|
115
|
-
void getSessionTitle(activateMsg.projectSlug, activateMsg.sessionId).then(function (sessionTitle) {
|
|
116
|
-
if (sessionTitle) {
|
|
117
|
-
sendTo(clientId, { type: "session:history", projectSlug: activateMsg.projectSlug, sessionId: activateMsg.sessionId, messages: [], title: sessionTitle });
|
|
118
|
-
}
|
|
119
|
-
}).catch(function () { });
|
|
120
|
-
void Promise.all([
|
|
121
|
-
getSessionUsage(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
|
|
122
|
-
getContextBreakdown(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
|
|
123
|
-
]).then(function (results) {
|
|
124
|
-
try {
|
|
125
|
-
var usage = results[0];
|
|
126
|
-
if (usage) {
|
|
127
|
-
sendTo(clientId, {
|
|
128
|
-
type: "chat:context_usage",
|
|
129
|
-
inputTokens: usage.inputTokens,
|
|
130
|
-
outputTokens: usage.outputTokens,
|
|
131
|
-
cacheReadTokens: usage.cacheReadTokens,
|
|
132
|
-
cacheCreationTokens: usage.cacheCreationTokens,
|
|
133
|
-
contextWindow: usage.contextWindow,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
catch (err) {
|
|
138
|
-
log.session("Error sending context usage: %O", err);
|
|
139
|
-
}
|
|
140
|
-
try {
|
|
141
|
-
var breakdown = results[1];
|
|
142
|
-
if (breakdown) {
|
|
143
|
-
sendTo(clientId, {
|
|
144
|
-
type: "chat:context_breakdown",
|
|
145
|
-
segments: breakdown.segments,
|
|
146
|
-
contextWindow: breakdown.contextWindow,
|
|
147
|
-
autocompactAt: breakdown.autocompactAt,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
catch (err) {
|
|
152
|
-
log.session("Error sending context breakdown: %O", err);
|
|
153
|
-
}
|
|
154
|
-
}).catch(function (err) {
|
|
155
|
-
log.session("Failed to activate session: %O", err);
|
|
156
|
-
sendTo(clientId, { type: "chat:error", message: "Failed to activate session" });
|
|
157
|
-
});
|
|
158
|
-
}, 50);
|
|
159
140
|
return;
|
|
160
141
|
}
|
|
161
142
|
if (message.type === "session:rename") {
|
|
@@ -168,7 +149,7 @@ registerHandler("session", function (clientId, message) {
|
|
|
168
149
|
void renameSession(projectSlug, renameMsg.sessionId, renameMsg.title).then(function () {
|
|
169
150
|
invalidateSessionCache(projectSlug);
|
|
170
151
|
void listSessions(projectSlug, { limit: 40 }).then(function (result) {
|
|
171
|
-
|
|
152
|
+
broadcastToProject(projectSlug, {
|
|
172
153
|
type: "session:list",
|
|
173
154
|
projectSlug,
|
|
174
155
|
sessions: result.sessions,
|
|
@@ -189,7 +170,7 @@ registerHandler("session", function (clientId, message) {
|
|
|
189
170
|
void deleteSession(deleteProjectSlug, deleteMsg.sessionId).then(function () {
|
|
190
171
|
removeSessionFromIndex(deleteProjectSlug, deleteMsg.sessionId);
|
|
191
172
|
void listSessions(deleteProjectSlug, { limit: 40 }).then(function (result) {
|
|
192
|
-
|
|
173
|
+
broadcastToProject(deleteProjectSlug, {
|
|
193
174
|
type: "session:list",
|
|
194
175
|
projectSlug: deleteProjectSlug,
|
|
195
176
|
sessions: result.sessions,
|