From fffc862fe6dd69ccdec46c320fefa75fe3408838 Mon Sep 17 00:00:00 2001 From: WangCham <651122857@qq.com> Date: Sat, 9 May 2026 11:49:44 +0800 Subject: [PATCH] feat: refactor market --- src/langbot/pkg/plugin/connector.py | 48 +- web/src/app/home/add-extension/page.tsx | 239 +++++++ web/src/app/home/add-plugin/page.tsx | 586 ++++++++++++++++++ .../components/home-sidebar/HomeSidebar.tsx | 1 - .../home-sidebar/sidbarConfigList.tsx | 39 +- web/src/app/home/mcp/MCPDetailContent.tsx | 7 + .../PluginInstalledComponent.tsx | 8 +- web/src/app/home/plugins/page.tsx | 48 -- web/src/app/home/skills/page.tsx | 42 +- web/src/i18n/locales/en-US.ts | 8 +- web/src/i18n/locales/es-ES.ts | 3 + web/src/i18n/locales/ja-JP.ts | 3 + web/src/i18n/locales/ru-RU.ts | 3 + web/src/i18n/locales/th-TH.ts | 3 + web/src/i18n/locales/vi-VN.ts | 3 + web/src/i18n/locales/zh-Hans.ts | 10 +- web/src/i18n/locales/zh-Hant.ts | 64 ++ web/src/router.tsx | 22 + 18 files changed, 1021 insertions(+), 116 deletions(-) create mode 100644 web/src/app/home/add-extension/page.tsx create mode 100644 web/src/app/home/add-plugin/page.tsx diff --git a/src/langbot/pkg/plugin/connector.py b/src/langbot/pkg/plugin/connector.py index 4e9e8f8e..1008ec91 100644 --- a/src/langbot/pkg/plugin/connector.py +++ b/src/langbot/pkg/plugin/connector.py @@ -316,13 +316,17 @@ class PluginRuntimeConnector(ManagedRuntimeConnector): raise Exception(f'MCP {plugin_author}/{plugin_name} has no config') elif mcp_resp.status_code == 404: # Try skill endpoint - download ZIP and install - self.ap.logger.info(f'Installing skill from marketplace: {plugin_author}/{plugin_name}') + self.ap.logger.info(f'Trying skill endpoint for: {plugin_author}/{plugin_name}') if task_context: - task_context.set_current_action('installing skill from marketplace') + task_context.set_current_action('checking skill marketplace') # Get skill detail to find version skill_resp = await client.get(f'{space_url}/api/v1/marketplace/skills/{plugin_author}/{plugin_name}') if skill_resp.status_code == 200: + self.ap.logger.info(f'Installing skill from marketplace: {plugin_author}/{plugin_name}') + if task_context: + task_context.set_current_action('installing skill from marketplace') + # Download the skill ZIP (no version needed - uses latest) if task_context: task_context.set_current_action('downloading skill package') @@ -340,8 +344,46 @@ class PluginRuntimeConnector(ManagedRuntimeConnector): # Install skill from ZIP using skill service await self._install_skill_from_zip(file_bytes, f'{plugin_author}-{plugin_name}', task_context) return + elif skill_resp.status_code == 404: + # Try plugin endpoint - get versions and download + self.ap.logger.info(f'Trying plugin endpoint for: {plugin_author}/{plugin_name}') + if task_context: + task_context.set_current_action('checking plugin marketplace') + + # Get plugin versions to find latest + versions_resp = await client.get( + f'{space_url}/api/v1/marketplace/plugins/{plugin_author}/{plugin_name}/versions' + ) + if versions_resp.status_code == 200: + versions_data = versions_resp.json().get('data', {}).get('versions', []) + if versions_data: + latest_version = versions_data[0].get('version', '') + if latest_version: + self.ap.logger.info(f'Installing plugin from marketplace: {plugin_author}/{plugin_name} v{latest_version}') + if task_context: + task_context.set_current_action('downloading plugin package') + + download_resp = await client.get( + f'{space_url}/api/v1/marketplace/plugins/download/{plugin_author}/{plugin_name}/{latest_version}' + ) + if download_resp.status_code != 200: + raise Exception(f'Failed to download plugin {plugin_author}/{plugin_name}: {download_resp.status_code}') + + file_bytes = download_resp.content + self._extract_deps_metadata(file_bytes, task_context) + file_key = await self.handler.send_file(file_bytes, 'lbpkg') + install_info['plugin_file_key'] = file_key + self.ap.logger.info(f'Transfered file {file_key} to plugin runtime') + # Continue to install via runtime + else: + raise Exception(f'No version found for plugin {plugin_author}/{plugin_name}') + else: + raise Exception(f'Plugin {plugin_author}/{plugin_name} has no versions') + else: + raise Exception(f'Plugin {plugin_author}/{plugin_name} not found in marketplace') else: - raise Exception(f'Skill {plugin_author}/{plugin_name} not found in marketplace') + skill_resp.raise_for_status() + raise Exception(f'Failed to get skill {plugin_author}/{plugin_name}') else: mcp_resp.raise_for_status() raise Exception(f'Failed to get MCP {plugin_author}/{plugin_name}') diff --git a/web/src/app/home/add-extension/page.tsx b/web/src/app/home/add-extension/page.tsx new file mode 100644 index 00000000..d4d2e7bf --- /dev/null +++ b/web/src/app/home/add-extension/page.tsx @@ -0,0 +1,239 @@ +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 { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { Download, PlusIcon, ChevronDownIcon } from 'lucide-react'; +import React, { useState, useCallback, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { httpClient, 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'; +import { usePluginInstallTasks } from '@/app/home/plugins/components/plugin-install-task'; + +enum PluginInstallStatus { + ASK_CONFIRM = 'ask_confirm', + INSTALLING = 'installing', + ERROR = 'error', +} + +export default function AddExtensionPage() { + const { t } = useTranslation(); + const navigate = useNavigate(); + + if (!systemInfo?.enable_marketplace) { + return ( +
{t('plugins.marketplace')}
+{t('plugins.enterRepoUrl')}
++ {t('plugins.selectRelease')} +
+ ++ {t('plugins.loading')} +
+ )} ++ {t('plugins.selectAsset')} +
+ ++ {t('plugins.confirmInstall')} +
+ +{t('plugins.installing')}
+{t('plugins.installFailed')}
+{installError}
++ {t('plugins.dragToUpload')} +
++ {t('plugins.selectFileToUpload')} +
+