@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.
- package/bin/lattice +6 -1
- package/dist/client/assets/{angular-html-BBi1Szz5.js → angular-html-N8PCEquT.js} +1 -1
- package/dist/client/assets/{angular-ts-2PMpSPqc.js → angular-ts-CJ8RJIPD.js} +1 -1
- package/dist/client/assets/{apl-B-NEtBvQ.js → apl-BD6tCLWN.js} +1 -1
- package/dist/client/assets/{astro-mxH1DA5Z.js → astro-CpIIfBs6.js} +1 -1
- package/dist/client/assets/{blade-BUQ9XSm3.js → blade-D3qgnjiV.js} +1 -1
- package/dist/client/assets/{c-DrQixLw7.js → c-Dr6ADN_t.js} +1 -1
- package/dist/client/assets/{cobol-42amjOMh.js → cobol-BIfDE0Hr.js} +1 -1
- package/dist/client/assets/{coffee-CMSEUKyy.js → coffee-DHQ57vfY.js} +1 -1
- package/dist/client/assets/{cpp-C4yJBzl2.js → cpp-CEBY6JOp.js} +1 -1
- package/dist/client/assets/{crystal-DZ27EoiA.js → crystal-D125CSmP.js} +1 -1
- package/dist/client/assets/{css-CdQwkSNm.js → css-CBmrkYSr.js} +1 -1
- package/dist/client/assets/{dist-CBRIe7YG.js → dist-A_mCRD1f.js} +2 -2
- package/dist/client/assets/{edge-J8VemHZC.js → edge-Ccsz7cJW.js} +1 -1
- package/dist/client/assets/{elixir-DU66qSn_.js → elixir-Do6gk14X.js} +1 -1
- package/dist/client/assets/{elm-8cz1JjRg.js → elm-Db22zT4C.js} +1 -1
- package/dist/client/assets/{erb-DMl63J6s.js → erb-MXVqAAJD.js} +1 -1
- package/dist/client/assets/{git-rebase-DjlZL8cj.js → git-rebase-B-LLWBOA.js} +1 -1
- package/dist/client/assets/{glimmer-js-Fu7vOQY-.js → glimmer-js-eWszRU73.js} +1 -1
- package/dist/client/assets/{glimmer-ts-AJljRs0A.js → glimmer-ts-VQmwGqUp.js} +1 -1
- package/dist/client/assets/{glsl-CfodGzSv.js → glsl-B8ilOfAl.js} +1 -1
- package/dist/client/assets/{graphql-C8GPKPx1.js → graphql-DnTqxeOc.js} +1 -1
- package/dist/client/assets/{hack-Gp6OuE21.js → hack-XJsHYSQb.js} +1 -1
- package/dist/client/assets/{haml-BOyG6GfM.js → haml-CQ7Vqzwp.js} +1 -1
- package/dist/client/assets/{handlebars-B9IoVRxd.js → handlebars-C4szooBf.js} +1 -1
- package/dist/client/assets/{html-CFrSukiU.js → html-B6EgAiSd.js} +1 -1
- package/dist/client/assets/{html-derivative-D0_gRK_M.js → html-derivative-DdinogQX.js} +1 -1
- package/dist/client/assets/{http-D4MmFixi.js → http-BSLxCgRq.js} +1 -1
- package/dist/client/assets/{hurl-D1xbk-LS.js → hurl-pOsTwNfp.js} +1 -1
- package/dist/client/assets/{index-DrTbNMzI.css → index-2jfMHAda.css} +1 -1
- package/dist/client/assets/{index-ZIuI5BCP.js → index-BHQ_8mvl.js} +62 -62
- package/dist/client/assets/{java-CfPMY1Dv.js → java-DRQLiiST.js} +1 -1
- package/dist/client/assets/{javascript-B8vNHeqm.js → javascript-DvEK2-47.js} +1 -1
- package/dist/client/assets/{jinja-CF0K8bFV.js → jinja-D2NYJ25y.js} +1 -1
- package/dist/client/assets/{jison-BP7my5TL.js → jison-DDZaLNAp.js} +1 -1
- package/dist/client/assets/{json-TeXNjpMZ.js → json-TGR0NIWd.js} +1 -1
- package/dist/client/assets/{jsx-C86NmHc2.js → jsx-BjUoPYga.js} +1 -1
- package/dist/client/assets/{julia-C58U_aQP.js → julia-C4gjSpFu.js} +1 -1
- package/dist/client/assets/{just-h6Vco8Qu.js → just-H351x5u_.js} +1 -1
- package/dist/client/assets/{latex-DRsNisjT.js → latex-BiTmf6gf.js} +1 -1
- package/dist/client/assets/{liquid-C9jXC9M7.js → liquid-86ufjRy-.js} +1 -1
- package/dist/client/assets/{lua-BUYvdn-F.js → lua-BNxR0F_8.js} +1 -1
- package/dist/client/assets/{marko-BqB4tqw-.js → marko-CvRxpRjM.js} +1 -1
- package/dist/client/assets/{mdc-C8Rs68Nw.js → mdc-CYbAIy2C.js} +1 -1
- package/dist/client/assets/{nginx-Dj6E3HRL.js → nginx-egdgMq-F.js} +1 -1
- package/dist/client/assets/{nim-BizJb4iF.js → nim-CXBJVz_w.js} +1 -1
- package/dist/client/assets/{perl-DnJAtWNn.js → perl-XRfMobzg.js} +1 -1
- package/dist/client/assets/{php-BeY_VEeb.js → php-Br7a8uil.js} +1 -1
- package/dist/client/assets/{pug-DXhFbVDh.js → pug-BVbbUVvy.js} +1 -1
- package/dist/client/assets/{qml-CngzXsz8.js → qml-ByKvrL1j.js} +1 -1
- package/dist/client/assets/{r-BMmy9apu.js → r-mVoV0Ni6.js} +1 -1
- package/dist/client/assets/{razor-kIo5DNUk.js → razor-T5O-9UJL.js} +1 -1
- package/dist/client/assets/{regexp-BIa5IayX.js → regexp-CioRuhuN.js} +1 -1
- package/dist/client/assets/{rst-McWv3WP0.js → rst-V__uTudD.js} +1 -1
- package/dist/client/assets/{ruby-CkenUDKe.js → ruby-C_PuKPTI.js} +1 -1
- package/dist/client/assets/{sas-Dgn9Yrr5.js → sas-D_DqqQH4.js} +1 -1
- package/dist/client/assets/{scss-Tfn9jzBB.js → scss-D-TjzZ4c.js} +1 -1
- package/dist/client/assets/{shellscript-vGpgBWOS.js → shellscript-E5759VHu.js} +1 -1
- package/dist/client/assets/{shellsession-7CA0dOzR.js → shellsession-AESTM-Pv.js} +1 -1
- package/dist/client/assets/{soy-DbBURueN.js → soy-QrbrrcDv.js} +1 -1
- package/dist/client/assets/{sql-BJGtbEM8.js → sql-0M8VcDHD.js} +1 -1
- package/dist/client/assets/{stata-L0kV9FQT.js → stata-CgeIpGtc.js} +1 -1
- package/dist/client/assets/{surrealql-C17jcHRl.js → surrealql-DBGwnZbw.js} +1 -1
- package/dist/client/assets/{svelte-B5_CmVcu.js → svelte-Cv0PvUc_.js} +1 -1
- package/dist/client/assets/{templ-C5f2EORt.js → templ-B9t7xRE4.js} +1 -1
- package/dist/client/assets/{tex-DvaBtCMN.js → tex-DhZZ8dr2.js} +1 -1
- package/dist/client/assets/{ts-tags-DlZzQdBU.js → ts-tags-BFv8sbnd.js} +1 -1
- package/dist/client/assets/{tsx-DaLOIju9.js → tsx-CXC9KSbY.js} +1 -1
- package/dist/client/assets/{twig-BB7320dl.js → twig-CM_OO66r.js} +1 -1
- package/dist/client/assets/{typescript-D8B-VgXj.js → typescript-BdgOTaoD.js} +1 -1
- package/dist/client/assets/{vue-DylGFDrG.js → vue-BnQhjnCm.js} +1 -1
- package/dist/client/assets/{vue-html-Dt8r3B9y.js → vue-html-CNnGecRI.js} +1 -1
- package/dist/client/assets/{vue-vine-DdN02uhY.js → vue-vine-DCuMkRhK.js} +1 -1
- package/dist/client/assets/{xml-CBEuB6I0.js → xml-CbTD7cB8.js} +1 -1
- package/dist/client/assets/{xsl-CuhlgDL5.js → xsl-uOqqo7cf.js} +1 -1
- package/dist/client/assets/{yaml-Da6ymghi.js → yaml-BNrLoH59.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +1 -1
- package/package.json +5 -2
- package/src/client/components/analytics/AnalyticsView.tsx +1 -1
- package/src/client/components/analytics/ChartCard.tsx +1 -1
- package/src/client/components/analytics/charts/NodeFleetOverview.tsx +1 -1
- package/src/client/components/chat/AttachmentChips.tsx +4 -4
- package/src/client/components/chat/ChatView.tsx +2 -2
- package/src/client/components/chat/CommandPalette.tsx +1 -1
- package/src/client/components/chat/ElicitationCard.tsx +3 -0
- package/src/client/components/chat/Message.tsx +7 -6
- package/src/client/components/chat/PromptQuestion.tsx +1 -1
- package/src/client/components/chat/TodoCard.tsx +1 -1
- package/src/client/components/chat/ToolGroup.tsx +1 -1
- package/src/client/components/chat/ToolResultRenderer.tsx +2 -2
- package/src/client/components/dashboard/DashboardView.tsx +1 -1
- package/src/client/components/dashboard/ProjectDashboardView.tsx +1 -1
- package/src/client/components/mesh/NodeBadge.tsx +1 -1
- package/src/client/components/mesh/PairingDialog.tsx +2 -2
- package/src/client/components/project-settings/ProjectClaude.tsx +1 -1
- package/src/client/components/project-settings/ProjectEnvironment.tsx +1 -1
- package/src/client/components/project-settings/ProjectGeneral.tsx +1 -1
- package/src/client/components/project-settings/ProjectMcp.tsx +1 -1
- package/src/client/components/project-settings/ProjectMemory.tsx +3 -3
- package/src/client/components/project-settings/ProjectPermissions.tsx +1 -1
- package/src/client/components/project-settings/ProjectPlugins.tsx +1 -1
- package/src/client/components/project-settings/ProjectRules.tsx +1 -1
- package/src/client/components/project-settings/ProjectSettingsView.tsx +1 -1
- package/src/client/components/project-settings/ProjectSkills.tsx +1 -1
- package/src/client/components/settings/Appearance.tsx +1 -1
- package/src/client/components/settings/BudgetSettings.tsx +1 -1
- package/src/client/components/settings/ClaudeSettings.tsx +1 -1
- package/src/client/components/settings/Editor.tsx +1 -1
- package/src/client/components/settings/Environment.tsx +1 -1
- package/src/client/components/settings/GlobalMcp.tsx +2 -2
- package/src/client/components/settings/GlobalPlugins.tsx +2 -2
- package/src/client/components/settings/GlobalRules.tsx +1 -1
- package/src/client/components/settings/GlobalSkills.tsx +1 -1
- package/src/client/components/settings/MeshStatus.tsx +1 -1
- package/src/client/components/settings/SkillMarketplace.tsx +1 -1
- package/src/client/components/settings/ThemeWizard.tsx +1 -1
- package/src/client/components/settings/mcp-shared.tsx +1 -1
- package/src/client/components/settings/skill-shared.tsx +2 -2
- package/src/client/components/setup/SetupWizard.tsx +5 -0
- package/src/client/components/sidebar/AddProjectModal.tsx +2 -2
- package/src/client/components/sidebar/NodeSettingsModal.tsx +2 -2
- package/src/client/components/sidebar/ProjectRail.tsx +1 -1
- package/src/client/components/sidebar/SessionList.tsx +2 -2
- package/src/client/components/sidebar/Sidebar.tsx +1 -1
- package/src/client/components/sidebar/UserIsland.tsx +1 -1
- package/src/client/components/ui/CommandPalette.tsx +1 -1
- package/src/client/components/ui/IconPicker.tsx +3 -1
- package/src/client/components/ui/KeyboardShortcuts.tsx +1 -1
- package/src/client/components/ui/UpdateBanner.tsx +1 -1
- package/src/client/components/workspace/BookmarksView.tsx +2 -2
- package/src/client/components/workspace/FileBrowser.tsx +1 -1
- package/src/client/components/workspace/FileTree.tsx +1 -1
- package/src/client/components/workspace/NoteCard.tsx +2 -1
- package/src/client/components/workspace/NotesView.tsx +1 -1
- package/src/client/components/workspace/ScheduledTasksView.tsx +1 -1
- package/src/client/components/workspace/TaskCard.tsx +2 -1
- package/src/client/components/workspace/TaskEditModal.tsx +2 -2
- package/src/client/components/workspace/TerminalInstance.tsx +1 -1
- package/src/client/hooks/useAnalytics.ts +7 -7
- package/src/client/hooks/useAttachments.ts +49 -16
- package/src/client/hooks/useBookmarks.ts +1 -1
- package/src/client/hooks/useEditorConfig.ts +1 -1
- package/src/client/hooks/useFocusTrap.ts +4 -2
- package/src/client/hooks/useIdleDetection.ts +6 -0
- package/src/client/hooks/useMesh.ts +1 -1
- package/src/client/hooks/useProjectSettings.ts +1 -1
- package/src/client/hooks/useProjects.ts +3 -3
- package/src/client/hooks/useSession.ts +31 -32
- package/src/client/hooks/useSkills.ts +2 -2
- package/src/client/hooks/useSpinnerVerb.ts +1 -1
- package/src/client/hooks/useVoiceRecorder.ts +5 -2
- package/src/client/hooks/useWebSocket.ts +1 -1
- package/src/client/providers/WebSocketProvider.tsx +9 -1
- package/src/client/router.tsx +1 -1
- package/src/client/stores/analytics.ts +1 -1
- package/src/client/stores/bookmarks.ts +1 -1
- package/src/client/stores/mesh.ts +1 -1
- package/src/client/stores/session.ts +1 -11
- package/src/client/stores/sidebar.ts +1 -1
- package/src/client/styles/global.css +12 -0
- package/src/server/analytics/engine.ts +1 -1
- package/src/server/auth/passphrase.ts +16 -6
- package/src/server/config.ts +2 -2
- package/src/server/daemon.ts +32 -12
- package/src/server/features/ralph-loop.ts +1 -1
- package/src/server/features/scheduler.ts +1 -1
- package/src/server/features/sticky-notes.ts +1 -1
- package/src/server/handlers/analytics.ts +1 -1
- package/src/server/handlers/attachment.ts +4 -3
- package/src/server/handlers/bookmarks.ts +1 -1
- package/src/server/handlers/chat.ts +1 -1
- package/src/server/handlers/editor.ts +1 -1
- package/src/server/handlers/fs.ts +1 -1
- package/src/server/handlers/loop.ts +1 -1
- package/src/server/handlers/memory.ts +1 -1
- package/src/server/handlers/mesh.ts +3 -3
- package/src/server/handlers/notes.ts +1 -1
- package/src/server/handlers/plugins.ts +1 -1
- package/src/server/handlers/project-settings.ts +1 -1
- package/src/server/handlers/scheduler.ts +1 -1
- package/src/server/handlers/session.ts +2 -1
- package/src/server/handlers/settings.ts +4 -4
- package/src/server/handlers/skills.ts +5 -5
- package/src/server/handlers/terminal.ts +12 -1
- package/src/server/handlers/themes.ts +1 -1
- package/src/server/handlers/update.ts +1 -1
- package/src/server/identity.ts +3 -1
- package/src/server/index.ts +2 -2
- package/src/server/mesh/connector.ts +1 -1
- package/src/server/mesh/peers.ts +1 -1
- package/src/server/mesh/proxy.ts +1 -1
- package/src/server/mesh/session-sync.ts +1 -1
- package/src/server/project/bookmarks.ts +1 -1
- package/src/server/project/context-breakdown.ts +1 -1
- package/src/server/project/file-browser.ts +1 -1
- package/src/server/project/registry.ts +1 -1
- package/src/server/project/sdk-bridge.ts +28 -2
- package/src/server/project/session.ts +7 -6
- package/src/server/tls.ts +15 -1
- package/src/server/tui.ts +2 -2
- package/src/server/ws/router.ts +1 -1
- package/tsconfig.json +1 -1
- package/vite.config.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useState, useCallback, useRef } from "react";
|
|
1
|
+
import { useState, useCallback, useRef, useEffect } from "react";
|
|
2
2
|
import { useWebSocket } from "./useWebSocket";
|
|
3
|
-
import type { ServerMessage } from "
|
|
3
|
+
import type { ServerMessage } from "#shared";
|
|
4
4
|
|
|
5
5
|
var CHUNK_SIZE = 64 * 1024;
|
|
6
6
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
@@ -76,6 +76,8 @@ export function useAttachments(): UseAttachmentsReturn {
|
|
|
76
76
|
var { send, subscribe, unsubscribe } = useWebSocket();
|
|
77
77
|
var pendingResolvers = useRef(new Map<string, { resolve: () => void; reject: (err: string) => void; timer: ReturnType<typeof setTimeout> }>());
|
|
78
78
|
var fileCache = useRef(new Map<string, File>());
|
|
79
|
+
var attachmentsRef = useRef(attachments);
|
|
80
|
+
attachmentsRef.current = attachments;
|
|
79
81
|
|
|
80
82
|
var updateAttachment = useCallback(function (id: string, updates: Partial<ClientAttachment>) {
|
|
81
83
|
setAttachments(function (prev) {
|
|
@@ -111,7 +113,11 @@ export function useAttachments(): UseAttachmentsReturn {
|
|
|
111
113
|
var start = chunkIndex * CHUNK_SIZE;
|
|
112
114
|
var end = Math.min(start + CHUNK_SIZE, bytes.length);
|
|
113
115
|
var chunk = bytes.slice(start, end);
|
|
114
|
-
var
|
|
116
|
+
var binary = "";
|
|
117
|
+
for (var bi = 0; bi < chunk.length; bi++) {
|
|
118
|
+
binary += String.fromCharCode(chunk[bi]);
|
|
119
|
+
}
|
|
120
|
+
var base64 = btoa(binary);
|
|
115
121
|
|
|
116
122
|
send({
|
|
117
123
|
type: "attachment:chunk",
|
|
@@ -178,9 +184,6 @@ export function useAttachments(): UseAttachmentsReturn {
|
|
|
178
184
|
if (file.size > MAX_FILE_SIZE) {
|
|
179
185
|
return;
|
|
180
186
|
}
|
|
181
|
-
if (attachments.length >= MAX_ATTACHMENTS) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
187
|
|
|
185
188
|
var id = crypto.randomUUID();
|
|
186
189
|
var mime = guessMimeType(file);
|
|
@@ -202,14 +205,24 @@ export function useAttachments(): UseAttachmentsReturn {
|
|
|
202
205
|
previewUrl,
|
|
203
206
|
};
|
|
204
207
|
|
|
205
|
-
|
|
206
|
-
setAttachments(function (prev) {
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
var added = false;
|
|
209
|
+
setAttachments(function (prev) {
|
|
210
|
+
if (prev.length >= MAX_ATTACHMENTS) {
|
|
211
|
+
return prev;
|
|
212
|
+
}
|
|
213
|
+
added = true;
|
|
214
|
+
return [...prev, att];
|
|
215
|
+
});
|
|
209
216
|
|
|
210
|
-
|
|
211
|
-
|
|
217
|
+
if (added) {
|
|
218
|
+
fileCache.current.set(id, file);
|
|
219
|
+
uploadFile(att, file);
|
|
220
|
+
} else if (previewUrl) {
|
|
221
|
+
URL.revokeObjectURL(previewUrl);
|
|
222
|
+
}
|
|
223
|
+
}, [uploadFile]);
|
|
212
224
|
|
|
225
|
+
var addPaste = useCallback(function (text: string) {
|
|
213
226
|
var id = crypto.randomUUID();
|
|
214
227
|
var blob = new Blob([text], { type: "text/plain" });
|
|
215
228
|
var file = new File([blob], "pasted-text.txt", { type: "text/plain" });
|
|
@@ -227,10 +240,20 @@ export function useAttachments(): UseAttachmentsReturn {
|
|
|
227
240
|
content: text,
|
|
228
241
|
};
|
|
229
242
|
|
|
230
|
-
|
|
231
|
-
setAttachments(function (prev) {
|
|
232
|
-
|
|
233
|
-
|
|
243
|
+
var added = false;
|
|
244
|
+
setAttachments(function (prev) {
|
|
245
|
+
if (prev.length >= MAX_ATTACHMENTS) {
|
|
246
|
+
return prev;
|
|
247
|
+
}
|
|
248
|
+
added = true;
|
|
249
|
+
return [...prev, att];
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (added) {
|
|
253
|
+
fileCache.current.set(id, file);
|
|
254
|
+
uploadFile(att, file);
|
|
255
|
+
}
|
|
256
|
+
}, [uploadFile]);
|
|
234
257
|
|
|
235
258
|
var removeAttachment = useCallback(function (id: string) {
|
|
236
259
|
setAttachments(function (prev) {
|
|
@@ -259,6 +282,16 @@ export function useAttachments(): UseAttachmentsReturn {
|
|
|
259
282
|
fileCache.current.clear();
|
|
260
283
|
}, [attachments]);
|
|
261
284
|
|
|
285
|
+
useEffect(function () {
|
|
286
|
+
return function () {
|
|
287
|
+
attachmentsRef.current.forEach(function (att) {
|
|
288
|
+
if (att.previewUrl) {
|
|
289
|
+
URL.revokeObjectURL(att.previewUrl);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
}, []);
|
|
294
|
+
|
|
262
295
|
var readyIds = attachments
|
|
263
296
|
.filter(function (a) { return a.status === "ready"; })
|
|
264
297
|
.map(function (a) { return a.id; });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
2
|
import { useStore } from "@tanstack/react-store";
|
|
3
|
-
import type { ServerMessage, BookmarkListResultMessage, MessageBookmark } from "
|
|
3
|
+
import type { ServerMessage, BookmarkListResultMessage, MessageBookmark } from "#shared";
|
|
4
4
|
import { useWebSocket } from "./useWebSocket";
|
|
5
5
|
import { getBookmarkStore, setBookmarks, setAllBookmarks } from "../stores/bookmarks";
|
|
6
6
|
import { getSessionStore } from "../stores/session";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
import { useWebSocket } from "./useWebSocket";
|
|
3
|
-
import type { ServerMessage, SettingsDataMessage } from "
|
|
3
|
+
import type { ServerMessage, SettingsDataMessage } from "#shared";
|
|
4
4
|
|
|
5
5
|
export function useEditorConfig() {
|
|
6
6
|
var ws = useWebSocket();
|
|
@@ -15,6 +15,8 @@ export function useFocusTrap(
|
|
|
15
15
|
active: boolean = true,
|
|
16
16
|
): void {
|
|
17
17
|
var previouslyFocusedRef = useRef<HTMLElement | null>(null);
|
|
18
|
+
var onCloseRef = useRef(onClose);
|
|
19
|
+
onCloseRef.current = onClose;
|
|
18
20
|
|
|
19
21
|
useEffect(function () {
|
|
20
22
|
if (!active) return;
|
|
@@ -31,7 +33,7 @@ export function useFocusTrap(
|
|
|
31
33
|
|
|
32
34
|
function handleKeyDown(e: KeyboardEvent) {
|
|
33
35
|
if (e.key === "Escape") {
|
|
34
|
-
|
|
36
|
+
onCloseRef.current();
|
|
35
37
|
return;
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -68,5 +70,5 @@ export function useFocusTrap(
|
|
|
68
70
|
previouslyFocusedRef.current.focus();
|
|
69
71
|
}
|
|
70
72
|
};
|
|
71
|
-
}, [containerRef,
|
|
73
|
+
}, [containerRef, active]);
|
|
72
74
|
}
|
|
@@ -7,7 +7,13 @@ export function useIdleDetection(): boolean {
|
|
|
7
7
|
var timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
8
8
|
|
|
9
9
|
useEffect(function () {
|
|
10
|
+
var lastReset = 0;
|
|
11
|
+
|
|
10
12
|
function resetTimer() {
|
|
13
|
+
var now = Date.now();
|
|
14
|
+
if (now - lastReset < 500) return;
|
|
15
|
+
lastReset = now;
|
|
16
|
+
|
|
11
17
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
12
18
|
setIsIdle(false);
|
|
13
19
|
timerRef.current = setTimeout(function () {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
import { useStore } from "@tanstack/react-store";
|
|
3
|
-
import type { ServerMessage, NodeInfo } from "
|
|
3
|
+
import type { ServerMessage, NodeInfo } from "#shared";
|
|
4
4
|
import { useWebSocket } from "./useWebSocket";
|
|
5
5
|
import {
|
|
6
6
|
getMeshStore,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect } from "react";
|
|
2
2
|
import { useWebSocket } from "./useWebSocket";
|
|
3
|
-
import type { ServerMessage, ProjectSettings, ProjectSettingsDataMessage, ProjectSettingsErrorMessage } from "
|
|
3
|
+
import type { ServerMessage, ProjectSettings, ProjectSettingsDataMessage, ProjectSettingsErrorMessage } from "#shared";
|
|
4
4
|
|
|
5
5
|
export function useProjectSettings(projectSlug: string | null) {
|
|
6
6
|
var { status, send, subscribe, unsubscribe } = useWebSocket();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { useStore } from "@tanstack/react-store";
|
|
3
|
-
import type { ProjectInfo } from "
|
|
4
|
-
import type { ProjectsListMessage } from "
|
|
5
|
-
import type { ServerMessage } from "
|
|
3
|
+
import type { ProjectInfo } from "#shared";
|
|
4
|
+
import type { ProjectsListMessage } from "#shared";
|
|
5
|
+
import type { ServerMessage } from "#shared";
|
|
6
6
|
import { useWebSocket } from "./useWebSocket";
|
|
7
7
|
import { setActiveProjectSlug, getSidebarStore } from "../stores/sidebar";
|
|
8
8
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
import { useStore } from "@tanstack/react-store";
|
|
3
|
-
import type { HistoryMessage } from "
|
|
3
|
+
import type { HistoryMessage } from "#shared";
|
|
4
4
|
import type {
|
|
5
5
|
ChatDeltaMessage,
|
|
6
6
|
ChatSendMessage,
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
ChatElicitationRequestMessage,
|
|
17
17
|
SessionHistoryMessage,
|
|
18
18
|
ServerMessage,
|
|
19
|
-
} from "
|
|
19
|
+
} from "#shared";
|
|
20
20
|
import { useWebSocket } from "./useWebSocket";
|
|
21
21
|
import { setActiveSessionId as setSidebarSessionId } from "../stores/sidebar";
|
|
22
22
|
import { updateSessionTabTitle, pinTab } from "../stores/workspace";
|
|
@@ -46,7 +46,6 @@ import {
|
|
|
46
46
|
setPromptSuggestion,
|
|
47
47
|
setFailedInput,
|
|
48
48
|
enqueueMessage,
|
|
49
|
-
dequeueMessage,
|
|
50
49
|
removeQueuedMessage,
|
|
51
50
|
updateQueuedMessage,
|
|
52
51
|
clearMessageQueue,
|
|
@@ -59,13 +58,6 @@ import {
|
|
|
59
58
|
} from "../stores/session";
|
|
60
59
|
import type { SessionState, BudgetStatus } from "../stores/session";
|
|
61
60
|
|
|
62
|
-
var subscriptionsActive = 0;
|
|
63
|
-
var activeStreamGeneration = 0;
|
|
64
|
-
var streamSessionId: string | null = null;
|
|
65
|
-
var lastSentText: string | null = null;
|
|
66
|
-
var lastUsedModel: string | undefined = undefined;
|
|
67
|
-
var lastUsedEffort: string | undefined = undefined;
|
|
68
|
-
|
|
69
61
|
export type { SessionState };
|
|
70
62
|
|
|
71
63
|
export interface UseSessionReturn extends SessionState {
|
|
@@ -91,6 +83,12 @@ export function useSession(): UseSessionReturn {
|
|
|
91
83
|
var sendRef = useRef(send);
|
|
92
84
|
sendRef.current = send;
|
|
93
85
|
var sendMessageRef = useRef(function (_text: string, _attachmentIds?: string[], _model?: string, _effort?: string) {});
|
|
86
|
+
var subscriptionsActiveRef = useRef(0);
|
|
87
|
+
var activeStreamGenerationRef = useRef(0);
|
|
88
|
+
var streamSessionIdRef = useRef<string | null>(null);
|
|
89
|
+
var lastSentTextRef = useRef<string | null>(null);
|
|
90
|
+
var lastUsedModelRef = useRef<string | undefined>(undefined);
|
|
91
|
+
var lastUsedEffortRef = useRef<string | undefined>(undefined);
|
|
94
92
|
|
|
95
93
|
function activateSession(projectSlug: string, sessionId: string) {
|
|
96
94
|
setActiveSession(projectSlug, sessionId);
|
|
@@ -113,11 +111,11 @@ export function useSession(): UseSessionReturn {
|
|
|
113
111
|
if (effort) {
|
|
114
112
|
msg.effort = effort;
|
|
115
113
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
activeStreamGenerationRef.current = getStreamGeneration();
|
|
115
|
+
streamSessionIdRef.current = currentSessionId;
|
|
116
|
+
lastSentTextRef.current = text;
|
|
117
|
+
lastUsedModelRef.current = model;
|
|
118
|
+
lastUsedEffortRef.current = effort;
|
|
121
119
|
setFailedInput(null);
|
|
122
120
|
setPromptSuggestion(null);
|
|
123
121
|
setIsProcessing(true);
|
|
@@ -134,15 +132,15 @@ export function useSession(): UseSessionReturn {
|
|
|
134
132
|
sendMessageRef.current = sendMessage;
|
|
135
133
|
|
|
136
134
|
useEffect(function () {
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
return function () {
|
|
135
|
+
subscriptionsActiveRef.current++;
|
|
136
|
+
if (subscriptionsActiveRef.current > 1) {
|
|
137
|
+
return function () { subscriptionsActiveRef.current--; };
|
|
140
138
|
}
|
|
141
139
|
|
|
142
140
|
function isStaleStream(): boolean {
|
|
143
|
-
if (
|
|
141
|
+
if (activeStreamGenerationRef.current !== getStreamGeneration()) return true;
|
|
144
142
|
var currentActiveId = getSessionStore().state.activeSessionId;
|
|
145
|
-
if (
|
|
143
|
+
if (streamSessionIdRef.current && currentActiveId && streamSessionIdRef.current !== currentActiveId) return true;
|
|
146
144
|
return false;
|
|
147
145
|
}
|
|
148
146
|
|
|
@@ -220,7 +218,7 @@ export function useSession(): UseSessionReturn {
|
|
|
220
218
|
function handleDone(msg: ServerMessage) {
|
|
221
219
|
if (isStaleStream()) return;
|
|
222
220
|
var m = msg as { type: string; cost: number; duration: number; sessionId?: string };
|
|
223
|
-
|
|
221
|
+
lastSentTextRef.current = null;
|
|
224
222
|
setIsProcessing(false);
|
|
225
223
|
setCurrentStatus(null);
|
|
226
224
|
setCurrentAssistantUuid(null);
|
|
@@ -234,7 +232,7 @@ export function useSession(): UseSessionReturn {
|
|
|
234
232
|
var combined = queue.join("\n\n");
|
|
235
233
|
clearMessageQueue();
|
|
236
234
|
setTimeout(function () {
|
|
237
|
-
sendMessageRef.current(combined, [],
|
|
235
|
+
sendMessageRef.current(combined, [], lastUsedModelRef.current, lastUsedEffortRef.current);
|
|
238
236
|
}, 100);
|
|
239
237
|
}
|
|
240
238
|
}
|
|
@@ -242,12 +240,13 @@ export function useSession(): UseSessionReturn {
|
|
|
242
240
|
function handleError(msg: ServerMessage) {
|
|
243
241
|
if (isStaleStream()) return;
|
|
244
242
|
var m = msg as { type: string; message?: string };
|
|
243
|
+
if (m.message && m.message.includes("Sent before connected")) return;
|
|
245
244
|
setIsProcessing(false);
|
|
246
245
|
setCurrentStatus(null);
|
|
247
246
|
setCurrentAssistantUuid(null);
|
|
248
|
-
if (
|
|
249
|
-
setFailedInput(
|
|
250
|
-
|
|
247
|
+
if (lastSentTextRef.current) {
|
|
248
|
+
setFailedInput(lastSentTextRef.current);
|
|
249
|
+
lastSentTextRef.current = null;
|
|
251
250
|
}
|
|
252
251
|
if (m.message) {
|
|
253
252
|
addSessionMessage({
|
|
@@ -324,7 +323,7 @@ export function useSession(): UseSessionReturn {
|
|
|
324
323
|
}
|
|
325
324
|
|
|
326
325
|
function handleHistoryPage(msg: ServerMessage) {
|
|
327
|
-
var m = msg as { type: string; sessionId: string; messages: HistoryMessage[]; hasMore: boolean };
|
|
326
|
+
var m = msg as { type: string; sessionId: string; messages: HistoryMessage[]; hasMore: boolean; totalMessages?: number };
|
|
328
327
|
var state = getSessionStore().state;
|
|
329
328
|
if (m.sessionId !== state.activeSessionId) return;
|
|
330
329
|
getSessionStore().setState(function (s) {
|
|
@@ -332,6 +331,7 @@ export function useSession(): UseSessionReturn {
|
|
|
332
331
|
...s,
|
|
333
332
|
messages: mergeToolResults(m.messages).concat(s.messages),
|
|
334
333
|
historyHasMore: m.hasMore,
|
|
334
|
+
historyTotalMessages: m.totalMessages ?? s.historyTotalMessages,
|
|
335
335
|
};
|
|
336
336
|
});
|
|
337
337
|
}
|
|
@@ -359,7 +359,7 @@ export function useSession(): UseSessionReturn {
|
|
|
359
359
|
}
|
|
360
360
|
var projectSlug = m.projectSlug || getSessionStore().state.activeProjectSlug;
|
|
361
361
|
setSidebarSessionId(m.sessionId);
|
|
362
|
-
|
|
362
|
+
streamSessionIdRef.current = m.sessionId;
|
|
363
363
|
if (m.title) {
|
|
364
364
|
updateSessionTabTitle(m.sessionId, m.title);
|
|
365
365
|
}
|
|
@@ -418,7 +418,6 @@ export function useSession(): UseSessionReturn {
|
|
|
418
418
|
setSessionTitle(m.title);
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
|
-
setSessionMessages(m.messages);
|
|
422
421
|
}
|
|
423
422
|
|
|
424
423
|
function handlePromptSuggestion(msg: ServerMessage) {
|
|
@@ -498,7 +497,7 @@ export function useSession(): UseSessionReturn {
|
|
|
498
497
|
subscribe("chat:rate_limit", handleRateLimit);
|
|
499
498
|
|
|
500
499
|
return function () {
|
|
501
|
-
|
|
500
|
+
subscriptionsActiveRef.current--;
|
|
502
501
|
unsubscribe("session:loading_progress", handleLoadingProgress);
|
|
503
502
|
unsubscribe("chat:user_message", handleUserMessage);
|
|
504
503
|
unsubscribe("chat:delta", handleDelta);
|
|
@@ -571,9 +570,9 @@ export function useSession(): UseSessionReturn {
|
|
|
571
570
|
},
|
|
572
571
|
dismissBudgetExceeded: function () {
|
|
573
572
|
setBudgetExceeded(false);
|
|
574
|
-
if (
|
|
575
|
-
setFailedInput(
|
|
576
|
-
|
|
573
|
+
if (lastSentTextRef.current) {
|
|
574
|
+
setFailedInput(lastSentTextRef.current);
|
|
575
|
+
lastSentTextRef.current = null;
|
|
577
576
|
}
|
|
578
577
|
},
|
|
579
578
|
rateLimits: state.rateLimits,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
|
-
import type { SkillInfo } from "
|
|
3
|
-
import type { ServerMessage } from "
|
|
2
|
+
import type { SkillInfo } from "#shared";
|
|
3
|
+
import type { ServerMessage } from "#shared";
|
|
4
4
|
import { useWebSocket } from "./useWebSocket";
|
|
5
5
|
|
|
6
6
|
export function useSkills(): SkillInfo[] {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
import { useWebSocket } from "./useWebSocket";
|
|
3
|
-
import type { ServerMessage, SettingsDataMessage } from "
|
|
3
|
+
import type { ServerMessage, SettingsDataMessage } from "#shared";
|
|
4
4
|
|
|
5
5
|
var DEFAULT_VERBS = ["Thinking", "Analyzing", "Processing", "Computing", "Evaluating"];
|
|
6
6
|
|
|
@@ -63,8 +63,11 @@ export function useVoiceRecorder(): UseVoiceRecorderReturn {
|
|
|
63
63
|
var recognitionRef = useRef<SpeechRecognitionLike | null>(null);
|
|
64
64
|
var timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
65
65
|
var finalTranscriptRef = useRef("");
|
|
66
|
+
var interimTranscriptRef = useRef("");
|
|
66
67
|
var startTimeRef = useRef(0);
|
|
67
68
|
|
|
69
|
+
interimTranscriptRef.current = interimTranscript;
|
|
70
|
+
|
|
68
71
|
var SpeechRecognitionClass = typeof window !== "undefined"
|
|
69
72
|
? (window as unknown as { SpeechRecognition?: new () => SpeechRecognitionLike; webkitSpeechRecognition?: new () => SpeechRecognitionLike }).SpeechRecognition
|
|
70
73
|
|| (window as unknown as { webkitSpeechRecognition?: new () => SpeechRecognitionLike }).webkitSpeechRecognition
|
|
@@ -140,10 +143,10 @@ export function useVoiceRecorder(): UseVoiceRecorderReturn {
|
|
|
140
143
|
if (recognitionRef.current) {
|
|
141
144
|
recognitionRef.current.stop();
|
|
142
145
|
}
|
|
143
|
-
var transcript = finalTranscriptRef.current ||
|
|
146
|
+
var transcript = finalTranscriptRef.current || interimTranscriptRef.current;
|
|
144
147
|
cleanup();
|
|
145
148
|
return transcript;
|
|
146
|
-
}, [
|
|
149
|
+
}, [cleanup]);
|
|
147
150
|
|
|
148
151
|
var cancel = useCallback(function () {
|
|
149
152
|
if (recognitionRef.current) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
import type { ClientMessage, ServerMessage } from "
|
|
3
|
+
import type { ClientMessage, ServerMessage } from "#shared";
|
|
4
4
|
import { WebSocketContext, getWebSocketUrl } from "../hooks/useWebSocket";
|
|
5
5
|
import type { WebSocketStatus } from "../hooks/useWebSocket";
|
|
6
6
|
import { showToast } from "../components/ui/Toast";
|
|
@@ -21,6 +21,7 @@ export function WebSocketProvider(props: WebSocketProviderProps) {
|
|
|
21
21
|
var unmountedRef = useRef<boolean>(false);
|
|
22
22
|
var listenersRef = useRef<Map<string, Set<(msg: ServerMessage) => void>>>(new Map());
|
|
23
23
|
var hasConnectedRef = useRef<boolean>(false);
|
|
24
|
+
var outgoingQueueRef = useRef<ClientMessage[]>([]);
|
|
24
25
|
|
|
25
26
|
function connect() {
|
|
26
27
|
if (unmountedRef.current) {
|
|
@@ -54,6 +55,11 @@ export function WebSocketProvider(props: WebSocketProviderProps) {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
hasConnectedRef.current = true;
|
|
58
|
+
var queued = outgoingQueueRef.current;
|
|
59
|
+
outgoingQueueRef.current = [];
|
|
60
|
+
queued.forEach(function (msg) {
|
|
61
|
+
ws.send(JSON.stringify(msg));
|
|
62
|
+
});
|
|
57
63
|
};
|
|
58
64
|
|
|
59
65
|
ws.onmessage = function (event: MessageEvent) {
|
|
@@ -131,6 +137,8 @@ export function WebSocketProvider(props: WebSocketProviderProps) {
|
|
|
131
137
|
var ws = wsRef.current;
|
|
132
138
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
133
139
|
ws.send(JSON.stringify(msg));
|
|
140
|
+
} else {
|
|
141
|
+
outgoingQueueRef.current.push(msg);
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
|
package/src/client/router.tsx
CHANGED
|
@@ -319,7 +319,7 @@ function RemoveProjectConfirm() {
|
|
|
319
319
|
|
|
320
320
|
return (
|
|
321
321
|
<div ref={removeModalRef} className="fixed inset-0 z-[9999] flex items-center justify-center" role="dialog" aria-modal="true" aria-label="Remove Project">
|
|
322
|
-
<div className="absolute inset-0 bg-
|
|
322
|
+
<div className="absolute inset-0 bg-base-content/50" onClick={sidebar.closeConfirmRemove} />
|
|
323
323
|
<div className="relative bg-base-200 border border-base-content/15 rounded-2xl shadow-2xl w-full max-w-sm mx-4 overflow-hidden">
|
|
324
324
|
<div className="px-5 py-4 border-b border-base-content/15">
|
|
325
325
|
<h2 className="text-[15px] font-mono font-bold text-base-content">Remove Project</h2>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Store } from "@tanstack/react-store";
|
|
2
|
-
import type { AnalyticsPayload, AnalyticsPeriod, AnalyticsScope, AnalyticsSectionName } from "
|
|
2
|
+
import type { AnalyticsPayload, AnalyticsPeriod, AnalyticsScope, AnalyticsSectionName } from "#shared";
|
|
3
3
|
|
|
4
4
|
export interface AnalyticsState {
|
|
5
5
|
data: Partial<AnalyticsPayload>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Store } from "@tanstack/react-store";
|
|
2
|
-
import type { HistoryMessage } from "
|
|
2
|
+
import type { HistoryMessage } from "#shared";
|
|
3
3
|
|
|
4
4
|
export interface ContextUsage {
|
|
5
5
|
inputTokens: number;
|
|
@@ -505,16 +505,6 @@ export function enqueueMessage(text: string): void {
|
|
|
505
505
|
});
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
-
export function dequeueMessage(): string | null {
|
|
509
|
-
var queue = sessionStore.state.messageQueue;
|
|
510
|
-
if (queue.length === 0) return null;
|
|
511
|
-
var first = queue[0];
|
|
512
|
-
sessionStore.setState(function (state) {
|
|
513
|
-
return { ...state, messageQueue: state.messageQueue.slice(1) };
|
|
514
|
-
});
|
|
515
|
-
return first;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
508
|
export function removeQueuedMessage(index: number): void {
|
|
519
509
|
sessionStore.setState(function (state) {
|
|
520
510
|
var updated = state.messageQueue.slice();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Store } from "@tanstack/react-store";
|
|
2
|
-
import type { ProjectSettingsSection } from "
|
|
2
|
+
import type { ProjectSettingsSection } from "#shared";
|
|
3
3
|
import { encodeWorkspaceUrl, decodeWorkspaceUrl, isLegacySessionUrl, shortSessionId } from "../lib/workspace-url";
|
|
4
4
|
import type { DecodedWorkspace } from "../lib/workspace-url";
|
|
5
5
|
import { getWorkspaceStore, restoreWorkspace, setUrlSyncCallback, switchProjectWorkspace, setCurrentProjectKey } from "./workspace";
|
|
@@ -267,6 +267,10 @@ html, body {
|
|
|
267
267
|
min-width: 44px;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
.btn-xs.min-h-0 {
|
|
271
|
+
min-height: 44px;
|
|
272
|
+
}
|
|
273
|
+
|
|
270
274
|
.select-xs,
|
|
271
275
|
.select-sm {
|
|
272
276
|
min-width: unset;
|
|
@@ -277,6 +281,14 @@ html, body {
|
|
|
277
281
|
min-width: 36px;
|
|
278
282
|
padding: 10px 12px;
|
|
279
283
|
}
|
|
284
|
+
|
|
285
|
+
.icon-action {
|
|
286
|
+
min-height: 44px;
|
|
287
|
+
min-width: 44px;
|
|
288
|
+
display: inline-flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
justify-content: center;
|
|
291
|
+
}
|
|
280
292
|
}
|
|
281
293
|
|
|
282
294
|
/* Neutralize DaisyUI 5's root scroll lock for the drawer.
|
|
@@ -2,7 +2,7 @@ import { readdirSync, existsSync, readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import type { AnalyticsPayload, AnalyticsPeriod, AnalyticsScope, AnalyticsSectionName } from "
|
|
5
|
+
import type { AnalyticsPayload, AnalyticsPeriod, AnalyticsScope, AnalyticsSectionName } from "#shared";
|
|
6
6
|
import { estimateCost, projectPathToHash } from "../project/session";
|
|
7
7
|
import { loadConfig } from "../config";
|
|
8
8
|
|
|
@@ -1,17 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { scrypt, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
function scryptAsync(password: string, salt: string, keylen: number): Promise<Buffer> {
|
|
4
|
+
return new Promise(function (resolve, reject) {
|
|
5
|
+
scrypt(password, salt, keylen, function (err, derivedKey) {
|
|
6
|
+
if (err) reject(err);
|
|
7
|
+
else resolve(derivedKey);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
}
|
|
2
11
|
|
|
3
12
|
var TOKEN_TTL = 86400000;
|
|
4
13
|
var CLEANUP_INTERVAL = 600000;
|
|
5
14
|
|
|
6
15
|
var activeSessions = new Map<string, number>();
|
|
7
16
|
|
|
8
|
-
export function hashPassphrase(passphrase: string): string {
|
|
17
|
+
export async function hashPassphrase(passphrase: string): Promise<string> {
|
|
9
18
|
var salt = randomBytes(16).toString("hex");
|
|
10
|
-
var hash =
|
|
19
|
+
var hash = (await scryptAsync(passphrase, salt, 64)).toString("hex");
|
|
11
20
|
return salt + ":" + hash;
|
|
12
21
|
}
|
|
13
22
|
|
|
14
|
-
export function verifyPassphrase(passphrase: string, storedHash: string): boolean {
|
|
23
|
+
export async function verifyPassphrase(passphrase: string, storedHash: string): Promise<boolean> {
|
|
15
24
|
var parts = storedHash.split(":");
|
|
16
25
|
if (parts.length !== 2) {
|
|
17
26
|
return false;
|
|
@@ -19,7 +28,7 @@ export function verifyPassphrase(passphrase: string, storedHash: string): boolea
|
|
|
19
28
|
var salt = parts[0];
|
|
20
29
|
var hash = parts[1];
|
|
21
30
|
try {
|
|
22
|
-
var derived =
|
|
31
|
+
var derived = await scryptAsync(passphrase, salt, 64);
|
|
23
32
|
var expected = Buffer.from(hash, "hex");
|
|
24
33
|
if (derived.length !== expected.length) {
|
|
25
34
|
return false;
|
|
@@ -58,7 +67,7 @@ export function clearSessions(): void {
|
|
|
58
67
|
activeSessions.clear();
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
setInterval(function () {
|
|
70
|
+
var cleanupInterval = setInterval(function () {
|
|
62
71
|
var now = Date.now();
|
|
63
72
|
activeSessions.forEach(function (createdAt, token) {
|
|
64
73
|
if (now - createdAt > TOKEN_TTL) {
|
|
@@ -66,3 +75,4 @@ setInterval(function () {
|
|
|
66
75
|
}
|
|
67
76
|
});
|
|
68
77
|
}, CLEANUP_INTERVAL);
|
|
78
|
+
cleanupInterval.unref();
|
package/src/server/config.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir, hostname } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { DEFAULT_PORT, LATTICE_HOME_DIR } from "
|
|
5
|
-
import type { LatticeConfig } from "
|
|
4
|
+
import { DEFAULT_PORT, LATTICE_HOME_DIR } from "#shared";
|
|
5
|
+
import type { LatticeConfig } from "#shared";
|
|
6
6
|
|
|
7
7
|
var home = process.env.LATTICE_HOME || join(homedir(), LATTICE_HOME_DIR);
|
|
8
8
|
var cachedConfig: LatticeConfig | null = null;
|