@cryptiklemur/lattice 1.2.0 → 1.4.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/.serena/project.yml +138 -0
- package/bun.lock +705 -2
- package/client/index.html +1 -13
- package/client/package.json +6 -1
- package/client/src/App.tsx +2 -0
- package/client/src/commands.ts +36 -0
- package/client/src/components/chat/AttachmentChips.tsx +116 -0
- package/client/src/components/chat/ChatInput.tsx +250 -73
- package/client/src/components/chat/ChatView.tsx +242 -10
- package/client/src/components/chat/CommandPalette.tsx +162 -0
- package/client/src/components/chat/Message.tsx +23 -2
- package/client/src/components/chat/PromptQuestion.tsx +271 -0
- package/client/src/components/chat/TodoCard.tsx +57 -0
- package/client/src/components/chat/ToolResultRenderer.tsx +2 -1
- package/client/src/components/chat/VoiceRecorder.tsx +85 -0
- package/client/src/components/project-settings/ProjectClaude.tsx +14 -0
- package/client/src/components/project-settings/ProjectMemory.tsx +12 -2
- package/client/src/components/project-settings/ProjectNotifications.tsx +48 -0
- package/client/src/components/project-settings/ProjectRules.tsx +10 -1
- package/client/src/components/project-settings/ProjectSettingsView.tsx +6 -0
- package/client/src/components/settings/Appearance.tsx +1 -0
- package/client/src/components/settings/ClaudeSettings.tsx +24 -0
- package/client/src/components/settings/Editor.tsx +123 -0
- package/client/src/components/settings/GlobalMcp.tsx +10 -1
- package/client/src/components/settings/GlobalMemory.tsx +19 -0
- package/client/src/components/settings/GlobalRules.tsx +149 -0
- package/client/src/components/settings/GlobalSkills.tsx +10 -0
- package/client/src/components/settings/Notifications.tsx +88 -0
- package/client/src/components/settings/SettingsView.tsx +12 -0
- package/client/src/components/settings/skill-shared.tsx +2 -1
- package/client/src/components/setup/SetupWizard.tsx +1 -1
- package/client/src/components/sidebar/AddProjectModal.tsx +3 -2
- package/client/src/components/sidebar/NodeSettingsModal.tsx +23 -1
- package/client/src/components/sidebar/ProjectDropdown.tsx +176 -27
- package/client/src/components/sidebar/SettingsSidebar.tsx +11 -1
- package/client/src/components/sidebar/Sidebar.tsx +35 -2
- package/client/src/components/sidebar/UserIsland.tsx +18 -7
- package/client/src/components/ui/UpdatePrompt.tsx +47 -0
- package/client/src/components/workspace/FileBrowser.tsx +174 -0
- package/client/src/components/workspace/FileTree.tsx +129 -0
- package/client/src/components/workspace/FileViewer.tsx +211 -0
- package/client/src/components/workspace/NoteCard.tsx +119 -0
- package/client/src/components/workspace/NotesView.tsx +102 -0
- package/client/src/components/workspace/ScheduledTasksView.tsx +117 -0
- package/client/src/components/workspace/SplitPane.tsx +81 -0
- package/client/src/components/workspace/TabBar.tsx +185 -0
- package/client/src/components/workspace/TaskCard.tsx +158 -0
- package/client/src/components/workspace/TaskEditModal.tsx +114 -0
- package/client/src/components/{panels/Terminal.tsx → workspace/TerminalInstance.tsx} +50 -7
- package/client/src/components/workspace/TerminalView.tsx +110 -0
- package/client/src/components/workspace/WorkspaceView.tsx +116 -0
- package/client/src/hooks/useAttachments.ts +280 -0
- package/client/src/hooks/useEditorConfig.ts +28 -0
- package/client/src/hooks/useIdleDetection.ts +44 -0
- package/client/src/hooks/useInstallPrompt.ts +53 -0
- package/client/src/hooks/useNotifications.ts +54 -0
- package/client/src/hooks/useOnline.ts +6 -0
- package/client/src/hooks/useSession.ts +110 -4
- package/client/src/hooks/useSpinnerVerb.ts +36 -0
- package/client/src/hooks/useSwipeDrawer.ts +275 -0
- package/client/src/hooks/useVoiceRecorder.ts +123 -0
- package/client/src/hooks/useWorkspace.ts +48 -0
- package/client/src/providers/WebSocketProvider.tsx +18 -0
- package/client/src/router.tsx +48 -20
- package/client/src/stores/session.ts +136 -0
- package/client/src/stores/sidebar.ts +3 -2
- package/client/src/stores/workspace.ts +254 -0
- package/client/src/styles/global.css +131 -0
- package/client/src/utils/editorUrl.ts +62 -0
- package/client/vite.config.ts +53 -1
- package/package.json +1 -1
- package/server/src/daemon.ts +11 -1
- package/server/src/features/scheduler.ts +23 -0
- package/server/src/features/sticky-notes.ts +5 -3
- package/server/src/handlers/attachment.ts +172 -0
- package/server/src/handlers/chat.ts +43 -2
- package/server/src/handlers/editor.ts +40 -0
- package/server/src/handlers/fs.ts +10 -2
- package/server/src/handlers/memory.ts +3 -0
- package/server/src/handlers/notes.ts +4 -2
- package/server/src/handlers/scheduler.ts +18 -1
- package/server/src/handlers/session.ts +14 -8
- package/server/src/handlers/settings.ts +37 -2
- package/server/src/handlers/terminal.ts +13 -6
- package/server/src/project/pty-worker.cjs +83 -0
- package/server/src/project/sdk-bridge.ts +266 -11
- package/server/src/project/terminal.ts +78 -34
- package/shared/src/messages.ts +145 -4
- package/shared/src/models.ts +27 -1
- package/shared/src/project-settings.ts +1 -1
- package/tp.js +19 -0
- package/client/public/manifest.json +0 -24
- package/client/public/sw.js +0 -61
- package/client/src/components/panels/FileBrowser.tsx +0 -241
- package/client/src/components/panels/StickyNotes.tsx +0 -187
package/shared/src/messages.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
Attachment,
|
|
3
2
|
FileEntry,
|
|
4
3
|
HistoryMessage,
|
|
5
4
|
LatticeConfig,
|
|
@@ -44,7 +43,7 @@ export interface SessionListRequestMessage {
|
|
|
44
43
|
export interface ChatSendMessage {
|
|
45
44
|
type: "chat:send";
|
|
46
45
|
text: string;
|
|
47
|
-
|
|
46
|
+
attachmentIds?: string[];
|
|
48
47
|
model?: string;
|
|
49
48
|
effort?: string;
|
|
50
49
|
}
|
|
@@ -71,14 +70,85 @@ export interface ChatSetPermissionModeMessage {
|
|
|
71
70
|
mode: "default" | "acceptEdits" | "plan" | "dontAsk";
|
|
72
71
|
}
|
|
73
72
|
|
|
73
|
+
export interface AttachmentChunkMessage {
|
|
74
|
+
type: "attachment:chunk";
|
|
75
|
+
attachmentId: string;
|
|
76
|
+
chunkIndex: number;
|
|
77
|
+
totalChunks: number;
|
|
78
|
+
data: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface AttachmentCompleteMessage {
|
|
82
|
+
type: "attachment:complete";
|
|
83
|
+
attachmentId: string;
|
|
84
|
+
attachmentType: "file" | "image" | "paste";
|
|
85
|
+
name: string;
|
|
86
|
+
mimeType: string;
|
|
87
|
+
size: number;
|
|
88
|
+
lineCount?: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface AttachmentProgressMessage {
|
|
92
|
+
type: "attachment:progress";
|
|
93
|
+
attachmentId: string;
|
|
94
|
+
received: number;
|
|
95
|
+
total: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface AttachmentErrorMessage {
|
|
99
|
+
type: "attachment:error";
|
|
100
|
+
attachmentId: string;
|
|
101
|
+
error: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface ChatPromptRequestMessage {
|
|
105
|
+
type: "chat:prompt_request";
|
|
106
|
+
requestId: string;
|
|
107
|
+
questions: Array<{
|
|
108
|
+
question: string;
|
|
109
|
+
header: string;
|
|
110
|
+
options: Array<{ label: string; description: string; preview?: string }>;
|
|
111
|
+
multiSelect: boolean;
|
|
112
|
+
}>;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface ChatPromptResponseMessage {
|
|
116
|
+
type: "chat:prompt_response";
|
|
117
|
+
requestId: string;
|
|
118
|
+
answers: Record<string, string>;
|
|
119
|
+
annotations?: Record<string, { notes?: string; preview?: string }>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ChatPromptResolvedMessage {
|
|
123
|
+
type: "chat:prompt_resolved";
|
|
124
|
+
requestId: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface ChatTodoUpdateMessage {
|
|
128
|
+
type: "chat:todo_update";
|
|
129
|
+
todos: Array<{
|
|
130
|
+
id: string;
|
|
131
|
+
content: string;
|
|
132
|
+
status: "pending" | "in_progress" | "completed";
|
|
133
|
+
priority: "high" | "medium" | "low";
|
|
134
|
+
}>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ChatPlanModeMessage {
|
|
138
|
+
type: "chat:plan_mode";
|
|
139
|
+
active: boolean;
|
|
140
|
+
}
|
|
141
|
+
|
|
74
142
|
export interface FsListMessage {
|
|
75
143
|
type: "fs:list";
|
|
76
144
|
path: string;
|
|
145
|
+
projectSlug?: string;
|
|
77
146
|
}
|
|
78
147
|
|
|
79
148
|
export interface FsReadMessage {
|
|
80
149
|
type: "fs:read";
|
|
81
150
|
path: string;
|
|
151
|
+
projectSlug?: string;
|
|
82
152
|
}
|
|
83
153
|
|
|
84
154
|
export interface FsWriteMessage {
|
|
@@ -89,6 +159,7 @@ export interface FsWriteMessage {
|
|
|
89
159
|
|
|
90
160
|
export interface TerminalCreateMessage {
|
|
91
161
|
type: "terminal:create";
|
|
162
|
+
projectSlug?: string;
|
|
92
163
|
}
|
|
93
164
|
|
|
94
165
|
export interface TerminalInputMessage {
|
|
@@ -168,13 +239,23 @@ export interface SchedulerToggleMessage {
|
|
|
168
239
|
taskId: string;
|
|
169
240
|
}
|
|
170
241
|
|
|
242
|
+
export interface SchedulerUpdateMessage {
|
|
243
|
+
type: "scheduler:update";
|
|
244
|
+
taskId: string;
|
|
245
|
+
name?: string;
|
|
246
|
+
prompt?: string;
|
|
247
|
+
cron?: string;
|
|
248
|
+
}
|
|
249
|
+
|
|
171
250
|
export interface NotesListMessage {
|
|
172
251
|
type: "notes:list";
|
|
252
|
+
projectSlug?: string;
|
|
173
253
|
}
|
|
174
254
|
|
|
175
255
|
export interface NotesCreateMessage {
|
|
176
256
|
type: "notes:create";
|
|
177
257
|
content: string;
|
|
258
|
+
projectSlug?: string;
|
|
178
259
|
}
|
|
179
260
|
|
|
180
261
|
export interface NotesUpdateMessage {
|
|
@@ -256,6 +337,24 @@ export interface BrowseSuggestionsMessage {
|
|
|
256
337
|
type: "browse:suggestions";
|
|
257
338
|
}
|
|
258
339
|
|
|
340
|
+
export interface EditorOpenMessage {
|
|
341
|
+
type: "editor:open";
|
|
342
|
+
path: string;
|
|
343
|
+
line?: number;
|
|
344
|
+
projectSlug?: string;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface EditorDetectMessage {
|
|
348
|
+
type: "editor:detect";
|
|
349
|
+
editorType: string;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export interface EditorDetectResultMessage {
|
|
353
|
+
type: "editor:detect_result";
|
|
354
|
+
editorType: string;
|
|
355
|
+
path: string | null;
|
|
356
|
+
}
|
|
357
|
+
|
|
259
358
|
export interface ProjectSettingsGetMessage {
|
|
260
359
|
type: "project-settings:get";
|
|
261
360
|
projectSlug: string;
|
|
@@ -280,12 +379,18 @@ export interface ProjectSettingsErrorMessage {
|
|
|
280
379
|
message: string;
|
|
281
380
|
}
|
|
282
381
|
|
|
382
|
+
export interface SessionStopExternalMessage {
|
|
383
|
+
type: "session:stop_external";
|
|
384
|
+
sessionId: string;
|
|
385
|
+
}
|
|
386
|
+
|
|
283
387
|
export type ClientMessage =
|
|
284
388
|
| SessionCreateMessage
|
|
285
389
|
| SessionActivateMessage
|
|
286
390
|
| SessionRenameMessage
|
|
287
391
|
| SessionDeleteMessage
|
|
288
392
|
| SessionListRequestMessage
|
|
393
|
+
| SessionStopExternalMessage
|
|
289
394
|
| ChatSendMessage
|
|
290
395
|
| ChatPermissionResponseMessage
|
|
291
396
|
| ChatRewindMessage
|
|
@@ -309,12 +414,15 @@ export type ClientMessage =
|
|
|
309
414
|
| SchedulerCreateMessage
|
|
310
415
|
| SchedulerDeleteMessage
|
|
311
416
|
| SchedulerToggleMessage
|
|
417
|
+
| SchedulerUpdateMessage
|
|
312
418
|
| NotesListMessage
|
|
313
419
|
| NotesCreateMessage
|
|
314
420
|
| NotesUpdateMessage
|
|
315
421
|
| NotesDeleteMessage
|
|
316
422
|
| SkillsListRequestMessage
|
|
317
423
|
| ChatSetPermissionModeMessage
|
|
424
|
+
| AttachmentChunkMessage
|
|
425
|
+
| AttachmentCompleteMessage
|
|
318
426
|
| ProjectSettingsGetMessage
|
|
319
427
|
| ProjectSettingsUpdateMessage
|
|
320
428
|
| SessionListAllRequestMessage
|
|
@@ -328,7 +436,10 @@ export type ClientMessage =
|
|
|
328
436
|
| MemoryViewMessage
|
|
329
437
|
| MemorySaveMessage
|
|
330
438
|
| MemoryDeleteMessage
|
|
331
|
-
| BrowseSuggestionsMessage
|
|
439
|
+
| BrowseSuggestionsMessage
|
|
440
|
+
| EditorOpenMessage
|
|
441
|
+
| EditorDetectMessage
|
|
442
|
+
| ChatPromptResponseMessage;
|
|
332
443
|
|
|
333
444
|
export interface SessionListMessage {
|
|
334
445
|
type: "session:list";
|
|
@@ -348,6 +459,13 @@ export interface SessionHistoryMessage {
|
|
|
348
459
|
messages: HistoryMessage[];
|
|
349
460
|
title?: string;
|
|
350
461
|
interrupted?: boolean;
|
|
462
|
+
busy?: boolean;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export interface SessionBusyMessage {
|
|
466
|
+
type: "session:busy";
|
|
467
|
+
sessionId: string;
|
|
468
|
+
busy: boolean;
|
|
351
469
|
}
|
|
352
470
|
|
|
353
471
|
export interface ChatUserMessage {
|
|
@@ -380,6 +498,11 @@ export interface ChatDoneMessage {
|
|
|
380
498
|
duration: number;
|
|
381
499
|
}
|
|
382
500
|
|
|
501
|
+
export interface ChatPromptSuggestionMessage {
|
|
502
|
+
type: "chat:prompt_suggestion";
|
|
503
|
+
suggestion: string;
|
|
504
|
+
}
|
|
505
|
+
|
|
383
506
|
export interface ChatErrorMessage {
|
|
384
507
|
type: "chat:error";
|
|
385
508
|
message: string;
|
|
@@ -502,6 +625,9 @@ export interface SettingsDataMessage {
|
|
|
502
625
|
config: LatticeConfig;
|
|
503
626
|
mcpServers?: Record<string, McpServerConfig>;
|
|
504
627
|
globalSkills?: SkillInfo[];
|
|
628
|
+
globalRules?: Array<{ filename: string; content: string }>;
|
|
629
|
+
spinnerVerbs?: string[];
|
|
630
|
+
wslDistro?: string;
|
|
505
631
|
}
|
|
506
632
|
|
|
507
633
|
export interface LoopStatusMessage {
|
|
@@ -531,6 +657,11 @@ export interface SchedulerTaskCreatedMessage {
|
|
|
531
657
|
task: ScheduledTask;
|
|
532
658
|
}
|
|
533
659
|
|
|
660
|
+
export interface SchedulerTaskUpdatedMessage {
|
|
661
|
+
type: "scheduler:task_updated";
|
|
662
|
+
task: ScheduledTask;
|
|
663
|
+
}
|
|
664
|
+
|
|
534
665
|
export interface NotesListResultMessage {
|
|
535
666
|
type: "notes:list_result";
|
|
536
667
|
notes: StickyNote[];
|
|
@@ -641,11 +772,13 @@ export type ServerMessage =
|
|
|
641
772
|
| SessionListMessage
|
|
642
773
|
| SessionCreatedMessage
|
|
643
774
|
| SessionHistoryMessage
|
|
775
|
+
| SessionBusyMessage
|
|
644
776
|
| ChatUserMessage
|
|
645
777
|
| ChatDeltaMessage
|
|
646
778
|
| ChatToolStartMessage
|
|
647
779
|
| ChatToolResultMessage
|
|
648
780
|
| ChatDoneMessage
|
|
781
|
+
| ChatPromptSuggestionMessage
|
|
649
782
|
| ChatErrorMessage
|
|
650
783
|
| ChatPermissionRequestMessage
|
|
651
784
|
| ChatStatusMessage
|
|
@@ -669,6 +802,7 @@ export type ServerMessage =
|
|
|
669
802
|
| LoopDeltaMessage
|
|
670
803
|
| SchedulerTasksMessage
|
|
671
804
|
| SchedulerTaskCreatedMessage
|
|
805
|
+
| SchedulerTaskUpdatedMessage
|
|
672
806
|
| NotesListResultMessage
|
|
673
807
|
| NoteCreatedMessage
|
|
674
808
|
| NoteUpdatedMessage
|
|
@@ -687,7 +821,14 @@ export type ServerMessage =
|
|
|
687
821
|
| MemoryViewResultMessage
|
|
688
822
|
| MemorySaveResultMessage
|
|
689
823
|
| MemoryDeleteResultMessage
|
|
690
|
-
| BrowseSuggestionsResultMessage
|
|
824
|
+
| BrowseSuggestionsResultMessage
|
|
825
|
+
| EditorDetectResultMessage
|
|
826
|
+
| AttachmentProgressMessage
|
|
827
|
+
| AttachmentErrorMessage
|
|
828
|
+
| ChatPromptRequestMessage
|
|
829
|
+
| ChatPromptResolvedMessage
|
|
830
|
+
| ChatTodoUpdateMessage
|
|
831
|
+
| ChatPlanModeMessage;
|
|
691
832
|
|
|
692
833
|
export interface MeshHelloMessage {
|
|
693
834
|
type: "mesh:hello";
|
package/shared/src/models.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface ProjectSummary {
|
|
|
20
20
|
export interface ProjectInfo extends ProjectSummary {
|
|
21
21
|
nodeName: string;
|
|
22
22
|
isRemote: boolean;
|
|
23
|
+
ideProjectName?: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export interface SessionSummary {
|
|
@@ -40,9 +41,12 @@ export interface FileEntry {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export interface Attachment {
|
|
43
|
-
type: "file" | "image";
|
|
44
|
+
type: "file" | "image" | "paste";
|
|
44
45
|
name: string;
|
|
45
46
|
content: string;
|
|
47
|
+
mimeType?: string;
|
|
48
|
+
size: number;
|
|
49
|
+
lineCount?: number;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
export interface HistoryMessage {
|
|
@@ -63,6 +67,20 @@ export interface HistoryMessage {
|
|
|
63
67
|
model?: string;
|
|
64
68
|
costEstimate?: number;
|
|
65
69
|
duration?: number;
|
|
70
|
+
promptQuestions?: Array<{
|
|
71
|
+
question: string;
|
|
72
|
+
header: string;
|
|
73
|
+
options: Array<{ label: string; description: string; preview?: string }>;
|
|
74
|
+
multiSelect: boolean;
|
|
75
|
+
}>;
|
|
76
|
+
promptAnswers?: Record<string, string>;
|
|
77
|
+
promptStatus?: "pending" | "answered" | "timed_out";
|
|
78
|
+
todos?: Array<{
|
|
79
|
+
id: string;
|
|
80
|
+
content: string;
|
|
81
|
+
status: "pending" | "in_progress" | "completed";
|
|
82
|
+
priority: "high" | "medium" | "low";
|
|
83
|
+
}>;
|
|
66
84
|
}
|
|
67
85
|
|
|
68
86
|
export interface PeerInfo {
|
|
@@ -87,6 +105,13 @@ export interface LatticeConfig {
|
|
|
87
105
|
env: Record<string, string>;
|
|
88
106
|
icon?: ProjectIcon;
|
|
89
107
|
}>;
|
|
108
|
+
editor?: {
|
|
109
|
+
type: "vscode" | "vscode-insiders" | "cursor" | "webstorm" | "intellij" | "pycharm" | "goland" | "notepad++" | "sublime" | "custom";
|
|
110
|
+
paths?: Record<string, string>;
|
|
111
|
+
customCommand?: string;
|
|
112
|
+
};
|
|
113
|
+
setupComplete?: boolean;
|
|
114
|
+
wsl?: boolean | "auto";
|
|
90
115
|
}
|
|
91
116
|
|
|
92
117
|
export interface StickyNote {
|
|
@@ -94,6 +119,7 @@ export interface StickyNote {
|
|
|
94
119
|
content: string;
|
|
95
120
|
createdAt: number;
|
|
96
121
|
updatedAt: number;
|
|
122
|
+
projectSlug?: string;
|
|
97
123
|
}
|
|
98
124
|
|
|
99
125
|
export interface ScheduledTask {
|
|
@@ -15,7 +15,7 @@ export type McpServerConfig =
|
|
|
15
15
|
| { type: "sse"; url: string; headers?: Record<string, string> };
|
|
16
16
|
|
|
17
17
|
export type ProjectSettingsSection =
|
|
18
|
-
| "general" | "claude" | "environment" | "mcp" | "skills" | "rules" | "permissions" | "memory";
|
|
18
|
+
| "general" | "claude" | "environment" | "mcp" | "skills" | "rules" | "permissions" | "memory" | "notifications";
|
|
19
19
|
|
|
20
20
|
export interface ProjectSettings {
|
|
21
21
|
title: string;
|
package/tp.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
var p = Bun.spawn(["bun", "run", "/home/aequasi/projects/cryptiklemur/lattice/server/src/project/pty-worker.js"], {
|
|
2
|
+
stdin: "pipe", stdout: "pipe", stderr: "pipe", cwd: "/tmp"
|
|
3
|
+
});
|
|
4
|
+
async function readOut() {
|
|
5
|
+
var d = new TextDecoder();
|
|
6
|
+
var rd = p.stdout.getReader();
|
|
7
|
+
while (true) { var x = await rd.read(); if (x.done) break; process.stdout.write("[OUT] " + d.decode(x.value)); }
|
|
8
|
+
}
|
|
9
|
+
async function readErr() {
|
|
10
|
+
var d = new TextDecoder();
|
|
11
|
+
var rd = p.stderr.getReader();
|
|
12
|
+
while (true) { var x = await rd.read(); if (x.done) break; process.stderr.write("[ERR] " + d.decode(x.value)); }
|
|
13
|
+
}
|
|
14
|
+
readOut();
|
|
15
|
+
readErr();
|
|
16
|
+
p.stdin.write('{"type":"create","cwd":"/tmp"}\n');
|
|
17
|
+
setTimeout(function() { p.stdin.write('{"type":"input","data":"echo hello\\r"}\n'); }, 1000);
|
|
18
|
+
setTimeout(function() { p.stdin.write('{"type":"kill"}\n'); }, 3000);
|
|
19
|
+
setTimeout(function() { process.exit(); }, 4000);
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "Lattice",
|
|
3
|
-
"short_name": "Lattice",
|
|
4
|
-
"description": "Multi-machine agentic dashboard for Claude Code",
|
|
5
|
-
"display": "standalone",
|
|
6
|
-
"start_url": "/",
|
|
7
|
-
"scope": "/",
|
|
8
|
-
"theme_color": "#0d0d0d",
|
|
9
|
-
"background_color": "#0d0d0d",
|
|
10
|
-
"icons": [
|
|
11
|
-
{
|
|
12
|
-
"src": "/icons/icon-192.svg",
|
|
13
|
-
"sizes": "192x192",
|
|
14
|
-
"type": "image/svg+xml",
|
|
15
|
-
"purpose": "any maskable"
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"src": "/icons/icon-512.svg",
|
|
19
|
-
"sizes": "512x512",
|
|
20
|
-
"type": "image/svg+xml",
|
|
21
|
-
"purpose": "any maskable"
|
|
22
|
-
}
|
|
23
|
-
]
|
|
24
|
-
}
|
package/client/public/sw.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
var CACHE_NAME = "lattice-v1";
|
|
2
|
-
var STATIC_ASSETS = [
|
|
3
|
-
"/",
|
|
4
|
-
"/manifest.json",
|
|
5
|
-
"/icons/icon-192.svg",
|
|
6
|
-
"/icons/icon-512.svg",
|
|
7
|
-
];
|
|
8
|
-
|
|
9
|
-
self.addEventListener("install", function (event) {
|
|
10
|
-
event.waitUntil(
|
|
11
|
-
caches.open(CACHE_NAME).then(function (cache) {
|
|
12
|
-
return cache.addAll(STATIC_ASSETS);
|
|
13
|
-
}).then(function () {
|
|
14
|
-
return self.skipWaiting();
|
|
15
|
-
})
|
|
16
|
-
);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
self.addEventListener("activate", function (event) {
|
|
20
|
-
event.waitUntil(
|
|
21
|
-
caches.keys().then(function (keys) {
|
|
22
|
-
return Promise.all(
|
|
23
|
-
keys.filter(function (key) {
|
|
24
|
-
return key !== CACHE_NAME;
|
|
25
|
-
}).map(function (key) {
|
|
26
|
-
return caches.delete(key);
|
|
27
|
-
})
|
|
28
|
-
);
|
|
29
|
-
}).then(function () {
|
|
30
|
-
return self.clients.claim();
|
|
31
|
-
})
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
self.addEventListener("fetch", function (event) {
|
|
36
|
-
var url = new URL(event.request.url);
|
|
37
|
-
|
|
38
|
-
if (url.pathname.startsWith("/ws") || url.pathname.startsWith("/auth")) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (event.request.method !== "GET") {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
event.respondWith(
|
|
47
|
-
caches.match(event.request).then(function (cached) {
|
|
48
|
-
var networkRequest = fetch(event.request).then(function (response) {
|
|
49
|
-
if (response && response.status === 200 && response.type === "basic") {
|
|
50
|
-
var clone = response.clone();
|
|
51
|
-
caches.open(CACHE_NAME).then(function (cache) {
|
|
52
|
-
cache.put(event.request, clone);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return response;
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return cached || networkRequest;
|
|
59
|
-
})
|
|
60
|
-
);
|
|
61
|
-
});
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { ChevronRight, FileIcon } from "lucide-react";
|
|
3
|
-
import type { FileEntry, FsListResultMessage, FsReadResultMessage } from "@lattice/shared";
|
|
4
|
-
import { useWebSocket } from "../../hooks/useWebSocket";
|
|
5
|
-
import type { ServerMessage } from "@lattice/shared";
|
|
6
|
-
|
|
7
|
-
interface TreeNode {
|
|
8
|
-
entry: FileEntry;
|
|
9
|
-
children: TreeNode[] | null;
|
|
10
|
-
expanded: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function buildNodes(entries: FileEntry[]): TreeNode[] {
|
|
14
|
-
return entries.map(function (entry) {
|
|
15
|
-
return { entry, children: null, expanded: false };
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface FileTreeItemProps {
|
|
20
|
-
node: TreeNode;
|
|
21
|
-
depth: number;
|
|
22
|
-
selectedPath: string | null;
|
|
23
|
-
onToggle: (path: string) => void;
|
|
24
|
-
onSelect: (path: string) => void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function FileTreeItem(props: FileTreeItemProps) {
|
|
28
|
-
var { node, depth, selectedPath, onToggle, onSelect } = props;
|
|
29
|
-
var isSelected = selectedPath === node.entry.path;
|
|
30
|
-
var isDir = node.entry.isDirectory;
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<div>
|
|
34
|
-
<div
|
|
35
|
-
onClick={function () {
|
|
36
|
-
if (isDir) {
|
|
37
|
-
onToggle(node.entry.path);
|
|
38
|
-
} else {
|
|
39
|
-
onSelect(node.entry.path);
|
|
40
|
-
}
|
|
41
|
-
}}
|
|
42
|
-
className={
|
|
43
|
-
"flex items-center gap-1.5 py-[3px] pr-2 cursor-pointer text-[13px] rounded select-none " +
|
|
44
|
-
(isSelected ? "bg-base-300 text-primary" : "text-base-content hover:bg-base-200")
|
|
45
|
-
}
|
|
46
|
-
style={{ paddingLeft: (8 + depth * 16) + "px" }}
|
|
47
|
-
>
|
|
48
|
-
{isDir ? (
|
|
49
|
-
<ChevronRight
|
|
50
|
-
size={12}
|
|
51
|
-
className="flex-shrink-0 text-base-content/40 transition-transform duration-[120ms]"
|
|
52
|
-
style={{ transform: node.expanded ? "rotate(90deg)" : "none" }}
|
|
53
|
-
/>
|
|
54
|
-
) : (
|
|
55
|
-
<FileIcon
|
|
56
|
-
size={12}
|
|
57
|
-
className="flex-shrink-0 text-base-content/40"
|
|
58
|
-
/>
|
|
59
|
-
)}
|
|
60
|
-
<span
|
|
61
|
-
className={
|
|
62
|
-
"truncate " +
|
|
63
|
-
(isDir ? "text-info" : "text-base-content")
|
|
64
|
-
}
|
|
65
|
-
>
|
|
66
|
-
{node.entry.name}
|
|
67
|
-
</span>
|
|
68
|
-
</div>
|
|
69
|
-
{isDir && node.expanded && node.children && (
|
|
70
|
-
<div>
|
|
71
|
-
{node.children.map(function (child) {
|
|
72
|
-
return (
|
|
73
|
-
<FileTreeItem
|
|
74
|
-
key={child.entry.path}
|
|
75
|
-
node={child}
|
|
76
|
-
depth={depth + 1}
|
|
77
|
-
selectedPath={selectedPath}
|
|
78
|
-
onToggle={onToggle}
|
|
79
|
-
onSelect={onSelect}
|
|
80
|
-
/>
|
|
81
|
-
);
|
|
82
|
-
})}
|
|
83
|
-
</div>
|
|
84
|
-
)}
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function FileBrowser() {
|
|
90
|
-
var { send, subscribe, unsubscribe } = useWebSocket();
|
|
91
|
-
var [rootNodes, setRootNodes] = useState<TreeNode[]>([]);
|
|
92
|
-
var [selectedPath, setSelectedPath] = useState<string | null>(null);
|
|
93
|
-
var [fileContent, setFileContent] = useState<string | null>(null);
|
|
94
|
-
var [loadingContent, setLoadingContent] = useState(false);
|
|
95
|
-
var nodesRef = useRef<TreeNode[]>([]);
|
|
96
|
-
|
|
97
|
-
nodesRef.current = rootNodes;
|
|
98
|
-
|
|
99
|
-
var handleListResult = useCallback(function (msg: ServerMessage) {
|
|
100
|
-
var listMsg = msg as FsListResultMessage;
|
|
101
|
-
var newNodes = buildNodes(listMsg.entries);
|
|
102
|
-
|
|
103
|
-
if (listMsg.path === "" || listMsg.path === ".") {
|
|
104
|
-
setRootNodes(newNodes);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function updateNodes(nodes: TreeNode[]): TreeNode[] {
|
|
109
|
-
return nodes.map(function (node) {
|
|
110
|
-
if (node.entry.path === listMsg.path) {
|
|
111
|
-
return Object.assign({}, node, { children: newNodes, expanded: true });
|
|
112
|
-
}
|
|
113
|
-
if (node.children) {
|
|
114
|
-
return Object.assign({}, node, { children: updateNodes(node.children) });
|
|
115
|
-
}
|
|
116
|
-
return node;
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
setRootNodes(function (prev) {
|
|
121
|
-
return updateNodes(prev);
|
|
122
|
-
});
|
|
123
|
-
}, []);
|
|
124
|
-
|
|
125
|
-
var handleReadResult = useCallback(function (msg: ServerMessage) {
|
|
126
|
-
var readMsg = msg as FsReadResultMessage;
|
|
127
|
-
setFileContent(readMsg.content);
|
|
128
|
-
setLoadingContent(false);
|
|
129
|
-
}, []);
|
|
130
|
-
|
|
131
|
-
var handleFsChanged = useCallback(function (msg: ServerMessage) {
|
|
132
|
-
var changedPath = (msg as { path: string }).path;
|
|
133
|
-
if (changedPath === selectedPath) {
|
|
134
|
-
send({ type: "fs:read", path: changedPath });
|
|
135
|
-
}
|
|
136
|
-
}, [selectedPath, send]);
|
|
137
|
-
|
|
138
|
-
useEffect(function () {
|
|
139
|
-
subscribe("fs:list_result", handleListResult);
|
|
140
|
-
subscribe("fs:read_result", handleReadResult);
|
|
141
|
-
subscribe("fs:changed", handleFsChanged);
|
|
142
|
-
|
|
143
|
-
send({ type: "fs:list", path: "." });
|
|
144
|
-
|
|
145
|
-
return function () {
|
|
146
|
-
unsubscribe("fs:list_result", handleListResult);
|
|
147
|
-
unsubscribe("fs:read_result", handleReadResult);
|
|
148
|
-
unsubscribe("fs:changed", handleFsChanged);
|
|
149
|
-
};
|
|
150
|
-
}, [handleListResult, handleReadResult, handleFsChanged, send, subscribe, unsubscribe]);
|
|
151
|
-
|
|
152
|
-
function handleToggle(path: string) {
|
|
153
|
-
function findAndToggle(nodes: TreeNode[]): TreeNode[] {
|
|
154
|
-
return nodes.map(function (node) {
|
|
155
|
-
if (node.entry.path === path) {
|
|
156
|
-
if (!node.expanded && !node.children) {
|
|
157
|
-
send({ type: "fs:list", path });
|
|
158
|
-
return Object.assign({}, node, { expanded: true });
|
|
159
|
-
}
|
|
160
|
-
return Object.assign({}, node, { expanded: !node.expanded });
|
|
161
|
-
}
|
|
162
|
-
if (node.children) {
|
|
163
|
-
return Object.assign({}, node, { children: findAndToggle(node.children) });
|
|
164
|
-
}
|
|
165
|
-
return node;
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
setRootNodes(function (prev) {
|
|
169
|
-
return findAndToggle(prev);
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function handleSelect(path: string) {
|
|
174
|
-
setSelectedPath(path);
|
|
175
|
-
setFileContent(null);
|
|
176
|
-
setLoadingContent(true);
|
|
177
|
-
send({ type: "fs:read", path });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return (
|
|
181
|
-
<div className="flex h-full w-full overflow-hidden bg-base-100">
|
|
182
|
-
<div className="w-[220px] flex-shrink-0 border-r border-base-300 overflow-y-auto p-2">
|
|
183
|
-
<div className="text-[11px] font-semibold tracking-[0.06em] uppercase text-base-content/40 px-2 pb-2 pt-1">
|
|
184
|
-
Files
|
|
185
|
-
</div>
|
|
186
|
-
{rootNodes.length === 0 ? (
|
|
187
|
-
<div className="px-2 py-3 text-[12px] text-base-content/40">
|
|
188
|
-
Loading...
|
|
189
|
-
</div>
|
|
190
|
-
) : (
|
|
191
|
-
rootNodes.map(function (node) {
|
|
192
|
-
return (
|
|
193
|
-
<FileTreeItem
|
|
194
|
-
key={node.entry.path}
|
|
195
|
-
node={node}
|
|
196
|
-
depth={0}
|
|
197
|
-
selectedPath={selectedPath}
|
|
198
|
-
onToggle={handleToggle}
|
|
199
|
-
onSelect={handleSelect}
|
|
200
|
-
/>
|
|
201
|
-
);
|
|
202
|
-
})
|
|
203
|
-
)}
|
|
204
|
-
</div>
|
|
205
|
-
|
|
206
|
-
<div className="flex-1 flex flex-col overflow-hidden">
|
|
207
|
-
{selectedPath && (
|
|
208
|
-
<div className="h-9 flex-shrink-0 flex items-center px-4 border-b border-base-300 bg-base-200 text-[12px] text-base-content/60 font-mono overflow-hidden">
|
|
209
|
-
<span className="truncate">{selectedPath}</span>
|
|
210
|
-
</div>
|
|
211
|
-
)}
|
|
212
|
-
|
|
213
|
-
<div className="flex-1 overflow-auto">
|
|
214
|
-
{!selectedPath && (
|
|
215
|
-
<div className="h-full flex items-center justify-center text-base-content/40 text-[13px]">
|
|
216
|
-
Select a file to view its contents
|
|
217
|
-
</div>
|
|
218
|
-
)}
|
|
219
|
-
|
|
220
|
-
{selectedPath && loadingContent && (
|
|
221
|
-
<div className="h-full flex items-center justify-center text-base-content/40 text-[13px]">
|
|
222
|
-
Loading...
|
|
223
|
-
</div>
|
|
224
|
-
)}
|
|
225
|
-
|
|
226
|
-
{selectedPath && !loadingContent && fileContent !== null && (
|
|
227
|
-
<pre className="m-0 p-4 text-[13px] font-mono text-base-content leading-relaxed whitespace-pre-wrap break-words">
|
|
228
|
-
{fileContent}
|
|
229
|
-
</pre>
|
|
230
|
-
)}
|
|
231
|
-
|
|
232
|
-
{selectedPath && !loadingContent && fileContent === null && (
|
|
233
|
-
<div className="h-full flex items-center justify-center text-base-content/40 text-[13px]">
|
|
234
|
-
Cannot display this file (binary or too large)
|
|
235
|
-
</div>
|
|
236
|
-
)}
|
|
237
|
-
</div>
|
|
238
|
-
</div>
|
|
239
|
-
</div>
|
|
240
|
-
);
|
|
241
|
-
}
|