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