@myrialabs/clopen 0.0.1

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 (456) hide show
  1. package/.env.example +6 -0
  2. package/.github/workflows/release.yml +60 -0
  3. package/.github/workflows/test.yml +40 -0
  4. package/CONTRIBUTING.md +499 -0
  5. package/LICENSE +21 -0
  6. package/README.md +209 -0
  7. package/backend/index.ts +156 -0
  8. package/backend/lib/chat/helpers.ts +42 -0
  9. package/backend/lib/chat/index.ts +2 -0
  10. package/backend/lib/chat/stream-manager.ts +1126 -0
  11. package/backend/lib/database/README.md +77 -0
  12. package/backend/lib/database/index.ts +119 -0
  13. package/backend/lib/database/migrations/001_create_projects_table.ts +31 -0
  14. package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +33 -0
  15. package/backend/lib/database/migrations/003_create_messages_table.ts +32 -0
  16. package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -0
  17. package/backend/lib/database/migrations/005_create_settings_table.ts +24 -0
  18. package/backend/lib/database/migrations/006_add_user_to_messages.ts +58 -0
  19. package/backend/lib/database/migrations/007_create_stream_states_table.ts +41 -0
  20. package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +62 -0
  21. package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -0
  22. package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -0
  23. package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -0
  24. package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -0
  25. package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -0
  26. package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -0
  27. package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -0
  28. package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -0
  29. package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -0
  30. package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -0
  31. package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -0
  32. package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -0
  33. package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -0
  34. package/backend/lib/database/migrations/index.ts +154 -0
  35. package/backend/lib/database/queries/checkpoint-queries.ts +87 -0
  36. package/backend/lib/database/queries/engine-queries.ts +75 -0
  37. package/backend/lib/database/queries/index.ts +9 -0
  38. package/backend/lib/database/queries/message-queries.ts +472 -0
  39. package/backend/lib/database/queries/project-queries.ts +117 -0
  40. package/backend/lib/database/queries/session-queries.ts +271 -0
  41. package/backend/lib/database/queries/settings-queries.ts +34 -0
  42. package/backend/lib/database/queries/snapshot-queries.ts +326 -0
  43. package/backend/lib/database/queries/utils-queries.ts +59 -0
  44. package/backend/lib/database/seeders/index.ts +13 -0
  45. package/backend/lib/database/seeders/settings_seeder.ts +84 -0
  46. package/backend/lib/database/utils/connection.ts +174 -0
  47. package/backend/lib/database/utils/index.ts +4 -0
  48. package/backend/lib/database/utils/migration-runner.ts +118 -0
  49. package/backend/lib/database/utils/seeder-runner.ts +121 -0
  50. package/backend/lib/engine/adapters/claude/environment.ts +164 -0
  51. package/backend/lib/engine/adapters/claude/error-handler.ts +60 -0
  52. package/backend/lib/engine/adapters/claude/index.ts +1 -0
  53. package/backend/lib/engine/adapters/claude/path-utils.ts +38 -0
  54. package/backend/lib/engine/adapters/claude/stream.ts +177 -0
  55. package/backend/lib/engine/adapters/opencode/index.ts +2 -0
  56. package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -0
  57. package/backend/lib/engine/adapters/opencode/server.ts +104 -0
  58. package/backend/lib/engine/adapters/opencode/stream.ts +755 -0
  59. package/backend/lib/engine/index.ts +196 -0
  60. package/backend/lib/engine/types.ts +58 -0
  61. package/backend/lib/files/file-operations.ts +478 -0
  62. package/backend/lib/files/file-reading.ts +308 -0
  63. package/backend/lib/files/file-watcher.ts +383 -0
  64. package/backend/lib/files/path-browsing.ts +382 -0
  65. package/backend/lib/git/git-executor.ts +88 -0
  66. package/backend/lib/git/git-parser.ts +411 -0
  67. package/backend/lib/git/git-service.ts +505 -0
  68. package/backend/lib/mcp/README.md +1144 -0
  69. package/backend/lib/mcp/config.ts +316 -0
  70. package/backend/lib/mcp/index.ts +35 -0
  71. package/backend/lib/mcp/project-context.ts +236 -0
  72. package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -0
  73. package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -0
  74. package/backend/lib/mcp/servers/browser-automation/index.ts +791 -0
  75. package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -0
  76. package/backend/lib/mcp/servers/helper.ts +143 -0
  77. package/backend/lib/mcp/servers/index.ts +45 -0
  78. package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -0
  79. package/backend/lib/mcp/servers/weather/index.ts +31 -0
  80. package/backend/lib/mcp/stdio-server.ts +103 -0
  81. package/backend/lib/mcp/types.ts +65 -0
  82. package/backend/lib/preview/browser/browser-audio-capture.ts +86 -0
  83. package/backend/lib/preview/browser/browser-console-manager.ts +263 -0
  84. package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -0
  85. package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -0
  86. package/backend/lib/preview/browser/browser-mcp-control.ts +415 -0
  87. package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -0
  88. package/backend/lib/preview/browser/browser-navigation-tracker.ts +104 -0
  89. package/backend/lib/preview/browser/browser-pool.ts +357 -0
  90. package/backend/lib/preview/browser/browser-preview-service.ts +882 -0
  91. package/backend/lib/preview/browser/browser-tab-manager.ts +935 -0
  92. package/backend/lib/preview/browser/browser-video-capture.ts +695 -0
  93. package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -0
  94. package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -0
  95. package/backend/lib/preview/browser/scripts/video-stream.ts +438 -0
  96. package/backend/lib/preview/browser/types.ts +359 -0
  97. package/backend/lib/preview/index.ts +24 -0
  98. package/backend/lib/project/index.ts +2 -0
  99. package/backend/lib/project/status-manager.ts +182 -0
  100. package/backend/lib/shared/index.ts +2 -0
  101. package/backend/lib/shared/port-utils.ts +25 -0
  102. package/backend/lib/shared/process-manager.ts +281 -0
  103. package/backend/lib/snapshot/blob-store.ts +227 -0
  104. package/backend/lib/snapshot/gitignore.ts +307 -0
  105. package/backend/lib/snapshot/helpers.ts +397 -0
  106. package/backend/lib/snapshot/snapshot-service.ts +483 -0
  107. package/backend/lib/terminal/helpers.ts +14 -0
  108. package/backend/lib/terminal/index.ts +8 -0
  109. package/backend/lib/terminal/pty-manager.ts +4 -0
  110. package/backend/lib/terminal/pty-session-manager.ts +387 -0
  111. package/backend/lib/terminal/shell-utils.ts +313 -0
  112. package/backend/lib/terminal/stream-manager.ts +293 -0
  113. package/backend/lib/tunnel/global-tunnel-manager.ts +243 -0
  114. package/backend/lib/tunnel/project-tunnel-manager.ts +311 -0
  115. package/backend/lib/user/helpers.ts +87 -0
  116. package/backend/lib/utils/ws.ts +944 -0
  117. package/backend/lib/vite-dev.ts +353 -0
  118. package/backend/middleware/cors.ts +15 -0
  119. package/backend/middleware/error-handler.ts +49 -0
  120. package/backend/middleware/logger.ts +9 -0
  121. package/backend/types/api.ts +24 -0
  122. package/backend/ws/README.md +1505 -0
  123. package/backend/ws/chat/background.ts +198 -0
  124. package/backend/ws/chat/index.ts +21 -0
  125. package/backend/ws/chat/stream.ts +707 -0
  126. package/backend/ws/engine/claude/accounts.ts +401 -0
  127. package/backend/ws/engine/claude/index.ts +13 -0
  128. package/backend/ws/engine/claude/status.ts +43 -0
  129. package/backend/ws/engine/index.ts +14 -0
  130. package/backend/ws/engine/opencode/index.ts +11 -0
  131. package/backend/ws/engine/opencode/status.ts +30 -0
  132. package/backend/ws/engine/utils.ts +36 -0
  133. package/backend/ws/files/index.ts +30 -0
  134. package/backend/ws/files/read.ts +189 -0
  135. package/backend/ws/files/search.ts +453 -0
  136. package/backend/ws/files/watch.ts +124 -0
  137. package/backend/ws/files/write.ts +143 -0
  138. package/backend/ws/git/branch.ts +106 -0
  139. package/backend/ws/git/commit.ts +39 -0
  140. package/backend/ws/git/conflict.ts +68 -0
  141. package/backend/ws/git/diff.ts +69 -0
  142. package/backend/ws/git/index.ts +24 -0
  143. package/backend/ws/git/log.ts +41 -0
  144. package/backend/ws/git/remote.ts +214 -0
  145. package/backend/ws/git/staging.ts +84 -0
  146. package/backend/ws/git/status.ts +90 -0
  147. package/backend/ws/index.ts +69 -0
  148. package/backend/ws/mcp/index.ts +61 -0
  149. package/backend/ws/messages/crud.ts +74 -0
  150. package/backend/ws/messages/index.ts +14 -0
  151. package/backend/ws/preview/browser/cleanup.ts +129 -0
  152. package/backend/ws/preview/browser/console.ts +114 -0
  153. package/backend/ws/preview/browser/interact.ts +513 -0
  154. package/backend/ws/preview/browser/mcp.ts +129 -0
  155. package/backend/ws/preview/browser/native-ui.ts +235 -0
  156. package/backend/ws/preview/browser/stats.ts +55 -0
  157. package/backend/ws/preview/browser/tab-info.ts +126 -0
  158. package/backend/ws/preview/browser/tab.ts +166 -0
  159. package/backend/ws/preview/browser/webcodecs.ts +293 -0
  160. package/backend/ws/preview/index.ts +146 -0
  161. package/backend/ws/projects/crud.ts +113 -0
  162. package/backend/ws/projects/index.ts +25 -0
  163. package/backend/ws/projects/presence.ts +46 -0
  164. package/backend/ws/projects/status.ts +116 -0
  165. package/backend/ws/sessions/crud.ts +327 -0
  166. package/backend/ws/sessions/index.ts +33 -0
  167. package/backend/ws/settings/crud.ts +112 -0
  168. package/backend/ws/settings/index.ts +14 -0
  169. package/backend/ws/snapshot/index.ts +17 -0
  170. package/backend/ws/snapshot/restore.ts +173 -0
  171. package/backend/ws/snapshot/timeline.ts +141 -0
  172. package/backend/ws/system/index.ts +14 -0
  173. package/backend/ws/system/operations.ts +49 -0
  174. package/backend/ws/terminal/index.ts +40 -0
  175. package/backend/ws/terminal/persistence.ts +153 -0
  176. package/backend/ws/terminal/session.ts +382 -0
  177. package/backend/ws/terminal/stream.ts +79 -0
  178. package/backend/ws/tunnel/index.ts +14 -0
  179. package/backend/ws/tunnel/operations.ts +91 -0
  180. package/backend/ws/types.ts +20 -0
  181. package/backend/ws/user/crud.ts +156 -0
  182. package/backend/ws/user/index.ts +14 -0
  183. package/bin/clopen.ts +307 -0
  184. package/bun.lock +1352 -0
  185. package/frontend/App.svelte +34 -0
  186. package/frontend/app.css +313 -0
  187. package/frontend/lib/app-environment.ts +10 -0
  188. package/frontend/lib/components/chat/ChatInterface.svelte +407 -0
  189. package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +57 -0
  190. package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +224 -0
  191. package/frontend/lib/components/chat/formatters/TextMessage.svelte +395 -0
  192. package/frontend/lib/components/chat/formatters/Tools.svelte +70 -0
  193. package/frontend/lib/components/chat/formatters/index.ts +3 -0
  194. package/frontend/lib/components/chat/input/ChatInput.svelte +421 -0
  195. package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -0
  196. package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -0
  197. package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -0
  198. package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -0
  199. package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -0
  200. package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -0
  201. package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -0
  202. package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -0
  203. package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -0
  204. package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -0
  205. package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -0
  206. package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -0
  207. package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -0
  208. package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -0
  209. package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -0
  210. package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -0
  211. package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -0
  212. package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -0
  213. package/frontend/lib/components/chat/shared/index.ts +2 -0
  214. package/frontend/lib/components/chat/shared/utils.ts +116 -0
  215. package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -0
  216. package/frontend/lib/components/chat/tools/BashTool.svelte +46 -0
  217. package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -0
  218. package/frontend/lib/components/chat/tools/EditTool.svelte +48 -0
  219. package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +32 -0
  220. package/frontend/lib/components/chat/tools/GlobTool.svelte +51 -0
  221. package/frontend/lib/components/chat/tools/GrepTool.svelte +90 -0
  222. package/frontend/lib/components/chat/tools/KillShellTool.svelte +26 -0
  223. package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +31 -0
  224. package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +38 -0
  225. package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +34 -0
  226. package/frontend/lib/components/chat/tools/ReadTool.svelte +41 -0
  227. package/frontend/lib/components/chat/tools/TaskTool.svelte +64 -0
  228. package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +75 -0
  229. package/frontend/lib/components/chat/tools/WebFetchTool.svelte +35 -0
  230. package/frontend/lib/components/chat/tools/WebSearchTool.svelte +84 -0
  231. package/frontend/lib/components/chat/tools/WriteTool.svelte +33 -0
  232. package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +79 -0
  233. package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +408 -0
  234. package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -0
  235. package/frontend/lib/components/chat/tools/components/InfoLine.svelte +19 -0
  236. package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +27 -0
  237. package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +54 -0
  238. package/frontend/lib/components/chat/tools/components/index.ts +7 -0
  239. package/frontend/lib/components/chat/tools/index.ts +26 -0
  240. package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +249 -0
  241. package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -0
  242. package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -0
  243. package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -0
  244. package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +87 -0
  245. package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -0
  246. package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -0
  247. package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -0
  248. package/frontend/lib/components/checkpoint/timeline/config.ts +44 -0
  249. package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -0
  250. package/frontend/lib/components/checkpoint/timeline/types.ts +65 -0
  251. package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -0
  252. package/frontend/lib/components/common/Alert.svelte +139 -0
  253. package/frontend/lib/components/common/AvatarBubble.svelte +56 -0
  254. package/frontend/lib/components/common/Button.svelte +71 -0
  255. package/frontend/lib/components/common/Card.svelte +102 -0
  256. package/frontend/lib/components/common/Checkbox.svelte +48 -0
  257. package/frontend/lib/components/common/Dialog.svelte +249 -0
  258. package/frontend/lib/components/common/FolderBrowser.svelte +843 -0
  259. package/frontend/lib/components/common/Icon.svelte +58 -0
  260. package/frontend/lib/components/common/Input.svelte +72 -0
  261. package/frontend/lib/components/common/Lightbox.svelte +233 -0
  262. package/frontend/lib/components/common/LoadingScreen.svelte +52 -0
  263. package/frontend/lib/components/common/LoadingSpinner.svelte +48 -0
  264. package/frontend/lib/components/common/Modal.svelte +177 -0
  265. package/frontend/lib/components/common/ModalProvider.svelte +28 -0
  266. package/frontend/lib/components/common/ModelSelector.svelte +110 -0
  267. package/frontend/lib/components/common/MonacoEditor.svelte +569 -0
  268. package/frontend/lib/components/common/NotificationToast.svelte +113 -0
  269. package/frontend/lib/components/common/PageTemplate.svelte +76 -0
  270. package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -0
  271. package/frontend/lib/components/common/Select.svelte +98 -0
  272. package/frontend/lib/components/common/Textarea.svelte +80 -0
  273. package/frontend/lib/components/common/ThemeToggle.svelte +44 -0
  274. package/frontend/lib/components/common/lucide-icons.ts +1642 -0
  275. package/frontend/lib/components/common/material-icons.ts +1082 -0
  276. package/frontend/lib/components/common/xterm/XTerm.svelte +796 -0
  277. package/frontend/lib/components/common/xterm/index.ts +16 -0
  278. package/frontend/lib/components/common/xterm/terminal-config.ts +68 -0
  279. package/frontend/lib/components/common/xterm/types.ts +31 -0
  280. package/frontend/lib/components/common/xterm/xterm-service.ts +353 -0
  281. package/frontend/lib/components/files/FileNode.svelte +384 -0
  282. package/frontend/lib/components/files/FileTree.svelte +681 -0
  283. package/frontend/lib/components/files/FileViewer.svelte +728 -0
  284. package/frontend/lib/components/files/SearchResults.svelte +303 -0
  285. package/frontend/lib/components/git/BranchManager.svelte +458 -0
  286. package/frontend/lib/components/git/ChangesSection.svelte +107 -0
  287. package/frontend/lib/components/git/CommitForm.svelte +76 -0
  288. package/frontend/lib/components/git/ConflictResolver.svelte +158 -0
  289. package/frontend/lib/components/git/DiffViewer.svelte +364 -0
  290. package/frontend/lib/components/git/FileChangeItem.svelte +97 -0
  291. package/frontend/lib/components/git/GitButton.svelte +33 -0
  292. package/frontend/lib/components/git/GitLog.svelte +361 -0
  293. package/frontend/lib/components/git/GitModal.svelte +80 -0
  294. package/frontend/lib/components/history/HistoryModal.svelte +563 -0
  295. package/frontend/lib/components/history/HistoryView.svelte +615 -0
  296. package/frontend/lib/components/index.ts +34 -0
  297. package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -0
  298. package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -0
  299. package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +757 -0
  300. package/frontend/lib/components/preview/browser/components/Container.svelte +450 -0
  301. package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -0
  302. package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -0
  303. package/frontend/lib/components/preview/browser/components/Toolbar.svelte +339 -0
  304. package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +36 -0
  305. package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -0
  306. package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -0
  307. package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -0
  308. package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -0
  309. package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -0
  310. package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -0
  311. package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -0
  312. package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -0
  313. package/frontend/lib/components/preview/index.ts +2 -0
  314. package/frontend/lib/components/settings/SettingsModal.svelte +235 -0
  315. package/frontend/lib/components/settings/SettingsView.svelte +36 -0
  316. package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -0
  317. package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -0
  318. package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -0
  319. package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -0
  320. package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -0
  321. package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -0
  322. package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -0
  323. package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -0
  324. package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -0
  325. package/frontend/lib/components/settings/user/UserSettings.svelte +197 -0
  326. package/frontend/lib/components/terminal/Terminal.svelte +368 -0
  327. package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -0
  328. package/frontend/lib/components/terminal/TerminalView.svelte +55 -0
  329. package/frontend/lib/components/tunnel/TunnelActive.svelte +142 -0
  330. package/frontend/lib/components/tunnel/TunnelButton.svelte +54 -0
  331. package/frontend/lib/components/tunnel/TunnelInactive.svelte +284 -0
  332. package/frontend/lib/components/tunnel/TunnelModal.svelte +47 -0
  333. package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -0
  334. package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -0
  335. package/frontend/lib/components/workspace/MobileNavigator.svelte +403 -0
  336. package/frontend/lib/components/workspace/PanelContainer.svelte +100 -0
  337. package/frontend/lib/components/workspace/PanelHeader.svelte +505 -0
  338. package/frontend/lib/components/workspace/ViewMenu.svelte +162 -0
  339. package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -0
  340. package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -0
  341. package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -0
  342. package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -0
  343. package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +85 -0
  344. package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -0
  345. package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -0
  346. package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -0
  347. package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -0
  348. package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -0
  349. package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -0
  350. package/frontend/lib/constants/preview.ts +45 -0
  351. package/frontend/lib/services/chat/chat.service.ts +704 -0
  352. package/frontend/lib/services/chat/index.ts +7 -0
  353. package/frontend/lib/services/notification/global-stream-monitor.ts +86 -0
  354. package/frontend/lib/services/notification/index.ts +8 -0
  355. package/frontend/lib/services/notification/push.service.ts +144 -0
  356. package/frontend/lib/services/notification/sound.service.ts +127 -0
  357. package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -0
  358. package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -0
  359. package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -0
  360. package/frontend/lib/services/preview/index.ts +23 -0
  361. package/frontend/lib/services/project/index.ts +8 -0
  362. package/frontend/lib/services/project/status.service.ts +159 -0
  363. package/frontend/lib/services/snapshot/snapshot.service.ts +47 -0
  364. package/frontend/lib/services/terminal/background/index.ts +130 -0
  365. package/frontend/lib/services/terminal/background/session-restore.ts +274 -0
  366. package/frontend/lib/services/terminal/background/stream-manager.ts +286 -0
  367. package/frontend/lib/services/terminal/index.ts +14 -0
  368. package/frontend/lib/services/terminal/persistence.service.ts +260 -0
  369. package/frontend/lib/services/terminal/project.service.ts +953 -0
  370. package/frontend/lib/services/terminal/session.service.ts +364 -0
  371. package/frontend/lib/services/terminal/terminal.service.ts +369 -0
  372. package/frontend/lib/stores/core/app.svelte.ts +117 -0
  373. package/frontend/lib/stores/core/files.svelte.ts +73 -0
  374. package/frontend/lib/stores/core/presence.svelte.ts +48 -0
  375. package/frontend/lib/stores/core/projects.svelte.ts +317 -0
  376. package/frontend/lib/stores/core/sessions.svelte.ts +383 -0
  377. package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -0
  378. package/frontend/lib/stores/features/models.svelte.ts +89 -0
  379. package/frontend/lib/stores/features/settings.svelte.ts +87 -0
  380. package/frontend/lib/stores/features/terminal.svelte.ts +701 -0
  381. package/frontend/lib/stores/features/tunnel.svelte.ts +161 -0
  382. package/frontend/lib/stores/features/user.svelte.ts +96 -0
  383. package/frontend/lib/stores/ui/chat-input.svelte.ts +57 -0
  384. package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -0
  385. package/frontend/lib/stores/ui/dialog.svelte.ts +59 -0
  386. package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -0
  387. package/frontend/lib/stores/ui/notification.svelte.ts +166 -0
  388. package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -0
  389. package/frontend/lib/stores/ui/theme.svelte.ts +179 -0
  390. package/frontend/lib/stores/ui/workspace.svelte.ts +754 -0
  391. package/frontend/lib/types/native-ui.ts +73 -0
  392. package/frontend/lib/utils/chat/date-separator.ts +39 -0
  393. package/frontend/lib/utils/chat/message-grouper.ts +219 -0
  394. package/frontend/lib/utils/chat/message-processor.ts +135 -0
  395. package/frontend/lib/utils/chat/tool-handler.ts +161 -0
  396. package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -0
  397. package/frontend/lib/utils/click-outside.ts +20 -0
  398. package/frontend/lib/utils/context-manager.ts +257 -0
  399. package/frontend/lib/utils/file-icon-mappings.ts +769 -0
  400. package/frontend/lib/utils/folder-icon-mappings.ts +1030 -0
  401. package/frontend/lib/utils/git-status.ts +68 -0
  402. package/frontend/lib/utils/platform.ts +113 -0
  403. package/frontend/lib/utils/port-check.ts +65 -0
  404. package/frontend/lib/utils/terminalFormatter.ts +207 -0
  405. package/frontend/lib/utils/theme.ts +6 -0
  406. package/frontend/lib/utils/tree-visualizer.ts +320 -0
  407. package/frontend/lib/utils/ws.ts +44 -0
  408. package/frontend/main.ts +13 -0
  409. package/index.html +70 -0
  410. package/package.json +111 -0
  411. package/scripts/generate-icons.ts +87 -0
  412. package/scripts/pre-publish-check.sh +142 -0
  413. package/scripts/setup-hooks.sh +134 -0
  414. package/scripts/validate-branch-name.sh +47 -0
  415. package/scripts/validate-commit-msg.sh +42 -0
  416. package/shared/constants/engines.ts +134 -0
  417. package/shared/types/database/connection.ts +16 -0
  418. package/shared/types/database/index.ts +6 -0
  419. package/shared/types/database/schema.ts +141 -0
  420. package/shared/types/engine/index.ts +45 -0
  421. package/shared/types/filesystem/index.ts +22 -0
  422. package/shared/types/git.ts +171 -0
  423. package/shared/types/messaging/index.ts +239 -0
  424. package/shared/types/messaging/tool.ts +526 -0
  425. package/shared/types/network/api.ts +18 -0
  426. package/shared/types/network/index.ts +5 -0
  427. package/shared/types/stores/app.ts +23 -0
  428. package/shared/types/stores/dialog.ts +21 -0
  429. package/shared/types/stores/index.ts +3 -0
  430. package/shared/types/stores/settings.ts +15 -0
  431. package/shared/types/terminal/index.ts +44 -0
  432. package/shared/types/ui/components.ts +61 -0
  433. package/shared/types/ui/icons.ts +23 -0
  434. package/shared/types/ui/index.ts +22 -0
  435. package/shared/types/ui/notifications.ts +14 -0
  436. package/shared/types/ui/theme.ts +12 -0
  437. package/shared/types/websocket/index.ts +43 -0
  438. package/shared/types/window.d.ts +13 -0
  439. package/shared/utils/anonymous-user.ts +168 -0
  440. package/shared/utils/async.ts +10 -0
  441. package/shared/utils/diff-calculator.ts +184 -0
  442. package/shared/utils/file-type-detection.ts +166 -0
  443. package/shared/utils/logger.ts +158 -0
  444. package/shared/utils/message-formatter.ts +79 -0
  445. package/shared/utils/path.ts +47 -0
  446. package/shared/utils/ws-client.ts +768 -0
  447. package/shared/utils/ws-server.ts +660 -0
  448. package/static/audio/notification.ogg +0 -0
  449. package/static/favicon.svg +8 -0
  450. package/static/fonts/dm-sans/dm-sans-italic-latin-ext.woff2 +0 -0
  451. package/static/fonts/dm-sans/dm-sans-italic-latin.woff2 +0 -0
  452. package/static/fonts/dm-sans/dm-sans-normal-latin-ext.woff2 +0 -0
  453. package/static/fonts/dm-sans/dm-sans-normal-latin.woff2 +0 -0
  454. package/static/fonts/dm-sans.css +96 -0
  455. package/svelte.config.js +20 -0
  456. package/vite.config.ts +33 -0
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Snapshot Restore Handler (Unified - replaces undo.ts and redo.ts)
3
+ *
4
+ * Single restore operation that moves HEAD to any checkpoint.
5
+ * Works identically regardless of whether the target is on the
6
+ * current path, a branch, or an orphaned node.
7
+ */
8
+
9
+ import { t } from 'elysia';
10
+ import { createRouter } from '$shared/utils/ws-server';
11
+ import { messageQueries, sessionQueries, projectQueries, snapshotQueries, checkpointQueries } from '../../lib/database/queries';
12
+ import { snapshotService } from '../../lib/snapshot/snapshot-service';
13
+ import { debug } from '$shared/utils/logger';
14
+ import {
15
+ buildCheckpointTree,
16
+ getCheckpointPathToRoot,
17
+ findSessionEnd
18
+ } from '../../lib/snapshot/helpers';
19
+ import { ws } from '$backend/lib/utils/ws';
20
+
21
+ export const restoreHandler = createRouter()
22
+ .http('snapshot:restore', {
23
+ data: t.Object({
24
+ messageId: t.String(),
25
+ sessionId: t.String()
26
+ }),
27
+ response: t.Object({
28
+ restoredTo: t.Object({
29
+ messageId: t.String(),
30
+ timestamp: t.String()
31
+ }),
32
+ filesRestored: t.Optional(t.Number())
33
+ })
34
+ }, async ({ data, conn }) => {
35
+ const { messageId, sessionId } = data;
36
+
37
+ debug.log('snapshot', 'RESTORE - Moving HEAD to checkpoint');
38
+ debug.log('snapshot', `Target checkpoint: ${messageId}`);
39
+ debug.log('snapshot', `Session: ${sessionId}`);
40
+
41
+ // 1. Get the checkpoint message
42
+ const checkpointMessage = messageQueries.getById(messageId);
43
+ if (!checkpointMessage) {
44
+ throw new Error('Checkpoint message not found');
45
+ }
46
+
47
+ // 2. Get current HEAD
48
+ const currentHead = sessionQueries.getHead(sessionId);
49
+ debug.log('snapshot', `Current HEAD: ${currentHead}`);
50
+
51
+ // 3. Get all messages and build checkpoint tree
52
+ const allMessages = messageQueries.getAllBySessionId(sessionId);
53
+ const { parentMap } = buildCheckpointTree(allMessages);
54
+
55
+ // 4. Find session end (last message of checkpoint's session)
56
+ const sessionEnd = findSessionEnd(checkpointMessage, allMessages);
57
+ const isSameAsCheckpoint = sessionEnd.id === messageId;
58
+ debug.log('snapshot', `Session end: ${sessionEnd.id} (checkpoint: ${messageId}, sameAsCheckpoint: ${isSameAsCheckpoint})`);
59
+
60
+ if (isSameAsCheckpoint) {
61
+ debug.warn('snapshot', '⚠️ Session end is the SAME as checkpoint message! This means no assistant children were found.');
62
+ debug.warn('snapshot', `Checkpoint parent_message_id: ${checkpointMessage.parent_message_id}`);
63
+ // List all direct children of this checkpoint to debug
64
+ const directChildren = allMessages.filter(m => m.parent_message_id === messageId);
65
+ debug.warn('snapshot', `Direct children of checkpoint: ${directChildren.length}`);
66
+ for (const child of directChildren) {
67
+ try {
68
+ const sdk = JSON.parse(child.sdk_message);
69
+ debug.warn('snapshot', ` child=${child.id.slice(0, 8)} type=${sdk.type} ts=${child.timestamp}`);
70
+ } catch {
71
+ debug.warn('snapshot', ` child=${child.id.slice(0, 8)} (parse error)`);
72
+ }
73
+ }
74
+ }
75
+
76
+ // If session end is already HEAD, nothing to do (but still restore files)
77
+ if (sessionEnd.id === currentHead) {
78
+ debug.log('snapshot', 'Already at this checkpoint HEAD');
79
+ }
80
+
81
+ // 5. Update HEAD to session end
82
+ sessionQueries.updateHead(sessionId, sessionEnd.id);
83
+ debug.log('snapshot', `HEAD updated to: ${sessionEnd.id}`);
84
+
85
+ // 5b. Update latest_sdk_session_id so resume works correctly
86
+ // Walk backward from sessionEnd to find the last assistant message with session_id
87
+ {
88
+ let walkId: string | null = sessionEnd.id;
89
+ let foundSdkSessionId: string | null = null;
90
+ const msgLookup = new Map(allMessages.map(m => [m.id, m]));
91
+
92
+ while (walkId) {
93
+ const walkMsg = msgLookup.get(walkId);
94
+ if (!walkMsg) break;
95
+
96
+ try {
97
+ const sdk = JSON.parse(walkMsg.sdk_message);
98
+ if (sdk.session_id) {
99
+ foundSdkSessionId = sdk.session_id;
100
+ break;
101
+ }
102
+ } catch { /* skip */ }
103
+
104
+ walkId = walkMsg.parent_message_id || null;
105
+ }
106
+
107
+ if (foundSdkSessionId) {
108
+ sessionQueries.updateLatestSdkSessionId(sessionId, foundSdkSessionId);
109
+ debug.log('snapshot', `latest_sdk_session_id updated to: ${foundSdkSessionId}`);
110
+ } else {
111
+ debug.warn('snapshot', 'Could not find SDK session_id for resume - resume may not work correctly');
112
+ }
113
+ }
114
+
115
+ // 6. Update checkpoint_tree_state for ancestors
116
+ const checkpointPath = getCheckpointPathToRoot(messageId, parentMap);
117
+ if (checkpointPath.length > 1) {
118
+ checkpointQueries.updateActiveChildrenAlongPath(sessionId, checkpointPath);
119
+ }
120
+
121
+ // 7. Restore file system state from snapshot
122
+ // Walk backward from session end to checkpoint to find the best (most recent) snapshot
123
+ let filesRestored = 0;
124
+ const msgMap = new Map(allMessages.map(m => [m.id, m]));
125
+
126
+ let snapshot = null;
127
+ let walkId: string | null = sessionEnd.id;
128
+ while (walkId) {
129
+ const s = snapshotQueries.getByMessageId(walkId);
130
+ if (s) {
131
+ snapshot = s;
132
+ break;
133
+ }
134
+ // Don't walk past the checkpoint message
135
+ if (walkId === messageId) break;
136
+ const walkMsg = msgMap.get(walkId);
137
+ if (!walkMsg) break;
138
+ walkId = walkMsg.parent_message_id || null;
139
+ }
140
+
141
+ debug.log('snapshot', `Snapshot found: ${snapshot ? `${snapshot.id} (for message ${snapshot.message_id})` : 'none'}`);
142
+
143
+ if (snapshot) {
144
+ const session = sessionQueries.getById(sessionId);
145
+ if (session) {
146
+ const project = projectQueries.getById(session.project_id);
147
+ if (project) {
148
+ await snapshotService.restoreSnapshot(project.path, snapshot);
149
+ debug.log('snapshot', 'Files restored from snapshot');
150
+ filesRestored = 1;
151
+ }
152
+ }
153
+ }
154
+
155
+ // 8. Broadcast messages-changed to users in the chat session
156
+ try {
157
+ ws.emit.chatSession(sessionId, 'chat:messages-changed', {
158
+ sessionId,
159
+ reason: 'restore',
160
+ timestamp: new Date().toISOString()
161
+ });
162
+ } catch (err) {
163
+ debug.error('snapshot', 'Failed to broadcast messages-changed:', err);
164
+ }
165
+
166
+ return {
167
+ restoredTo: {
168
+ messageId: sessionEnd.id,
169
+ timestamp: sessionEnd.timestamp
170
+ },
171
+ filesRestored
172
+ };
173
+ });
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Snapshot Timeline WebSocket Handler (Rewritten)
3
+ *
4
+ * Builds timeline from parent_message_id tree structure.
5
+ * No longer depends on branch_id markers.
6
+ */
7
+
8
+ import { t } from 'elysia';
9
+ import { createRouter } from '$shared/utils/ws-server';
10
+ import { messageQueries, sessionQueries, checkpointQueries, snapshotQueries } from '../../lib/database/queries';
11
+ import { debug } from '$shared/utils/logger';
12
+ import {
13
+ extractMessageText,
14
+ buildCheckpointTree,
15
+ getCheckpointPathToRoot,
16
+ findCheckpointForHead,
17
+ isDescendant,
18
+ getCheckpointFileStats
19
+ } from '../../lib/snapshot/helpers';
20
+ import type { CheckpointNode, TimelineResponse } from '../../lib/snapshot/helpers';
21
+ import type { SDKMessage } from '$shared/types/messaging';
22
+
23
+ export const timelineHandler = createRouter()
24
+ .http('snapshot:get-timeline', {
25
+ data: t.Object({
26
+ sessionId: t.String()
27
+ }),
28
+ response: t.Object({
29
+ nodes: t.Array(t.Any()),
30
+ currentHeadId: t.Union([t.String(), t.Null()])
31
+ })
32
+ }, async ({ data }) => {
33
+ const { sessionId } = data;
34
+
35
+ debug.log('snapshot', 'TIMELINE - Building tree view');
36
+
37
+ // 1. Get current HEAD
38
+ const currentHead = sessionQueries.getHead(sessionId);
39
+ debug.log('snapshot', `Current HEAD: ${currentHead || 'null'}`);
40
+
41
+ if (!currentHead) {
42
+ return { nodes: [], currentHeadId: null };
43
+ }
44
+
45
+ // 2. Get all messages
46
+ const allMessages = messageQueries.getAllBySessionId(sessionId);
47
+ debug.log('snapshot', `Total messages: ${allMessages.length}`);
48
+
49
+ if (allMessages.length === 0) {
50
+ return { nodes: [], currentHeadId: null };
51
+ }
52
+
53
+ // 3. Build checkpoint tree
54
+ const { checkpoints, parentMap, childrenMap } = buildCheckpointTree(allMessages);
55
+ debug.log('snapshot', `Checkpoints found: ${checkpoints.length}`);
56
+
57
+ if (checkpoints.length === 0) {
58
+ return { nodes: [], currentHeadId: null };
59
+ }
60
+
61
+ const checkpointIdSet = new Set(checkpoints.map(c => c.id));
62
+
63
+ // 4. Find which checkpoint HEAD belongs to
64
+ const activeCheckpointId = findCheckpointForHead(currentHead, allMessages, checkpointIdSet);
65
+ debug.log('snapshot', `Active checkpoint: ${activeCheckpointId}`);
66
+
67
+ // 5. Build active path (from root to active checkpoint)
68
+ const activePathIds = new Set<string>();
69
+ if (activeCheckpointId) {
70
+ const activePath = getCheckpointPathToRoot(activeCheckpointId, parentMap);
71
+ for (const id of activePath) {
72
+ activePathIds.add(id);
73
+ }
74
+ }
75
+
76
+ // 6. Get active children map from database
77
+ const activeChildrenMap = checkpointQueries.getAllActiveChildren(sessionId);
78
+
79
+ // 7. Sort checkpoints by timestamp for file stats calculation
80
+ const sortedCheckpoints = [...checkpoints].sort(
81
+ (a, b) => a.timestamp.localeCompare(b.timestamp)
82
+ );
83
+
84
+ // Build next-checkpoint timestamp map for stats
85
+ const nextTimestampMap = new Map<string, string>();
86
+ for (let i = 0; i < sortedCheckpoints.length; i++) {
87
+ const next = sortedCheckpoints[i + 1];
88
+ if (next) {
89
+ nextTimestampMap.set(sortedCheckpoints[i].id, next.timestamp);
90
+ }
91
+ }
92
+
93
+ // 8. Build response nodes
94
+ const nodes: CheckpointNode[] = [];
95
+
96
+ for (const cp of checkpoints) {
97
+ const sdk = JSON.parse(cp.sdk_message) as SDKMessage;
98
+ const messageText = extractMessageText(sdk).trim().slice(0, 100);
99
+ const parentCpId = parentMap.get(cp.id) || null;
100
+ const activeChildId = activeChildrenMap.get(cp.id) || null;
101
+ const isOnActivePath = activePathIds.has(cp.id);
102
+ const isCurrent = cp.id === activeCheckpointId;
103
+
104
+ // Orphaned = descendant of active checkpoint in the checkpoint tree
105
+ let isOrphaned = false;
106
+ if (activeCheckpointId && !isOnActivePath) {
107
+ isOrphaned = isDescendant(cp.id, activeCheckpointId, childrenMap);
108
+ }
109
+
110
+ // File stats
111
+ const nextTimestamp = nextTimestampMap.get(cp.id);
112
+ const stats = getCheckpointFileStats(cp, allMessages, nextTimestamp);
113
+
114
+ const snapshot = snapshotQueries.getByMessageId(cp.id);
115
+
116
+ nodes.push({
117
+ id: cp.id,
118
+ messageId: cp.id,
119
+ parentId: parentCpId,
120
+ activeChildId,
121
+ timestamp: cp.timestamp,
122
+ messageText,
123
+ isOnActivePath,
124
+ isOrphaned,
125
+ isCurrent,
126
+ hasSnapshot: !!snapshot,
127
+ senderName: cp.sender_name,
128
+ filesChanged: stats.filesChanged,
129
+ insertions: stats.insertions,
130
+ deletions: stats.deletions
131
+ });
132
+ }
133
+
134
+ debug.log('snapshot', `Timeline nodes: ${nodes.length}`);
135
+ debug.log('snapshot', `Active path: ${activePathIds.size} nodes`);
136
+
137
+ return {
138
+ nodes,
139
+ currentHeadId: activeCheckpointId
140
+ };
141
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * System Router
3
+ *
4
+ * Combines all system WebSocket handlers into a single router.
5
+ *
6
+ * Structure:
7
+ * - operations.ts: HTTP endpoints for system operations (clear-data)
8
+ */
9
+
10
+ import { createRouter } from '$shared/utils/ws-server';
11
+ import { operationsHandler } from './operations';
12
+
13
+ export const systemRouter = createRouter()
14
+ .merge(operationsHandler);
@@ -0,0 +1,49 @@
1
+ /**
2
+ * System Operations
3
+ *
4
+ * HTTP endpoints for system-level operations:
5
+ * - Clear all database data
6
+ */
7
+
8
+ import { t } from 'elysia';
9
+ import { createRouter } from '$shared/utils/ws-server';
10
+ import { initializeDatabase, getDatabase } from '../../lib/database';
11
+ import { debug } from '$shared/utils/logger';
12
+
13
+ export const operationsHandler = createRouter()
14
+ // Clear all database data
15
+ .http('system:clear-data', {
16
+ data: t.Object({}),
17
+ response: t.Object({
18
+ cleared: t.Boolean(),
19
+ tablesCount: t.Number()
20
+ })
21
+ }, async () => {
22
+ debug.log('server', 'Clearing all database data...');
23
+
24
+ // Initialize database first to ensure it exists
25
+ await initializeDatabase();
26
+
27
+ // Get database connection
28
+ const db = getDatabase();
29
+
30
+ // Get all table names
31
+ const tables = db.prepare(`
32
+ SELECT name FROM sqlite_master
33
+ WHERE type='table'
34
+ AND name NOT LIKE 'sqlite_%'
35
+ `).all() as { name: string }[];
36
+
37
+ // Delete all data from each table
38
+ for (const table of tables) {
39
+ db.prepare(`DELETE FROM ${table.name}`).run();
40
+ debug.log('server', `Cleared table: ${table.name}`);
41
+ }
42
+
43
+ debug.log('server', 'Database cleared successfully');
44
+
45
+ return {
46
+ cleared: true,
47
+ tablesCount: tables.length
48
+ };
49
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Terminal Router
3
+ *
4
+ * Combines all terminal WebSocket handlers into a single router.
5
+ * Replaces SSE-based terminal streaming with WebSocket bi-directional communication.
6
+ *
7
+ * Structure:
8
+ * - session.ts: HTTP endpoints for session management (create, resize, kill, cancel, check-shell, pty-status)
9
+ * - stream.ts: Real-time events for terminal I/O (input, output, exit, directory, ready)
10
+ * - persistence.ts: Stream persistence and reconnection (stream-status, missed-output, reconnect)
11
+ */
12
+
13
+ import { t } from 'elysia';
14
+ import { createRouter } from '$shared/utils/ws-server';
15
+ import { sessionHandler } from './session';
16
+ import { streamHandler } from './stream';
17
+ import { persistenceHandler } from './persistence';
18
+
19
+ export const terminalRouter = createRouter()
20
+ // Session Management (HTTP)
21
+ .merge(sessionHandler)
22
+
23
+ // Stream Events (Real-time I/O)
24
+ .merge(streamHandler)
25
+
26
+ // Stream Persistence (HTTP + Events)
27
+ .merge(persistenceHandler)
28
+
29
+ // Collaborative broadcast events (Server → Client)
30
+ .emit('terminal:tab-created', t.Object({
31
+ sessionId: t.String(),
32
+ streamId: t.String(),
33
+ pid: t.Number(),
34
+ currentDirectory: t.String(),
35
+ cols: t.Number(),
36
+ rows: t.Number()
37
+ }))
38
+ .emit('terminal:tab-closed', t.Object({
39
+ sessionId: t.String()
40
+ }));
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Terminal Stream Persistence
3
+ *
4
+ * Handles terminal stream persistence and reconnection:
5
+ * - Get stream status
6
+ * - Retrieve missed output
7
+ * - Reconnect to existing streams
8
+ */
9
+
10
+ import { t } from 'elysia';
11
+ import { createRouter } from '$shared/utils/ws-server';
12
+ import { terminalStreamManager } from '../../lib/terminal/stream-manager';
13
+ import { debug } from '$shared/utils/logger';
14
+ import { ws } from '$backend/lib/utils/ws';
15
+ import { ptySessionManager } from '../../lib/terminal/pty-session-manager';
16
+
17
+ export const persistenceHandler = createRouter()
18
+ // Get stream status
19
+ .http('terminal:stream-status', {
20
+ data: t.Object({
21
+ streamId: t.String()
22
+ }),
23
+ response: t.Any()
24
+ }, async ({ data }) => {
25
+ const { streamId } = data;
26
+
27
+ const status = terminalStreamManager.getStreamStatus(streamId);
28
+
29
+ if (!status) {
30
+ throw new Error('Stream not found');
31
+ }
32
+
33
+ return status;
34
+ })
35
+
36
+ // Get missed output
37
+ .http('terminal:missed-output', {
38
+ data: t.Object({
39
+ sessionId: t.String(),
40
+ streamId: t.Optional(t.String()),
41
+ fromIndex: t.Optional(t.Number())
42
+ }),
43
+ response: t.Object({
44
+ sessionId: t.String(),
45
+ streamId: t.Union([t.String(), t.Null()]),
46
+ output: t.Array(t.String()),
47
+ outputCount: t.Number(),
48
+ status: t.String(),
49
+ fromIndex: t.Number(),
50
+ timestamp: t.String()
51
+ })
52
+ }, async ({ data }) => {
53
+ const { sessionId, streamId, fromIndex = 0 } = data;
54
+
55
+ // Try to get output from stream manager (memory or cache)
56
+ let output: string[] = [];
57
+
58
+ if (streamId) {
59
+ // If streamId is provided, get output from that specific stream
60
+ output = terminalStreamManager.getOutput(streamId, fromIndex);
61
+ } else {
62
+ // Otherwise try to load cached output for the session
63
+ const cachedOutput = terminalStreamManager.loadCachedOutput(sessionId);
64
+ if (cachedOutput) {
65
+ output = cachedOutput.slice(fromIndex);
66
+ }
67
+ }
68
+
69
+ // Get stream status if available
70
+ const streamStatus = streamId ? terminalStreamManager.getStreamStatus(streamId) : null;
71
+
72
+ return {
73
+ sessionId,
74
+ streamId: streamId || null,
75
+ output,
76
+ outputCount: output.length,
77
+ status: streamStatus?.status || 'unknown',
78
+ fromIndex,
79
+ timestamp: new Date().toISOString()
80
+ };
81
+ })
82
+
83
+ // Reconnect to stream
84
+ .on('terminal:reconnect', {
85
+ data: t.Object({
86
+ streamId: t.String(),
87
+ sessionId: t.String(),
88
+ fromIndex: t.Optional(t.Number())
89
+ })
90
+ }, async ({ data, conn }) => {
91
+ const { streamId, sessionId, fromIndex = 0 } = data;
92
+ const projectId = ws.getProjectId(conn);
93
+
94
+ const stream = terminalStreamManager.getStream(streamId);
95
+
96
+ if (!stream) {
97
+ ws.emit.project(projectId, 'terminal:error', {
98
+ sessionId,
99
+ error: 'Stream not found'
100
+ });
101
+ return;
102
+ }
103
+
104
+ try {
105
+ // Broadcast missed output (frontend filters by sessionId for one-time replay)
106
+ const existingOutput = terminalStreamManager.getOutput(streamId, fromIndex);
107
+
108
+ if (existingOutput.length > 0) {
109
+ for (const output of existingOutput) {
110
+ ws.emit.project(projectId, 'terminal:output', {
111
+ sessionId,
112
+ content: output,
113
+ timestamp: new Date().toISOString()
114
+ });
115
+ }
116
+ }
117
+
118
+ if (stream.status === 'active') {
119
+ // If no dataListeners exist yet (create-session hasn't been called),
120
+ // set up a project-broadcast listener so ongoing output reaches all clients.
121
+ // This listener will be replaced when terminal:create-session is called later.
122
+ const ptySession = ptySessionManager.getSession(sessionId);
123
+ if (ptySession && ptySession.dataListeners.size === 0) {
124
+ debug.log('terminal', `📡 Reconnect: No dataListeners, setting up broadcast listener for session: ${sessionId}`);
125
+
126
+ const broadcastListener = (output: string) => {
127
+ const currentSeq = ptySessionManager.getSession(sessionId)?.outputSeq || 0;
128
+ ws.emit.project(projectId, 'terminal:output', {
129
+ sessionId,
130
+ content: output,
131
+ seq: currentSeq,
132
+ projectId,
133
+ timestamp: new Date().toISOString()
134
+ });
135
+ };
136
+ ptySession.dataListeners.add(broadcastListener);
137
+ }
138
+ // No polling needed - dataListener handles ongoing output via ws.emit.project()
139
+ } else {
140
+ // Stream is not active, broadcast exit event (frontend filters by sessionId)
141
+ ws.emit.project(projectId, 'terminal:exit', {
142
+ sessionId,
143
+ exitCode: 0
144
+ });
145
+ }
146
+ } catch (error) {
147
+ debug.error('terminal', 'Error in stream reconnect:', error);
148
+ ws.emit.project(projectId, 'terminal:error', {
149
+ sessionId,
150
+ error: error instanceof Error ? error.message : 'Unknown error'
151
+ });
152
+ }
153
+ })