diff --git a/src/langbot/pkg/api/http/controller/groups/plugins.py b/src/langbot/pkg/api/http/controller/groups/plugins.py index e2979598..8ebd2f43 100644 --- a/src/langbot/pkg/api/http/controller/groups/plugins.py +++ b/src/langbot/pkg/api/http/controller/groups/plugins.py @@ -21,6 +21,22 @@ class PluginsRouterGroup(group.RouterGroup): return self.success(data={'plugins': plugins}) + @self.route('/debug-info', methods=['GET'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY) + async def _() -> str: + """Get plugin debug information including debug URL and key""" + debug_info = await self.ap.plugin_connector.get_debug_info() + + # Get debug URL from config + plugin_config = self.ap.instance_config.data.get('plugin', {}) + debug_url = plugin_config.get('display_plugin_debug_url', 'http://localhost:5401') + + return self.success( + data={ + 'debug_url': debug_url, + 'plugin_debug_key': debug_info.get('plugin_debug_key', ''), + } + ) + @self.route( '///upgrade', methods=['POST'], diff --git a/src/langbot/pkg/plugin/connector.py b/src/langbot/pkg/plugin/connector.py index 3e206b3e..1ae54375 100644 --- a/src/langbot/pkg/plugin/connector.py +++ b/src/langbot/pkg/plugin/connector.py @@ -385,6 +385,12 @@ class PluginRuntimeConnector: async def get_plugin_assets(self, plugin_author: str, plugin_name: str, filepath: str) -> dict[str, Any]: return await self.handler.get_plugin_assets(plugin_author, plugin_name, filepath) + async def get_debug_info(self) -> dict[str, Any]: + """Get debug information including debug key and WS URL""" + if not self.is_enable_plugin: + return {} + return await self.handler.get_debug_info() + async def emit_event( self, event: events.BaseEventModel, diff --git a/src/langbot/pkg/plugin/handler.py b/src/langbot/pkg/plugin/handler.py index 881f8c65..155721b3 100644 --- a/src/langbot/pkg/plugin/handler.py +++ b/src/langbot/pkg/plugin/handler.py @@ -758,3 +758,12 @@ class RuntimeConnectionHandler(handler.Handler): timeout=30, ) return result + + async def get_debug_info(self) -> dict[str, Any]: + """Get debug information including debug key and WS URL""" + result = await self.call_action( + LangBotToRuntimeAction.GET_DEBUG_INFO, + {}, + timeout=10, + ) + return result diff --git a/src/langbot/templates/config.yaml b/src/langbot/templates/config.yaml index 28c4d57b..2df38662 100644 --- a/src/langbot/templates/config.yaml +++ b/src/langbot/templates/config.yaml @@ -48,3 +48,4 @@ plugin: runtime_ws_url: 'ws://langbot_plugin_runtime:5400/control/ws' enable_marketplace: true cloud_service_url: 'https://space.langbot.app' + display_plugin_debug_url: 'http://localhost:5401' diff --git a/web/src/app/home/plugins/page.tsx b/web/src/app/home/plugins/page.tsx index e589f728..3444fa7f 100644 --- a/web/src/app/home/plugins/page.tsx +++ b/web/src/app/home/plugins/page.tsx @@ -24,6 +24,9 @@ import { Power, Github, ChevronLeft, + Code, + Copy, + Bug, } from 'lucide-react'; import { DropdownMenu, @@ -38,6 +41,11 @@ import { DialogTitle, DialogFooter, } from '@/components/ui/dialog'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; import { Input } from '@/components/ui/input'; import React, { useState, useRef, useCallback, useEffect } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; @@ -105,6 +113,11 @@ export default function PluginConfigPage() { ); const [isEditMode, setIsEditMode] = useState(false); const [refreshKey, setRefreshKey] = useState(0); + const [debugInfo, setDebugInfo] = useState<{ + debug_url: string; + plugin_debug_key: string; + } | null>(null); + const [debugPopoverOpen, setDebugPopoverOpen] = useState(false); useEffect(() => { const fetchPluginSystemStatus = async () => { @@ -374,6 +387,22 @@ export default function PluginConfigPage() { [uploadPluginFile, isPluginSystemReady, t], ); + const handleShowDebugInfo = async () => { + try { + const info = await httpClient.getPluginDebugInfo(); + setDebugInfo(info); + setDebugPopoverOpen(true); + } catch (error) { + console.error('Failed to fetch debug info:', error); + toast.error(t('plugins.failedToGetDebugInfo')); + } + }; + + const handleCopyDebugInfo = (text: string) => { + navigator.clipboard.writeText(text); + toast.success(t('plugins.copiedToClipboard')); + }; + const renderPluginDisabledState = () => (
@@ -466,7 +495,92 @@ export default function PluginConfigPage() { -
+
+ {activeTab === 'installed' && ( + + + + + +
+ {/* Header with icon and title */} +
+ +

+ {t('plugins.debugInfoTitle')} +

+
+ + {/* Debug URL row */} +
+ + + +
+ + {/* Debug Key row */} +
+
+ + + +
+ {!debugInfo?.plugin_debug_key && ( +

+ {t('plugins.debugKeyDisabled')} +

+ )} +
+
+
+
+ )}