@myrialabs/clopen 0.2.0 → 0.2.2
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/backend/{lib/auth → auth}/auth-service.ts +2 -2
- package/backend/{lib/chat → chat}/stream-manager.ts +1 -1
- package/backend/{lib/engine → engine}/adapters/claude/environment.ts +1 -1
- package/backend/{lib/engine → engine}/adapters/opencode/message-converter.ts +1 -1
- package/backend/{lib/files → files}/file-watcher.ts +1 -1
- package/backend/{lib/git → git}/git-executor.ts +1 -1
- package/backend/index.ts +7 -7
- 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/terminal → terminal}/shell-utils.ts +1 -1
- package/backend/{lib/utils → utils}/ws.ts +24 -2
- package/backend/ws/README.md +9 -9
- package/backend/ws/auth/index.ts +4 -0
- package/backend/ws/auth/invites.ts +2 -2
- package/backend/ws/auth/login.ts +23 -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 +1 -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/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 +6 -6
- 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 +3 -3
- 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 +3 -3
- 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 +11 -11
- package/frontend/{lib/components → components}/history/HistoryView.svelte +12 -12
- 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 +3 -3
- 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 +10 -10
- package/frontend/{lib/components → components}/workspace/panels/GitPanel.svelte +14 -14
- 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 +14 -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 +2 -2
- package/frontend/{lib/utils → utils}/theme.ts +1 -1
- package/frontend/{lib/utils → utils}/ws.ts +1 -1
- package/package.json +1 -1
- package/scripts/dev.ts +1 -1
- package/scripts/generate-icons.ts +2 -2
- 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/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/message-queries.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/connection.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/server.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}/blob-store.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}/env.ts +0 -0
- /package/backend/{lib/shared → utils}/index.ts +0 -0
- /package/backend/{lib/shared → utils}/port-utils.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
|
}
|
|
@@ -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';
|
|
@@ -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);
|
|
@@ -580,6 +580,28 @@ class WSServer {
|
|
|
580
580
|
debug.log('websocket', `Connection ${id} auth cleared`);
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
+
/**
|
|
584
|
+
* Clear authentication state for ALL active connections.
|
|
585
|
+
* Used when switching auth mode to 'required' — invalidates every
|
|
586
|
+
* connection's in-memory auth so the auth gate blocks subsequent messages.
|
|
587
|
+
* Returns the number of connections that were cleared.
|
|
588
|
+
*/
|
|
589
|
+
clearAllAuth(): number {
|
|
590
|
+
let cleared = 0;
|
|
591
|
+
for (const [id, state] of this.connectionState) {
|
|
592
|
+
if (state.authenticated) {
|
|
593
|
+
state.authenticated = false;
|
|
594
|
+
state.role = null;
|
|
595
|
+
state.sessionTokenHash = null;
|
|
596
|
+
cleared++;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (cleared > 0) {
|
|
600
|
+
debug.log('websocket', `Cleared auth on ${cleared} active connection(s)`);
|
|
601
|
+
}
|
|
602
|
+
return cleared;
|
|
603
|
+
}
|
|
604
|
+
|
|
583
605
|
/**
|
|
584
606
|
* Check if connection can receive data (backpressure check)
|
|
585
607
|
*/
|
package/backend/ws/README.md
CHANGED
|
@@ -466,7 +466,7 @@ try {
|
|
|
466
466
|
|
|
467
467
|
**Backend:**
|
|
468
468
|
```typescript
|
|
469
|
-
import { ws } from '$backend/
|
|
469
|
+
import { ws } from '$backend/utils/ws';
|
|
470
470
|
|
|
471
471
|
.on('terminal:input', {
|
|
472
472
|
data: t.Object({
|
|
@@ -589,7 +589,7 @@ ws.emit.global(event, payload) // → all connected clients
|
|
|
589
589
|
Emit events from external sources (PTY, file watchers, etc.):
|
|
590
590
|
|
|
591
591
|
```typescript
|
|
592
|
-
import { ws } from '$backend/
|
|
592
|
+
import { ws } from '$backend/utils/ws';
|
|
593
593
|
|
|
594
594
|
// PTY output
|
|
595
595
|
pty.onData((output) => {
|
|
@@ -632,7 +632,7 @@ browser.on('screencast-frame', (frame) => {
|
|
|
632
632
|
|
|
633
633
|
**Backend - Connection Setup:**
|
|
634
634
|
```typescript
|
|
635
|
-
import { ws } from '$backend/
|
|
635
|
+
import { ws } from '$backend/utils/ws';
|
|
636
636
|
|
|
637
637
|
app.ws('/ws', {
|
|
638
638
|
open(wsRaw) {
|
|
@@ -751,10 +751,10 @@ Merge another router.
|
|
|
751
751
|
|
|
752
752
|
### WebSocket Singleton API
|
|
753
753
|
|
|
754
|
-
**File: `backend/
|
|
754
|
+
**File: `backend/utils/ws.ts`**
|
|
755
755
|
|
|
756
756
|
```typescript
|
|
757
|
-
import { ws } from '$backend/
|
|
757
|
+
import { ws } from '$backend/utils/ws';
|
|
758
758
|
|
|
759
759
|
// Emit events
|
|
760
760
|
ws.emit.user(userId, 'event', payload)
|
|
@@ -781,7 +781,7 @@ ws.getConnectionCount(projectId?)
|
|
|
781
781
|
|
|
782
782
|
### Frontend Client API
|
|
783
783
|
|
|
784
|
-
**File: `frontend/
|
|
784
|
+
**File: `frontend/shared/ws.ts`**
|
|
785
785
|
|
|
786
786
|
```typescript
|
|
787
787
|
import ws from '$lib/utils/ws';
|
|
@@ -865,7 +865,7 @@ renderTree(tree);
|
|
|
865
865
|
```typescript
|
|
866
866
|
import { t } from 'elysia';
|
|
867
867
|
import { createRouter } from '$shared/utils/ws-server';
|
|
868
|
-
import { ws } from '$backend/
|
|
868
|
+
import { ws } from '$backend/utils/ws';
|
|
869
869
|
|
|
870
870
|
export const streamHandler = createRouter()
|
|
871
871
|
.on('terminal:input', {
|
|
@@ -942,7 +942,7 @@ onDestroy(() => {
|
|
|
942
942
|
```typescript
|
|
943
943
|
import { t } from 'elysia';
|
|
944
944
|
import { createRouter } from '$shared/utils/ws-server';
|
|
945
|
-
import { ws } from '$backend/
|
|
945
|
+
import { ws } from '$backend/utils/ws';
|
|
946
946
|
|
|
947
947
|
export const interactHandler = createRouter()
|
|
948
948
|
.on('browser:interact', {
|
|
@@ -1485,7 +1485,7 @@ cleanup();
|
|
|
1485
1485
|
### Event Emission
|
|
1486
1486
|
|
|
1487
1487
|
```typescript
|
|
1488
|
-
import { ws } from '$backend/
|
|
1488
|
+
import { ws } from '$backend/utils/ws';
|
|
1489
1489
|
|
|
1490
1490
|
// User-specific (all connections of a user)
|
|
1491
1491
|
ws.emit.user(userId, 'event', payload);
|
package/backend/ws/auth/index.ts
CHANGED
|
@@ -14,4 +14,8 @@ export const authRouter = createRouter()
|
|
|
14
14
|
.emit('auth:error', t.Object({
|
|
15
15
|
error: t.String(),
|
|
16
16
|
blockedAction: t.String()
|
|
17
|
+
}))
|
|
18
|
+
// Declare auth:force-logout event (emitted when auth mode switches to required)
|
|
19
|
+
.emit('auth:force-logout', t.Object({
|
|
20
|
+
reason: t.String()
|
|
17
21
|
}));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t } from 'elysia';
|
|
2
2
|
import { createRouter } from '$shared/utils/ws-server';
|
|
3
|
-
import { createInvite, listInvites, revokeInvite } from '$backend/
|
|
4
|
-
import { ws } from '$backend/
|
|
3
|
+
import { createInvite, listInvites, revokeInvite } from '$backend/auth/auth-service';
|
|
4
|
+
import { ws } from '$backend/utils/ws';
|
|
5
5
|
|
|
6
6
|
export const inviteHandler = createRouter()
|
|
7
7
|
// Create invite token (admin only — enforced by auth gate)
|