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