diff --git a/src/langbot/pkg/plugin/connector.py b/src/langbot/pkg/plugin/connector.py index 888eff14..5a59f1ed 100644 --- a/src/langbot/pkg/plugin/connector.py +++ b/src/langbot/pkg/plugin/connector.py @@ -103,6 +103,16 @@ class PluginRuntimeConnector(ManagedRuntimeConnector): self.handler_task = asyncio.create_task(self.handler.run()) _ = await self.handler.ping() + # Push the configured marketplace (Space) URL to the runtime so it + # downloads plugins from the same Space LangBot is bound to, rather + # than relying on the runtime's own env/default. + space_url = self.ap.instance_config.data.get('space', {}).get('url', '').rstrip('/') + if space_url: + try: + await self.handler.set_runtime_config(cloud_service_url=space_url) + self.ap.logger.info(f'Pushed marketplace URL to plugin runtime: {space_url}') + except Exception as e: + self.ap.logger.warning(f'Failed to push runtime config: {e}') self.ap.logger.info('Connected to plugin runtime.') await self.handler_task diff --git a/src/langbot/pkg/plugin/handler.py b/src/langbot/pkg/plugin/handler.py index 60922003..f5a8511e 100644 --- a/src/langbot/pkg/plugin/handler.py +++ b/src/langbot/pkg/plugin/handler.py @@ -779,6 +779,16 @@ class RuntimeConnectionHandler(handler.Handler): timeout=10, ) + async def set_runtime_config(self, cloud_service_url: str) -> dict[str, Any]: + """Push runtime configuration (e.g. marketplace URL) to the runtime.""" + return await self.call_action( + LangBotToRuntimeAction.SET_RUNTIME_CONFIG, + { + 'cloud_service_url': cloud_service_url, + }, + timeout=10, + ) + async def install_plugin( self, install_source: str, install_info: dict[str, Any] ) -> typing.AsyncGenerator[dict[str, Any], None]: diff --git a/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx b/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx index d14d2e0a..1f3efeea 100644 --- a/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx +++ b/web/src/app/home/plugins/components/plugin-market/PluginMarketComponent.tsx @@ -29,7 +29,10 @@ import PluginMarketCardComponent from './plugin-market-card/PluginMarketCardComp import { PluginMarketCardVO } from './plugin-market-card/PluginMarketCardVO'; import { RecommendationLists } from './RecommendationLists'; import type { RecommendationList } from './RecommendationLists'; -import { getCloudServiceClientSync } from '@/app/infra/http'; +import { + getCloudServiceClient, + getCloudServiceClientSync, +} from '@/app/infra/http'; import { useTranslation } from 'react-i18next'; import { PluginV4, PluginV4Status } from '@/app/infra/entities/plugin'; import { extractI18nObject } from '@/i18n/I18nProvider'; @@ -253,17 +256,23 @@ function MarketPageContent({ // 初始加载 useEffect(() => { - fetchPlugins(1, false, true); - fetchAvailableTags(); - fetchRecommendationLists(); + // Resolve the cloud service base URL (from system info) before any + // marketplace fetch — otherwise the sync client may still hold the default + // URL and hit space.langbot.app instead of the configured instance. + (async () => { + await getCloudServiceClient(); + fetchPlugins(1, false, true); + fetchAvailableTags(); + fetchRecommendationLists(); + })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 获取推荐列表(精选,混合插件/MCP/Skill) const fetchRecommendationLists = async () => { try { - const { lists } = - await getCloudServiceClientSync().getRecommendationLists(); + const client = await getCloudServiceClient(); + const { lists } = await client.getRecommendationLists(); setRecommendationLists(lists || []); } catch (error) { console.error('Failed to fetch recommendation lists:', error);