@cryptiklemur/lattice 2.0.0 → 4.0.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 (204) hide show
  1. package/bin/lattice +6 -1
  2. package/dist/client/assets/{angular-html-BBi1Szz5.js → angular-html-N8PCEquT.js} +1 -1
  3. package/dist/client/assets/{angular-ts-2PMpSPqc.js → angular-ts-CJ8RJIPD.js} +1 -1
  4. package/dist/client/assets/{apl-B-NEtBvQ.js → apl-BD6tCLWN.js} +1 -1
  5. package/dist/client/assets/{astro-mxH1DA5Z.js → astro-CpIIfBs6.js} +1 -1
  6. package/dist/client/assets/{blade-BUQ9XSm3.js → blade-D3qgnjiV.js} +1 -1
  7. package/dist/client/assets/{c-DrQixLw7.js → c-Dr6ADN_t.js} +1 -1
  8. package/dist/client/assets/{cobol-42amjOMh.js → cobol-BIfDE0Hr.js} +1 -1
  9. package/dist/client/assets/{coffee-CMSEUKyy.js → coffee-DHQ57vfY.js} +1 -1
  10. package/dist/client/assets/{cpp-C4yJBzl2.js → cpp-CEBY6JOp.js} +1 -1
  11. package/dist/client/assets/{crystal-DZ27EoiA.js → crystal-D125CSmP.js} +1 -1
  12. package/dist/client/assets/{css-CdQwkSNm.js → css-CBmrkYSr.js} +1 -1
  13. package/dist/client/assets/{dist-CBRIe7YG.js → dist-A_mCRD1f.js} +2 -2
  14. package/dist/client/assets/{edge-J8VemHZC.js → edge-Ccsz7cJW.js} +1 -1
  15. package/dist/client/assets/{elixir-DU66qSn_.js → elixir-Do6gk14X.js} +1 -1
  16. package/dist/client/assets/{elm-8cz1JjRg.js → elm-Db22zT4C.js} +1 -1
  17. package/dist/client/assets/{erb-DMl63J6s.js → erb-MXVqAAJD.js} +1 -1
  18. package/dist/client/assets/{git-rebase-DjlZL8cj.js → git-rebase-B-LLWBOA.js} +1 -1
  19. package/dist/client/assets/{glimmer-js-Fu7vOQY-.js → glimmer-js-eWszRU73.js} +1 -1
  20. package/dist/client/assets/{glimmer-ts-AJljRs0A.js → glimmer-ts-VQmwGqUp.js} +1 -1
  21. package/dist/client/assets/{glsl-CfodGzSv.js → glsl-B8ilOfAl.js} +1 -1
  22. package/dist/client/assets/{graphql-C8GPKPx1.js → graphql-DnTqxeOc.js} +1 -1
  23. package/dist/client/assets/{hack-Gp6OuE21.js → hack-XJsHYSQb.js} +1 -1
  24. package/dist/client/assets/{haml-BOyG6GfM.js → haml-CQ7Vqzwp.js} +1 -1
  25. package/dist/client/assets/{handlebars-B9IoVRxd.js → handlebars-C4szooBf.js} +1 -1
  26. package/dist/client/assets/{html-CFrSukiU.js → html-B6EgAiSd.js} +1 -1
  27. package/dist/client/assets/{html-derivative-D0_gRK_M.js → html-derivative-DdinogQX.js} +1 -1
  28. package/dist/client/assets/{http-D4MmFixi.js → http-BSLxCgRq.js} +1 -1
  29. package/dist/client/assets/{hurl-D1xbk-LS.js → hurl-pOsTwNfp.js} +1 -1
  30. package/dist/client/assets/{index-DrTbNMzI.css → index-2jfMHAda.css} +1 -1
  31. package/dist/client/assets/{index-ZIuI5BCP.js → index-BHQ_8mvl.js} +62 -62
  32. package/dist/client/assets/{java-CfPMY1Dv.js → java-DRQLiiST.js} +1 -1
  33. package/dist/client/assets/{javascript-B8vNHeqm.js → javascript-DvEK2-47.js} +1 -1
  34. package/dist/client/assets/{jinja-CF0K8bFV.js → jinja-D2NYJ25y.js} +1 -1
  35. package/dist/client/assets/{jison-BP7my5TL.js → jison-DDZaLNAp.js} +1 -1
  36. package/dist/client/assets/{json-TeXNjpMZ.js → json-TGR0NIWd.js} +1 -1
  37. package/dist/client/assets/{jsx-C86NmHc2.js → jsx-BjUoPYga.js} +1 -1
  38. package/dist/client/assets/{julia-C58U_aQP.js → julia-C4gjSpFu.js} +1 -1
  39. package/dist/client/assets/{just-h6Vco8Qu.js → just-H351x5u_.js} +1 -1
  40. package/dist/client/assets/{latex-DRsNisjT.js → latex-BiTmf6gf.js} +1 -1
  41. package/dist/client/assets/{liquid-C9jXC9M7.js → liquid-86ufjRy-.js} +1 -1
  42. package/dist/client/assets/{lua-BUYvdn-F.js → lua-BNxR0F_8.js} +1 -1
  43. package/dist/client/assets/{marko-BqB4tqw-.js → marko-CvRxpRjM.js} +1 -1
  44. package/dist/client/assets/{mdc-C8Rs68Nw.js → mdc-CYbAIy2C.js} +1 -1
  45. package/dist/client/assets/{nginx-Dj6E3HRL.js → nginx-egdgMq-F.js} +1 -1
  46. package/dist/client/assets/{nim-BizJb4iF.js → nim-CXBJVz_w.js} +1 -1
  47. package/dist/client/assets/{perl-DnJAtWNn.js → perl-XRfMobzg.js} +1 -1
  48. package/dist/client/assets/{php-BeY_VEeb.js → php-Br7a8uil.js} +1 -1
  49. package/dist/client/assets/{pug-DXhFbVDh.js → pug-BVbbUVvy.js} +1 -1
  50. package/dist/client/assets/{qml-CngzXsz8.js → qml-ByKvrL1j.js} +1 -1
  51. package/dist/client/assets/{r-BMmy9apu.js → r-mVoV0Ni6.js} +1 -1
  52. package/dist/client/assets/{razor-kIo5DNUk.js → razor-T5O-9UJL.js} +1 -1
  53. package/dist/client/assets/{regexp-BIa5IayX.js → regexp-CioRuhuN.js} +1 -1
  54. package/dist/client/assets/{rst-McWv3WP0.js → rst-V__uTudD.js} +1 -1
  55. package/dist/client/assets/{ruby-CkenUDKe.js → ruby-C_PuKPTI.js} +1 -1
  56. package/dist/client/assets/{sas-Dgn9Yrr5.js → sas-D_DqqQH4.js} +1 -1
  57. package/dist/client/assets/{scss-Tfn9jzBB.js → scss-D-TjzZ4c.js} +1 -1
  58. package/dist/client/assets/{shellscript-vGpgBWOS.js → shellscript-E5759VHu.js} +1 -1
  59. package/dist/client/assets/{shellsession-7CA0dOzR.js → shellsession-AESTM-Pv.js} +1 -1
  60. package/dist/client/assets/{soy-DbBURueN.js → soy-QrbrrcDv.js} +1 -1
  61. package/dist/client/assets/{sql-BJGtbEM8.js → sql-0M8VcDHD.js} +1 -1
  62. package/dist/client/assets/{stata-L0kV9FQT.js → stata-CgeIpGtc.js} +1 -1
  63. package/dist/client/assets/{surrealql-C17jcHRl.js → surrealql-DBGwnZbw.js} +1 -1
  64. package/dist/client/assets/{svelte-B5_CmVcu.js → svelte-Cv0PvUc_.js} +1 -1
  65. package/dist/client/assets/{templ-C5f2EORt.js → templ-B9t7xRE4.js} +1 -1
  66. package/dist/client/assets/{tex-DvaBtCMN.js → tex-DhZZ8dr2.js} +1 -1
  67. package/dist/client/assets/{ts-tags-DlZzQdBU.js → ts-tags-BFv8sbnd.js} +1 -1
  68. package/dist/client/assets/{tsx-DaLOIju9.js → tsx-CXC9KSbY.js} +1 -1
  69. package/dist/client/assets/{twig-BB7320dl.js → twig-CM_OO66r.js} +1 -1
  70. package/dist/client/assets/{typescript-D8B-VgXj.js → typescript-BdgOTaoD.js} +1 -1
  71. package/dist/client/assets/{vue-DylGFDrG.js → vue-BnQhjnCm.js} +1 -1
  72. package/dist/client/assets/{vue-html-Dt8r3B9y.js → vue-html-CNnGecRI.js} +1 -1
  73. package/dist/client/assets/{vue-vine-DdN02uhY.js → vue-vine-DCuMkRhK.js} +1 -1
  74. package/dist/client/assets/{xml-CBEuB6I0.js → xml-CbTD7cB8.js} +1 -1
  75. package/dist/client/assets/{xsl-CuhlgDL5.js → xsl-uOqqo7cf.js} +1 -1
  76. package/dist/client/assets/{yaml-Da6ymghi.js → yaml-BNrLoH59.js} +1 -1
  77. package/dist/client/index.html +2 -2
  78. package/dist/client/sw.js +1 -1
  79. package/package.json +5 -2
  80. package/src/client/components/analytics/AnalyticsView.tsx +1 -1
  81. package/src/client/components/analytics/ChartCard.tsx +1 -1
  82. package/src/client/components/analytics/charts/NodeFleetOverview.tsx +1 -1
  83. package/src/client/components/chat/AttachmentChips.tsx +4 -4
  84. package/src/client/components/chat/ChatView.tsx +2 -2
  85. package/src/client/components/chat/CommandPalette.tsx +1 -1
  86. package/src/client/components/chat/ElicitationCard.tsx +3 -0
  87. package/src/client/components/chat/Message.tsx +7 -6
  88. package/src/client/components/chat/PromptQuestion.tsx +1 -1
  89. package/src/client/components/chat/TodoCard.tsx +1 -1
  90. package/src/client/components/chat/ToolGroup.tsx +1 -1
  91. package/src/client/components/chat/ToolResultRenderer.tsx +2 -2
  92. package/src/client/components/dashboard/DashboardView.tsx +1 -1
  93. package/src/client/components/dashboard/ProjectDashboardView.tsx +1 -1
  94. package/src/client/components/mesh/NodeBadge.tsx +1 -1
  95. package/src/client/components/mesh/PairingDialog.tsx +2 -2
  96. package/src/client/components/project-settings/ProjectClaude.tsx +1 -1
  97. package/src/client/components/project-settings/ProjectEnvironment.tsx +1 -1
  98. package/src/client/components/project-settings/ProjectGeneral.tsx +1 -1
  99. package/src/client/components/project-settings/ProjectMcp.tsx +1 -1
  100. package/src/client/components/project-settings/ProjectMemory.tsx +3 -3
  101. package/src/client/components/project-settings/ProjectPermissions.tsx +1 -1
  102. package/src/client/components/project-settings/ProjectPlugins.tsx +1 -1
  103. package/src/client/components/project-settings/ProjectRules.tsx +1 -1
  104. package/src/client/components/project-settings/ProjectSettingsView.tsx +1 -1
  105. package/src/client/components/project-settings/ProjectSkills.tsx +1 -1
  106. package/src/client/components/settings/Appearance.tsx +1 -1
  107. package/src/client/components/settings/BudgetSettings.tsx +1 -1
  108. package/src/client/components/settings/ClaudeSettings.tsx +1 -1
  109. package/src/client/components/settings/Editor.tsx +1 -1
  110. package/src/client/components/settings/Environment.tsx +1 -1
  111. package/src/client/components/settings/GlobalMcp.tsx +2 -2
  112. package/src/client/components/settings/GlobalPlugins.tsx +2 -2
  113. package/src/client/components/settings/GlobalRules.tsx +1 -1
  114. package/src/client/components/settings/GlobalSkills.tsx +1 -1
  115. package/src/client/components/settings/MeshStatus.tsx +1 -1
  116. package/src/client/components/settings/SkillMarketplace.tsx +1 -1
  117. package/src/client/components/settings/ThemeWizard.tsx +1 -1
  118. package/src/client/components/settings/mcp-shared.tsx +1 -1
  119. package/src/client/components/settings/skill-shared.tsx +2 -2
  120. package/src/client/components/setup/SetupWizard.tsx +5 -0
  121. package/src/client/components/sidebar/AddProjectModal.tsx +2 -2
  122. package/src/client/components/sidebar/NodeSettingsModal.tsx +2 -2
  123. package/src/client/components/sidebar/ProjectRail.tsx +1 -1
  124. package/src/client/components/sidebar/SessionList.tsx +2 -2
  125. package/src/client/components/sidebar/Sidebar.tsx +1 -1
  126. package/src/client/components/sidebar/UserIsland.tsx +1 -1
  127. package/src/client/components/ui/CommandPalette.tsx +1 -1
  128. package/src/client/components/ui/IconPicker.tsx +3 -1
  129. package/src/client/components/ui/KeyboardShortcuts.tsx +1 -1
  130. package/src/client/components/ui/UpdateBanner.tsx +1 -1
  131. package/src/client/components/workspace/BookmarksView.tsx +2 -2
  132. package/src/client/components/workspace/FileBrowser.tsx +1 -1
  133. package/src/client/components/workspace/FileTree.tsx +1 -1
  134. package/src/client/components/workspace/NoteCard.tsx +2 -1
  135. package/src/client/components/workspace/NotesView.tsx +1 -1
  136. package/src/client/components/workspace/ScheduledTasksView.tsx +1 -1
  137. package/src/client/components/workspace/TaskCard.tsx +2 -1
  138. package/src/client/components/workspace/TaskEditModal.tsx +2 -2
  139. package/src/client/components/workspace/TerminalInstance.tsx +1 -1
  140. package/src/client/hooks/useAnalytics.ts +7 -7
  141. package/src/client/hooks/useAttachments.ts +49 -16
  142. package/src/client/hooks/useBookmarks.ts +1 -1
  143. package/src/client/hooks/useEditorConfig.ts +1 -1
  144. package/src/client/hooks/useFocusTrap.ts +4 -2
  145. package/src/client/hooks/useIdleDetection.ts +6 -0
  146. package/src/client/hooks/useMesh.ts +1 -1
  147. package/src/client/hooks/useProjectSettings.ts +1 -1
  148. package/src/client/hooks/useProjects.ts +3 -3
  149. package/src/client/hooks/useSession.ts +31 -32
  150. package/src/client/hooks/useSkills.ts +2 -2
  151. package/src/client/hooks/useSpinnerVerb.ts +1 -1
  152. package/src/client/hooks/useVoiceRecorder.ts +5 -2
  153. package/src/client/hooks/useWebSocket.ts +1 -1
  154. package/src/client/providers/WebSocketProvider.tsx +9 -1
  155. package/src/client/router.tsx +1 -1
  156. package/src/client/stores/analytics.ts +1 -1
  157. package/src/client/stores/bookmarks.ts +1 -1
  158. package/src/client/stores/mesh.ts +1 -1
  159. package/src/client/stores/session.ts +1 -11
  160. package/src/client/stores/sidebar.ts +1 -1
  161. package/src/client/styles/global.css +12 -0
  162. package/src/server/analytics/engine.ts +1 -1
  163. package/src/server/auth/passphrase.ts +16 -6
  164. package/src/server/config.ts +2 -2
  165. package/src/server/daemon.ts +32 -12
  166. package/src/server/features/ralph-loop.ts +1 -1
  167. package/src/server/features/scheduler.ts +1 -1
  168. package/src/server/features/sticky-notes.ts +1 -1
  169. package/src/server/handlers/analytics.ts +1 -1
  170. package/src/server/handlers/attachment.ts +4 -3
  171. package/src/server/handlers/bookmarks.ts +1 -1
  172. package/src/server/handlers/chat.ts +1 -1
  173. package/src/server/handlers/editor.ts +1 -1
  174. package/src/server/handlers/fs.ts +1 -1
  175. package/src/server/handlers/loop.ts +1 -1
  176. package/src/server/handlers/memory.ts +1 -1
  177. package/src/server/handlers/mesh.ts +3 -3
  178. package/src/server/handlers/notes.ts +1 -1
  179. package/src/server/handlers/plugins.ts +1 -1
  180. package/src/server/handlers/project-settings.ts +1 -1
  181. package/src/server/handlers/scheduler.ts +1 -1
  182. package/src/server/handlers/session.ts +2 -1
  183. package/src/server/handlers/settings.ts +4 -4
  184. package/src/server/handlers/skills.ts +5 -5
  185. package/src/server/handlers/terminal.ts +12 -1
  186. package/src/server/handlers/themes.ts +1 -1
  187. package/src/server/handlers/update.ts +1 -1
  188. package/src/server/identity.ts +3 -1
  189. package/src/server/index.ts +2 -2
  190. package/src/server/mesh/connector.ts +1 -1
  191. package/src/server/mesh/peers.ts +1 -1
  192. package/src/server/mesh/proxy.ts +1 -1
  193. package/src/server/mesh/session-sync.ts +1 -1
  194. package/src/server/project/bookmarks.ts +1 -1
  195. package/src/server/project/context-breakdown.ts +1 -1
  196. package/src/server/project/file-browser.ts +1 -1
  197. package/src/server/project/registry.ts +1 -1
  198. package/src/server/project/sdk-bridge.ts +28 -2
  199. package/src/server/project/session.ts +7 -6
  200. package/src/server/tls.ts +15 -1
  201. package/src/server/tui.ts +2 -2
  202. package/src/server/ws/router.ts +1 -1
  203. package/tsconfig.json +1 -1
  204. package/vite.config.ts +1 -1
@@ -18,7 +18,7 @@ import { startMeshConnections, onPeerConnected, onPeerDisconnected, onPeerMessag
18
18
  import { handleProxyRequest, handleProxyResponse } from "./mesh/proxy";
19
19
  import { verifyPassphrase, generateSessionToken, addSession, isValidSession } from "./auth/passphrase";
20
20
  import { ensureCerts } from "./tls";
21
- import type { ClientMessage, MeshMessage } from "@lattice/shared";
21
+ import type { ClientMessage, MeshMessage } from "#shared";
22
22
  import { log } from "./logger";
23
23
  import { detectIdeProjectName } from "./handlers/settings";
24
24
  import "./handlers/session";
@@ -336,9 +336,26 @@ export async function startDaemon(portOverride?: number | null): Promise<void> {
336
336
  var app = express();
337
337
  app.use(express.json());
338
338
 
339
- app.post("/auth", function (req, res) {
339
+ var authAttempts = new Map<string, number[]>();
340
+ var AUTH_RATE_LIMIT = 5;
341
+ var AUTH_RATE_WINDOW = 60000;
342
+
343
+ app.post("/auth", async function (req, res) {
344
+ var ip = req.ip || req.socket.remoteAddress || "unknown";
345
+ var now = Date.now();
346
+ var attempts = authAttempts.get(ip) || [];
347
+ attempts = attempts.filter(function (t) { return now - t < AUTH_RATE_WINDOW; });
348
+
349
+ if (attempts.length >= AUTH_RATE_LIMIT) {
350
+ res.status(429).json({ ok: false, error: "Too many attempts. Try again later." });
351
+ return;
352
+ }
353
+
354
+ attempts.push(now);
355
+ authAttempts.set(ip, attempts);
356
+
340
357
  var passphrase = (req.body as { passphrase?: string }).passphrase || "";
341
- if (!config.passphraseHash || verifyPassphrase(passphrase, config.passphraseHash)) {
358
+ if (!config.passphraseHash || await verifyPassphrase(passphrase, config.passphraseHash)) {
342
359
  var token = generateSessionToken();
343
360
  addSession(token);
344
361
  res.setHeader("Set-Cookie", "lattice_auth=" + token + "; HttpOnly; Path=/; SameSite=Strict");
@@ -381,20 +398,23 @@ export async function startDaemon(portOverride?: number | null): Promise<void> {
381
398
  res.status(400).send("Missing path parameter");
382
399
  return;
383
400
  }
384
- var resolved = resolve(reqFilePath);
385
- if (!existsSync(resolved)) {
386
- for (var pi = 0; pi < config.projects.length; pi++) {
387
- var projectResolved = join(config.projects[pi].path, reqFilePath);
388
- if (existsSync(projectResolved)) {
389
- resolved = projectResolved;
390
- break;
391
- }
401
+
402
+ var resolved: string | null = null;
403
+
404
+ for (var pi = 0; pi < config.projects.length; pi++) {
405
+ var projectPath = resolve(config.projects[pi].path);
406
+ var candidate = resolve(projectPath, reqFilePath);
407
+ if (candidate.startsWith(projectPath + "/") && existsSync(candidate)) {
408
+ resolved = candidate;
409
+ break;
392
410
  }
393
411
  }
394
- if (!existsSync(resolved)) {
412
+
413
+ if (!resolved) {
395
414
  res.status(404).send("File not found");
396
415
  return;
397
416
  }
417
+
398
418
  var stat = statSync(resolved);
399
419
  res.setHeader("Content-Type", getMimeType(resolved));
400
420
  res.setHeader("Content-Length", stat.size);
@@ -5,7 +5,7 @@ import { query } from "@anthropic-ai/claude-agent-sdk";
5
5
  import type { SDKMessage, SDKPartialAssistantMessage, SDKResultMessage } from "@anthropic-ai/claude-agent-sdk";
6
6
  import { broadcast } from "../ws/broadcast";
7
7
  import { getProjectBySlug } from "../project/registry";
8
- import type { LoopStatus } from "@lattice/shared";
8
+ import type { LoopStatus } from "#shared";
9
9
 
10
10
  var activeLoops = new Map<string, LoopStatus>();
11
11
 
@@ -3,7 +3,7 @@ import { join } from "node:path";
3
3
  import { randomBytes } from "node:crypto";
4
4
  import { getLatticeHome } from "../config";
5
5
  import { broadcast } from "../ws/broadcast";
6
- import type { ScheduledTask } from "@lattice/shared";
6
+ import type { ScheduledTask } from "#shared";
7
7
 
8
8
  var schedulesFile = "";
9
9
  var tasks: ScheduledTask[] = [];
@@ -2,7 +2,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeF
2
2
  import { join } from "node:path";
3
3
  import { randomBytes } from "node:crypto";
4
4
  import { getLatticeHome } from "../config";
5
- import type { StickyNote } from "@lattice/shared";
5
+ import type { StickyNote } from "#shared";
6
6
 
7
7
  var notesFile = "";
8
8
  var notes: StickyNote[] = [];
@@ -1,4 +1,4 @@
1
- import type { ClientMessage } from "@lattice/shared";
1
+ import type { ClientMessage } from "#shared";
2
2
  import { registerHandler } from "../ws/router";
3
3
  import { sendTo } from "../ws/broadcast";
4
4
  import { streamAnalyticsSections } from "../analytics/engine";
@@ -1,5 +1,5 @@
1
- import type { Attachment } from "@lattice/shared";
2
- import type { AttachmentChunkMessage, AttachmentCompleteMessage, ClientMessage } from "@lattice/shared";
1
+ import type { Attachment } from "#shared";
2
+ import type { AttachmentChunkMessage, AttachmentCompleteMessage, ClientMessage } from "#shared";
3
3
  import { registerHandler } from "../ws/router";
4
4
  import { sendTo } from "../ws/broadcast";
5
5
 
@@ -176,7 +176,7 @@ export function cleanupClient(clientId: string): void {
176
176
  completed.delete(clientId);
177
177
  }
178
178
 
179
- setInterval(function () {
179
+ var ttlCleanupInterval = setInterval(function () {
180
180
  var now = Date.now();
181
181
  stores.forEach(function (store) {
182
182
  store.forEach(function (pending, id) {
@@ -186,3 +186,4 @@ setInterval(function () {
186
186
  });
187
187
  });
188
188
  }, CLEANUP_INTERVAL_MS);
189
+ ttlCleanupInterval.unref();
@@ -3,7 +3,7 @@ import type {
3
3
  BookmarkListMessage,
4
4
  BookmarkAddMessage,
5
5
  BookmarkRemoveMessage,
6
- } from "@lattice/shared";
6
+ } from "#shared";
7
7
  import { registerHandler } from "../ws/router";
8
8
  import { sendTo } from "../ws/broadcast";
9
9
  import { listBookmarks, addBookmark, removeBookmark } from "../project/bookmarks";
@@ -1,4 +1,4 @@
1
- import type { ChatSendMessage, ChatPermissionResponseMessage, ChatSetPermissionModeMessage, ChatPromptResponseMessage, ClientMessage } from "@lattice/shared";
1
+ import type { ChatSendMessage, ChatPermissionResponseMessage, ChatSetPermissionModeMessage, ChatPromptResponseMessage, ClientMessage } from "#shared";
2
2
  import { registerHandler } from "../ws/router";
3
3
  import { sendTo } from "../ws/broadcast";
4
4
  import { getProjectBySlug } from "../project/registry";
@@ -1,7 +1,7 @@
1
1
  import { execSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
- import type { ClientMessage, EditorDetectMessage, EditorEnsureProjectMessage } from "@lattice/shared";
4
+ import type { ClientMessage, EditorDetectMessage, EditorEnsureProjectMessage } from "#shared";
5
5
  import { registerHandler } from "../ws/router";
6
6
  import { sendTo } from "../ws/broadcast";
7
7
  import { loadConfig } from "../config";
@@ -1,4 +1,4 @@
1
- import type { ClientMessage, FsListMessage, FsReadMessage, FsWriteMessage } from "@lattice/shared";
1
+ import type { ClientMessage, FsListMessage, FsReadMessage, FsWriteMessage } from "#shared";
2
2
  import { registerHandler } from "../ws/router";
3
3
  import { sendTo, broadcast } from "../ws/broadcast";
4
4
  import { getProjectBySlug } from "../project/registry";
@@ -1,4 +1,4 @@
1
- import type { ClientMessage, LoopStartMessage, LoopStopMessage, LoopStatusRequestMessage } from "@lattice/shared";
1
+ import type { ClientMessage, LoopStartMessage, LoopStopMessage, LoopStatusRequestMessage } from "#shared";
2
2
  import { registerHandler } from "../ws/router";
3
3
  import { sendTo } from "../ws/broadcast";
4
4
  import { startLoop, stopLoop, getLoopStatus } from "../features/ralph-loop";
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync, writeFileSync, readdirSync, unlinkSync, mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { homedir } from "node:os";
4
- import type { ClientMessage } from "@lattice/shared";
4
+ import type { ClientMessage } from "#shared";
5
5
  import { registerHandler } from "../ws/router";
6
6
  import { sendTo } from "../ws/broadcast";
7
7
  import { loadConfig } from "../config";
@@ -1,7 +1,7 @@
1
- import type { ClientMessage, MeshPairMessage, MeshUnpairMessage, NodeInfo } from "@lattice/shared";
1
+ import type { ClientMessage, MeshPairMessage, MeshUnpairMessage, NodeInfo } from "#shared";
2
2
  import { log } from "../logger";
3
3
  import { handleProxyRequest, handleProxyResponse } from "../mesh/proxy";
4
- import type { MeshProxyRequestMessage, MeshProxyResponseMessage } from "@lattice/shared";
4
+ import type { MeshProxyRequestMessage, MeshProxyResponseMessage } from "#shared";
5
5
  import { registerHandler } from "../ws/router";
6
6
  import { sendTo, broadcast } from "../ws/broadcast";
7
7
  import { loadConfig } from "../config";
@@ -10,7 +10,7 @@ import { generateInviteCode, parseInviteCode, validatePairingToken, consumePairi
10
10
  import { addPeer, removePeer, loadPeers, getPeer } from "../mesh/peers";
11
11
  import { getConnectedPeerIds, connectToPeer, reconnectPeer, getPeerConnection, disconnectPeer, getConnectedPeerProjects, registerInboundPeer } from "../mesh/connector";
12
12
  import { getClientWebSocket, registerVirtualClient, removeVirtualClient } from "../ws/broadcast";
13
- import type { PeerInfo } from "@lattice/shared";
13
+ import type { PeerInfo } from "#shared";
14
14
  import { networkInterfaces } from "node:os";
15
15
  import { existsSync, readFileSync } from "node:fs";
16
16
  import { execSync } from "node:child_process";
@@ -4,7 +4,7 @@ import type {
4
4
  NotesCreateMessage,
5
5
  NotesUpdateMessage,
6
6
  NotesDeleteMessage,
7
- } from "@lattice/shared";
7
+ } from "#shared";
8
8
  import { registerHandler } from "../ws/router";
9
9
  import { sendTo, broadcast } from "../ws/broadcast";
10
10
  import { listNotes, createNote, updateNote, deleteNote } from "../features/sticky-notes";
@@ -9,7 +9,7 @@ import type {
9
9
  PluginDetails,
10
10
  PluginError,
11
11
  MarketplacePluginEntry,
12
- } from "@lattice/shared";
12
+ } from "#shared";
13
13
  import { registerHandler } from "../ws/router";
14
14
  import { sendTo } from "../ws/broadcast";
15
15
 
@@ -1,4 +1,4 @@
1
- import type { ClientMessage, ProjectSettingsGetMessage, ProjectSettingsUpdateMessage, ProjectSettings, McpServerConfig } from "@lattice/shared";
1
+ import type { ClientMessage, ProjectSettingsGetMessage, ProjectSettingsUpdateMessage, ProjectSettings, McpServerConfig } from "#shared";
2
2
  import { registerHandler } from "../ws/router";
3
3
  import { sendTo } from "../ws/broadcast";
4
4
  import { loadConfig, saveConfig, invalidateConfigCache } from "../config";
@@ -4,7 +4,7 @@ import type {
4
4
  SchedulerDeleteMessage,
5
5
  SchedulerToggleMessage,
6
6
  SchedulerUpdateMessage,
7
- } from "@lattice/shared";
7
+ } from "#shared";
8
8
  import { registerHandler } from "../ws/router";
9
9
  import { sendTo } from "../ws/broadcast";
10
10
  import { listTasks, createTask, deleteTask, toggleTask, updateTask } from "../features/scheduler";
@@ -7,7 +7,7 @@ import type {
7
7
  SessionListRequestMessage,
8
8
  SessionPreviewRequestMessage,
9
9
  SessionRenameMessage,
10
- } from "@lattice/shared";
10
+ } from "#shared";
11
11
  import { registerHandler } from "../ws/router";
12
12
  import { sendTo } from "../ws/broadcast";
13
13
  import { loadConfig } from "../config";
@@ -91,6 +91,7 @@ registerHandler("session", function (clientId: string, message: ClientMessage) {
91
91
  sessionId: pageMsg.sessionId,
92
92
  messages: page.messages,
93
93
  hasMore: page.hasMore,
94
+ totalMessages: page.totalMessages,
94
95
  });
95
96
  });
96
97
  return;
@@ -1,9 +1,9 @@
1
- import type { ClientMessage, SettingsGetMessage, SettingsUpdateMessage } from "@lattice/shared";
1
+ import type { ClientMessage, SettingsGetMessage, SettingsUpdateMessage } from "#shared";
2
2
  import { registerHandler } from "../ws/router";
3
3
  import { sendTo, broadcast } from "../ws/broadcast";
4
4
  import { loadConfig, saveConfig } from "../config";
5
5
  import { addProject, removeProject } from "../project/registry";
6
- import type { LatticeConfig } from "@lattice/shared";
6
+ import type { LatticeConfig } from "#shared";
7
7
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
8
8
  import { join } from "node:path";
9
9
  import { homedir } from "node:os";
@@ -67,7 +67,7 @@ registerHandler("settings", function (clientId: string, message: ClientMessage)
67
67
  sendTo(clientId, {
68
68
  type: "settings:data",
69
69
  config: configWithClaudeMd,
70
- mcpServers: readGlobalMcpServers() as Record<string, import("@lattice/shared").McpServerConfig>,
70
+ mcpServers: readGlobalMcpServers() as Record<string, import("#shared").McpServerConfig>,
71
71
  globalSkills: readGlobalSkills(),
72
72
  globalRules: readGlobalRules(),
73
73
  spinnerVerbs: loadSpinnerVerbs(),
@@ -130,7 +130,7 @@ registerHandler("settings", function (clientId: string, message: ClientMessage)
130
130
  sendTo(clientId, {
131
131
  type: "settings:data",
132
132
  config: updatedWithClaudeMd,
133
- mcpServers: readGlobalMcpServers() as Record<string, import("@lattice/shared").McpServerConfig>,
133
+ mcpServers: readGlobalMcpServers() as Record<string, import("#shared").McpServerConfig>,
134
134
  globalSkills: readGlobalSkills(),
135
135
  globalRules: readGlobalRules(),
136
136
  spinnerVerbs: loadSpinnerVerbs(),
@@ -2,8 +2,8 @@ import { readdirSync, readFileSync, existsSync, lstatSync, realpathSync, statSyn
2
2
  import { join, sep, dirname } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { execSync, spawn } from "node:child_process";
5
- import type { ClientMessage } from "@lattice/shared";
6
- import type { SkillInfo } from "@lattice/shared";
5
+ import type { ClientMessage } from "#shared";
6
+ import type { SkillInfo } from "#shared";
7
7
  import { registerHandler } from "../ws/router";
8
8
  import { sendTo } from "../ws/broadcast";
9
9
  import { loadConfig } from "../config";
@@ -292,7 +292,7 @@ registerHandler("skills", function (clientId: string, message: ClientMessage) {
292
292
  sendTo(clientId, {
293
293
  type: "settings:data",
294
294
  config: globalConfig,
295
- mcpServers: readGlobalMcpServers() as Record<string, import("@lattice/shared").McpServerConfig>,
295
+ mcpServers: readGlobalMcpServers() as Record<string, import("#shared").McpServerConfig>,
296
296
  globalSkills: readGlobalSkills(),
297
297
  });
298
298
  }
@@ -333,7 +333,7 @@ registerHandler("skills", function (clientId: string, message: ClientMessage) {
333
333
  sendTo(clientId, {
334
334
  type: "settings:data",
335
335
  config: delConfig,
336
- mcpServers: readGlobalMcpServers() as Record<string, import("@lattice/shared").McpServerConfig>,
336
+ mcpServers: readGlobalMcpServers() as Record<string, import("#shared").McpServerConfig>,
337
337
  globalSkills: readGlobalSkills(),
338
338
  });
339
339
  } catch (err) {
@@ -366,7 +366,7 @@ registerHandler("skills", function (clientId: string, message: ClientMessage) {
366
366
  sendTo(clientId, {
367
367
  type: "settings:data",
368
368
  config: updConfig,
369
- mcpServers: readGlobalMcpServers() as Record<string, import("@lattice/shared").McpServerConfig>,
369
+ mcpServers: readGlobalMcpServers() as Record<string, import("#shared").McpServerConfig>,
370
370
  globalSkills: readGlobalSkills(),
371
371
  });
372
372
  });
@@ -1,4 +1,4 @@
1
- import type { ClientMessage, TerminalCreateMessage, TerminalInputMessage, TerminalResizeMessage } from "@lattice/shared";
1
+ import type { ClientMessage, TerminalCreateMessage, TerminalInputMessage, TerminalResizeMessage } from "#shared";
2
2
  import { registerHandler } from "../ws/router";
3
3
  import { sendTo } from "../ws/broadcast";
4
4
  import { createTerminal, destroyTerminal, writeToTerminal, resizeTerminal } from "../project/terminal";
@@ -65,12 +65,23 @@ registerHandler("terminal", function(clientId: string, message: ClientMessage) {
65
65
 
66
66
  if (message.type === "terminal:input") {
67
67
  var inputMsg = message as TerminalInputMessage;
68
+ var clientSet = clientTerminals.get(clientId);
69
+ if (!clientSet || !clientSet.has(inputMsg.termId)) {
70
+ return;
71
+ }
72
+ if (typeof inputMsg.data === "string" && inputMsg.data.length > 65536) {
73
+ return;
74
+ }
68
75
  writeToTerminal(inputMsg.termId, inputMsg.data);
69
76
  return;
70
77
  }
71
78
 
72
79
  if (message.type === "terminal:resize") {
73
80
  var resizeMsg = message as TerminalResizeMessage;
81
+ var resizeClientSet = clientTerminals.get(clientId);
82
+ if (!resizeClientSet || !resizeClientSet.has(resizeMsg.termId)) {
83
+ return;
84
+ }
74
85
  resizeTerminal(resizeMsg.termId, resizeMsg.cols, resizeMsg.rows);
75
86
  return;
76
87
  }
@@ -4,7 +4,7 @@ import type {
4
4
  ClientMessage,
5
5
  ThemeSaveMessage,
6
6
  ThemeDeleteMessage,
7
- } from "@lattice/shared";
7
+ } from "#shared";
8
8
  import { registerHandler } from "../ws/router";
9
9
  import { sendTo, broadcast } from "../ws/broadcast";
10
10
  import { getLatticeHome } from "../config";
@@ -2,7 +2,7 @@ import { chmodSync, writeFileSync, accessSync, copyFileSync, unlinkSync, constan
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { execSync, spawn } from "node:child_process";
5
- import type { ClientMessage } from "@lattice/shared";
5
+ import type { ClientMessage } from "#shared";
6
6
  import { registerHandler } from "../ws/router";
7
7
  import { sendTo, broadcast } from "../ws/broadcast";
8
8
  import { checkForUpdate, getPackageName, getGitHubRepo, getInstallMode } from "../update-checker";
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync, readFileSync, writeFileSync, chmodSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { randomUUID, generateKeyPairSync } from "node:crypto";
4
4
  import { getLatticeHome } from "./config";
@@ -25,6 +25,7 @@ export function loadOrCreateIdentity(): NodeIdentity {
25
25
  stored.publicKey = keys.publicKey;
26
26
  stored.privateKey = keys.privateKey;
27
27
  writeFileSync(path, JSON.stringify(stored, null, 2), "utf-8");
28
+ chmodSync(path, 0o600);
28
29
  return stored;
29
30
  }
30
31
  var keys = generateEd25519Keypair();
@@ -35,6 +36,7 @@ export function loadOrCreateIdentity(): NodeIdentity {
35
36
  createdAt: Date.now(),
36
37
  };
37
38
  writeFileSync(path, JSON.stringify(identity, null, 2), "utf-8");
39
+ chmodSync(path, 0o600);
38
40
  return identity;
39
41
  }
40
42
 
@@ -6,7 +6,7 @@ import { existsSync, readFileSync, writeFileSync, unlinkSync, openSync } from "n
6
6
  import { join, dirname } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { spawn, execSync } from "node:child_process";
9
- import { DAEMON_PID_FILE } from "@lattice/shared";
9
+ import { DAEMON_PID_FILE } from "#shared";
10
10
  import { getLatticeHome, loadConfig } from "./config";
11
11
 
12
12
  var __filename_local = fileURLToPath(import.meta.url);
@@ -143,7 +143,7 @@ async function runDaemon(): Promise<void> {
143
143
  if (onboarding.passphrase) {
144
144
  var { hashPassphrase } = await import("./auth/passphrase");
145
145
  var config = loadConfig();
146
- config.passphraseHash = hashPassphrase(onboarding.passphrase);
146
+ config.passphraseHash = await hashPassphrase(onboarding.passphrase);
147
147
  var { saveConfig: saveCfg } = await import("./config");
148
148
  saveCfg(config);
149
149
  }
@@ -1,4 +1,4 @@
1
- import type { MeshMessage, MeshHelloMessage, MeshSessionSyncMessage, MeshSessionRequestMessage } from "@lattice/shared";
1
+ import type { MeshMessage, MeshHelloMessage, MeshSessionSyncMessage, MeshSessionRequestMessage } from "#shared";
2
2
  import * as peersModule from "./peers";
3
3
  import { loadPeers } from "./peers";
4
4
  import { loadOrCreateIdentity } from "../identity";
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import type { PeerInfo } from "@lattice/shared";
3
+ import type { PeerInfo } from "#shared";
4
4
  import { getLatticeHome } from "../config";
5
5
 
6
6
  function getPeersPath(): string {
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import type { ClientMessage, MeshProxyRequestMessage, MeshProxyResponseMessage, ServerMessage } from "@lattice/shared";
2
+ import type { ClientMessage, MeshProxyRequestMessage, MeshProxyResponseMessage, ServerMessage } from "#shared";
3
3
  import { getPeerConnection } from "./connector";
4
4
  import { sendTo, broadcast, registerVirtualClient, removeVirtualClient } from "../ws/broadcast";
5
5
  import { routeMessage } from "../ws/router";
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, appendFileSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { homedir } from "node:os";
4
- import type { MeshSessionSyncMessage, MeshSessionRequestMessage } from "@lattice/shared";
4
+ import type { MeshSessionSyncMessage, MeshSessionRequestMessage } from "#shared";
5
5
  import { getConnectedPeerIds, getPeerConnection } from "./connector";
6
6
  import { getLatticeHome } from "../config";
7
7
 
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "
2
2
  import { join } from "node:path";
3
3
  import { randomBytes } from "node:crypto";
4
4
  import { getLatticeHome } from "../config";
5
- import type { MessageBookmark } from "@lattice/shared";
5
+ import type { MessageBookmark } from "#shared";
6
6
 
7
7
  var bookmarksFile = "";
8
8
  var bookmarks: MessageBookmark[] = [];
@@ -2,7 +2,7 @@ import { encodingForModel } from "js-tiktoken";
2
2
  import { existsSync, readFileSync, readdirSync, openSync, readSync, fstatSync, closeSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { homedir } from "node:os";
5
- import type { ContextBreakdownSegment } from "@lattice/shared";
5
+ import type { ContextBreakdownSegment } from "#shared";
6
6
  import { guessContextWindow } from "./session";
7
7
  import { loadConfig } from "../config";
8
8
  import { getInstalledPluginCount, getPluginSkillRuleTokenEstimate } from "../handlers/plugins";
@@ -1,6 +1,6 @@
1
1
  import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
2
  import { join, resolve, relative } from "node:path";
3
- import type { FileEntry } from "@lattice/shared";
3
+ import type { FileEntry } from "#shared";
4
4
 
5
5
  var MAX_FILE_SIZE = 512 * 1024;
6
6
 
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { basename } from "node:path";
3
3
  import { loadConfig, saveConfig } from "../config";
4
- import type { ProjectSummary } from "@lattice/shared";
4
+ import type { ProjectSummary } from "#shared";
5
5
 
6
6
  export function listProjects(nodeId: string): ProjectSummary[] {
7
7
  var config = loadConfig();
@@ -3,7 +3,7 @@ import { query } from "@anthropic-ai/claude-agent-sdk";
3
3
  import type { SDKMessage, SDKPartialAssistantMessage, SDKResultMessage, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
4
4
  import type { CanUseTool, PermissionMode, PermissionResult, PermissionUpdate } from "@anthropic-ai/claude-agent-sdk";
5
5
  type MessageParam = SDKUserMessage["message"];
6
- import type { Attachment } from "@lattice/shared";
6
+ import type { Attachment } from "#shared";
7
7
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
8
8
  import { join, resolve } from "node:path";
9
9
  import { homedir } from "node:os";
@@ -777,7 +777,6 @@ export function startChatStream(options: ChatStreamOptions): void {
777
777
 
778
778
  var mq = createMessageQueue();
779
779
  var firstMsg = buildSDKUserMessage(prompt, attachments, sessionId);
780
- mq.push(firstMsg);
781
780
 
782
781
  var stream = query({ prompt: mq as any, options: queryOptions });
783
782
  pendingStreams.delete(sessionId);
@@ -804,6 +803,13 @@ export function startChatStream(options: ChatStreamOptions): void {
804
803
  broadcast({ type: "session:busy", sessionId, busy: true }, clientId);
805
804
 
806
805
  void (async function () {
806
+ var retried = false;
807
+ try {
808
+ await stream.initializationResult();
809
+ } catch (initErr) {
810
+ log.chat("Session %s SDK initialization warning: %O", sessionId, initErr);
811
+ }
812
+ mq.push(firstMsg);
807
813
  try {
808
814
  for await (var msg of stream) {
809
815
  processMessage(sessionStream, msg);
@@ -812,6 +818,26 @@ export function startChatStream(options: ChatStreamOptions): void {
812
818
  var errMsg = err instanceof Error ? err.message : String(err);
813
819
  if (errMsg.includes("aborted") || errMsg.includes("AbortError")) {
814
820
  log.chat("Session %s stream aborted", sessionId);
821
+ } else if (errMsg.includes("Sent before connected") && !retried) {
822
+ retried = true;
823
+ log.chat("Session %s SDK WebSocket race condition, retrying after delay...", sessionId);
824
+ await new Promise(function (r) { setTimeout(r, 500); });
825
+ try {
826
+ var retryMq = createMessageQueue();
827
+ var retryStream = query({ prompt: retryMq as any, options: queryOptions });
828
+ retryMq.push(firstMsg);
829
+ sessionStream.queryInstance = retryStream;
830
+ sessionStream.messageQueue = retryMq;
831
+ for await (var retryMsg of retryStream) {
832
+ processMessage(sessionStream, retryMsg);
833
+ }
834
+ } catch (retryErr: unknown) {
835
+ var retryErrMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
836
+ if (!retryErrMsg.includes("aborted") && !retryErrMsg.includes("AbortError")) {
837
+ console.error("[lattice] SDK stream retry error: " + retryErrMsg);
838
+ sendTo(sessionStream.clientId, { type: "chat:error", message: retryErrMsg });
839
+ }
840
+ }
815
841
  } else {
816
842
  console.error("[lattice] SDK stream error: " + errMsg);
817
843
  sendTo(sessionStream.clientId, { type: "chat:error", message: errMsg });
@@ -10,7 +10,7 @@ import * as fsPromises from "node:fs/promises";
10
10
  import { join } from "node:path";
11
11
  import { randomUUID } from "node:crypto";
12
12
  import { homedir } from "node:os";
13
- import type { HistoryMessage, SessionPreview, SessionSummary } from "@lattice/shared";
13
+ import type { HistoryMessage, SessionPreview, SessionSummary } from "#shared";
14
14
  import { loadConfig } from "../config";
15
15
  import { log } from "../logger";
16
16
 
@@ -629,7 +629,7 @@ export async function loadSessionHistory(projectSlug: string, sessionId: string)
629
629
  }
630
630
  }
631
631
 
632
- export async function getSessionHistoryPage(sessionId: string, beforeIndex: number, limit: number, projectSlug?: string): Promise<{ messages: HistoryMessage[]; hasMore: boolean }> {
632
+ export async function getSessionHistoryPage(sessionId: string, beforeIndex: number, limit: number, projectSlug?: string): Promise<{ messages: HistoryMessage[]; hasMore: boolean; totalMessages: number }> {
633
633
  var cached = historyCache.get(sessionId);
634
634
  if (!cached && projectSlug) {
635
635
  var projectPath = getProjectPath(projectSlug);
@@ -643,16 +643,17 @@ export async function getSessionHistoryPage(sessionId: string, beforeIndex: numb
643
643
  cached = historyCache.get(sessionId)!;
644
644
  log.session("getSessionHistoryPage: full load for %s, %d messages", sessionId.slice(0, 8), allMessages.length);
645
645
  } catch {
646
- return { messages: [], hasMore: false };
646
+ return { messages: [], hasMore: false, totalMessages: 0 };
647
647
  }
648
648
  }
649
- if (!cached) return { messages: [], hasMore: false };
649
+ if (!cached) return { messages: [], hasMore: false, totalMessages: 0 };
650
650
 
651
- var endIdx = Math.max(0, beforeIndex);
651
+ var total = cached.messages.length;
652
+ var endIdx = Math.min(Math.max(0, beforeIndex), total);
652
653
  var startIdx = Math.max(0, endIdx - limit);
653
654
  var page = cached.messages.slice(startIdx, endIdx);
654
655
 
655
- return { messages: page, hasMore: startIdx > 0 };
656
+ return { messages: page, hasMore: startIdx > 0, totalMessages: total };
656
657
  }
657
658
 
658
659
  export function createSession(projectSlug: string): SessionSummary {
package/src/server/tls.ts CHANGED
@@ -16,12 +16,26 @@ export function getCertsDir(): string {
16
16
  return certsDir;
17
17
  }
18
18
 
19
+ function isCertExpiringSoon(certPath: string): boolean {
20
+ try {
21
+ var result = spawnSync("openssl", ["x509", "-enddate", "-noout", "-in", certPath], { encoding: "utf-8" });
22
+ if (result.status !== 0) return true;
23
+ var match = result.stdout.match(/notAfter=(.+)/);
24
+ if (!match) return true;
25
+ var expiryDate = new Date(match[1]);
26
+ var daysLeft = (expiryDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24);
27
+ return daysLeft < 30;
28
+ } catch {
29
+ return true;
30
+ }
31
+ }
32
+
19
33
  export function ensureCerts(): CertPaths {
20
34
  var certsDir = getCertsDir();
21
35
  var certPath = join(certsDir, "cert.pem");
22
36
  var keyPath = join(certsDir, "key.pem");
23
37
 
24
- if (existsSync(certPath) && existsSync(keyPath)) {
38
+ if (existsSync(certPath) && existsSync(keyPath) && !isCertExpiringSoon(certPath)) {
25
39
  return { cert: certPath, key: keyPath };
26
40
  }
27
41
 
package/src/server/tui.ts CHANGED
@@ -2,8 +2,8 @@ import * as p from "@clack/prompts";
2
2
  import { existsSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { getLatticeHome, loadConfig, saveConfig } from "./config";
5
- import type { LatticeConfig } from "@lattice/shared";
6
- import { DEFAULT_PORT } from "@lattice/shared";
5
+ import type { LatticeConfig } from "#shared";
6
+ import { DEFAULT_PORT } from "#shared";
7
7
 
8
8
  var BANNER = `
9
9
  \x1b[36m██╗\x1b[0m \x1b[36m█████╗\x1b[0m \x1b[36m████████╗████████╗██╗\x1b[0m \x1b[36m██████╗\x1b[0m \x1b[36m███████╗\x1b[0m