@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,478 @@
1
+ import { dirname, extname, join } from 'path';
2
+ import { stat as fsStat } from 'node:fs/promises';
3
+
4
+ import { debug } from '$shared/utils/logger';
5
+
6
+ // Import Bun native functions and create wrappers for better API compatibility
7
+ const { spawn } = Bun;
8
+
9
+ // Bun-compatible readdir implementation (cross-platform)
10
+ async function readdir(path: string): Promise<string[]> {
11
+ let proc;
12
+ if (process.platform === 'win32') {
13
+ proc = Bun.spawn(['cmd', '/c', 'dir', '/b', path], { stdout: 'pipe', stderr: 'ignore' });
14
+ } else {
15
+ proc = Bun.spawn(['ls', '-1', path], { stdout: 'pipe', stderr: 'ignore' });
16
+ }
17
+ const result = await new Response(proc.stdout).text();
18
+ // Split and clean up, removing \r characters for Windows compatibility
19
+ return result.trim().split(/\r?\n/).map(line => line.trim()).filter(Boolean);
20
+ }
21
+
22
+ async function mkdir(path: string, options?: { recursive?: boolean }) {
23
+ // Pure Bun approach: create directory by writing a temp file
24
+ try {
25
+ const tempFile = join(path, '.bun_mkdir_temp');
26
+ await Bun.write(tempFile, '');
27
+ // Clean up the temp file
28
+ try {
29
+ if (process.platform === 'win32') {
30
+ await spawn(['cmd', '/c', 'del', '/f', '/q', tempFile.replace(/\//g, '\\')], {
31
+ stdout: 'ignore',
32
+ stderr: 'ignore'
33
+ }).exited;
34
+ } else {
35
+ await spawn(['rm', '-f', tempFile], {
36
+ stdout: 'ignore',
37
+ stderr: 'ignore'
38
+ }).exited;
39
+ }
40
+ } catch {
41
+ // Ignore cleanup errors
42
+ }
43
+ } catch (error) {
44
+ throw new Error(`Failed to create directory ${path}: ${error}`);
45
+ }
46
+ }
47
+
48
+ async function copyFile(src: string, dest: string) {
49
+ const srcFile = Bun.file(src);
50
+ const content = await srcFile.arrayBuffer();
51
+ await Bun.write(dest, content);
52
+ }
53
+
54
+ async function copyDirectory(src: string, dest: string) {
55
+ // Create destination directory
56
+ await mkdir(dest, { recursive: true });
57
+
58
+ // List items in source
59
+ const items = await readdir(src);
60
+
61
+ for (const item of items) {
62
+ const srcPath = join(src, item);
63
+ const destPath = join(dest, item);
64
+
65
+ const file = Bun.file(srcPath);
66
+ try {
67
+ const stats = await file.stat();
68
+ if (stats.isDirectory()) {
69
+ await copyDirectory(srcPath, destPath);
70
+ } else {
71
+ await copyFile(srcPath, destPath);
72
+ }
73
+ } catch {
74
+ // Skip items that can't be read
75
+ continue;
76
+ }
77
+ }
78
+ }
79
+
80
+ async function rename(oldPath: string, newPath: string) {
81
+ if (process.platform === 'win32') {
82
+ const proc = spawn(['cmd', '/c', 'move', oldPath.replace(/\//g, '\\'), newPath.replace(/\//g, '\\')], { stdout: 'ignore', stderr: 'ignore' });
83
+ await proc.exited;
84
+ } else {
85
+ const proc = spawn(['mv', oldPath, newPath], { stdout: 'ignore', stderr: 'ignore' });
86
+ await proc.exited;
87
+ }
88
+ }
89
+
90
+ async function unlink(path: string) {
91
+ if (process.platform === 'win32') {
92
+ const proc = spawn(['cmd', '/c', 'del', '/f', '/q', path.replace(/\//g, '\\')], { stdout: 'ignore', stderr: 'ignore' });
93
+ await proc.exited;
94
+ } else {
95
+ const proc = spawn(['rm', '-f', path], { stdout: 'ignore', stderr: 'ignore' });
96
+ await proc.exited;
97
+ }
98
+ }
99
+
100
+ async function rm(path: string, options?: { recursive?: boolean }) {
101
+ if (process.platform === 'win32') {
102
+ const proc = spawn(['cmd', '/c', 'rmdir', '/s', '/q', path.replace(/\//g, '\\')], { stdout: 'ignore', stderr: 'ignore' });
103
+ await proc.exited;
104
+ } else {
105
+ const proc = spawn(['rm', '-rf', path], { stdout: 'ignore', stderr: 'ignore' });
106
+ await proc.exited;
107
+ }
108
+ }
109
+
110
+ async function rmdir(path: string, options?: { recursive?: boolean }) {
111
+ return rm(path, options);
112
+ }
113
+
114
+ export async function writeFileOperation(filePath: string, content: string) {
115
+ if (!filePath) {
116
+ throw new Error('File path is required');
117
+ }
118
+
119
+ if (typeof content !== 'string') {
120
+ throw new Error('Content must be a string');
121
+ }
122
+
123
+ try {
124
+ debug.log('file', 'Writing file:', { filePath, contentLength: content.length });
125
+ await Bun.write(filePath, content);
126
+ const file = Bun.file(filePath);
127
+ const stats = await file.stat();
128
+ debug.log('file', 'File written successfully:', { size: stats.size });
129
+
130
+ return {
131
+ message: 'File saved successfully',
132
+ size: stats.size,
133
+ modified: stats.mtime.toISOString()
134
+ };
135
+ } catch (error) {
136
+ debug.error('file', 'Write file error:', error);
137
+ throw new Error(`Failed to write file: ${error}`);
138
+ }
139
+ }
140
+
141
+ export async function createFileOperation(filePath: string, content: string = '') {
142
+ if (!filePath) {
143
+ throw new Error('File path is required');
144
+ }
145
+
146
+ try {
147
+ // Normalize path for Windows only
148
+ const normalizedFilePath = process.platform === 'win32' ?
149
+ filePath.replace(/\//g, '\\') : filePath;
150
+
151
+ // Create parent directory if it doesn't exist
152
+ const parentDir = dirname(normalizedFilePath);
153
+ const parentFile = Bun.file(parentDir);
154
+ if (!(await parentFile.exists())) {
155
+ await mkdir(parentDir, { recursive: true });
156
+ }
157
+
158
+ // Create empty file if it doesn't exist
159
+ const file = Bun.file(normalizedFilePath);
160
+ if (!(await file.exists())) {
161
+ await Bun.write(normalizedFilePath, content);
162
+ const stats = await file.stat();
163
+
164
+ return {
165
+ message: 'File created successfully',
166
+ path: normalizedFilePath,
167
+ size: stats.size,
168
+ modified: stats.mtime.toISOString()
169
+ };
170
+ } else {
171
+ throw new Error('File already exists');
172
+ }
173
+ } catch (error) {
174
+ debug.error('file', 'Create file error:', error);
175
+ if (error instanceof Error) {
176
+ if (error.message === 'File already exists') {
177
+ throw error;
178
+ }
179
+ if (error.message.includes('EPERM')) {
180
+ throw new Error('Permission denied - you may not have sufficient permissions to create files in this location');
181
+ } else if (error.message.includes('ENOENT')) {
182
+ throw new Error('Invalid path or parent directory does not exist');
183
+ }
184
+ throw error;
185
+ }
186
+ throw new Error('Failed to create file');
187
+ }
188
+ }
189
+
190
+ export async function createDirectoryOperation(dirPath: string) {
191
+ if (!dirPath) {
192
+ throw new Error('Directory path is required');
193
+ }
194
+
195
+ try {
196
+ // Normalize path for Windows only
197
+ const normalizedDirPath = process.platform === 'win32' ?
198
+ dirPath.replace(/\//g, '\\') : dirPath;
199
+
200
+ const dirFile = Bun.file(normalizedDirPath);
201
+ if (!(await dirFile.exists())) {
202
+ await mkdir(normalizedDirPath, { recursive: true });
203
+ const stats = await dirFile.stat();
204
+
205
+ return {
206
+ message: 'Directory created successfully',
207
+ path: normalizedDirPath,
208
+ modified: stats.mtime.toISOString()
209
+ };
210
+ } else {
211
+ throw new Error('Directory already exists');
212
+ }
213
+ } catch (error) {
214
+ debug.error('file', 'Create directory error:', error);
215
+ if (error instanceof Error) {
216
+ if (error.message === 'Directory already exists') {
217
+ throw error;
218
+ }
219
+ if (error.message.includes('EPERM')) {
220
+ throw new Error('Permission denied - you may not have sufficient permissions to create directories in this location');
221
+ } else if (error.message.includes('ENOENT')) {
222
+ throw new Error('Invalid path or parent directory does not exist');
223
+ }
224
+ throw error;
225
+ }
226
+ throw new Error('Failed to create directory');
227
+ }
228
+ }
229
+
230
+ export async function renameOperation(oldPath: string, newPath: string) {
231
+ if (!oldPath || !newPath) {
232
+ throw new Error('Both old and new paths are required');
233
+ }
234
+
235
+ try {
236
+ // Normalize paths for Windows only
237
+ const normalizedOldPath = process.platform === 'win32' ?
238
+ oldPath.replace(/\//g, '\\') : oldPath;
239
+ const normalizedNewPath = process.platform === 'win32' ?
240
+ newPath.replace(/\//g, '\\') : newPath;
241
+
242
+ const oldFile = Bun.file(normalizedOldPath);
243
+ const newFile = Bun.file(normalizedNewPath);
244
+
245
+ if (!(await oldFile.exists())) {
246
+ throw new Error('Source path does not exist');
247
+ }
248
+
249
+ if (await newFile.exists()) {
250
+ throw new Error('Destination path already exists');
251
+ }
252
+
253
+ // On Windows, add a small delay to handle file locks
254
+ if (process.platform === 'win32') {
255
+ await new Promise(resolve => setTimeout(resolve, 100));
256
+ }
257
+
258
+ await rename(normalizedOldPath, normalizedNewPath);
259
+ const stats = await newFile.stat();
260
+
261
+ return {
262
+ message: 'File/directory renamed successfully',
263
+ oldPath: normalizedOldPath,
264
+ newPath: normalizedNewPath,
265
+ modified: stats.mtime.toISOString()
266
+ };
267
+ } catch (error) {
268
+ debug.error('file', 'Rename error:', error);
269
+ if (error instanceof Error) {
270
+ if (error.message === 'Source path does not exist' ||
271
+ error.message === 'Destination path already exists') {
272
+ throw error;
273
+ }
274
+ if (error.message.includes('EPERM')) {
275
+ throw new Error('Permission denied - file may be in use or you may not have sufficient permissions');
276
+ } else if (error.message.includes('ENOENT')) {
277
+ throw new Error('File or directory not found');
278
+ } else if (error.message.includes('EEXIST')) {
279
+ throw new Error('Destination already exists');
280
+ }
281
+ throw error;
282
+ }
283
+ throw new Error('Failed to rename');
284
+ }
285
+ }
286
+
287
+ export async function duplicateOperation(sourcePath: string, targetPath: string) {
288
+ if (!sourcePath || !targetPath) {
289
+ throw new Error('Both source and target paths are required');
290
+ }
291
+
292
+ try {
293
+ // Normalize paths for Windows only
294
+ const normalizedSourcePath = process.platform === 'win32' ?
295
+ sourcePath.replace(/\//g, '\\') : sourcePath;
296
+ const normalizedTargetPath = process.platform === 'win32' ?
297
+ targetPath.replace(/\//g, '\\') : targetPath;
298
+
299
+ // Use node:fs stat to check existence (works for both files AND directories)
300
+ let sourceStats;
301
+ try {
302
+ sourceStats = await fsStat(normalizedSourcePath);
303
+ } catch {
304
+ throw new Error('Source file does not exist');
305
+ }
306
+
307
+ let targetExists = false;
308
+ try {
309
+ await fsStat(normalizedTargetPath);
310
+ targetExists = true;
311
+ } catch {
312
+ // Target doesn't exist - good
313
+ }
314
+
315
+ if (targetExists) {
316
+ throw new Error('Target file already exists');
317
+ }
318
+
319
+ // On Windows, add a small delay to handle file locks
320
+ if (process.platform === 'win32') {
321
+ await new Promise(resolve => setTimeout(resolve, 100));
322
+ }
323
+
324
+ if (sourceStats.isFile()) {
325
+ await copyFile(normalizedSourcePath, normalizedTargetPath);
326
+ } else if (sourceStats.isDirectory()) {
327
+ await copyDirectory(normalizedSourcePath, normalizedTargetPath);
328
+ } else {
329
+ throw new Error('Unsupported file type');
330
+ }
331
+
332
+ const targetStats = await fsStat(normalizedTargetPath);
333
+
334
+ return {
335
+ message: sourceStats.isDirectory() ? 'Folder duplicated successfully' : 'File duplicated successfully',
336
+ sourcePath: normalizedSourcePath,
337
+ targetPath: normalizedTargetPath,
338
+ size: sourceStats.isDirectory() ? 0 : targetStats.size,
339
+ modified: targetStats.mtime.toISOString()
340
+ };
341
+ } catch (error) {
342
+ debug.error('file', 'Duplicate error:', error);
343
+ if (error instanceof Error) {
344
+ if (error.message === 'Source file does not exist' ||
345
+ error.message === 'Target file already exists') {
346
+ throw error;
347
+ }
348
+ if (error.message.includes('EPERM')) {
349
+ throw new Error('Permission denied - file may be in use or you may not have sufficient permissions');
350
+ } else if (error.message.includes('ENOENT')) {
351
+ throw new Error('File or directory not found');
352
+ } else if (error.message.includes('EEXIST')) {
353
+ throw new Error('Target file already exists');
354
+ }
355
+ throw error;
356
+ }
357
+ throw new Error('Failed to duplicate file');
358
+ }
359
+ }
360
+
361
+ export async function uploadFileOperation(file: { name: string; type: string; size: number; data: Uint8Array } | any, targetPath: string) {
362
+ if (!file || !file.name || !file.data) {
363
+ throw new Error('File is required for upload');
364
+ }
365
+
366
+ if (!targetPath) {
367
+ throw new Error('Target path is required');
368
+ }
369
+
370
+ try {
371
+ // Normalize target path for Windows only
372
+ const normalizedTargetPath = process.platform === 'win32' ?
373
+ targetPath.replace(/\//g, '\\') : targetPath;
374
+ const finalPath = join(normalizedTargetPath, file.name);
375
+
376
+ // Create parent directory if it doesn't exist
377
+ const targetDir = Bun.file(normalizedTargetPath);
378
+ if (!(await targetDir.exists())) {
379
+ await mkdir(normalizedTargetPath, { recursive: true });
380
+ }
381
+
382
+ // Check if file already exists
383
+ const finalFile = Bun.file(finalPath);
384
+ if (await finalFile.exists()) {
385
+ throw new Error('File already exists');
386
+ }
387
+
388
+ // Write file
389
+ await Bun.write(finalPath, file.data);
390
+ const stats = await finalFile.stat();
391
+
392
+ return {
393
+ message: 'File uploaded successfully',
394
+ path: finalPath,
395
+ size: stats.size,
396
+ modified: stats.mtime.toISOString()
397
+ };
398
+ } catch (error) {
399
+ debug.error('file', 'Upload file error:', error);
400
+ if (error instanceof Error) {
401
+ if (error.message === 'File already exists') {
402
+ throw error;
403
+ }
404
+ if (error.message.includes('EPERM')) {
405
+ throw new Error('Permission denied - you may not have sufficient permissions to upload files to this location');
406
+ } else if (error.message.includes('ENOENT')) {
407
+ throw new Error('Target directory does not exist or is invalid');
408
+ } else if (error.message.includes('ENOSPC')) {
409
+ throw new Error('Not enough space on disk');
410
+ }
411
+ throw error;
412
+ }
413
+ throw new Error('Failed to upload file');
414
+ }
415
+ }
416
+
417
+ export async function deleteOperation(filePath: string, force: boolean = false) {
418
+ if (!filePath) {
419
+ throw new Error('File path is required');
420
+ }
421
+
422
+ const file = Bun.file(filePath);
423
+ let stats;
424
+ try {
425
+ stats = await file.stat();
426
+ } catch {
427
+ throw new Error('File or directory does not exist');
428
+ }
429
+
430
+ try {
431
+ if (stats.isFile()) {
432
+ await unlink(filePath);
433
+ return {
434
+ message: 'File deleted successfully',
435
+ path: filePath
436
+ };
437
+ } else if (stats.isDirectory()) {
438
+ // Check if directory is empty
439
+ const items = await readdir(filePath);
440
+ if (items.length > 0 && !force) {
441
+ throw new Error('Directory is not empty. Empty the directory first or use force delete.');
442
+ }
443
+
444
+ if (force && items.length > 0) {
445
+ // Use rm with recursive option for force delete
446
+ // This is safer than manual recursion and handles edge cases better
447
+ await rm(filePath, { recursive: true });
448
+ } else {
449
+ // For empty directories, use rmdir
450
+ await rmdir(filePath);
451
+ }
452
+
453
+ return {
454
+ message: 'Directory deleted successfully',
455
+ path: filePath
456
+ };
457
+ } else {
458
+ throw new Error('Path is neither a file nor directory');
459
+ }
460
+ } catch (error) {
461
+ debug.error('file', 'Delete error:', error);
462
+ if (error instanceof Error) {
463
+ if (error.message === 'Directory is not empty. Empty the directory first or use force delete.' ||
464
+ error.message === 'Path is neither a file nor directory') {
465
+ throw error;
466
+ }
467
+ if (error.message.includes('EPERM')) {
468
+ throw new Error('Permission denied - the folder may be in use or protected');
469
+ } else if (error.message.includes('ENOTEMPTY')) {
470
+ throw new Error('Directory is not empty');
471
+ } else if (error.message.includes('EBUSY')) {
472
+ throw new Error('Resource is busy or locked');
473
+ }
474
+ throw error;
475
+ }
476
+ throw new Error('Failed to delete');
477
+ }
478
+ }