ruflo 3.5.2 → 3.5.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/dist/rvf.manifest.json +295 -0
- package/package.json +16 -2
- package/src/chat-ui/Dockerfile +25 -0
- package/src/chat-ui/patch-mcp-url-safety.sh +28 -0
- package/src/chat-ui/static/chatui/icon-144x144.png +0 -0
- package/src/chat-ui/static/chatui/omni-welcome.gif +0 -0
- package/src/config/config.example.json +76 -0
- package/src/mcp-bridge/Dockerfile +45 -0
- package/src/mcp-bridge/index.js +1668 -0
- package/src/mcp-bridge/mcp-stdio-kernel.js +159 -0
- package/src/mcp-bridge/package.json +17 -0
- package/src/mcp-bridge/test-harness.js +470 -0
- package/src/nginx/Dockerfile +10 -0
- package/src/nginx/nginx.conf +67 -0
- package/src/nginx/static/favicon-dark.svg +4 -0
- package/src/nginx/static/favicon.svg +4 -0
- package/src/nginx/static/icon.svg +5 -0
- package/src/nginx/static/logo.svg +9 -0
- package/src/nginx/static/manifest.json +22 -0
- package/src/nginx/static/welcome.js +184 -0
- package/src/ruvocal/.claude/skills/add-model-descriptions/SKILL.md +73 -0
- package/src/ruvocal/.devcontainer/Dockerfile +9 -0
- package/src/ruvocal/.devcontainer/devcontainer.json +36 -0
- package/src/ruvocal/.dockerignore +13 -0
- package/src/ruvocal/.env +194 -0
- package/src/ruvocal/.env.ci +1 -0
- package/src/ruvocal/.eslintignore +13 -0
- package/src/ruvocal/.eslintrc.cjs +45 -0
- package/src/ruvocal/.github/ISSUE_TEMPLATE/bug-report--chat-ui-.md +43 -0
- package/src/ruvocal/.github/ISSUE_TEMPLATE/config-support.md +9 -0
- package/src/ruvocal/.github/ISSUE_TEMPLATE/feature-request--chat-ui-.md +17 -0
- package/src/ruvocal/.github/ISSUE_TEMPLATE/huggingchat.md +11 -0
- package/src/ruvocal/.github/release.yml +16 -0
- package/src/ruvocal/.github/workflows/build-docs.yml +18 -0
- package/src/ruvocal/.github/workflows/build-image.yml +142 -0
- package/src/ruvocal/.github/workflows/build-pr-docs.yml +20 -0
- package/src/ruvocal/.github/workflows/deploy-dev.yml +63 -0
- package/src/ruvocal/.github/workflows/deploy-prod.yml +78 -0
- package/src/ruvocal/.github/workflows/lint-and-test.yml +84 -0
- package/src/ruvocal/.github/workflows/slugify.yaml +72 -0
- package/src/ruvocal/.github/workflows/trufflehog.yml +17 -0
- package/src/ruvocal/.github/workflows/upload-pr-documentation.yml +16 -0
- package/src/ruvocal/.husky/lint-stage-config.js +4 -0
- package/src/ruvocal/.husky/pre-commit +2 -0
- package/src/ruvocal/.prettierignore +14 -0
- package/src/ruvocal/.prettierrc +7 -0
- package/src/ruvocal/.vscode/launch.json +11 -0
- package/src/ruvocal/.vscode/settings.json +14 -0
- package/src/ruvocal/CLAUDE.md +126 -0
- package/src/ruvocal/Dockerfile +93 -0
- package/src/ruvocal/LICENSE +203 -0
- package/src/ruvocal/PRIVACY.md +41 -0
- package/src/ruvocal/README.md +190 -0
- package/src/ruvocal/chart/Chart.yaml +5 -0
- package/src/ruvocal/chart/env/dev.yaml +260 -0
- package/src/ruvocal/chart/env/prod.yaml +273 -0
- package/src/ruvocal/chart/templates/_helpers.tpl +22 -0
- package/src/ruvocal/chart/templates/config.yaml +10 -0
- package/src/ruvocal/chart/templates/deployment.yaml +81 -0
- package/src/ruvocal/chart/templates/hpa.yaml +45 -0
- package/src/ruvocal/chart/templates/infisical.yaml +24 -0
- package/src/ruvocal/chart/templates/ingress-internal.yaml +32 -0
- package/src/ruvocal/chart/templates/ingress.yaml +32 -0
- package/src/ruvocal/chart/templates/network-policy.yaml +36 -0
- package/src/ruvocal/chart/templates/service-account.yaml +13 -0
- package/src/ruvocal/chart/templates/service-monitor.yaml +17 -0
- package/src/ruvocal/chart/templates/service.yaml +21 -0
- package/src/ruvocal/chart/values.yaml +73 -0
- package/src/ruvocal/docker-compose.yml +21 -0
- package/src/ruvocal/docs/adr/ADR-029-HUGGINGFACE-CHAT-UI-CLOUD-RUN.md +1236 -0
- package/src/ruvocal/docs/adr/ADR-033-RUVECTOR-RUFLO-MCP-INTEGRATION.md +111 -0
- package/src/ruvocal/docs/adr/ADR-034-OPTIONAL-MCP-BACKENDS.md +117 -0
- package/src/ruvocal/docs/adr/ADR-035-MCP-TOOL-GROUPS.md +186 -0
- package/src/ruvocal/docs/adr/ADR-037-AUTOPILOT-CHAT-MODE.md +1500 -0
- package/src/ruvocal/docs/adr/ADR-038-RUVOCAL-FORK.md +286 -0
- package/src/ruvocal/docs/source/_toctree.yml +30 -0
- package/src/ruvocal/docs/source/configuration/common-issues.md +38 -0
- package/src/ruvocal/docs/source/configuration/llm-router.md +105 -0
- package/src/ruvocal/docs/source/configuration/mcp-tools.md +84 -0
- package/src/ruvocal/docs/source/configuration/metrics.md +9 -0
- package/src/ruvocal/docs/source/configuration/open-id.md +57 -0
- package/src/ruvocal/docs/source/configuration/overview.md +89 -0
- package/src/ruvocal/docs/source/configuration/theming.md +20 -0
- package/src/ruvocal/docs/source/developing/architecture.md +48 -0
- package/src/ruvocal/docs/source/index.md +53 -0
- package/src/ruvocal/docs/source/installation/docker.md +43 -0
- package/src/ruvocal/docs/source/installation/helm.md +43 -0
- package/src/ruvocal/docs/source/installation/local.md +62 -0
- package/src/ruvocal/entrypoint.sh +19 -0
- package/src/ruvocal/mcp-bridge/.claude-flow/agents/store.json +27 -0
- package/src/ruvocal/mcp-bridge/.claude-flow/daemon-state.json +130 -0
- package/src/ruvocal/mcp-bridge/.claude-flow/daemon.log +0 -0
- package/src/ruvocal/mcp-bridge/.claude-flow/daemon.pid +1 -0
- package/src/ruvocal/mcp-bridge/.claude-flow/tasks/store.json +21 -0
- package/src/ruvocal/mcp-bridge/.swarm/hnsw.index +0 -0
- package/src/ruvocal/mcp-bridge/.swarm/hnsw.metadata.json +1 -0
- package/src/ruvocal/mcp-bridge/.swarm/memory.db +0 -0
- package/src/ruvocal/mcp-bridge/.swarm/model-router-state.json +14 -0
- package/src/ruvocal/mcp-bridge/.swarm/schema.sql +305 -0
- package/src/ruvocal/mcp-bridge/Dockerfile +45 -0
- package/src/ruvocal/mcp-bridge/cloudbuild.yaml +49 -0
- package/src/ruvocal/mcp-bridge/index.js +1864 -0
- package/src/ruvocal/mcp-bridge/mcp-stdio-kernel.js +159 -0
- package/src/ruvocal/mcp-bridge/package-lock.json +762 -0
- package/src/ruvocal/mcp-bridge/package.json +17 -0
- package/src/ruvocal/mcp-bridge/test-harness.js +470 -0
- package/src/ruvocal/models/add-your-models-here.txt +1 -0
- package/src/ruvocal/package-lock.json +11741 -0
- package/src/ruvocal/package.json +121 -0
- package/src/ruvocal/postcss.config.js +6 -0
- package/src/ruvocal/rvf.manifest.json +204 -0
- package/src/ruvocal/scripts/config.ts +64 -0
- package/src/ruvocal/scripts/generate-welcome.mjs +181 -0
- package/src/ruvocal/scripts/populate.ts +288 -0
- package/src/ruvocal/scripts/samples.txt +194 -0
- package/src/ruvocal/scripts/setups/vitest-setup-client.ts +0 -0
- package/src/ruvocal/scripts/setups/vitest-setup-server.ts +44 -0
- package/src/ruvocal/scripts/updateLocalEnv.ts +48 -0
- package/src/ruvocal/src/ambient.d.ts +7 -0
- package/src/ruvocal/src/app.d.ts +29 -0
- package/src/ruvocal/src/app.html +53 -0
- package/src/ruvocal/src/hooks.server.ts +32 -0
- package/src/ruvocal/src/hooks.ts +6 -0
- package/src/ruvocal/src/lib/APIClient.ts +148 -0
- package/src/ruvocal/src/lib/actions/clickOutside.ts +18 -0
- package/src/ruvocal/src/lib/actions/snapScrollToBottom.ts +346 -0
- package/src/ruvocal/src/lib/buildPrompt.ts +33 -0
- package/src/ruvocal/src/lib/components/AnnouncementBanner.svelte +20 -0
- package/src/ruvocal/src/lib/components/BackgroundGenerationPoller.svelte +168 -0
- package/src/ruvocal/src/lib/components/CodeBlock.svelte +73 -0
- package/src/ruvocal/src/lib/components/CopyToClipBoardBtn.svelte +92 -0
- package/src/ruvocal/src/lib/components/DeleteConversationModal.svelte +75 -0
- package/src/ruvocal/src/lib/components/EditConversationModal.svelte +100 -0
- package/src/ruvocal/src/lib/components/ExpandNavigation.svelte +22 -0
- package/src/ruvocal/src/lib/components/HoverTooltip.svelte +44 -0
- package/src/ruvocal/src/lib/components/HtmlPreviewModal.svelte +143 -0
- package/src/ruvocal/src/lib/components/InfiniteScroll.svelte +50 -0
- package/src/ruvocal/src/lib/components/MobileNav.svelte +300 -0
- package/src/ruvocal/src/lib/components/Modal.svelte +115 -0
- package/src/ruvocal/src/lib/components/ModelCardMetadata.svelte +71 -0
- package/src/ruvocal/src/lib/components/NavConversationItem.svelte +151 -0
- package/src/ruvocal/src/lib/components/NavMenu.svelte +295 -0
- package/src/ruvocal/src/lib/components/Pagination.svelte +97 -0
- package/src/ruvocal/src/lib/components/PaginationArrow.svelte +27 -0
- package/src/ruvocal/src/lib/components/Portal.svelte +24 -0
- package/src/ruvocal/src/lib/components/RetryBtn.svelte +18 -0
- package/src/ruvocal/src/lib/components/RuFloUniverse.svelte +185 -0
- package/src/ruvocal/src/lib/components/ScrollToBottomBtn.svelte +47 -0
- package/src/ruvocal/src/lib/components/ScrollToPreviousBtn.svelte +77 -0
- package/src/ruvocal/src/lib/components/ShareConversationModal.svelte +182 -0
- package/src/ruvocal/src/lib/components/StopGeneratingBtn.svelte +69 -0
- package/src/ruvocal/src/lib/components/SubscribeModal.svelte +87 -0
- package/src/ruvocal/src/lib/components/Switch.svelte +36 -0
- package/src/ruvocal/src/lib/components/SystemPromptModal.svelte +44 -0
- package/src/ruvocal/src/lib/components/Toast.svelte +27 -0
- package/src/ruvocal/src/lib/components/Tooltip.svelte +30 -0
- package/src/ruvocal/src/lib/components/WelcomeModal.svelte +46 -0
- package/src/ruvocal/src/lib/components/chat/Alternatives.svelte +77 -0
- package/src/ruvocal/src/lib/components/chat/BlockWrapper.svelte +72 -0
- package/src/ruvocal/src/lib/components/chat/ChatInput.svelte +490 -0
- package/src/ruvocal/src/lib/components/chat/ChatIntroduction.svelte +123 -0
- package/src/ruvocal/src/lib/components/chat/ChatMessage.svelte +548 -0
- package/src/ruvocal/src/lib/components/chat/ChatWindow.svelte +939 -0
- package/src/ruvocal/src/lib/components/chat/FileDropzone.svelte +92 -0
- package/src/ruvocal/src/lib/components/chat/ImageLightbox.svelte +66 -0
- package/src/ruvocal/src/lib/components/chat/MarkdownBlock.svelte +23 -0
- package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte +69 -0
- package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte.test.ts +58 -0
- package/src/ruvocal/src/lib/components/chat/MessageAvatar.svelte +103 -0
- package/src/ruvocal/src/lib/components/chat/ModelSwitch.svelte +64 -0
- package/src/ruvocal/src/lib/components/chat/OpenReasoningResults.svelte +81 -0
- package/src/ruvocal/src/lib/components/chat/TaskGroup.svelte +88 -0
- package/src/ruvocal/src/lib/components/chat/ToolUpdate.svelte +273 -0
- package/src/ruvocal/src/lib/components/chat/UploadedFile.svelte +253 -0
- package/src/ruvocal/src/lib/components/chat/UrlFetchModal.svelte +203 -0
- package/src/ruvocal/src/lib/components/chat/VoiceRecorder.svelte +214 -0
- package/src/ruvocal/src/lib/components/icons/IconBurger.svelte +20 -0
- package/src/ruvocal/src/lib/components/icons/IconCheap.svelte +20 -0
- package/src/ruvocal/src/lib/components/icons/IconChevron.svelte +24 -0
- package/src/ruvocal/src/lib/components/icons/IconDazzled.svelte +40 -0
- package/src/ruvocal/src/lib/components/icons/IconFast.svelte +20 -0
- package/src/ruvocal/src/lib/components/icons/IconLoading.svelte +22 -0
- package/src/ruvocal/src/lib/components/icons/IconMCP.svelte +28 -0
- package/src/ruvocal/src/lib/components/icons/IconMoon.svelte +21 -0
- package/src/ruvocal/src/lib/components/icons/IconNew.svelte +20 -0
- package/src/ruvocal/src/lib/components/icons/IconOmni.svelte +90 -0
- package/src/ruvocal/src/lib/components/icons/IconPaperclip.svelte +24 -0
- package/src/ruvocal/src/lib/components/icons/IconPro.svelte +37 -0
- package/src/ruvocal/src/lib/components/icons/IconShare.svelte +21 -0
- package/src/ruvocal/src/lib/components/icons/IconSun.svelte +93 -0
- package/src/ruvocal/src/lib/components/icons/Logo.svelte +68 -0
- package/src/ruvocal/src/lib/components/icons/LogoHuggingFaceBorderless.svelte +54 -0
- package/src/ruvocal/src/lib/components/mcp/AddServerForm.svelte +250 -0
- package/src/ruvocal/src/lib/components/mcp/MCPServerManager.svelte +185 -0
- package/src/ruvocal/src/lib/components/mcp/ServerCard.svelte +203 -0
- package/src/ruvocal/src/lib/components/players/AudioPlayer.svelte +82 -0
- package/src/ruvocal/src/lib/components/voice/AudioWaveform.svelte +96 -0
- package/src/ruvocal/src/lib/constants/mcpExamples.ts +135 -0
- package/src/ruvocal/src/lib/constants/mime.ts +11 -0
- package/src/ruvocal/src/lib/constants/pagination.ts +1 -0
- package/src/ruvocal/src/lib/constants/publicSepToken.ts +1 -0
- package/src/ruvocal/src/lib/constants/routerExamples.ts +209 -0
- package/src/ruvocal/src/lib/createShareLink.ts +27 -0
- package/src/ruvocal/src/lib/jobs/refresh-conversation-stats.ts +297 -0
- package/src/ruvocal/src/lib/migrations/lock.ts +56 -0
- package/src/ruvocal/src/lib/migrations/migrations.spec.ts +74 -0
- package/src/ruvocal/src/lib/migrations/migrations.ts +109 -0
- package/src/ruvocal/src/lib/migrations/routines/01-update-search-assistants.ts +50 -0
- package/src/ruvocal/src/lib/migrations/routines/02-update-assistants-models.ts +48 -0
- package/src/ruvocal/src/lib/migrations/routines/04-update-message-updates.ts +151 -0
- package/src/ruvocal/src/lib/migrations/routines/05-update-message-files.ts +56 -0
- package/src/ruvocal/src/lib/migrations/routines/06-trim-message-updates.ts +56 -0
- package/src/ruvocal/src/lib/migrations/routines/08-update-featured-to-review.ts +32 -0
- package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.spec.ts +214 -0
- package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.ts +88 -0
- package/src/ruvocal/src/lib/migrations/routines/10-update-reports-assistantid.ts +29 -0
- package/src/ruvocal/src/lib/migrations/routines/index.ts +15 -0
- package/src/ruvocal/src/lib/server/__tests__/conversation-stop-generating.spec.ts +103 -0
- package/src/ruvocal/src/lib/server/abortRegistry.ts +57 -0
- package/src/ruvocal/src/lib/server/abortedGenerations.ts +43 -0
- package/src/ruvocal/src/lib/server/adminToken.ts +62 -0
- package/src/ruvocal/src/lib/server/api/__tests__/conversations-id.spec.ts +296 -0
- package/src/ruvocal/src/lib/server/api/__tests__/conversations-message.spec.ts +216 -0
- package/src/ruvocal/src/lib/server/api/__tests__/conversations.spec.ts +235 -0
- package/src/ruvocal/src/lib/server/api/__tests__/misc.spec.ts +72 -0
- package/src/ruvocal/src/lib/server/api/__tests__/testHelpers.ts +86 -0
- package/src/ruvocal/src/lib/server/api/__tests__/user-reports.spec.ts +78 -0
- package/src/ruvocal/src/lib/server/api/__tests__/user.spec.ts +239 -0
- package/src/ruvocal/src/lib/server/api/types.ts +37 -0
- package/src/ruvocal/src/lib/server/api/utils/requireAuth.ts +22 -0
- package/src/ruvocal/src/lib/server/api/utils/resolveConversation.ts +69 -0
- package/src/ruvocal/src/lib/server/api/utils/resolveModel.ts +27 -0
- package/src/ruvocal/src/lib/server/api/utils/superjsonResponse.ts +15 -0
- package/src/ruvocal/src/lib/server/apiToken.ts +11 -0
- package/src/ruvocal/src/lib/server/auth.ts +554 -0
- package/src/ruvocal/src/lib/server/config.ts +187 -0
- package/src/ruvocal/src/lib/server/conversation.ts +83 -0
- package/src/ruvocal/src/lib/server/database/__tests__/rvf.spec.ts +709 -0
- package/src/ruvocal/src/lib/server/database/postgres.ts +700 -0
- package/src/ruvocal/src/lib/server/database/rvf.ts +1078 -0
- package/src/ruvocal/src/lib/server/database.ts +145 -0
- package/src/ruvocal/src/lib/server/endpoints/document.ts +68 -0
- package/src/ruvocal/src/lib/server/endpoints/endpoints.ts +43 -0
- package/src/ruvocal/src/lib/server/endpoints/images.ts +211 -0
- package/src/ruvocal/src/lib/server/endpoints/openai/endpointOai.ts +266 -0
- package/src/ruvocal/src/lib/server/endpoints/openai/openAIChatToTextGenerationStream.ts +212 -0
- package/src/ruvocal/src/lib/server/endpoints/openai/openAICompletionToTextGenerationStream.ts +32 -0
- package/src/ruvocal/src/lib/server/endpoints/preprocessMessages.ts +61 -0
- package/src/ruvocal/src/lib/server/exitHandler.ts +59 -0
- package/src/ruvocal/src/lib/server/files/downloadFile.ts +34 -0
- package/src/ruvocal/src/lib/server/files/uploadFile.ts +29 -0
- package/src/ruvocal/src/lib/server/findRepoRoot.ts +13 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-Black.ttf +0 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-Bold.ttf +0 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-ExtraBold.ttf +0 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-ExtraLight.ttf +0 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-Light.ttf +0 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-Medium.ttf +0 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-Regular.ttf +0 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-SemiBold.ttf +0 -0
- package/src/ruvocal/src/lib/server/fonts/Inter-Thin.ttf +0 -0
- package/src/ruvocal/src/lib/server/generateFromDefaultEndpoint.ts +46 -0
- package/src/ruvocal/src/lib/server/hooks/error.ts +37 -0
- package/src/ruvocal/src/lib/server/hooks/fetch.ts +22 -0
- package/src/ruvocal/src/lib/server/hooks/handle.ts +250 -0
- package/src/ruvocal/src/lib/server/hooks/init.ts +51 -0
- package/src/ruvocal/src/lib/server/isURLLocal.spec.ts +31 -0
- package/src/ruvocal/src/lib/server/isURLLocal.ts +74 -0
- package/src/ruvocal/src/lib/server/logger.ts +42 -0
- package/src/ruvocal/src/lib/server/mcp/clientPool.ts +70 -0
- package/src/ruvocal/src/lib/server/mcp/hf.ts +32 -0
- package/src/ruvocal/src/lib/server/mcp/httpClient.ts +122 -0
- package/src/ruvocal/src/lib/server/mcp/registry.ts +76 -0
- package/src/ruvocal/src/lib/server/mcp/tools.ts +196 -0
- package/src/ruvocal/src/lib/server/metrics.ts +255 -0
- package/src/ruvocal/src/lib/server/models.ts +518 -0
- package/src/ruvocal/src/lib/server/requestContext.ts +55 -0
- package/src/ruvocal/src/lib/server/router/arch.ts +230 -0
- package/src/ruvocal/src/lib/server/router/endpoint.ts +316 -0
- package/src/ruvocal/src/lib/server/router/multimodal.ts +28 -0
- package/src/ruvocal/src/lib/server/router/policy.ts +49 -0
- package/src/ruvocal/src/lib/server/router/toolsRoute.ts +51 -0
- package/src/ruvocal/src/lib/server/router/types.ts +21 -0
- package/src/ruvocal/src/lib/server/sendSlack.ts +23 -0
- package/src/ruvocal/src/lib/server/textGeneration/generate.ts +258 -0
- package/src/ruvocal/src/lib/server/textGeneration/index.ts +95 -0
- package/src/ruvocal/src/lib/server/textGeneration/mcp/fileRefs.ts +155 -0
- package/src/ruvocal/src/lib/server/textGeneration/mcp/routerResolution.ts +108 -0
- package/src/ruvocal/src/lib/server/textGeneration/mcp/runMcpFlow.ts +822 -0
- package/src/ruvocal/src/lib/server/textGeneration/mcp/toolInvocation.ts +349 -0
- package/src/ruvocal/src/lib/server/textGeneration/reasoning.ts +23 -0
- package/src/ruvocal/src/lib/server/textGeneration/title.ts +83 -0
- package/src/ruvocal/src/lib/server/textGeneration/types.ts +26 -0
- package/src/ruvocal/src/lib/server/textGeneration/utils/prepareFiles.ts +88 -0
- package/src/ruvocal/src/lib/server/textGeneration/utils/routing.ts +21 -0
- package/src/ruvocal/src/lib/server/textGeneration/utils/toolPrompt.ts +49 -0
- package/src/ruvocal/src/lib/server/urlSafety.ts +72 -0
- package/src/ruvocal/src/lib/server/usageLimits.ts +30 -0
- package/src/ruvocal/src/lib/stores/autopilotStore.svelte.ts +175 -0
- package/src/ruvocal/src/lib/stores/backgroundGenerations.svelte.ts +32 -0
- package/src/ruvocal/src/lib/stores/backgroundGenerations.ts +1 -0
- package/src/ruvocal/src/lib/stores/errors.ts +9 -0
- package/src/ruvocal/src/lib/stores/isAborted.ts +3 -0
- package/src/ruvocal/src/lib/stores/isPro.ts +4 -0
- package/src/ruvocal/src/lib/stores/loading.ts +3 -0
- package/src/ruvocal/src/lib/stores/mcpServers.ts +345 -0
- package/src/ruvocal/src/lib/stores/pendingChatInput.ts +3 -0
- package/src/ruvocal/src/lib/stores/pendingMessage.ts +9 -0
- package/src/ruvocal/src/lib/stores/settings.ts +182 -0
- package/src/ruvocal/src/lib/stores/shareModal.ts +13 -0
- package/src/ruvocal/src/lib/stores/titleUpdate.ts +8 -0
- package/src/ruvocal/src/lib/switchTheme.ts +124 -0
- package/src/ruvocal/src/lib/types/AbortedGeneration.ts +8 -0
- package/src/ruvocal/src/lib/types/Assistant.ts +31 -0
- package/src/ruvocal/src/lib/types/AssistantStats.ts +11 -0
- package/src/ruvocal/src/lib/types/ConfigKey.ts +4 -0
- package/src/ruvocal/src/lib/types/ConvSidebar.ts +9 -0
- package/src/ruvocal/src/lib/types/Conversation.ts +27 -0
- package/src/ruvocal/src/lib/types/ConversationStats.ts +13 -0
- package/src/ruvocal/src/lib/types/Message.ts +41 -0
- package/src/ruvocal/src/lib/types/MessageEvent.ts +10 -0
- package/src/ruvocal/src/lib/types/MessageUpdate.ts +139 -0
- package/src/ruvocal/src/lib/types/MigrationResult.ts +7 -0
- package/src/ruvocal/src/lib/types/Model.ts +23 -0
- package/src/ruvocal/src/lib/types/Report.ts +12 -0
- package/src/ruvocal/src/lib/types/Review.ts +6 -0
- package/src/ruvocal/src/lib/types/Semaphore.ts +19 -0
- package/src/ruvocal/src/lib/types/Session.ts +22 -0
- package/src/ruvocal/src/lib/types/Settings.ts +86 -0
- package/src/ruvocal/src/lib/types/SharedConversation.ts +9 -0
- package/src/ruvocal/src/lib/types/Template.ts +6 -0
- package/src/ruvocal/src/lib/types/Timestamps.ts +4 -0
- package/src/ruvocal/src/lib/types/TokenCache.ts +6 -0
- package/src/ruvocal/src/lib/types/Tool.ts +74 -0
- package/src/ruvocal/src/lib/types/UrlDependency.ts +5 -0
- package/src/ruvocal/src/lib/types/User.ts +14 -0
- package/src/ruvocal/src/lib/utils/PublicConfig.svelte.ts +75 -0
- package/src/ruvocal/src/lib/utils/auth.ts +17 -0
- package/src/ruvocal/src/lib/utils/chunk.ts +33 -0
- package/src/ruvocal/src/lib/utils/cookiesAreEnabled.ts +13 -0
- package/src/ruvocal/src/lib/utils/debounce.ts +17 -0
- package/src/ruvocal/src/lib/utils/deepestChild.ts +6 -0
- package/src/ruvocal/src/lib/utils/favicon.ts +21 -0
- package/src/ruvocal/src/lib/utils/fetchJSON.ts +23 -0
- package/src/ruvocal/src/lib/utils/file2base64.ts +14 -0
- package/src/ruvocal/src/lib/utils/formatUserCount.ts +37 -0
- package/src/ruvocal/src/lib/utils/generationState.spec.ts +75 -0
- package/src/ruvocal/src/lib/utils/generationState.ts +26 -0
- package/src/ruvocal/src/lib/utils/getHref.ts +41 -0
- package/src/ruvocal/src/lib/utils/getReturnFromGenerator.ts +7 -0
- package/src/ruvocal/src/lib/utils/haptics.ts +64 -0
- package/src/ruvocal/src/lib/utils/hashConv.ts +12 -0
- package/src/ruvocal/src/lib/utils/hf.ts +17 -0
- package/src/ruvocal/src/lib/utils/isDesktop.ts +7 -0
- package/src/ruvocal/src/lib/utils/isUrl.ts +8 -0
- package/src/ruvocal/src/lib/utils/isVirtualKeyboard.ts +16 -0
- package/src/ruvocal/src/lib/utils/loadAttachmentsFromUrls.ts +115 -0
- package/src/ruvocal/src/lib/utils/marked.spec.ts +96 -0
- package/src/ruvocal/src/lib/utils/marked.ts +531 -0
- package/src/ruvocal/src/lib/utils/mcpValidation.ts +147 -0
- package/src/ruvocal/src/lib/utils/mergeAsyncGenerators.ts +38 -0
- package/src/ruvocal/src/lib/utils/messageUpdates.spec.ts +262 -0
- package/src/ruvocal/src/lib/utils/messageUpdates.ts +324 -0
- package/src/ruvocal/src/lib/utils/mime.ts +56 -0
- package/src/ruvocal/src/lib/utils/models.ts +14 -0
- package/src/ruvocal/src/lib/utils/parseBlocks.ts +120 -0
- package/src/ruvocal/src/lib/utils/parseIncompleteMarkdown.ts +644 -0
- package/src/ruvocal/src/lib/utils/parseStringToList.ts +10 -0
- package/src/ruvocal/src/lib/utils/randomUuid.ts +14 -0
- package/src/ruvocal/src/lib/utils/searchTokens.ts +33 -0
- package/src/ruvocal/src/lib/utils/sha256.ts +7 -0
- package/src/ruvocal/src/lib/utils/stringifyError.ts +12 -0
- package/src/ruvocal/src/lib/utils/sum.ts +3 -0
- package/src/ruvocal/src/lib/utils/template.spec.ts +59 -0
- package/src/ruvocal/src/lib/utils/template.ts +53 -0
- package/src/ruvocal/src/lib/utils/timeout.ts +9 -0
- package/src/ruvocal/src/lib/utils/toolProgress.spec.ts +46 -0
- package/src/ruvocal/src/lib/utils/toolProgress.ts +11 -0
- package/src/ruvocal/src/lib/utils/tree/addChildren.spec.ts +102 -0
- package/src/ruvocal/src/lib/utils/tree/addChildren.ts +48 -0
- package/src/ruvocal/src/lib/utils/tree/addSibling.spec.ts +81 -0
- package/src/ruvocal/src/lib/utils/tree/addSibling.ts +41 -0
- package/src/ruvocal/src/lib/utils/tree/buildSubtree.spec.ts +110 -0
- package/src/ruvocal/src/lib/utils/tree/buildSubtree.ts +24 -0
- package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.spec.ts +31 -0
- package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.ts +36 -0
- package/src/ruvocal/src/lib/utils/tree/isMessageId.spec.ts +15 -0
- package/src/ruvocal/src/lib/utils/tree/isMessageId.ts +5 -0
- package/src/ruvocal/src/lib/utils/tree/tree.d.ts +14 -0
- package/src/ruvocal/src/lib/utils/tree/treeHelpers.spec.ts +167 -0
- package/src/ruvocal/src/lib/utils/updates.ts +39 -0
- package/src/ruvocal/src/lib/utils/urlParams.ts +13 -0
- package/src/ruvocal/src/lib/workers/autopilotWorker.ts +221 -0
- package/src/ruvocal/src/lib/workers/detailFetchWorker.ts +100 -0
- package/src/ruvocal/src/lib/workers/markdownWorker.ts +61 -0
- package/src/ruvocal/src/routes/+error.svelte +20 -0
- package/src/ruvocal/src/routes/+layout.svelte +324 -0
- package/src/ruvocal/src/routes/+layout.ts +91 -0
- package/src/ruvocal/src/routes/+page.svelte +168 -0
- package/src/ruvocal/src/routes/.well-known/oauth-cimd/+server.ts +37 -0
- package/src/ruvocal/src/routes/__debug/openai/+server.ts +21 -0
- package/src/ruvocal/src/routes/admin/export/+server.ts +159 -0
- package/src/ruvocal/src/routes/admin/stats/compute/+server.ts +16 -0
- package/src/ruvocal/src/routes/api/conversation/[id]/+server.ts +40 -0
- package/src/ruvocal/src/routes/api/conversation/[id]/message/[messageId]/+server.ts +42 -0
- package/src/ruvocal/src/routes/api/conversations/+server.ts +48 -0
- package/src/ruvocal/src/routes/api/fetch-url/+server.ts +147 -0
- package/src/ruvocal/src/routes/api/mcp/health/+server.ts +292 -0
- package/src/ruvocal/src/routes/api/mcp/servers/+server.ts +32 -0
- package/src/ruvocal/src/routes/api/models/+server.ts +25 -0
- package/src/ruvocal/src/routes/api/transcribe/+server.ts +104 -0
- package/src/ruvocal/src/routes/api/user/+server.ts +15 -0
- package/src/ruvocal/src/routes/api/user/validate-token/+server.ts +20 -0
- package/src/ruvocal/src/routes/api/v2/conversations/+server.ts +48 -0
- package/src/ruvocal/src/routes/api/v2/conversations/[id]/+server.ts +94 -0
- package/src/ruvocal/src/routes/api/v2/conversations/[id]/message/[messageId]/+server.ts +43 -0
- package/src/ruvocal/src/routes/api/v2/conversations/import-share/+server.ts +23 -0
- package/src/ruvocal/src/routes/api/v2/debug/config/+server.ts +16 -0
- package/src/ruvocal/src/routes/api/v2/debug/refresh/+server.ts +30 -0
- package/src/ruvocal/src/routes/api/v2/export/+server.ts +196 -0
- package/src/ruvocal/src/routes/api/v2/feature-flags/+server.ts +14 -0
- package/src/ruvocal/src/routes/api/v2/models/+server.ts +38 -0
- package/src/ruvocal/src/routes/api/v2/models/[namespace]/+server.ts +8 -0
- package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/+server.ts +8 -0
- package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/subscribe/+server.ts +28 -0
- package/src/ruvocal/src/routes/api/v2/models/[namespace]/subscribe/+server.ts +28 -0
- package/src/ruvocal/src/routes/api/v2/models/old/+server.ts +7 -0
- package/src/ruvocal/src/routes/api/v2/models/refresh/+server.ts +33 -0
- package/src/ruvocal/src/routes/api/v2/public-config/+server.ts +7 -0
- package/src/ruvocal/src/routes/api/v2/user/+server.ts +17 -0
- package/src/ruvocal/src/routes/api/v2/user/billing-orgs/+server.ts +73 -0
- package/src/ruvocal/src/routes/api/v2/user/reports/+server.ts +17 -0
- package/src/ruvocal/src/routes/api/v2/user/settings/+server.ts +103 -0
- package/src/ruvocal/src/routes/conversation/+server.ts +115 -0
- package/src/ruvocal/src/routes/conversation/[id]/+page.svelte +582 -0
- package/src/ruvocal/src/routes/conversation/[id]/+page.ts +60 -0
- package/src/ruvocal/src/routes/conversation/[id]/+server.ts +736 -0
- package/src/ruvocal/src/routes/conversation/[id]/message/[messageId]/prompt/+server.ts +66 -0
- package/src/ruvocal/src/routes/conversation/[id]/output/[sha256]/+server.ts +58 -0
- package/src/ruvocal/src/routes/conversation/[id]/share/+server.ts +69 -0
- package/src/ruvocal/src/routes/conversation/[id]/stop-generating/+server.ts +35 -0
- package/src/ruvocal/src/routes/healthcheck/+server.ts +3 -0
- package/src/ruvocal/src/routes/login/+server.ts +5 -0
- package/src/ruvocal/src/routes/login/callback/+server.ts +103 -0
- package/src/ruvocal/src/routes/login/callback/updateUser.spec.ts +157 -0
- package/src/ruvocal/src/routes/login/callback/updateUser.ts +215 -0
- package/src/ruvocal/src/routes/logout/+server.ts +18 -0
- package/src/ruvocal/src/routes/metrics/+server.ts +18 -0
- package/src/ruvocal/src/routes/models/+page.svelte +233 -0
- package/src/ruvocal/src/routes/models/[...model]/+page.svelte +161 -0
- package/src/ruvocal/src/routes/models/[...model]/+page.ts +14 -0
- package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/+server.ts +64 -0
- package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte +28 -0
- package/src/ruvocal/src/routes/privacy/+page.svelte +11 -0
- package/src/ruvocal/src/routes/r/[id]/+page.ts +34 -0
- package/src/ruvocal/src/routes/settings/(nav)/+layout.svelte +282 -0
- package/src/ruvocal/src/routes/settings/(nav)/+layout.ts +1 -0
- package/src/ruvocal/src/routes/settings/(nav)/+page.svelte +0 -0
- package/src/ruvocal/src/routes/settings/(nav)/+server.ts +53 -0
- package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.svelte +464 -0
- package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.ts +14 -0
- package/src/ruvocal/src/routes/settings/(nav)/application/+page.svelte +362 -0
- package/src/ruvocal/src/routes/settings/+layout.svelte +40 -0
- package/src/ruvocal/src/styles/highlight-js.css +195 -0
- package/src/ruvocal/src/styles/main.css +144 -0
- package/src/ruvocal/static/chatui/apple-touch-icon.png +0 -0
- package/src/ruvocal/static/chatui/favicon-dark.svg +3 -0
- package/src/ruvocal/static/chatui/favicon-dev.svg +3 -0
- package/src/ruvocal/static/chatui/favicon.ico +0 -0
- package/src/ruvocal/static/chatui/favicon.svg +3 -0
- package/src/ruvocal/static/chatui/icon-128x128.png +0 -0
- package/src/ruvocal/static/chatui/icon-144x144.png +0 -0
- package/src/ruvocal/static/chatui/icon-192x192.png +0 -0
- package/src/ruvocal/static/chatui/icon-256x256.png +0 -0
- package/src/ruvocal/static/chatui/icon-36x36.png +0 -0
- package/src/ruvocal/static/chatui/icon-48x48.png +0 -0
- package/src/ruvocal/static/chatui/icon-512x512.png +0 -0
- package/src/ruvocal/static/chatui/icon-72x72.png +0 -0
- package/src/ruvocal/static/chatui/icon-96x96.png +0 -0
- package/src/ruvocal/static/chatui/icon.svg +3 -0
- package/src/ruvocal/static/chatui/logo.svg +7 -0
- package/src/ruvocal/static/chatui/manifest.json +54 -0
- package/src/ruvocal/static/chatui/omni-welcome.gif +0 -0
- package/src/ruvocal/static/chatui/omni-welcome.png +0 -0
- package/src/ruvocal/static/chatui/welcome.js +184 -0
- package/src/ruvocal/static/chatui/welcome.svg +1 -0
- package/src/ruvocal/static/huggingchat/apple-touch-icon.png +0 -0
- package/src/ruvocal/static/huggingchat/assistants-thumbnail.png +0 -0
- package/src/ruvocal/static/huggingchat/castle-example.jpg +0 -0
- package/src/ruvocal/static/huggingchat/favicon-dark.svg +4 -0
- package/src/ruvocal/static/huggingchat/favicon-dev.svg +4 -0
- package/src/ruvocal/static/huggingchat/favicon.ico +0 -0
- package/src/ruvocal/static/huggingchat/favicon.svg +4 -0
- package/src/ruvocal/static/huggingchat/fulltext-logo.svg +2 -0
- package/src/ruvocal/static/huggingchat/icon-128x128.png +0 -0
- package/src/ruvocal/static/huggingchat/icon-144x144.png +0 -0
- package/src/ruvocal/static/huggingchat/icon-192x192.png +0 -0
- package/src/ruvocal/static/huggingchat/icon-256x256.png +0 -0
- package/src/ruvocal/static/huggingchat/icon-36x36.png +0 -0
- package/src/ruvocal/static/huggingchat/icon-48x48.png +0 -0
- package/src/ruvocal/static/huggingchat/icon-512x512.png +0 -0
- package/src/ruvocal/static/huggingchat/icon-72x72.png +0 -0
- package/src/ruvocal/static/huggingchat/icon-96x96.png +0 -0
- package/src/ruvocal/static/huggingchat/icon.svg +4 -0
- package/src/ruvocal/static/huggingchat/logo.svg +4 -0
- package/src/ruvocal/static/huggingchat/manifest.json +54 -0
- package/src/ruvocal/static/huggingchat/omni-welcome.gif +0 -0
- package/src/ruvocal/static/huggingchat/routes.chat.json +226 -0
- package/src/ruvocal/static/huggingchat/thumbnail.png +0 -0
- package/src/ruvocal/static/huggingchat/tools-thumbnail.png +0 -0
- package/src/ruvocal/static/robots.txt +10 -0
- package/src/ruvocal/stub/@reflink/reflink/index.js +0 -0
- package/src/ruvocal/stub/@reflink/reflink/package.json +5 -0
- package/src/ruvocal/svelte.config.js +53 -0
- package/src/ruvocal/tailwind.config.cjs +30 -0
- package/src/ruvocal/tsconfig.json +19 -0
- package/src/ruvocal/vite.config.ts +87 -0
- package/src/scripts/deploy.sh +116 -0
- package/src/scripts/generate-config.js +245 -0
- package/src/scripts/generate-welcome.js +187 -0
- package/src/scripts/package-rvf.sh +116 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
3
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
|
+
import type { KeyValuePair } from "$lib/types/Tool";
|
|
5
|
+
import { config } from "$lib/server/config";
|
|
6
|
+
import { logger } from "$lib/server/logger";
|
|
7
|
+
import type { RequestHandler } from "./$types";
|
|
8
|
+
import { isValidUrl } from "$lib/server/urlSafety";
|
|
9
|
+
import { isStrictHfMcpLogin, hasNonEmptyToken, isExaMcpServer } from "$lib/server/mcp/hf";
|
|
10
|
+
|
|
11
|
+
interface HealthCheckRequest {
|
|
12
|
+
url: string;
|
|
13
|
+
headers?: KeyValuePair[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface HealthCheckResponse {
|
|
17
|
+
ready: boolean;
|
|
18
|
+
tools?: Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
inputSchema?: unknown;
|
|
22
|
+
}>;
|
|
23
|
+
error?: string;
|
|
24
|
+
authRequired?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const POST: RequestHandler = async ({ request, locals }) => {
|
|
28
|
+
let client: Client | undefined;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const body: HealthCheckRequest = await request.json();
|
|
32
|
+
const { url, headers } = body;
|
|
33
|
+
|
|
34
|
+
if (!url) {
|
|
35
|
+
return new Response(JSON.stringify({ ready: false, error: "URL is required" }), {
|
|
36
|
+
status: 400,
|
|
37
|
+
headers: { "Content-Type": "application/json" },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// URL validation handled above
|
|
42
|
+
|
|
43
|
+
if (!isValidUrl(url)) {
|
|
44
|
+
return new Response(
|
|
45
|
+
JSON.stringify({
|
|
46
|
+
ready: false,
|
|
47
|
+
error: "Invalid or unsafe URL (only HTTPS is supported)",
|
|
48
|
+
} as HealthCheckResponse),
|
|
49
|
+
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Inject Exa API key for mcp.exa.ai servers via URL param
|
|
54
|
+
let finalUrl = url;
|
|
55
|
+
try {
|
|
56
|
+
const exaApiKey = config.EXA_API_KEY;
|
|
57
|
+
if (isExaMcpServer(url) && hasNonEmptyToken(exaApiKey)) {
|
|
58
|
+
const urlObj = new URL(url);
|
|
59
|
+
if (!urlObj.searchParams.has("exaApiKey")) {
|
|
60
|
+
urlObj.searchParams.set("exaApiKey", exaApiKey);
|
|
61
|
+
finalUrl = urlObj.toString();
|
|
62
|
+
logger.debug({}, "[MCP Health] injected Exa API key");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
// best-effort injection
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const baseUrl = new URL(finalUrl);
|
|
70
|
+
|
|
71
|
+
// Minimal header handling
|
|
72
|
+
const headersRecord: Record<string, string> = headers?.length
|
|
73
|
+
? Object.fromEntries(headers.map((h) => [h.key, h.value]))
|
|
74
|
+
: {};
|
|
75
|
+
if (!headersRecord["Accept"]) {
|
|
76
|
+
headersRecord["Accept"] = "application/json, text/event-stream";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// If enabled, attach the logged-in user's HF token only for the official HF MCP endpoint
|
|
80
|
+
try {
|
|
81
|
+
const shouldForward = config.MCP_FORWARD_HF_USER_TOKEN === "true";
|
|
82
|
+
const userToken =
|
|
83
|
+
(locals as unknown as { hfAccessToken?: string } | undefined)?.hfAccessToken ??
|
|
84
|
+
(locals as unknown as { token?: string } | undefined)?.token;
|
|
85
|
+
const hasAuth = typeof headersRecord["Authorization"] === "string";
|
|
86
|
+
const isHfMcpTarget = isStrictHfMcpLogin(url);
|
|
87
|
+
if (shouldForward && !hasAuth && isHfMcpTarget && hasNonEmptyToken(userToken)) {
|
|
88
|
+
headersRecord["Authorization"] = `Bearer ${userToken}`;
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// best-effort overlay
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Add an abort timeout to outbound requests (align with fetch-url: 30s)
|
|
95
|
+
const controller = new AbortController();
|
|
96
|
+
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
|
97
|
+
const signal = controller.signal;
|
|
98
|
+
const requestInit: RequestInit = {
|
|
99
|
+
headers: headersRecord,
|
|
100
|
+
signal,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
let httpError: Error | undefined;
|
|
104
|
+
let lastError: Error | undefined;
|
|
105
|
+
|
|
106
|
+
// Try Streamable HTTP transport first
|
|
107
|
+
try {
|
|
108
|
+
logger.info({}, `[MCP Health] Trying HTTP transport for ${url}`);
|
|
109
|
+
client = new Client({
|
|
110
|
+
name: "chat-ui-health-check",
|
|
111
|
+
version: "1.0.0",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const transport = new StreamableHTTPClientTransport(baseUrl, { requestInit });
|
|
115
|
+
logger.info({}, `[MCP Health] Connecting to ${url}...`);
|
|
116
|
+
await client.connect(transport);
|
|
117
|
+
logger.info({}, `[MCP Health] Connected successfully via HTTP`);
|
|
118
|
+
|
|
119
|
+
// Connection successful, get tools
|
|
120
|
+
const toolsResponse = await client.listTools();
|
|
121
|
+
|
|
122
|
+
// Disconnect after getting tools
|
|
123
|
+
await client.close();
|
|
124
|
+
|
|
125
|
+
if (toolsResponse && toolsResponse.tools) {
|
|
126
|
+
const response: HealthCheckResponse = {
|
|
127
|
+
ready: true,
|
|
128
|
+
tools: toolsResponse.tools.map((tool) => ({
|
|
129
|
+
name: tool.name,
|
|
130
|
+
description: tool.description,
|
|
131
|
+
inputSchema: tool.inputSchema,
|
|
132
|
+
})),
|
|
133
|
+
authRequired: false,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const res = new Response(JSON.stringify(response), {
|
|
137
|
+
status: 200,
|
|
138
|
+
headers: { "Content-Type": "application/json" },
|
|
139
|
+
});
|
|
140
|
+
clearTimeout(timeoutId);
|
|
141
|
+
return res;
|
|
142
|
+
} else {
|
|
143
|
+
const res = new Response(
|
|
144
|
+
JSON.stringify({
|
|
145
|
+
ready: false,
|
|
146
|
+
error: "Connected but no tools available",
|
|
147
|
+
authRequired: false,
|
|
148
|
+
} as HealthCheckResponse),
|
|
149
|
+
{
|
|
150
|
+
status: 503,
|
|
151
|
+
headers: { "Content-Type": "application/json" },
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
clearTimeout(timeoutId);
|
|
155
|
+
return res;
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
httpError = error instanceof Error ? error : new Error(String(error));
|
|
159
|
+
lastError = httpError;
|
|
160
|
+
logger.warn(lastError.message, "Streamable HTTP failed, trying SSE transport...");
|
|
161
|
+
|
|
162
|
+
// Close failed client
|
|
163
|
+
try {
|
|
164
|
+
await client?.close();
|
|
165
|
+
} catch {
|
|
166
|
+
// Ignore
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Try SSE transport
|
|
170
|
+
try {
|
|
171
|
+
logger.info({}, `[MCP Health] Trying SSE transport for ${url}`);
|
|
172
|
+
client = new Client({
|
|
173
|
+
name: "chat-ui-health-check",
|
|
174
|
+
version: "1.0.0",
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const sseTransport = new SSEClientTransport(baseUrl, { requestInit });
|
|
178
|
+
logger.info({}, `[MCP Health] Connecting via SSE...`);
|
|
179
|
+
await client.connect(sseTransport);
|
|
180
|
+
logger.info({}, `[MCP Health] Connected successfully via SSE`);
|
|
181
|
+
|
|
182
|
+
// Connection successful, get tools
|
|
183
|
+
const toolsResponse = await client.listTools();
|
|
184
|
+
|
|
185
|
+
// Disconnect after getting tools
|
|
186
|
+
await client.close();
|
|
187
|
+
|
|
188
|
+
if (toolsResponse && toolsResponse.tools) {
|
|
189
|
+
const response: HealthCheckResponse = {
|
|
190
|
+
ready: true,
|
|
191
|
+
tools: toolsResponse.tools.map((tool) => ({
|
|
192
|
+
name: tool.name,
|
|
193
|
+
description: tool.description,
|
|
194
|
+
inputSchema: tool.inputSchema,
|
|
195
|
+
})),
|
|
196
|
+
authRequired: false,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const res = new Response(JSON.stringify(response), {
|
|
200
|
+
status: 200,
|
|
201
|
+
headers: { "Content-Type": "application/json" },
|
|
202
|
+
});
|
|
203
|
+
clearTimeout(timeoutId);
|
|
204
|
+
return res;
|
|
205
|
+
} else {
|
|
206
|
+
const res = new Response(
|
|
207
|
+
JSON.stringify({
|
|
208
|
+
ready: false,
|
|
209
|
+
error: "Connected but no tools available",
|
|
210
|
+
authRequired: false,
|
|
211
|
+
} as HealthCheckResponse),
|
|
212
|
+
{
|
|
213
|
+
status: 503,
|
|
214
|
+
headers: { "Content-Type": "application/json" },
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
clearTimeout(timeoutId);
|
|
218
|
+
return res;
|
|
219
|
+
}
|
|
220
|
+
} catch (sseError) {
|
|
221
|
+
lastError = sseError instanceof Error ? sseError : new Error(String(sseError));
|
|
222
|
+
// Prefer the HTTP error when both failed so UI shows the primary failure (e.g., HTTP 500) instead
|
|
223
|
+
// of the fallback SSE message.
|
|
224
|
+
if (httpError) {
|
|
225
|
+
lastError = new Error(
|
|
226
|
+
`HTTP transport failed: ${httpError.message}; SSE fallback failed: ${lastError.message}`,
|
|
227
|
+
{ cause: sseError instanceof Error ? sseError : undefined }
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
logger.error(lastError, "Both transports failed.");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Both transports failed
|
|
235
|
+
let errorMessage = lastError?.message || "Failed to connect to MCP server";
|
|
236
|
+
|
|
237
|
+
// Detect unauthorized to signal auth requirement
|
|
238
|
+
const lower = (errorMessage || "").toLowerCase();
|
|
239
|
+
const authRequired =
|
|
240
|
+
lower.includes("unauthorized") ||
|
|
241
|
+
lower.includes("forbidden") ||
|
|
242
|
+
lower.includes("401") ||
|
|
243
|
+
lower.includes("403");
|
|
244
|
+
|
|
245
|
+
// Provide more helpful error messages
|
|
246
|
+
if (authRequired) {
|
|
247
|
+
errorMessage =
|
|
248
|
+
"Authentication required. Provide appropriate Authorization headers in the server configuration.";
|
|
249
|
+
} else if (errorMessage.includes("not valid JSON")) {
|
|
250
|
+
errorMessage =
|
|
251
|
+
"Server returned invalid response. This might not be a valid MCP endpoint. MCP servers should respond to POST requests at /mcp with JSON-RPC messages.";
|
|
252
|
+
} else if (errorMessage.includes("fetch failed") || errorMessage.includes("ECONNREFUSED")) {
|
|
253
|
+
errorMessage = `Cannot connect to ${url}. Please verify the server is running and accessible.`;
|
|
254
|
+
} else if (errorMessage.includes("CORS")) {
|
|
255
|
+
errorMessage = `CORS error. The MCP server needs to allow requests from this origin.`;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const res = new Response(
|
|
259
|
+
JSON.stringify({
|
|
260
|
+
ready: false,
|
|
261
|
+
error: errorMessage,
|
|
262
|
+
authRequired,
|
|
263
|
+
} as HealthCheckResponse),
|
|
264
|
+
{
|
|
265
|
+
status: 503,
|
|
266
|
+
headers: { "Content-Type": "application/json" },
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
clearTimeout(timeoutId);
|
|
270
|
+
return res;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
logger.error(error, "MCP health check failed");
|
|
273
|
+
|
|
274
|
+
// Clean up client if it exists
|
|
275
|
+
try {
|
|
276
|
+
await client?.close();
|
|
277
|
+
} catch {
|
|
278
|
+
// Ignore
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const response: HealthCheckResponse = {
|
|
282
|
+
ready: false,
|
|
283
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const res = new Response(JSON.stringify(response), {
|
|
287
|
+
status: 503,
|
|
288
|
+
headers: { "Content-Type": "application/json" },
|
|
289
|
+
});
|
|
290
|
+
return res;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { MCPServer } from "$lib/types/Tool";
|
|
2
|
+
import { config } from "$lib/server/config";
|
|
3
|
+
|
|
4
|
+
export async function GET() {
|
|
5
|
+
// Parse MCP_SERVERS environment variable
|
|
6
|
+
const mcpServersEnv = config.MCP_SERVERS || "[]";
|
|
7
|
+
|
|
8
|
+
let servers: Array<{ name: string; url: string; headers?: Record<string, string> }> = [];
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
servers = JSON.parse(mcpServersEnv);
|
|
12
|
+
if (!Array.isArray(servers)) {
|
|
13
|
+
servers = [];
|
|
14
|
+
}
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error("Failed to parse MCP_SERVERS env variable:", error);
|
|
17
|
+
servers = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Convert internal server config to client MCPServer format
|
|
21
|
+
const mcpServers: MCPServer[] = servers.map((server) => ({
|
|
22
|
+
id: `base-${server.name}`, // Stable ID based on name
|
|
23
|
+
name: server.name,
|
|
24
|
+
url: server.url,
|
|
25
|
+
type: "base" as const,
|
|
26
|
+
// headers intentionally omitted
|
|
27
|
+
isLocked: false, // Base servers can be toggled by users
|
|
28
|
+
status: undefined, // Status determined client-side via health check
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
return Response.json(mcpServers);
|
|
32
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { models } from "$lib/server/models";
|
|
2
|
+
|
|
3
|
+
export async function GET() {
|
|
4
|
+
const res = models
|
|
5
|
+
.filter((m) => m.unlisted == false)
|
|
6
|
+
.map((model) => ({
|
|
7
|
+
id: model.id,
|
|
8
|
+
name: model.name,
|
|
9
|
+
websiteUrl: model.websiteUrl ?? "https://huggingface.co",
|
|
10
|
+
modelUrl: model.modelUrl ?? "https://huggingface.co",
|
|
11
|
+
// tokenizer removed in this build
|
|
12
|
+
datasetName: model.datasetName,
|
|
13
|
+
datasetUrl: model.datasetUrl,
|
|
14
|
+
displayName: model.displayName,
|
|
15
|
+
description: model.description ?? "",
|
|
16
|
+
logoUrl: model.logoUrl,
|
|
17
|
+
promptExamples: model.promptExamples ?? [],
|
|
18
|
+
preprompt: model.preprompt ?? "",
|
|
19
|
+
multimodal: model.multimodal ?? false,
|
|
20
|
+
supportsTools: (model as unknown as { supportsTools?: boolean }).supportsTools ?? false,
|
|
21
|
+
unlisted: model.unlisted ?? false,
|
|
22
|
+
hasInferenceAPI: model.hasInferenceAPI ?? false,
|
|
23
|
+
}));
|
|
24
|
+
return Response.json(res);
|
|
25
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { error, json } from "@sveltejs/kit";
|
|
2
|
+
import { config } from "$lib/server/config";
|
|
3
|
+
import { getApiToken } from "$lib/server/apiToken";
|
|
4
|
+
import { logger } from "$lib/server/logger";
|
|
5
|
+
|
|
6
|
+
const MAX_AUDIO_SIZE = 25 * 1024 * 1024; // 25MB
|
|
7
|
+
const TRANSCRIPTION_TIMEOUT = 60000; // 60 seconds
|
|
8
|
+
|
|
9
|
+
const ALLOWED_CONTENT_TYPES = [
|
|
10
|
+
"audio/webm",
|
|
11
|
+
"audio/ogg",
|
|
12
|
+
"audio/wav",
|
|
13
|
+
"audio/flac",
|
|
14
|
+
"audio/mpeg",
|
|
15
|
+
"audio/mp4",
|
|
16
|
+
"audio/x-wav",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export async function POST({ request, locals }) {
|
|
20
|
+
const transcriptionModel = config.get("TRANSCRIPTION_MODEL");
|
|
21
|
+
|
|
22
|
+
if (!transcriptionModel) {
|
|
23
|
+
throw error(503, "Transcription is not configured");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const token = getApiToken(locals);
|
|
27
|
+
|
|
28
|
+
if (!token) {
|
|
29
|
+
throw error(401, "Authentication required");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const rawContentType = request.headers.get("content-type") || "";
|
|
33
|
+
// Normalize content-type: Safari sends "audio/webm; codecs=opus" (with space)
|
|
34
|
+
// but HF API expects "audio/webm;codecs=opus" (no space)
|
|
35
|
+
const contentType = rawContentType.replace(/;\s+/g, ";");
|
|
36
|
+
const isAllowed = ALLOWED_CONTENT_TYPES.some((type) => contentType.includes(type));
|
|
37
|
+
|
|
38
|
+
if (!isAllowed) {
|
|
39
|
+
logger.warn({ contentType }, "Unsupported audio format for transcription");
|
|
40
|
+
throw error(400, `Unsupported audio format: ${contentType}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const contentLength = parseInt(request.headers.get("content-length") || "0");
|
|
44
|
+
if (contentLength > MAX_AUDIO_SIZE) {
|
|
45
|
+
throw error(413, "Audio file too large (max 25MB)");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const audioBuffer = await request.arrayBuffer();
|
|
50
|
+
|
|
51
|
+
if (audioBuffer.byteLength > MAX_AUDIO_SIZE) {
|
|
52
|
+
throw error(413, "Audio file too large (max 25MB)");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const baseUrl =
|
|
56
|
+
config.get("TRANSCRIPTION_BASE_URL") || "https://router.huggingface.co/hf-inference/models";
|
|
57
|
+
const apiUrl = `${baseUrl}/${transcriptionModel}`;
|
|
58
|
+
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const timeoutId = setTimeout(() => controller.abort(), TRANSCRIPTION_TIMEOUT);
|
|
61
|
+
|
|
62
|
+
const response = await fetch(apiUrl, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: `Bearer ${token}`,
|
|
66
|
+
"Content-Type": contentType,
|
|
67
|
+
// Bill to organization if configured
|
|
68
|
+
...(locals?.billingOrganization ? { "X-HF-Bill-To": locals.billingOrganization } : {}),
|
|
69
|
+
},
|
|
70
|
+
body: audioBuffer,
|
|
71
|
+
signal: controller.signal,
|
|
72
|
+
}).finally(() => clearTimeout(timeoutId));
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const errorText = await response.text();
|
|
76
|
+
logger.error(
|
|
77
|
+
{ status: response.status, error: errorText, model: transcriptionModel },
|
|
78
|
+
"Whisper API error"
|
|
79
|
+
);
|
|
80
|
+
throw error(response.status, `Transcription failed: ${errorText}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const result = await response.json();
|
|
84
|
+
|
|
85
|
+
// Whisper API returns { text: "transcribed text" }
|
|
86
|
+
// Filter out responses that only contain dots (e.g. "..." returned for silence/unclear audio)
|
|
87
|
+
const text = (result.text || "").trim();
|
|
88
|
+
const isOnlyDots = /^\.+$/.test(text);
|
|
89
|
+
return json({ text: isOnlyDots ? "" : text });
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
92
|
+
logger.error({ model: transcriptionModel }, "Transcription timeout");
|
|
93
|
+
throw error(504, "Transcription took too long. Please try a shorter recording.");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Re-throw SvelteKit errors
|
|
97
|
+
if (err && typeof err === "object" && "status" in err) {
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
logger.error(err, "Transcription error");
|
|
102
|
+
throw error(500, "Failed to transcribe audio");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function GET({ locals }) {
|
|
2
|
+
if (locals.user) {
|
|
3
|
+
const res = {
|
|
4
|
+
id: locals.user._id,
|
|
5
|
+
username: locals.user.username,
|
|
6
|
+
name: locals.user.name,
|
|
7
|
+
email: locals.user.email,
|
|
8
|
+
avatarUrl: locals.user.avatarUrl,
|
|
9
|
+
hfUserId: locals.user.hfUserId,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
return Response.json(res);
|
|
13
|
+
}
|
|
14
|
+
return Response.json({ message: "Must be signed in" }, { status: 401 });
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { adminTokenManager } from "$lib/server/adminToken";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
const validateTokenSchema = z.object({
|
|
5
|
+
token: z.string(),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const POST = async ({ request, locals }) => {
|
|
9
|
+
const { success, data } = validateTokenSchema.safeParse(await request.json());
|
|
10
|
+
|
|
11
|
+
if (!success) {
|
|
12
|
+
return new Response(JSON.stringify({ error: "Invalid token" }), { status: 400 });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (adminTokenManager.checkToken(data.token, locals.sessionId)) {
|
|
16
|
+
return new Response(JSON.stringify({ valid: true }));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return new Response(JSON.stringify({ valid: false }));
|
|
20
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { RequestHandler } from "@sveltejs/kit";
|
|
2
|
+
import { superjsonResponse } from "$lib/server/api/utils/superjsonResponse";
|
|
3
|
+
import { requireAuth } from "$lib/server/api/utils/requireAuth";
|
|
4
|
+
import { collections } from "$lib/server/database";
|
|
5
|
+
import { authCondition } from "$lib/server/auth";
|
|
6
|
+
import type { Conversation } from "$lib/types/Conversation";
|
|
7
|
+
import { CONV_NUM_PER_PAGE } from "$lib/constants/pagination";
|
|
8
|
+
|
|
9
|
+
export const GET: RequestHandler = async ({ locals, url }) => {
|
|
10
|
+
requireAuth(locals);
|
|
11
|
+
|
|
12
|
+
const pageSize = CONV_NUM_PER_PAGE;
|
|
13
|
+
const p = parseInt(url.searchParams.get("p") ?? "0") || 0;
|
|
14
|
+
|
|
15
|
+
const convs = await collections.conversations
|
|
16
|
+
.find(authCondition(locals))
|
|
17
|
+
.project<Pick<Conversation, "_id" | "title" | "updatedAt" | "model">>({
|
|
18
|
+
title: 1,
|
|
19
|
+
updatedAt: 1,
|
|
20
|
+
model: 1,
|
|
21
|
+
})
|
|
22
|
+
.sort({ updatedAt: -1 })
|
|
23
|
+
.skip(p * pageSize)
|
|
24
|
+
.limit(pageSize + 1)
|
|
25
|
+
.toArray();
|
|
26
|
+
|
|
27
|
+
const hasMore = convs.length > pageSize;
|
|
28
|
+
const res = (hasMore ? convs.slice(0, pageSize) : convs).map((conv) => ({
|
|
29
|
+
_id: conv._id,
|
|
30
|
+
id: conv._id, // legacy param iOS
|
|
31
|
+
title: conv.title,
|
|
32
|
+
updatedAt: conv.updatedAt,
|
|
33
|
+
model: conv.model,
|
|
34
|
+
modelId: conv.model, // legacy param iOS
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
return superjsonResponse({ conversations: res, hasMore });
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const DELETE: RequestHandler = async ({ locals }) => {
|
|
41
|
+
requireAuth(locals);
|
|
42
|
+
|
|
43
|
+
const res = await collections.conversations.deleteMany({
|
|
44
|
+
...authCondition(locals),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return superjsonResponse(res.deletedCount);
|
|
48
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { error, type RequestHandler } from "@sveltejs/kit";
|
|
2
|
+
import { superjsonResponse } from "$lib/server/api/utils/superjsonResponse";
|
|
3
|
+
import { requireAuth } from "$lib/server/api/utils/requireAuth";
|
|
4
|
+
import { resolveConversation } from "$lib/server/api/utils/resolveConversation";
|
|
5
|
+
import { collections } from "$lib/server/database";
|
|
6
|
+
import { authCondition } from "$lib/server/auth";
|
|
7
|
+
import { ObjectId } from "mongodb";
|
|
8
|
+
import { validModelIdSchema } from "$lib/server/models";
|
|
9
|
+
|
|
10
|
+
export const GET: RequestHandler = async ({ locals, params, url }) => {
|
|
11
|
+
requireAuth(locals);
|
|
12
|
+
|
|
13
|
+
const conversation = await resolveConversation(
|
|
14
|
+
params.id ?? "",
|
|
15
|
+
locals,
|
|
16
|
+
url.searchParams.get("fromShare")
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return superjsonResponse({
|
|
20
|
+
messages: conversation.messages,
|
|
21
|
+
title: conversation.title,
|
|
22
|
+
model: conversation.model,
|
|
23
|
+
preprompt: conversation.preprompt,
|
|
24
|
+
rootMessageId: conversation.rootMessageId,
|
|
25
|
+
id: conversation._id.toString(),
|
|
26
|
+
updatedAt: conversation.updatedAt,
|
|
27
|
+
modelId: conversation.model,
|
|
28
|
+
shared: conversation.shared,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const DELETE: RequestHandler = async ({ locals, params }) => {
|
|
33
|
+
requireAuth(locals);
|
|
34
|
+
|
|
35
|
+
const id = params.id ?? "";
|
|
36
|
+
if (!ObjectId.isValid(id)) {
|
|
37
|
+
error(400, "Invalid conversation ID");
|
|
38
|
+
}
|
|
39
|
+
const res = await collections.conversations.deleteOne({
|
|
40
|
+
_id: new ObjectId(id),
|
|
41
|
+
...authCondition(locals),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (res.deletedCount === 0) {
|
|
45
|
+
error(404, "Conversation not found");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return superjsonResponse({ success: true });
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const PATCH: RequestHandler = async ({ locals, params, request }) => {
|
|
52
|
+
requireAuth(locals);
|
|
53
|
+
|
|
54
|
+
const body = await request.json();
|
|
55
|
+
const title = body?.title as string | undefined;
|
|
56
|
+
const model = body?.model as string | undefined;
|
|
57
|
+
|
|
58
|
+
if (title !== undefined) {
|
|
59
|
+
if (typeof title !== "string" || title.length === 0 || title.length > 100) {
|
|
60
|
+
error(400, "Title must be a string between 1 and 100 characters");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (model !== undefined) {
|
|
65
|
+
if (!validModelIdSchema.safeParse(model).success) {
|
|
66
|
+
error(400, "Invalid model ID");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const updateValues = {
|
|
71
|
+
...(title !== undefined && {
|
|
72
|
+
title: title.replace(/<\/?think>/gi, "").trim(),
|
|
73
|
+
}),
|
|
74
|
+
...(model !== undefined && { model }),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const id = params.id ?? "";
|
|
78
|
+
if (!ObjectId.isValid(id)) {
|
|
79
|
+
error(400, "Invalid conversation ID");
|
|
80
|
+
}
|
|
81
|
+
const res = await collections.conversations.updateOne(
|
|
82
|
+
{
|
|
83
|
+
_id: new ObjectId(id),
|
|
84
|
+
...authCondition(locals),
|
|
85
|
+
},
|
|
86
|
+
{ $set: updateValues }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (typeof res.matchedCount === "number" ? res.matchedCount === 0 : res.modifiedCount === 0) {
|
|
90
|
+
error(404, "Conversation not found");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return superjsonResponse({ success: true });
|
|
94
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { error, type RequestHandler } from "@sveltejs/kit";
|
|
2
|
+
import { superjsonResponse } from "$lib/server/api/utils/superjsonResponse";
|
|
3
|
+
import { requireAuth } from "$lib/server/api/utils/requireAuth";
|
|
4
|
+
import { resolveConversation } from "$lib/server/api/utils/resolveConversation";
|
|
5
|
+
import { collections } from "$lib/server/database";
|
|
6
|
+
import { authCondition } from "$lib/server/auth";
|
|
7
|
+
import { ObjectId } from "mongodb";
|
|
8
|
+
|
|
9
|
+
export const DELETE: RequestHandler = async ({ locals, params }) => {
|
|
10
|
+
requireAuth(locals);
|
|
11
|
+
|
|
12
|
+
const id = params.id ?? "";
|
|
13
|
+
const messageId = params.messageId ?? "";
|
|
14
|
+
|
|
15
|
+
const conversation = await resolveConversation(id, locals);
|
|
16
|
+
|
|
17
|
+
if (!conversation.messages.map((m) => m.id).includes(messageId)) {
|
|
18
|
+
error(404, "Message not found");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const filteredMessages = conversation.messages
|
|
22
|
+
.filter(
|
|
23
|
+
(message) =>
|
|
24
|
+
!(message.id === messageId) && message.ancestors && !message.ancestors.includes(messageId)
|
|
25
|
+
)
|
|
26
|
+
.map((message) => {
|
|
27
|
+
if (message.children && message.children.includes(messageId)) {
|
|
28
|
+
message.children = message.children.filter((child) => child !== messageId);
|
|
29
|
+
}
|
|
30
|
+
return message;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const res = await collections.conversations.updateOne(
|
|
34
|
+
{ _id: new ObjectId(conversation._id), ...authCondition(locals) },
|
|
35
|
+
{ $set: { messages: filteredMessages } }
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (res.modifiedCount === 0) {
|
|
39
|
+
error(500, "Deleting message failed");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return superjsonResponse({ success: true });
|
|
43
|
+
};
|