'use client'; import MarketPage from '@/app/home/plugins/components/plugin-market/PluginMarketComponent'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Download } from 'lucide-react'; import React, { useState, useCallback } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; import { systemInfo } from '@/app/infra/http/HttpClient'; import { toast } from 'sonner'; import { useTranslation } from 'react-i18next'; import { PluginV4 } from '@/app/infra/entities/plugin'; import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext'; enum PluginInstallStatus { ASK_CONFIRM = 'ask_confirm', INSTALLING = 'installing', ERROR = 'error', } export default function MarketplacePage() { const { t } = useTranslation(); if (!systemInfo?.enable_marketplace) { return (

{t('plugins.marketplace')}

); } return ; } function MarketplaceContent() { const { t } = useTranslation(); const { refreshPlugins } = useSidebarData(); const [modalOpen, setModalOpen] = useState(false); const [installInfo, setInstallInfo] = useState>({}); const [pluginInstallStatus, setPluginInstallStatus] = useState(PluginInstallStatus.ASK_CONFIRM); const [installError, setInstallError] = useState(null); async function checkExtensionsLimit(): Promise { const maxExtensions = systemInfo.limitation?.max_extensions ?? -1; if (maxExtensions < 0) return true; try { const [pluginsResp, mcpResp] = await Promise.all([ httpClient.getPlugins(), httpClient.getMCPServers(), ]); const total = (pluginsResp.plugins?.length ?? 0) + (mcpResp.servers?.length ?? 0); if (total >= maxExtensions) { toast.error( t('limitation.maxExtensionsReached', { max: maxExtensions }), ); return false; } } catch { // If we can't check, let backend handle it } return true; } function watchTask(taskId: number) { let alreadySuccess = false; const interval = setInterval(() => { httpClient.getAsyncTask(taskId).then((resp) => { if (resp.runtime.done) { clearInterval(interval); if (resp.runtime.exception) { setInstallError(resp.runtime.exception); setPluginInstallStatus(PluginInstallStatus.ERROR); } else { if (!alreadySuccess) { toast.success(t('plugins.installSuccess')); alreadySuccess = true; } setModalOpen(false); refreshPlugins(); } } }); }, 1000); } const handleInstallPlugin = useCallback( async (plugin: PluginV4) => { if (!(await checkExtensionsLimit())) return; setInstallInfo({ plugin_author: plugin.author, plugin_name: plugin.name, plugin_version: plugin.latest_version, }); setPluginInstallStatus(PluginInstallStatus.ASK_CONFIRM); setInstallError(null); setModalOpen(true); }, [t], ); function handleModalConfirm() { setPluginInstallStatus(PluginInstallStatus.INSTALLING); httpClient .installPluginFromMarketplace( installInfo.plugin_author, installInfo.plugin_name, installInfo.plugin_version, ) .then((resp) => { const taskId = resp.task_id; watchTask(taskId); }) .catch((err) => { setInstallError(err.msg); setPluginInstallStatus(PluginInstallStatus.ERROR); }); } return ( <>
{ setModalOpen(open); if (!open) { setInstallError(null); } }} > {t('plugins.installPlugin')} {pluginInstallStatus === PluginInstallStatus.ASK_CONFIRM && (

{t('plugins.askConfirm', { name: installInfo.plugin_name, version: installInfo.plugin_version, })}

)} {pluginInstallStatus === PluginInstallStatus.INSTALLING && (

{t('plugins.installing')}

)} {pluginInstallStatus === PluginInstallStatus.ERROR && (

{t('plugins.installFailed')}

{installError}

)} {pluginInstallStatus === PluginInstallStatus.ASK_CONFIRM && ( <> )} {pluginInstallStatus === PluginInstallStatus.ERROR && ( )}
); }