@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.
- package/README.md +114 -0
- package/dist/client/assets/{angular-html-BMCIs4B9.js → angular-html-B_LrO4d_.js} +1 -1
- package/dist/client/assets/{angular-ts-Dl9U67Xi.js → angular-ts-DG2-AEn3.js} +1 -1
- package/dist/client/assets/{apl-Dc2Jp1EP.js → apl-D9AV-2KJ.js} +1 -1
- package/dist/client/assets/{astro-KOvSEh6g.js → astro-D55z-bYO.js} +1 -1
- package/dist/client/assets/{blade-Bp9mbJ5K.js → blade-Bs-tcMis.js} +1 -1
- package/dist/client/assets/{c-C0ETB1bm.js → c-CAs-oCwZ.js} +1 -1
- package/dist/client/assets/{cobol-DE5VmQJX.js → cobol-0B2WKsi0.js} +1 -1
- package/dist/client/assets/{coffee-COzJhfRU.js → coffee-BLi0y75x.js} +1 -1
- package/dist/client/assets/{cpp-jzhs79Ii.js → cpp-CTXpwK-o.js} +1 -1
- package/dist/client/assets/{crystal-Czf3Etrk.js → crystal-B_OPrNIv.js} +1 -1
- package/dist/client/assets/{css-Cf45eU4M.js → css-C9nlz4Z2.js} +1 -1
- package/dist/client/assets/{dist-Bk-EVR9B.js → dist-CzUlrFrL.js} +2 -2
- package/dist/client/assets/{edge-BmKSsYuX.js → edge-DE3SfmPJ.js} +1 -1
- package/dist/client/assets/{elixir-Ds7FEpTU.js → elixir-BS_1fIPf.js} +1 -1
- package/dist/client/assets/{elm-C_AmF7eK.js → elm-CrfkxZpb.js} +1 -1
- package/dist/client/assets/{erb-DevlC2ei.js → erb-B6Ab2Y07.js} +1 -1
- package/dist/client/assets/{git-rebase-CpJCeeHA.js → git-rebase-RkfMNUtm.js} +1 -1
- package/dist/client/assets/{glimmer-js-CFH-Kzxa.js → glimmer-js-CBA96B3V.js} +1 -1
- package/dist/client/assets/{glimmer-ts-BkiQ1tjj.js → glimmer-ts-WPYNrOak.js} +1 -1
- package/dist/client/assets/{glsl-DLuvkGQM.js → glsl-BVPbkPwC.js} +1 -1
- package/dist/client/assets/{graphql-CYjDXA-l.js → graphql-CplPyf-J.js} +1 -1
- package/dist/client/assets/{hack-CDfrJ_Sc.js → hack-Bqnr1D44.js} +1 -1
- package/dist/client/assets/{haml-C4YjrCNF.js → haml-Bk9H5oqC.js} +1 -1
- package/dist/client/assets/{handlebars-WIT13u08.js → handlebars-Dho-7JSJ.js} +1 -1
- package/dist/client/assets/{html-ntqTOVvn.js → html-Dx1dAJ5K.js} +1 -1
- package/dist/client/assets/{html-derivative-B3rc20Rp.js → html-derivative-DMEkbacz.js} +1 -1
- package/dist/client/assets/{http-jTU2ko3B.js → http-yzZRblhh.js} +1 -1
- package/dist/client/assets/{hurl-D2HM7b2B.js → hurl-DwA-puXE.js} +1 -1
- package/dist/client/assets/{index-CZNra7P3.js → index-BV9isded.js} +321 -88
- package/dist/client/assets/{index-DCKRhnC1.css → index-DcwXjiH0.css} +1 -1
- package/dist/client/assets/{java-BofMW2-d.js → java-da4cNL_S.js} +1 -1
- package/dist/client/assets/{javascript-DHlF2jGd.js → javascript-BRBtsTGZ.js} +1 -1
- package/dist/client/assets/{jinja-BXkUuA_p.js → jinja-KYkTqvLF.js} +1 -1
- package/dist/client/assets/{jison-CYSiwIU-.js → jison-CpJZ2qu3.js} +1 -1
- package/dist/client/assets/{json-5cpQWmWq.js → json-1ZKXsOcj.js} +1 -1
- package/dist/client/assets/{jsx-D8kfeKVb.js → jsx-Ci3IutCK.js} +1 -1
- package/dist/client/assets/{julia-BkW_r2Xd.js → julia-8BIB33ff.js} +1 -1
- package/dist/client/assets/{just-Dz61xACw.js → just-BlxIBqAl.js} +1 -1
- package/dist/client/assets/{latex-gqtZNh_M.js → latex-Dt6aIDIN.js} +1 -1
- package/dist/client/assets/{liquid-KcpeevnQ.js → liquid-rNU0f2fO.js} +1 -1
- package/dist/client/assets/{lua-C3Vw3Kyi.js → lua-9O59duWh.js} +1 -1
- package/dist/client/assets/{marko-DVaxNS7b.js → marko-cSK6Q4M9.js} +1 -1
- package/dist/client/assets/{mdc-DRm1pTBY.js → mdc-DD0DONjK.js} +1 -1
- package/dist/client/assets/{nginx-DYudX1m0.js → nginx-Bfzt7qPl.js} +1 -1
- package/dist/client/assets/{nim-DejMEf9z.js → nim-DOD72OhS.js} +1 -1
- package/dist/client/assets/{perl-D09qXnjW.js → perl-1cg42R6O.js} +1 -1
- package/dist/client/assets/{php-BrfQzrDQ.js → php-DfG0eJE0.js} +1 -1
- package/dist/client/assets/{pug-DPbk8XD2.js → pug-BJ4ktrjh.js} +1 -1
- package/dist/client/assets/{qml-BCh8MHc-.js → qml-BwGhenQ4.js} +1 -1
- package/dist/client/assets/{r-DCcyjvCV.js → r-DSKy_gZ9.js} +1 -1
- package/dist/client/assets/{razor-CHeljxcn.js → razor-morGoSMw.js} +1 -1
- package/dist/client/assets/{regexp-DpLv_uhV.js → regexp-DSQzd7Gw.js} +1 -1
- package/dist/client/assets/{rst-BmAGX4li.js → rst-CQ4DB61w.js} +1 -1
- package/dist/client/assets/{ruby-C28IDakj.js → ruby-dcUoNsdX.js} +1 -1
- package/dist/client/assets/{sas-Dq78Tt13.js → sas-BckOIwmR.js} +1 -1
- package/dist/client/assets/{scss-CCq8md_z.js → scss-CIDQomQQ.js} +1 -1
- package/dist/client/assets/{shellscript-Cl_slHLv.js → shellscript-DscXCB_p.js} +1 -1
- package/dist/client/assets/{shellsession-D50FTgCo.js → shellsession-C9EjZURj.js} +1 -1
- package/dist/client/assets/{soy-Da12pRro.js → soy-CVzyKHIE.js} +1 -1
- package/dist/client/assets/{sql-DRKhJcrs.js → sql-Ddk-vRG3.js} +1 -1
- package/dist/client/assets/{stata-E06HAz87.js → stata-BGl5cxb8.js} +1 -1
- package/dist/client/assets/{surrealql-CTaVrdiv.js → surrealql-3IZgL_Ze.js} +1 -1
- package/dist/client/assets/{svelte-j0H5d0BS.js → svelte-BRM0niF5.js} +1 -1
- package/dist/client/assets/{templ-BXfOHYWo.js → templ-qbgb30u-.js} +1 -1
- package/dist/client/assets/{tex-CGBWhBxV.js → tex-Ct_-hpJo.js} +1 -1
- package/dist/client/assets/{ts-tags-C-diQc0f.js → ts-tags-C85B12Rc.js} +1 -1
- package/dist/client/assets/{tsx-CUBPow91.js → tsx-d401eVG9.js} +1 -1
- package/dist/client/assets/{twig-BYoQufij.js → twig-CEvQ_MDP.js} +1 -1
- package/dist/client/assets/{typescript-CAiUPCi4.js → typescript-y2NNcyKH.js} +1 -1
- package/dist/client/assets/{vue-CEKqQRtx.js → vue-CiTZbygT.js} +1 -1
- package/dist/client/assets/{vue-html-DSRnbzbs.js → vue-html-B4nYUWKJ.js} +1 -1
- package/dist/client/assets/{vue-vine-PFaCZbOl.js → vue-vine-DdfbYtGV.js} +1 -1
- package/dist/client/assets/{xml-CpFeNxWK.js → xml-1B2HtnyO.js} +1 -1
- package/dist/client/assets/{xsl-DOlnqqgg.js → xsl-DoRYSDxo.js} +1 -1
- package/dist/client/assets/{yaml-Bv-3IQB_.js → yaml-DpbRFfH9.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +1 -1
- package/dist/server/daemon.js +75 -26
- package/dist/server/features/brainstorm.js +267 -0
- package/dist/server/handlers/attachment.js +66 -29
- package/dist/server/handlers/brainstorm.js +33 -0
- package/dist/server/handlers/fs.js +61 -72
- package/dist/server/handlers/memory.js +37 -52
- package/dist/server/handlers/mesh.js +4 -1
- package/dist/server/handlers/session.js +47 -58
- package/dist/server/mesh/connector.js +85 -1
- package/dist/server/project/file-browser.js +14 -14
- package/dist/server/project/session.js +98 -14
- package/dist/server/ws/broadcast.js +60 -0
- package/package.json +1 -1
|
@@ -1,19 +1,24 @@
|
|
|
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);
|
|
15
17
|
}
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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 =
|
|
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
|
|
162
|
-
if (!
|
|
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 =
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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 {
|
|
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,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
|
|
86
|
-
|
|
87
|
-
|
|
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:
|
|
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("
|
|
104
|
-
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" });
|
|
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
|
-
|
|
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
|
-
|
|
171
|
+
removeSessionFromIndex(deleteProjectSlug, deleteMsg.sessionId);
|
|
183
172
|
void listSessions(deleteProjectSlug, { limit: 40 }).then(function (result) {
|
|
184
|
-
|
|
173
|
+
broadcastToProject(deleteProjectSlug, {
|
|
185
174
|
type: "session:list",
|
|
186
175
|
projectSlug: deleteProjectSlug,
|
|
187
176
|
sessions: result.sessions,
|