@myrialabs/clopen 0.2.1 → 0.2.3

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 (422) hide show
  1. package/.dockerignore +5 -0
  2. package/.env.example +2 -5
  3. package/CONTRIBUTING.md +4 -0
  4. package/README.md +4 -2
  5. package/backend/{lib/auth → auth}/auth-service.ts +2 -2
  6. package/backend/{lib/chat → chat}/stream-manager.ts +1 -1
  7. package/backend/{lib/database → database}/queries/message-queries.ts +42 -0
  8. package/backend/{lib/database → database}/utils/connection.ts +5 -5
  9. package/backend/{lib/engine → engine}/adapters/claude/environment.ts +3 -4
  10. package/backend/{lib/engine → engine}/adapters/opencode/message-converter.ts +1 -1
  11. package/backend/{lib/engine → engine}/adapters/opencode/server.ts +7 -1
  12. package/backend/{lib/files → files}/file-watcher.ts +1 -1
  13. package/backend/{lib/git → git}/git-executor.ts +3 -2
  14. package/backend/index.ts +16 -16
  15. package/backend/{lib/mcp → mcp}/README.md +68 -79
  16. package/backend/{lib/mcp → mcp}/config.ts +1 -1
  17. package/backend/{lib/mcp → mcp}/servers/browser-automation/actions.ts +4 -4
  18. package/backend/{lib/mcp → mcp}/servers/browser-automation/browser.ts +3 -3
  19. package/backend/{lib/mcp → mcp}/servers/browser-automation/inspection.ts +3 -3
  20. package/backend/middleware/cors.ts +1 -1
  21. package/backend/middleware/error-handler.ts +1 -1
  22. package/backend/{lib/preview → preview}/browser/browser-preview-service.ts +1 -1
  23. package/backend/{lib/preview → preview}/browser/browser-tab-manager.ts +1 -1
  24. package/backend/{lib/preview → preview}/browser/types.ts +1 -1
  25. package/backend/{lib/project → project}/status-manager.ts +223 -223
  26. package/backend/{lib/snapshot → snapshot}/blob-store.ts +2 -2
  27. package/backend/{lib/terminal → terminal}/shell-utils.ts +1 -1
  28. package/backend/{lib/shared → utils}/env.ts +13 -15
  29. package/backend/{lib/shared → utils}/index.ts +4 -1
  30. package/backend/utils/paths.ts +11 -0
  31. package/backend/{lib/shared → utils}/port-utils.ts +19 -6
  32. package/backend/{lib/utils → utils}/ws.ts +2 -2
  33. package/backend/ws/README.md +9 -9
  34. package/backend/ws/auth/invites.ts +2 -2
  35. package/backend/ws/auth/login.ts +9 -9
  36. package/backend/ws/auth/status.ts +2 -2
  37. package/backend/ws/auth/users.ts +1 -1
  38. package/backend/ws/chat/background.ts +2 -2
  39. package/backend/ws/chat/stream.ts +3 -3
  40. package/backend/ws/engine/claude/accounts.ts +4 -4
  41. package/backend/ws/engine/claude/status.ts +1 -1
  42. package/backend/ws/files/read.ts +2 -2
  43. package/backend/ws/files/watch.ts +2 -2
  44. package/backend/ws/files/write.ts +1 -1
  45. package/backend/ws/git/branch.ts +2 -2
  46. package/backend/ws/git/commit.ts +2 -2
  47. package/backend/ws/git/conflict.ts +2 -2
  48. package/backend/ws/git/diff.ts +2 -2
  49. package/backend/ws/git/log.ts +2 -2
  50. package/backend/ws/git/remote.ts +2 -2
  51. package/backend/ws/git/staging.ts +2 -2
  52. package/backend/ws/git/status.ts +3 -3
  53. package/backend/ws/messages/crud.ts +53 -1
  54. package/backend/ws/preview/browser/cleanup.ts +2 -2
  55. package/backend/ws/preview/browser/console.ts +2 -2
  56. package/backend/ws/preview/browser/interact.ts +2 -2
  57. package/backend/ws/preview/browser/mcp.ts +1 -1
  58. package/backend/ws/preview/browser/native-ui.ts +2 -2
  59. package/backend/ws/preview/browser/stats.ts +2 -2
  60. package/backend/ws/preview/browser/tab-info.ts +2 -2
  61. package/backend/ws/preview/browser/tab.ts +2 -2
  62. package/backend/ws/preview/browser/webcodecs.ts +2 -2
  63. package/backend/ws/projects/crud.ts +3 -3
  64. package/backend/ws/projects/presence.ts +3 -3
  65. package/backend/ws/projects/status.ts +3 -3
  66. package/backend/ws/sessions/crud.ts +2 -2
  67. package/backend/ws/settings/crud.ts +2 -2
  68. package/backend/ws/snapshot/restore.ts +5 -5
  69. package/backend/ws/snapshot/timeline.ts +3 -3
  70. package/backend/ws/system/operations.ts +1 -1
  71. package/backend/ws/terminal/persistence.ts +3 -3
  72. package/backend/ws/terminal/session.ts +6 -6
  73. package/backend/ws/terminal/stream.ts +2 -2
  74. package/backend/ws/tunnel/operations.ts +1 -1
  75. package/backend/ws/user/crud.ts +4 -4
  76. package/bin/clopen.ts +15 -15
  77. package/docker-compose.yml +31 -0
  78. package/frontend/App.svelte +13 -13
  79. package/frontend/{lib/components → components}/auth/InvitePage.svelte +2 -2
  80. package/frontend/{lib/components → components}/auth/LoginPage.svelte +1 -1
  81. package/frontend/{lib/components → components}/auth/SetupPage.svelte +49 -17
  82. package/frontend/{lib/components → components}/chat/ChatInterface.svelte +14 -14
  83. package/frontend/{lib/components → components}/chat/formatters/ErrorMessage.svelte +1 -1
  84. package/frontend/{lib/components → components}/chat/formatters/MessageFormatter.svelte +4 -4
  85. package/frontend/{lib/components → components}/chat/formatters/TextMessage.svelte +1 -1
  86. package/frontend/{lib/components → components}/chat/formatters/Tools.svelte +1 -1
  87. package/frontend/{lib/components → components}/chat/input/ChatInput.svelte +12 -12
  88. package/frontend/{lib/components → components}/chat/input/components/ChatInputActions.svelte +1 -1
  89. package/frontend/{lib/components → components}/chat/input/components/EditModeIndicator.svelte +2 -2
  90. package/frontend/{lib/components → components}/chat/input/components/EngineModelPicker.svelte +9 -9
  91. package/frontend/{lib/components → components}/chat/input/components/FileAttachmentPreview.svelte +1 -1
  92. package/frontend/{lib/components → components}/chat/input/components/LoadingIndicator.svelte +2 -2
  93. package/frontend/{lib/components → components}/chat/input/composables/use-chat-actions.svelte.ts +8 -8
  94. package/frontend/{lib/components → components}/chat/input/composables/use-file-handling.svelte.ts +1 -1
  95. package/frontend/{lib/components → components}/chat/input/composables/use-input-state.svelte.ts +6 -6
  96. package/frontend/{lib/components → components}/chat/message/ChatMessage.svelte +8 -8
  97. package/frontend/{lib/components → components}/chat/message/ChatMessages.svelte +7 -7
  98. package/frontend/{lib/components → components}/chat/message/MessageBubble.svelte +1 -1
  99. package/frontend/{lib/components → components}/chat/message/MessageHeader.svelte +2 -2
  100. package/frontend/{lib/components → components}/chat/modal/DebugModal.svelte +2 -2
  101. package/frontend/{lib/components → components}/chat/modal/TokenUsageModal.svelte +2 -2
  102. package/frontend/{lib/components → components}/chat/tools/AskUserQuestionTool.svelte +4 -4
  103. package/frontend/{lib/components → components}/chat/tools/CustomMcpTool.svelte +1 -1
  104. package/frontend/{lib/components → components}/chat/tools/EditTool.svelte +1 -1
  105. package/frontend/{lib/components → components}/chat/tools/TodoWriteTool.svelte +1 -1
  106. package/frontend/{lib/components → components}/chat/tools/components/CodeBlock.svelte +2 -2
  107. package/frontend/{lib/components → components}/chat/tools/components/DiffBlock.svelte +1 -1
  108. package/frontend/{lib/components → components}/chat/tools/components/FileHeader.svelte +2 -2
  109. package/frontend/{lib/components → components}/chat/tools/components/InfoLine.svelte +1 -1
  110. package/frontend/{lib/components → components}/chat/widgets/FloatingTodoList.svelte +127 -13
  111. package/frontend/{lib/components → components}/chat/widgets/TokenUsage.svelte +4 -4
  112. package/frontend/{lib/components → components}/checkpoint/TimelineModal.svelte +10 -10
  113. package/frontend/{lib/components → components}/checkpoint/timeline/TimelineNode.svelte +1 -1
  114. package/frontend/{lib/components/common → components/common/display}/Icon.svelte +2 -2
  115. package/frontend/{lib/components/common → components/common/display}/PageTemplate.svelte +1 -1
  116. package/frontend/{lib/components/common → components/common/display}/ProjectUserAvatars.svelte +1 -1
  117. package/frontend/{lib/components/common → components/common/display}/ThemeToggle.svelte +1 -1
  118. package/frontend/{lib/components/common → components/common/editor}/MonacoEditor.svelte +2 -2
  119. package/frontend/{lib/components/common → components/common/feedback}/Alert.svelte +1 -1
  120. package/frontend/{lib/components/common → components/common/feedback}/ConnectionBanner.svelte +3 -3
  121. package/frontend/{lib/components/common → components/common/feedback}/LoadingScreen.svelte +1 -1
  122. package/frontend/{lib/components/common → components/common/feedback}/NotificationToast.svelte +2 -2
  123. package/frontend/{lib/components/common → components/common/feedback}/UpdateBanner.svelte +5 -5
  124. package/frontend/{lib/components/common → components/common/form}/Checkbox.svelte +1 -1
  125. package/frontend/{lib/components/common → components/common/form}/FolderBrowser.svelte +6 -6
  126. package/frontend/{lib/components/common → components/common/form}/Input.svelte +1 -1
  127. package/frontend/{lib/components/common → components/common/form}/ModelSelector.svelte +3 -3
  128. package/frontend/{lib/components/common → components/common/form}/Select.svelte +1 -1
  129. package/frontend/{lib/components/common → components/common/form}/Textarea.svelte +1 -1
  130. package/frontend/{lib/components/common → components/common/overlay}/Dialog.svelte +1 -1
  131. package/frontend/{lib/components/common → components/common/overlay}/Lightbox.svelte +3 -3
  132. package/frontend/{lib/components/common → components/common/overlay}/ModalProvider.svelte +2 -2
  133. package/frontend/{lib/components → components}/common/xterm/XTerm.svelte +5 -5
  134. package/frontend/{lib/components → components}/common/xterm/xterm-service.ts +2 -2
  135. package/frontend/{lib/components → components}/files/FileNode.svelte +3 -3
  136. package/frontend/{lib/components → components}/files/FileTree.svelte +5 -5
  137. package/frontend/{lib/components → components}/files/FileViewer.svelte +7 -7
  138. package/frontend/{lib/components → components}/files/SearchResults.svelte +3 -3
  139. package/frontend/{lib/components → components}/git/BranchManager.svelte +6 -6
  140. package/frontend/{lib/components → components}/git/ChangesSection.svelte +1 -1
  141. package/frontend/{lib/components → components}/git/CommitForm.svelte +1 -1
  142. package/frontend/{lib/components → components}/git/ConflictResolver.svelte +1 -1
  143. package/frontend/{lib/components → components}/git/DiffViewer.svelte +7 -7
  144. package/frontend/{lib/components → components}/git/FileChangeItem.svelte +3 -3
  145. package/frontend/{lib/components → components}/git/GitButton.svelte +1 -1
  146. package/frontend/{lib/components → components}/git/GitLog.svelte +2 -2
  147. package/frontend/{lib/components → components}/git/GitModal.svelte +3 -3
  148. package/frontend/{lib/components → components}/history/HistoryModal.svelte +41 -89
  149. package/frontend/{lib/components → components}/history/HistoryView.svelte +57 -104
  150. package/frontend/{lib/components → components}/index.ts +9 -9
  151. package/frontend/{lib/components → components}/preview/browser/BrowserPreview.svelte +3 -3
  152. package/frontend/{lib/components → components}/preview/browser/components/Canvas.svelte +2 -2
  153. package/frontend/{lib/components → components}/preview/browser/components/ConsolePanel.svelte +1 -1
  154. package/frontend/{lib/components → components}/preview/browser/components/Container.svelte +2 -2
  155. package/frontend/{lib/components → components}/preview/browser/components/ContextMenu.svelte +1 -1
  156. package/frontend/{lib/components → components}/preview/browser/components/SelectDropdown.svelte +1 -1
  157. package/frontend/{lib/components → components}/preview/browser/components/Toolbar.svelte +2 -2
  158. package/frontend/{lib/components → components}/preview/browser/core/cleanup.svelte.ts +1 -1
  159. package/frontend/{lib/components → components}/preview/browser/core/coordinator.svelte.ts +4 -4
  160. package/frontend/{lib/components → components}/preview/browser/core/interactions.svelte.ts +3 -3
  161. package/frontend/{lib/components → components}/preview/browser/core/mcp-handlers.svelte.ts +2 -2
  162. package/frontend/{lib/components → components}/preview/browser/core/native-ui-handlers.svelte.ts +2 -2
  163. package/frontend/{lib/components → components}/preview/browser/core/tab-manager.svelte.ts +1 -1
  164. package/frontend/{lib/components → components}/preview/browser/core/tab-operations.svelte.ts +3 -3
  165. package/frontend/{lib/components → components}/settings/SettingsModal.svelte +4 -4
  166. package/frontend/{lib/components → components}/settings/SettingsView.svelte +2 -2
  167. package/frontend/{lib/components → components}/settings/admin/InviteManagement.svelte +5 -5
  168. package/frontend/{lib/components → components}/settings/admin/UserManagement.svelte +5 -5
  169. package/frontend/{lib/components → components}/settings/appearance/AppearanceSettings.svelte +5 -5
  170. package/frontend/{lib/components → components}/settings/appearance/LayoutPresetSettings.svelte +3 -3
  171. package/frontend/{lib/components → components}/settings/appearance/LayoutPreview.svelte +1 -1
  172. package/frontend/{lib/components → components}/settings/engines/AIEnginesSettings.svelte +6 -6
  173. package/frontend/{lib/components → components}/settings/general/AdvancedSettings.svelte +5 -5
  174. package/frontend/{lib/components → components}/settings/general/AuthModeSettings.svelte +5 -5
  175. package/frontend/{lib/components → components}/settings/general/DataManagementSettings.svelte +9 -9
  176. package/frontend/{lib/components → components}/settings/general/UpdateSettings.svelte +3 -3
  177. package/frontend/{lib/components → components}/settings/model/ModelSettings.svelte +2 -2
  178. package/frontend/{lib/components → components}/settings/notifications/NotificationSettings.svelte +4 -4
  179. package/frontend/{lib/components → components}/settings/user/UserSettings.svelte +5 -5
  180. package/frontend/{lib/components → components}/terminal/Terminal.svelte +9 -9
  181. package/frontend/{lib/components → components}/terminal/TerminalTabs.svelte +1 -1
  182. package/frontend/{lib/components → components}/terminal/TerminalView.svelte +5 -5
  183. package/frontend/{lib/components → components}/tunnel/TunnelActive.svelte +3 -3
  184. package/frontend/{lib/components → components}/tunnel/TunnelButton.svelte +2 -2
  185. package/frontend/{lib/components → components}/tunnel/TunnelInactive.svelte +4 -4
  186. package/frontend/{lib/components → components}/tunnel/TunnelModal.svelte +2 -2
  187. package/frontend/{lib/components → components}/workspace/DesktopNavigator.svelte +372 -372
  188. package/frontend/{lib/components → components}/workspace/MobileNavigator.svelte +380 -380
  189. package/frontend/{lib/components → components}/workspace/PanelContainer.svelte +3 -3
  190. package/frontend/{lib/components → components}/workspace/PanelHeader.svelte +10 -10
  191. package/frontend/{lib/components → components}/workspace/ViewMenu.svelte +4 -4
  192. package/frontend/{lib/components → components}/workspace/WorkspaceLayout.svelte +17 -17
  193. package/frontend/{lib/components → components}/workspace/layout/DesktopLayout.svelte +1 -1
  194. package/frontend/{lib/components → components}/workspace/layout/MobileLayout.svelte +1 -1
  195. package/frontend/{lib/components → components}/workspace/layout/split-pane/Container.svelte +1 -1
  196. package/frontend/{lib/components → components}/workspace/layout/split-pane/Handle.svelte +2 -2
  197. package/frontend/{lib/components → components}/workspace/layout/split-pane/Layout.svelte +2 -2
  198. package/frontend/{lib/components → components}/workspace/panels/ChatPanel.svelte +14 -14
  199. package/frontend/{lib/components → components}/workspace/panels/FilesPanel.svelte +51 -13
  200. package/frontend/{lib/components → components}/workspace/panels/GitPanel.svelte +55 -17
  201. package/frontend/{lib/components → components}/workspace/panels/PreviewPanel.svelte +5 -5
  202. package/frontend/{lib/components → components}/workspace/panels/TerminalPanel.svelte +6 -6
  203. package/frontend/{lib/services → services}/chat/chat.service.ts +8 -8
  204. package/frontend/{lib/services → services}/notification/global-stream-monitor.ts +117 -117
  205. package/frontend/{lib/services → services}/notification/push.service.ts +1 -1
  206. package/frontend/{lib/services → services}/notification/sound.service.ts +1 -1
  207. package/frontend/{lib/services → services}/preview/browser/browser-console.service.ts +1 -1
  208. package/frontend/{lib/services → services}/preview/browser/browser-webcodecs.service.ts +1 -1
  209. package/frontend/{lib/services → services}/preview/browser/mcp-integration.svelte.ts +2 -2
  210. package/frontend/{lib/services → services}/project/status.service.ts +160 -160
  211. package/frontend/{lib/services → services}/snapshot/snapshot.service.ts +2 -2
  212. package/frontend/{lib/services → services}/terminal/background/index.ts +1 -1
  213. package/frontend/{lib/services → services}/terminal/background/session-restore.ts +1 -1
  214. package/frontend/{lib/services → services}/terminal/background/stream-manager.ts +1 -1
  215. package/frontend/{lib/services → services}/terminal/project.service.ts +2 -2
  216. package/frontend/{lib/services → services}/terminal/terminal.service.ts +2 -2
  217. package/frontend/{lib/stores → stores}/core/app.svelte.ts +1 -1
  218. package/frontend/{lib/stores → stores}/core/presence.svelte.ts +4 -4
  219. package/frontend/{lib/stores → stores}/core/projects.svelte.ts +5 -5
  220. package/frontend/{lib/stores → stores}/core/sessions.svelte.ts +3 -3
  221. package/frontend/{lib/stores → stores}/features/auth.svelte.ts +30 -2
  222. package/frontend/{lib/stores → stores}/features/claude-accounts.svelte.ts +1 -1
  223. package/frontend/{lib/stores → stores}/features/models.svelte.ts +1 -1
  224. package/frontend/{lib/stores → stores}/features/settings.svelte.ts +2 -2
  225. package/frontend/{lib/stores → stores}/features/terminal.svelte.ts +4 -4
  226. package/frontend/{lib/stores → stores}/features/tunnel.svelte.ts +1 -1
  227. package/frontend/{lib/stores → stores}/features/user.svelte.ts +1 -1
  228. package/frontend/{lib/stores → stores}/ui/edit-mode.svelte.ts +3 -3
  229. package/frontend/{lib/stores → stores}/ui/update.svelte.ts +8 -2
  230. package/frontend/{lib/utils → utils}/theme.ts +1 -1
  231. package/frontend/{lib/utils → utils}/ws.ts +1 -1
  232. package/package.json +2 -2
  233. package/scripts/dev.ts +4 -3
  234. package/scripts/generate-icons.ts +2 -2
  235. package/scripts/start.ts +24 -0
  236. package/shared/types/ui/icons.ts +4 -4
  237. package/shared/utils/anonymous-user.ts +1 -1
  238. package/shared/utils/ws-server.ts +3 -3
  239. package/vite.config.ts +2 -2
  240. /package/backend/{lib/auth → auth}/index.ts +0 -0
  241. /package/backend/{lib/auth → auth}/permissions.ts +0 -0
  242. /package/backend/{lib/auth → auth}/rate-limiter.ts +0 -0
  243. /package/backend/{lib/auth → auth}/tokens.ts +0 -0
  244. /package/backend/{lib/chat → chat}/helpers.ts +0 -0
  245. /package/backend/{lib/chat → chat}/index.ts +0 -0
  246. /package/backend/{lib/database → database}/README.md +0 -0
  247. /package/backend/{lib/database → database}/index.ts +0 -0
  248. /package/backend/{lib/database → database}/migrations/001_create_projects_table.ts +0 -0
  249. /package/backend/{lib/database → database}/migrations/002_create_chat_sessions_table.ts +0 -0
  250. /package/backend/{lib/database → database}/migrations/003_create_messages_table.ts +0 -0
  251. /package/backend/{lib/database → database}/migrations/004_create_prompt_templates_table.ts +0 -0
  252. /package/backend/{lib/database → database}/migrations/005_create_settings_table.ts +0 -0
  253. /package/backend/{lib/database → database}/migrations/006_add_user_to_messages.ts +0 -0
  254. /package/backend/{lib/database → database}/migrations/007_create_stream_states_table.ts +0 -0
  255. /package/backend/{lib/database → database}/migrations/008_create_message_snapshots_table.ts +0 -0
  256. /package/backend/{lib/database → database}/migrations/009_add_delta_snapshot_fields.ts +0 -0
  257. /package/backend/{lib/database → database}/migrations/010_add_soft_delete_and_branch_support.ts +0 -0
  258. /package/backend/{lib/database → database}/migrations/011_git_like_commit_graph.ts +0 -0
  259. /package/backend/{lib/database → database}/migrations/012_add_file_change_statistics.ts +0 -0
  260. /package/backend/{lib/database → database}/migrations/013_checkpoint_tree_state.ts +0 -0
  261. /package/backend/{lib/database → database}/migrations/014_add_engine_to_sessions.ts +0 -0
  262. /package/backend/{lib/database → database}/migrations/015_add_model_to_sessions.ts +0 -0
  263. /package/backend/{lib/database → database}/migrations/016_create_user_projects_table.ts +0 -0
  264. /package/backend/{lib/database → database}/migrations/017_add_current_session_to_user_projects.ts +0 -0
  265. /package/backend/{lib/database → database}/migrations/018_create_claude_accounts_table.ts +0 -0
  266. /package/backend/{lib/database → database}/migrations/019_add_claude_account_to_sessions.ts +0 -0
  267. /package/backend/{lib/database → database}/migrations/020_add_snapshot_tree_hash.ts +0 -0
  268. /package/backend/{lib/database → database}/migrations/021_drop_prompt_templates_table.ts +0 -0
  269. /package/backend/{lib/database → database}/migrations/022_add_snapshot_changes_column.ts +0 -0
  270. /package/backend/{lib/database → database}/migrations/023_create_user_unread_sessions_table.ts +0 -0
  271. /package/backend/{lib/database → database}/migrations/024_create_users_table.ts +0 -0
  272. /package/backend/{lib/database → database}/migrations/025_create_auth_sessions_table.ts +0 -0
  273. /package/backend/{lib/database → database}/migrations/026_create_invite_tokens_table.ts +0 -0
  274. /package/backend/{lib/database → database}/migrations/index.ts +0 -0
  275. /package/backend/{lib/database → database}/queries/auth-queries.ts +0 -0
  276. /package/backend/{lib/database → database}/queries/checkpoint-queries.ts +0 -0
  277. /package/backend/{lib/database → database}/queries/engine-queries.ts +0 -0
  278. /package/backend/{lib/database → database}/queries/index.ts +0 -0
  279. /package/backend/{lib/database → database}/queries/project-queries.ts +0 -0
  280. /package/backend/{lib/database → database}/queries/session-queries.ts +0 -0
  281. /package/backend/{lib/database → database}/queries/settings-queries.ts +0 -0
  282. /package/backend/{lib/database → database}/queries/snapshot-queries.ts +0 -0
  283. /package/backend/{lib/database → database}/queries/utils-queries.ts +0 -0
  284. /package/backend/{lib/database → database}/seeders/index.ts +0 -0
  285. /package/backend/{lib/database → database}/seeders/settings_seeder.ts +0 -0
  286. /package/backend/{lib/database → database}/utils/index.ts +0 -0
  287. /package/backend/{lib/database → database}/utils/migration-runner.ts +0 -0
  288. /package/backend/{lib/database → database}/utils/seeder-runner.ts +0 -0
  289. /package/backend/{lib/engine → engine}/adapters/claude/error-handler.ts +0 -0
  290. /package/backend/{lib/engine → engine}/adapters/claude/index.ts +0 -0
  291. /package/backend/{lib/engine → engine}/adapters/claude/path-utils.ts +0 -0
  292. /package/backend/{lib/engine → engine}/adapters/claude/stream.ts +0 -0
  293. /package/backend/{lib/engine → engine}/adapters/opencode/index.ts +0 -0
  294. /package/backend/{lib/engine → engine}/adapters/opencode/stream.ts +0 -0
  295. /package/backend/{lib/engine → engine}/index.ts +0 -0
  296. /package/backend/{lib/engine → engine}/types.ts +0 -0
  297. /package/backend/{lib/files → files}/file-operations.ts +0 -0
  298. /package/backend/{lib/files → files}/file-reading.ts +0 -0
  299. /package/backend/{lib/files → files}/path-browsing.ts +0 -0
  300. /package/backend/{lib/git → git}/git-parser.ts +0 -0
  301. /package/backend/{lib/git → git}/git-service.ts +0 -0
  302. /package/backend/{lib/mcp → mcp}/index.ts +0 -0
  303. /package/backend/{lib/mcp → mcp}/project-context.ts +0 -0
  304. /package/backend/{lib/mcp → mcp}/remote-server.ts +0 -0
  305. /package/backend/{lib/mcp → mcp}/servers/browser-automation/index.ts +0 -0
  306. /package/backend/{lib/mcp → mcp}/servers/helper.ts +0 -0
  307. /package/backend/{lib/mcp → mcp}/servers/index.ts +0 -0
  308. /package/backend/{lib/mcp → mcp}/servers/weather/get-temperature.ts +0 -0
  309. /package/backend/{lib/mcp → mcp}/servers/weather/index.ts +0 -0
  310. /package/backend/{lib/mcp → mcp}/types.ts +0 -0
  311. /package/backend/{lib/preview → preview}/browser/browser-audio-capture.ts +0 -0
  312. /package/backend/{lib/preview → preview}/browser/browser-console-manager.ts +0 -0
  313. /package/backend/{lib/preview → preview}/browser/browser-dialog-handler.ts +0 -0
  314. /package/backend/{lib/preview → preview}/browser/browser-interaction-handler.ts +0 -0
  315. /package/backend/{lib/preview → preview}/browser/browser-mcp-control.ts +0 -0
  316. /package/backend/{lib/preview → preview}/browser/browser-native-ui-handler.ts +0 -0
  317. /package/backend/{lib/preview → preview}/browser/browser-navigation-tracker.ts +0 -0
  318. /package/backend/{lib/preview → preview}/browser/browser-pool.ts +0 -0
  319. /package/backend/{lib/preview → preview}/browser/browser-video-capture.ts +0 -0
  320. /package/backend/{lib/preview → preview}/browser/scripts/audio-stream.ts +0 -0
  321. /package/backend/{lib/preview → preview}/browser/scripts/cursor-tracking.ts +0 -0
  322. /package/backend/{lib/preview → preview}/browser/scripts/video-stream.ts +0 -0
  323. /package/backend/{lib/preview → preview}/index.ts +0 -0
  324. /package/backend/{lib/project → project}/index.ts +0 -0
  325. /package/backend/{lib/snapshot → snapshot}/gitignore.ts +0 -0
  326. /package/backend/{lib/snapshot → snapshot}/helpers.ts +0 -0
  327. /package/backend/{lib/snapshot → snapshot}/snapshot-service.ts +0 -0
  328. /package/backend/{lib/terminal → terminal}/helpers.ts +0 -0
  329. /package/backend/{lib/terminal → terminal}/index.ts +0 -0
  330. /package/backend/{lib/terminal → terminal}/pty-manager.ts +0 -0
  331. /package/backend/{lib/terminal → terminal}/pty-session-manager.ts +0 -0
  332. /package/backend/{lib/terminal → terminal}/stream-manager.ts +0 -0
  333. /package/backend/{lib/tunnel → tunnel}/global-tunnel-manager.ts +0 -0
  334. /package/backend/{lib/tunnel → tunnel}/project-tunnel-manager.ts +0 -0
  335. /package/backend/{lib/shared → utils}/process-manager.ts +0 -0
  336. /package/backend/{lib/user/helpers.ts → utils/user-helpers.ts} +0 -0
  337. /package/frontend/{lib/app-environment.ts → app-environment.ts} +0 -0
  338. /package/frontend/{lib/components → components}/chat/formatters/index.ts +0 -0
  339. /package/frontend/{lib/components → components}/chat/input/components/DragDropOverlay.svelte +0 -0
  340. /package/frontend/{lib/components → components}/chat/input/composables/use-animations.svelte.ts +0 -0
  341. /package/frontend/{lib/components → components}/chat/input/composables/use-textarea-resize.svelte.ts +0 -0
  342. /package/frontend/{lib/components → components}/chat/message/DateSeparator.svelte +0 -0
  343. /package/frontend/{lib/components → components}/chat/shared/index.ts +0 -0
  344. /package/frontend/{lib/components → components}/chat/shared/utils.ts +0 -0
  345. /package/frontend/{lib/components → components}/chat/tools/AgentTool.svelte +0 -0
  346. /package/frontend/{lib/components → components}/chat/tools/BashOutputTool.svelte +0 -0
  347. /package/frontend/{lib/components → components}/chat/tools/BashTool.svelte +0 -0
  348. /package/frontend/{lib/components → components}/chat/tools/EnterPlanModeTool.svelte +0 -0
  349. /package/frontend/{lib/components → components}/chat/tools/ExitPlanModeTool.svelte +0 -0
  350. /package/frontend/{lib/components → components}/chat/tools/GlobTool.svelte +0 -0
  351. /package/frontend/{lib/components → components}/chat/tools/GrepTool.svelte +0 -0
  352. /package/frontend/{lib/components → components}/chat/tools/ListMcpResourcesTool.svelte +0 -0
  353. /package/frontend/{lib/components → components}/chat/tools/NotebookEditTool.svelte +0 -0
  354. /package/frontend/{lib/components → components}/chat/tools/ReadMcpResourceTool.svelte +0 -0
  355. /package/frontend/{lib/components → components}/chat/tools/ReadTool.svelte +0 -0
  356. /package/frontend/{lib/components → components}/chat/tools/TaskStopTool.svelte +0 -0
  357. /package/frontend/{lib/components → components}/chat/tools/TaskTool.svelte +0 -0
  358. /package/frontend/{lib/components → components}/chat/tools/WebFetchTool.svelte +0 -0
  359. /package/frontend/{lib/components → components}/chat/tools/WebSearchTool.svelte +0 -0
  360. /package/frontend/{lib/components → components}/chat/tools/WriteTool.svelte +0 -0
  361. /package/frontend/{lib/components → components}/chat/tools/components/StatsBadges.svelte +0 -0
  362. /package/frontend/{lib/components → components}/chat/tools/components/TerminalCommand.svelte +0 -0
  363. /package/frontend/{lib/components → components}/chat/tools/components/index.ts +0 -0
  364. /package/frontend/{lib/components → components}/chat/tools/index.ts +0 -0
  365. /package/frontend/{lib/components → components}/checkpoint/timeline/TimelineEdge.svelte +0 -0
  366. /package/frontend/{lib/components → components}/checkpoint/timeline/TimelineGraph.svelte +0 -0
  367. /package/frontend/{lib/components → components}/checkpoint/timeline/TimelineVersionGroup.svelte +0 -0
  368. /package/frontend/{lib/components → components}/checkpoint/timeline/animation.ts +0 -0
  369. /package/frontend/{lib/components → components}/checkpoint/timeline/config.ts +0 -0
  370. /package/frontend/{lib/components → components}/checkpoint/timeline/graph-builder.ts +0 -0
  371. /package/frontend/{lib/components → components}/checkpoint/timeline/types.ts +0 -0
  372. /package/frontend/{lib/components → components}/checkpoint/timeline/utils.ts +0 -0
  373. /package/frontend/{lib/components/common → components/common/display}/AvatarBubble.svelte +0 -0
  374. /package/frontend/{lib/components/common → components/common/display}/Button.svelte +0 -0
  375. /package/frontend/{lib/components/common → components/common/display}/Card.svelte +0 -0
  376. /package/frontend/{lib/components/common → components/common/feedback}/LoadingSpinner.svelte +0 -0
  377. /package/frontend/{lib/components → components}/common/lucide-icons.ts +0 -0
  378. /package/frontend/{lib/components → components}/common/material-icons.ts +0 -0
  379. /package/frontend/{lib/components/common → components/common/overlay}/Modal.svelte +0 -0
  380. /package/frontend/{lib/components → components}/common/xterm/index.ts +0 -0
  381. /package/frontend/{lib/components → components}/common/xterm/terminal-config.ts +0 -0
  382. /package/frontend/{lib/components → components}/common/xterm/types.ts +0 -0
  383. /package/frontend/{lib/components → components}/preview/browser/components/VirtualCursor.svelte +0 -0
  384. /package/frontend/{lib/components → components}/preview/browser/core/stream-handler.svelte.ts +0 -0
  385. /package/frontend/{lib/components → components}/preview/index.ts +0 -0
  386. /package/frontend/{lib/components → components}/settings/account/AccountSettings.svelte +0 -0
  387. /package/frontend/{lib/components → components}/settings/general/GeneralSettings.svelte +0 -0
  388. /package/frontend/{lib/components → components}/settings/security/SecuritySettings.svelte +0 -0
  389. /package/frontend/{lib/components → components}/settings/system/SystemSettings.svelte +0 -0
  390. /package/frontend/{lib/components → components}/tunnel/TunnelQRCode.svelte +0 -0
  391. /package/frontend/{lib/services → services}/chat/index.ts +0 -0
  392. /package/frontend/{lib/services → services}/notification/index.ts +0 -0
  393. /package/frontend/{lib/services → services}/preview/index.ts +0 -0
  394. /package/frontend/{lib/services → services}/project/index.ts +0 -0
  395. /package/frontend/{lib/services → services}/terminal/index.ts +0 -0
  396. /package/frontend/{lib/services → services}/terminal/persistence.service.ts +0 -0
  397. /package/frontend/{lib/services → services}/terminal/session.service.ts +0 -0
  398. /package/frontend/{lib/stores → stores}/core/files.svelte.ts +0 -0
  399. /package/frontend/{lib/stores → stores}/ui/chat-input.svelte.ts +0 -0
  400. /package/frontend/{lib/stores → stores}/ui/chat-model.svelte.ts +0 -0
  401. /package/frontend/{lib/stores → stores}/ui/connection.svelte.ts +0 -0
  402. /package/frontend/{lib/stores → stores}/ui/dialog.svelte.ts +0 -0
  403. /package/frontend/{lib/stores → stores}/ui/notification.svelte.ts +0 -0
  404. /package/frontend/{lib/stores → stores}/ui/settings-modal.svelte.ts +0 -0
  405. /package/frontend/{lib/stores → stores}/ui/theme.svelte.ts +0 -0
  406. /package/frontend/{lib/stores → stores}/ui/workspace.svelte.ts +0 -0
  407. /package/frontend/{lib/utils → utils}/chat/date-separator.ts +0 -0
  408. /package/frontend/{lib/utils → utils}/chat/message-grouper.ts +0 -0
  409. /package/frontend/{lib/utils → utils}/chat/message-processor.ts +0 -0
  410. /package/frontend/{lib/utils → utils}/chat/tool-handler.ts +0 -0
  411. /package/frontend/{lib/utils → utils}/chat/virtual-scroll.svelte.ts +0 -0
  412. /package/frontend/{lib/utils → utils}/click-outside.ts +0 -0
  413. /package/frontend/{lib/utils → utils}/context-manager.ts +0 -0
  414. /package/frontend/{lib/utils → utils}/file-icon-mappings.ts +0 -0
  415. /package/frontend/{lib/utils → utils}/folder-icon-mappings.ts +0 -0
  416. /package/frontend/{lib/utils → utils}/git-status.ts +0 -0
  417. /package/frontend/{lib/types → utils}/native-ui.ts +0 -0
  418. /package/frontend/{lib/utils → utils}/platform.ts +0 -0
  419. /package/frontend/{lib/utils → utils}/port-check.ts +0 -0
  420. /package/frontend/{lib/constants/preview.ts → utils/preview-constants.ts} +0 -0
  421. /package/frontend/{lib/utils/terminalFormatter.ts → utils/terminal-formatter.ts} +0 -0
  422. /package/frontend/{lib/utils → utils}/tree-visualizer.ts +0 -0
@@ -1,224 +1,224 @@
1
- /**
2
- * Project Status Data Service
3
- * Shared logic for getting project status data
4
- */
5
-
6
- import { streamManager, type StreamState } from '../chat/stream-manager.js';
7
- import { ws } from '../utils/ws.js';
8
-
9
- // Interactive tools that block the stream waiting for user input
10
- const INTERACTIVE_TOOLS = new Set(['AskUserQuestion']);
11
-
12
- /**
13
- * Check if an active stream is waiting for user input.
14
- * Scans stream messages for unanswered interactive tool_use blocks.
15
- * This is the backend single source of truth — works even when the
16
- * user is on a different project and not receiving chat events.
17
- */
18
- function detectStreamWaitingInput(stream: StreamState): boolean {
19
- if (stream.status !== 'active') return false;
20
-
21
- // SSEEventData.message is SDKMessage: { type, message: { content: [...] } }
22
- // Content blocks live at msg.message.content, NOT msg.content
23
- const answeredToolIds = new Set<string>();
24
- for (const event of stream.messages) {
25
- const msg = event.message;
26
- if (!msg || (msg as any).type !== 'user') continue;
27
- const content = Array.isArray((msg as any).message?.content) ? (msg as any).message.content : [];
28
- for (const item of content) {
29
- if (item.type === 'tool_result' && item.tool_use_id) {
30
- answeredToolIds.add(item.tool_use_id);
31
- }
32
- }
33
- }
34
-
35
- for (const event of stream.messages) {
36
- const msg = event.message;
37
- if (!msg || (msg as any).type !== 'assistant') continue;
38
- const content = Array.isArray((msg as any).message?.content) ? (msg as any).message.content : [];
39
- if (content.some((item: any) =>
40
- item.type === 'tool_use' && INTERACTIVE_TOOLS.has(item.name) && item.id && !answeredToolIds.has(item.id)
41
- )) {
42
- return true;
43
- }
44
- }
45
-
46
- return false;
47
- }
48
-
49
- // Store active users per project (shared with main endpoint)
50
- const projectUsers = new Map<string, Set<{ userId: string; userName: string; lastSeen: number }>>();
51
-
52
- // Cleanup inactive users after 5 minutes
53
- const USER_TIMEOUT = 5 * 60 * 1000;
54
-
55
- function cleanupInactiveUsers() {
56
- const now = Date.now();
57
- projectUsers.forEach((users, projectId) => {
58
- const activeUsers = new Set([...users].filter(user => now - user.lastSeen < USER_TIMEOUT));
59
- if (activeUsers.size === 0) {
60
- projectUsers.delete(projectId);
61
- } else {
62
- projectUsers.set(projectId, activeUsers);
63
- }
64
- });
65
- }
66
-
67
- // Get project status data
68
- export async function getProjectStatusData(projectId?: string) {
69
- cleanupInactiveUsers();
70
-
71
- if (projectId) {
72
- // Get status for specific project
73
- const allProjectStreams = streamManager.getProjectStreams(projectId);
74
- const users = projectUsers.get(projectId);
75
-
76
- // Filter to only count active streams
77
- const activeStreams = allProjectStreams.filter(s => s.status === 'active');
78
-
79
- // Get per-chat-session user presence from WS rooms
80
- const chatSessionUsers = ws.getProjectChatSessions(projectId);
81
-
82
- return {
83
- projectId,
84
- hasActiveStreams: activeStreams.length > 0,
85
- activeStreamCount: activeStreams.length,
86
- activeUsers: users ? [...users].map(u => ({
87
- userId: u.userId,
88
- userName: u.userName
89
- })) : [],
90
- streams: allProjectStreams.map(s => ({
91
- streamId: s.streamId,
92
- chatSessionId: s.chatSessionId,
93
- status: s.status,
94
- startedAt: s.startedAt,
95
- messagesCount: s.messages.length,
96
- isWaitingInput: detectStreamWaitingInput(s)
97
- })),
98
- chatSessionUsers: Object.fromEntries(
99
- Array.from(chatSessionUsers.entries()).map(([csId, csUsers]) => [
100
- csId,
101
- csUsers.map(u => {
102
- // Resolve userName from projectUsers
103
- const projectUser = users ? [...users].find(pu => pu.userId === u.userId) : undefined;
104
- return { userId: u.userId, userName: projectUser?.userName || u.userId };
105
- })
106
- ])
107
- )
108
- };
109
- } else {
110
- // Get status for all projects
111
- const allProjects = new Map<string, any>();
112
-
113
- // Get all active streams grouped by project
114
- const allStreams = streamManager.getAllStreams();
115
- allStreams.forEach(stream => {
116
- if (stream.projectId) {
117
- if (!allProjects.has(stream.projectId)) {
118
- allProjects.set(stream.projectId, {
119
- projectId: stream.projectId,
120
- hasActiveStreams: false,
121
- activeStreamCount: 0,
122
- activeUsers: [],
123
- streams: []
124
- });
125
- }
126
-
127
- const projectData = allProjects.get(stream.projectId);
128
- if (stream.status === 'active') {
129
- projectData.hasActiveStreams = true;
130
- projectData.activeStreamCount++;
131
- }
132
- projectData.streams.push({
133
- streamId: stream.streamId,
134
- chatSessionId: stream.chatSessionId,
135
- status: stream.status,
136
- startedAt: stream.startedAt,
137
- messagesCount: stream.messages.length,
138
- isWaitingInput: detectStreamWaitingInput(stream)
139
- });
140
- }
141
- });
142
-
143
- // Add active users to each project
144
- projectUsers.forEach((users, projectId) => {
145
- if (!allProjects.has(projectId)) {
146
- allProjects.set(projectId, {
147
- projectId,
148
- hasActiveStreams: false,
149
- activeStreamCount: 0,
150
- activeUsers: [],
151
- streams: [],
152
- chatSessionUsers: {}
153
- });
154
- }
155
-
156
- const projectData = allProjects.get(projectId);
157
- projectData.activeUsers = [...users].map(u => ({
158
- userId: u.userId,
159
- userName: u.userName
160
- }));
161
- });
162
-
163
- // Add per-chat-session user presence to each project
164
- for (const [projectId, projectData] of allProjects) {
165
- const chatSessionUsers = ws.getProjectChatSessions(projectId);
166
- const users = projectUsers.get(projectId);
167
- projectData.chatSessionUsers = Object.fromEntries(
168
- Array.from(chatSessionUsers.entries()).map(([csId, csUsers]) => [
169
- csId,
170
- csUsers.map(u => {
171
- const projectUser = users ? [...users].find(pu => pu.userId === u.userId) : undefined;
172
- return { userId: u.userId, userName: projectUser?.userName || u.userId };
173
- })
174
- ])
175
- );
176
- }
177
-
178
- return [...allProjects.values()];
179
- }
180
- }
181
-
182
- // Update user presence
183
- export function updateUserPresence(projectId: string, userId: string, userName: string, action: string) {
184
- cleanupInactiveUsers();
185
-
186
- if (action === 'leave') {
187
- // Remove user from project
188
- const users = projectUsers.get(projectId);
189
- if (users) {
190
- const updatedUsers = new Set([...users].filter(u => u.userId !== userId));
191
- if (updatedUsers.size === 0) {
192
- projectUsers.delete(projectId);
193
- } else {
194
- projectUsers.set(projectId, updatedUsers);
195
- }
196
- }
197
- } else {
198
- // Add or update user presence
199
- if (!projectUsers.has(projectId)) {
200
- projectUsers.set(projectId, new Set());
201
- }
202
-
203
- const users = projectUsers.get(projectId)!;
204
- // Remove old entry if exists
205
- const updatedUsers = new Set([...users].filter(u => u.userId !== userId));
206
- // Add new entry with updated timestamp
207
- updatedUsers.add({
208
- userId,
209
- userName,
210
- lastSeen: Date.now()
211
- });
212
- projectUsers.set(projectId, updatedUsers);
213
- }
214
-
215
- // Return current users for the project
216
- const currentUsers = projectUsers.get(projectId);
217
- return {
218
- projectId,
219
- activeUsers: currentUsers ? [...currentUsers].map(u => ({
220
- userId: u.userId,
221
- userName: u.userName
222
- })) : []
223
- };
1
+ /**
2
+ * Project Status Data Service
3
+ * Shared logic for getting project status data
4
+ */
5
+
6
+ import { streamManager, type StreamState } from '../chat/stream-manager.js';
7
+ import { ws } from '../utils/ws.js';
8
+
9
+ // Interactive tools that block the stream waiting for user input
10
+ const INTERACTIVE_TOOLS = new Set(['AskUserQuestion']);
11
+
12
+ /**
13
+ * Check if an active stream is waiting for user input.
14
+ * Scans stream messages for unanswered interactive tool_use blocks.
15
+ * This is the backend single source of truth — works even when the
16
+ * user is on a different project and not receiving chat events.
17
+ */
18
+ function detectStreamWaitingInput(stream: StreamState): boolean {
19
+ if (stream.status !== 'active') return false;
20
+
21
+ // SSEEventData.message is SDKMessage: { type, message: { content: [...] } }
22
+ // Content blocks live at msg.message.content, NOT msg.content
23
+ const answeredToolIds = new Set<string>();
24
+ for (const event of stream.messages) {
25
+ const msg = event.message;
26
+ if (!msg || (msg as any).type !== 'user') continue;
27
+ const content = Array.isArray((msg as any).message?.content) ? (msg as any).message.content : [];
28
+ for (const item of content) {
29
+ if (item.type === 'tool_result' && item.tool_use_id) {
30
+ answeredToolIds.add(item.tool_use_id);
31
+ }
32
+ }
33
+ }
34
+
35
+ for (const event of stream.messages) {
36
+ const msg = event.message;
37
+ if (!msg || (msg as any).type !== 'assistant') continue;
38
+ const content = Array.isArray((msg as any).message?.content) ? (msg as any).message.content : [];
39
+ if (content.some((item: any) =>
40
+ item.type === 'tool_use' && INTERACTIVE_TOOLS.has(item.name) && item.id && !answeredToolIds.has(item.id)
41
+ )) {
42
+ return true;
43
+ }
44
+ }
45
+
46
+ return false;
47
+ }
48
+
49
+ // Store active users per project (shared with main endpoint)
50
+ const projectUsers = new Map<string, Set<{ userId: string; userName: string; lastSeen: number }>>();
51
+
52
+ // Cleanup inactive users after 5 minutes
53
+ const USER_TIMEOUT = 5 * 60 * 1000;
54
+
55
+ function cleanupInactiveUsers() {
56
+ const now = Date.now();
57
+ projectUsers.forEach((users, projectId) => {
58
+ const activeUsers = new Set([...users].filter(user => now - user.lastSeen < USER_TIMEOUT));
59
+ if (activeUsers.size === 0) {
60
+ projectUsers.delete(projectId);
61
+ } else {
62
+ projectUsers.set(projectId, activeUsers);
63
+ }
64
+ });
65
+ }
66
+
67
+ // Get project status data
68
+ export async function getProjectStatusData(projectId?: string) {
69
+ cleanupInactiveUsers();
70
+
71
+ if (projectId) {
72
+ // Get status for specific project
73
+ const allProjectStreams = streamManager.getProjectStreams(projectId);
74
+ const users = projectUsers.get(projectId);
75
+
76
+ // Filter to only count active streams
77
+ const activeStreams = allProjectStreams.filter(s => s.status === 'active');
78
+
79
+ // Get per-chat-session user presence from WS rooms
80
+ const chatSessionUsers = ws.getProjectChatSessions(projectId);
81
+
82
+ return {
83
+ projectId,
84
+ hasActiveStreams: activeStreams.length > 0,
85
+ activeStreamCount: activeStreams.length,
86
+ activeUsers: users ? [...users].map(u => ({
87
+ userId: u.userId,
88
+ userName: u.userName
89
+ })) : [],
90
+ streams: allProjectStreams.map(s => ({
91
+ streamId: s.streamId,
92
+ chatSessionId: s.chatSessionId,
93
+ status: s.status,
94
+ startedAt: s.startedAt,
95
+ messagesCount: s.messages.length,
96
+ isWaitingInput: detectStreamWaitingInput(s)
97
+ })),
98
+ chatSessionUsers: Object.fromEntries(
99
+ Array.from(chatSessionUsers.entries()).map(([csId, csUsers]) => [
100
+ csId,
101
+ csUsers.map(u => {
102
+ // Resolve userName from projectUsers
103
+ const projectUser = users ? [...users].find(pu => pu.userId === u.userId) : undefined;
104
+ return { userId: u.userId, userName: projectUser?.userName || u.userId };
105
+ })
106
+ ])
107
+ )
108
+ };
109
+ } else {
110
+ // Get status for all projects
111
+ const allProjects = new Map<string, any>();
112
+
113
+ // Get all active streams grouped by project
114
+ const allStreams = streamManager.getAllStreams();
115
+ allStreams.forEach(stream => {
116
+ if (stream.projectId) {
117
+ if (!allProjects.has(stream.projectId)) {
118
+ allProjects.set(stream.projectId, {
119
+ projectId: stream.projectId,
120
+ hasActiveStreams: false,
121
+ activeStreamCount: 0,
122
+ activeUsers: [],
123
+ streams: []
124
+ });
125
+ }
126
+
127
+ const projectData = allProjects.get(stream.projectId);
128
+ if (stream.status === 'active') {
129
+ projectData.hasActiveStreams = true;
130
+ projectData.activeStreamCount++;
131
+ }
132
+ projectData.streams.push({
133
+ streamId: stream.streamId,
134
+ chatSessionId: stream.chatSessionId,
135
+ status: stream.status,
136
+ startedAt: stream.startedAt,
137
+ messagesCount: stream.messages.length,
138
+ isWaitingInput: detectStreamWaitingInput(stream)
139
+ });
140
+ }
141
+ });
142
+
143
+ // Add active users to each project
144
+ projectUsers.forEach((users, projectId) => {
145
+ if (!allProjects.has(projectId)) {
146
+ allProjects.set(projectId, {
147
+ projectId,
148
+ hasActiveStreams: false,
149
+ activeStreamCount: 0,
150
+ activeUsers: [],
151
+ streams: [],
152
+ chatSessionUsers: {}
153
+ });
154
+ }
155
+
156
+ const projectData = allProjects.get(projectId);
157
+ projectData.activeUsers = [...users].map(u => ({
158
+ userId: u.userId,
159
+ userName: u.userName
160
+ }));
161
+ });
162
+
163
+ // Add per-chat-session user presence to each project
164
+ for (const [projectId, projectData] of allProjects) {
165
+ const chatSessionUsers = ws.getProjectChatSessions(projectId);
166
+ const users = projectUsers.get(projectId);
167
+ projectData.chatSessionUsers = Object.fromEntries(
168
+ Array.from(chatSessionUsers.entries()).map(([csId, csUsers]) => [
169
+ csId,
170
+ csUsers.map(u => {
171
+ const projectUser = users ? [...users].find(pu => pu.userId === u.userId) : undefined;
172
+ return { userId: u.userId, userName: projectUser?.userName || u.userId };
173
+ })
174
+ ])
175
+ );
176
+ }
177
+
178
+ return [...allProjects.values()];
179
+ }
180
+ }
181
+
182
+ // Update user presence
183
+ export function updateUserPresence(projectId: string, userId: string, userName: string, action: string) {
184
+ cleanupInactiveUsers();
185
+
186
+ if (action === 'leave') {
187
+ // Remove user from project
188
+ const users = projectUsers.get(projectId);
189
+ if (users) {
190
+ const updatedUsers = new Set([...users].filter(u => u.userId !== userId));
191
+ if (updatedUsers.size === 0) {
192
+ projectUsers.delete(projectId);
193
+ } else {
194
+ projectUsers.set(projectId, updatedUsers);
195
+ }
196
+ }
197
+ } else {
198
+ // Add or update user presence
199
+ if (!projectUsers.has(projectId)) {
200
+ projectUsers.set(projectId, new Set());
201
+ }
202
+
203
+ const users = projectUsers.get(projectId)!;
204
+ // Remove old entry if exists
205
+ const updatedUsers = new Set([...users].filter(u => u.userId !== userId));
206
+ // Add new entry with updated timestamp
207
+ updatedUsers.add({
208
+ userId,
209
+ userName,
210
+ lastSeen: Date.now()
211
+ });
212
+ projectUsers.set(projectId, updatedUsers);
213
+ }
214
+
215
+ // Return current users for the project
216
+ const currentUsers = projectUsers.get(projectId);
217
+ return {
218
+ projectId,
219
+ activeUsers: currentUsers ? [...currentUsers].map(u => ({
220
+ userId: u.userId,
221
+ userName: u.userName
222
+ })) : []
223
+ };
224
224
  }
@@ -11,12 +11,12 @@
11
11
  */
12
12
 
13
13
  import { join } from 'path';
14
- import { homedir } from 'os';
15
14
  import fs from 'fs/promises';
16
15
  import { gzipSync, gunzipSync } from 'zlib';
17
16
  import { debug } from '$shared/utils/logger';
17
+ import { getClopenDir } from '../utils/index.js';
18
18
 
19
- const SNAPSHOTS_DIR = join(homedir(), '.clopen', 'snapshots');
19
+ const SNAPSHOTS_DIR = join(getClopenDir(), 'snapshots');
20
20
  const BLOBS_DIR = join(SNAPSHOTS_DIR, 'blobs');
21
21
  const TREES_DIR = join(SNAPSHOTS_DIR, 'trees');
22
22
 
@@ -7,7 +7,7 @@ import { join } from 'path';
7
7
  import { spawn, type IPty } from 'bun-pty';
8
8
 
9
9
  import { debug } from '$shared/utils/logger';
10
- import { getCleanSpawnEnv } from '../shared/env';
10
+ import { getCleanSpawnEnv } from '../utils/env';
11
11
  // Platform detection
12
12
  export const isWindows = process.platform === 'win32';
13
13
  export const isMacOS = process.platform === 'darwin';
@@ -22,12 +22,12 @@ const isDev = process.env.NODE_ENV !== 'production';
22
22
 
23
23
  export const SERVER_ENV = {
24
24
  NODE_ENV: (process.env.NODE_ENV || 'development') as string,
25
- /** Backend port — dev: PORT_BACKEND (default 9151), prod: PORT (default 9141) */
25
+ /** Backend port — dev: PORT_BACKEND (default 9161), prod: PORT (default 9141) */
26
26
  PORT: isDev
27
- ? (process.env.PORT_BACKEND ? parseInt(process.env.PORT_BACKEND) : 9151)
27
+ ? (process.env.PORT_BACKEND ? parseInt(process.env.PORT_BACKEND) : 9161)
28
28
  : (process.env.PORT ? parseInt(process.env.PORT) : 9141),
29
29
  /** Frontend port — only used in dev for Vite proxy coordination */
30
- PORT_FRONTEND: process.env.PORT_FRONTEND ? parseInt(process.env.PORT_FRONTEND) : 9141,
30
+ PORT_FRONTEND: process.env.PORT_FRONTEND ? parseInt(process.env.PORT_FRONTEND) : 9151,
31
31
  HOST: (process.env.HOST || 'localhost') as string,
32
32
  isDevelopment: isDev,
33
33
  } as const;
@@ -35,15 +35,13 @@ export const SERVER_ENV = {
35
35
  // ── .env parsing ────────────────────────────────────────────────────
36
36
 
37
37
  /**
38
- * Parse .env file into key-value map.
39
- * Knowing both key AND value lets us compare against process.env
40
- * to determine if Bun's auto-load is still in effect or if the
41
- * system/runtime changed the value after loading.
38
+ * Parse a .env file at the given path into a key-value record.
39
+ * Returns an empty object if the file doesn't exist or can't be read.
42
40
  */
43
- function parseDotEnv(): Map<string, string> {
44
- const entries = new Map<string, string>();
41
+ export function loadEnvFile(envPath: string): Record<string, string> {
42
+ const result: Record<string, string> = {};
45
43
  try {
46
- const content = readFileSync(join(process.cwd(), '.env'), 'utf-8');
44
+ const content = readFileSync(envPath, 'utf-8');
47
45
  for (const line of content.split('\n')) {
48
46
  let trimmed = line.trim();
49
47
  if (!trimmed || trimmed.startsWith('#')) continue;
@@ -51,22 +49,22 @@ function parseDotEnv(): Map<string, string> {
51
49
  const eqIdx = trimmed.indexOf('=');
52
50
  if (eqIdx <= 0) continue;
53
51
  const key = trimmed.substring(0, eqIdx).trim();
54
- // Strip surrounding quotes from value
55
52
  let value = trimmed.substring(eqIdx + 1).trim();
56
53
  if ((value.startsWith('"') && value.endsWith('"')) ||
57
54
  (value.startsWith("'") && value.endsWith("'"))) {
58
55
  value = value.slice(1, -1);
59
56
  }
60
- entries.set(key, value);
57
+ result[key] = value;
61
58
  }
62
59
  } catch {
63
60
  // .env doesn't exist or can't be read
64
61
  }
65
- return entries;
62
+ return result;
66
63
  }
67
64
 
68
- // Capture once at import time
69
- const dotEnv = parseDotEnv();
65
+ // Capture once at import time — read from CWD which is set to the clopen
66
+ // installation directory when spawned via bin/clopen.ts (cwd: __dirname).
67
+ const dotEnv = new Map(Object.entries(loadEnvFile(join(process.cwd(), '.env'))));
70
68
 
71
69
  // ── Filter definitions ──────────────────────────────────────────────
72
70
 
@@ -2,4 +2,7 @@
2
2
  export * from './process-manager.js';
3
3
 
4
4
  // Export environment sanitizer
5
- export * from './env.js';
5
+ export * from './env.js';
6
+
7
+ // Export path utilities
8
+ export * from './paths.js';
@@ -0,0 +1,11 @@
1
+ import { join } from 'path';
2
+ import { homedir } from 'os';
3
+
4
+ /**
5
+ * Returns the Clopen data directory.
6
+ * - development: ~/.clopen-dev
7
+ * - everything else (production, undefined): ~/.clopen
8
+ */
9
+ export function getClopenDir(): string {
10
+ return join(homedir(), process.env.NODE_ENV === 'development' ? '.clopen-dev' : '.clopen');
11
+ }
@@ -1,14 +1,18 @@
1
1
  /**
2
2
  * Port utilities for checking ports before server start.
3
3
  * Bun-optimized: uses Bun.connect for fast cross-platform port check.
4
+ *
5
+ * Checks BOTH IPv4 (127.0.0.1) and IPv6 (::1) — on Windows, 'localhost'
6
+ * may resolve to either address. A zombie process listening on [::1] would
7
+ * go undetected by an IPv4-only check, causing the new server to bind to a
8
+ * port that can't actually serve traffic (connections hang indefinitely).
4
9
  */
5
10
 
6
- /** Check if a port is currently in use */
7
- export async function isPortInUse(port: number): Promise<boolean> {
11
+ /** Try to connect to a specific host:port */
12
+ async function tryConnect(hostname: string, port: number): Promise<boolean> {
8
13
  try {
9
- // Bun-native TCP connect — fast cross-platform check
10
14
  const socket = await Bun.connect({
11
- hostname: '127.0.0.1',
15
+ hostname,
12
16
  port,
13
17
  socket: {
14
18
  data() {},
@@ -18,12 +22,21 @@ export async function isPortInUse(port: number): Promise<boolean> {
18
22
  }
19
23
  });
20
24
  socket.end();
21
- return true; // Connection succeeded = port in use
25
+ return true;
22
26
  } catch {
23
- return false; // Connection refused = port is free
27
+ return false;
24
28
  }
25
29
  }
26
30
 
31
+ /** Check if a port is currently in use on any localhost address (IPv4 + IPv6) */
32
+ export async function isPortInUse(port: number): Promise<boolean> {
33
+ const [v4, v6] = await Promise.all([
34
+ tryConnect('127.0.0.1', port),
35
+ tryConnect('::1', port),
36
+ ]);
37
+ return v4 || v6;
38
+ }
39
+
27
40
  /** Find an available port starting from the given port, incrementing on collision */
28
41
  export async function findAvailablePort(startPort: number, maxAttempts = 8): Promise<number> {
29
42
  let port = startPort;
@@ -16,7 +16,7 @@
16
16
  *
17
17
  * Usage:
18
18
  * ```ts
19
- * import { ws } from '$backend/lib/ws'
19
+ * import { ws } from '$backend/utils/ws'
20
20
  *
21
21
  * // Project-specific event
22
22
  * ws.emit.project(projectId, 'terminal:output', { content });
@@ -156,7 +156,7 @@ class WSServer {
156
156
  /**
157
157
  * Ensure a connection is registered, hydrated, and return its wsId.
158
158
  * Handles the race condition where message handlers fire before the async
159
- * open handler completes its await import('$backend/lib/utils/ws').
159
+ * open handler completes its await import('$backend/utils/ws').
160
160
  */
161
161
  private ensureRegistered(conn: WSConnection): string {
162
162
  const existingId = this.resolveId(conn);