diff --git a/packages/utils/src/toolManifest.ts b/packages/utils/src/toolManifest.ts index a7f6aeba56..7b471092c0 100644 --- a/packages/utils/src/toolManifest.ts +++ b/packages/utils/src/toolManifest.ts @@ -102,26 +102,5 @@ export const getToolManifest = async ( throw new TypeError('manifestInvalid', { cause: parser.error }); } - // 4. if exist OpenAPI api, merge the OpenAPIs to api - if (parser.data.openapi) { - const openapiJson = await fetchJSON(parser.data.openapi, useProxy); - - // avoid https://github.com/lobehub/lobe-chat/issues/9059 - if (typeof window !== 'undefined') { - try { - const { OpenAPIConvertor } = await import('@lobehub/chat-plugin-sdk/openapi'); - - const convertor = new OpenAPIConvertor(openapiJson); - const openAPIs = await convertor.convertOpenAPIToPluginSchema(); - - data.api = [...data.api, ...openAPIs]; - - data.settings = await convertor.convertAuthToSettingsSchema(data.settings); - } catch (error) { - throw new TypeError('openAPIInvalid', { cause: error }); - } - } - } - return data; }; diff --git a/plugins/vite/nodeModuleStub.ts b/plugins/vite/nodeModuleStub.ts index aac0350fbc..f20cd54f52 100644 --- a/plugins/vite/nodeModuleStub.ts +++ b/plugins/vite/nodeModuleStub.ts @@ -5,13 +5,11 @@ import type { Plugin } from 'vite'; * * - `node:stream`: dynamically imported in azureai provider behind `typeof window === 'undefined'` * guard — dead code in browser but Rollup still resolves it. - * - `@lobehub/chat-plugin-sdk/openapi`: dynamically imported in toolManifest, pulls in - * @apidevtools/swagger-parser which depends on Node built-ins (util, path). * - `node-fetch`: dynamically imported by klavis SDK's getFetchFn behind a runtime * Node.js version check — dead code in browser since native fetch is available. */ export function viteNodeModuleStub(): Plugin { - const stubbedModules = new Set(['node:stream', 'node-fetch', '@lobehub/chat-plugin-sdk/openapi']); + const stubbedModules = new Set(['node:stream', 'node-fetch']); const VIRTUAL_PREFIX = '\0node-stub:'; return { diff --git a/src/services/__tests__/__snapshots__/tool.test.ts.snap b/src/services/__tests__/__snapshots__/tool.test.ts.snap index 5ee0b67cb1..b70ed9fd88 100644 --- a/src/services/__tests__/__snapshots__/tool.test.ts.snap +++ b/src/services/__tests__/__snapshots__/tool.test.ts.snap @@ -77,128 +77,3 @@ getWolframCloudResults guidelines: "version": "1", } `; - -exports[`ToolService > getToolManifest > support OpenAPI manifest > should get plugin manifest 1`] = ` -{ - "$schema": "../node_modules/@lobehub/chat-plugin-sdk/schema.json", - "api": [ - { - "description": "Read Course Segments", - "name": "read_course_segments_course_segments__get", - "parameters": { - "properties": {}, - "type": "object", - }, - }, - { - "description": "Read Problem Set Item", - "name": "read_problem_set_item_problem_set__problem_set_id___question_number__get", - "parameters": { - "properties": { - "problem_set_id": { - "title": "Problem Set Id", - "type": "integer", - }, - "question_number": { - "title": "Question Number", - "type": "integer", - }, - }, - "required": [ - "problem_set_id", - "question_number", - ], - "type": "object", - }, - }, - { - "description": "Read Random Problem Set Items", - "name": "read_random_problem_set_items_problem_set_random__problem_set_id___n_items__get", - "parameters": { - "properties": { - "n_items": { - "title": "N Items", - "type": "integer", - }, - "problem_set_id": { - "title": "Problem Set Id", - "type": "integer", - }, - }, - "required": [ - "problem_set_id", - "n_items", - ], - "type": "object", - }, - }, - { - "description": "Read Range Of Problem Set Items", - "name": "read_range_of_problem_set_items_problem_set_range__problem_set_id___start___end__get", - "parameters": { - "properties": { - "end": { - "title": "End", - "type": "integer", - }, - "problem_set_id": { - "title": "Problem Set Id", - "type": "integer", - }, - "start": { - "title": "Start", - "type": "integer", - }, - }, - "required": [ - "problem_set_id", - "start", - "end", - ], - "type": "object", - }, - }, - { - "description": "Read User Id", - "name": "read_user_id_user__get", - "parameters": { - "properties": {}, - "type": "object", - }, - }, - ], - "author": "LobeHub", - "createAt": "2023-08-12", - "homepage": "https://github.com/lobehub/chat-plugin-realtime-weather", - "identifier": "realtime-weather", - "meta": { - "avatar": "🌈", - "description": "Get realtime weather information", - "tags": [ - "weather", - "realtime", - ], - "title": "Realtime Weather", - }, - "openapi": "http://fake-url.com/openapiUrl.json", - "settings": { - "properties": { - "HTTPBearer": { - "description": "HTTPBearer Bearer token", - "format": "password", - "title": "HTTPBearer", - "type": "string", - }, - }, - "required": [ - "HTTPBearer", - ], - "type": "object", - }, - "ui": { - "height": 310, - "url": "https://realtime-weather.chat-plugin.lobehub.com/iframe", - }, - "version": "1", -} -`; diff --git a/src/services/__tests__/tool.test.ts b/src/services/__tests__/tool.test.ts index d7bb96c74a..2962b1e9d0 100644 --- a/src/services/__tests__/tool.test.ts +++ b/src/services/__tests__/tool.test.ts @@ -1,7 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { toolService } from '../tool'; -import openAPIV3 from './openai/OpenAPI_V3.json'; import OpenAIPlugin from './openai/plugin.json'; // Mocking modules and functions @@ -136,85 +135,6 @@ describe('ToolService', () => { expect(e).toEqual(new TypeError('fetchError')); } }); - - describe('support OpenAPI manifest', () => { - it('should get plugin manifest', async () => { - const manifestUrl = 'http://fake-url.com/manifest.json'; - const openapiUrl = 'http://fake-url.com/openapiUrl.json'; - - const fakeManifest = { - $schema: '../node_modules/@lobehub/chat-plugin-sdk/schema.json', - api: [], - openapi: openapiUrl, - author: 'LobeHub', - createAt: '2023-08-12', - homepage: 'https://github.com/lobehub/chat-plugin-realtime-weather', - identifier: 'realtime-weather', - meta: { - avatar: '🌈', - tags: ['weather', 'realtime'], - title: 'Realtime Weather', - description: 'Get realtime weather information', - }, - ui: { - url: 'https://realtime-weather.chat-plugin.lobehub.com/iframe', - height: 310, - }, - version: '1', - }; - - global.fetch = vi.fn((url) => - Promise.resolve({ - ok: true, - headers: new Headers({ 'content-type': 'application/json' }), - json: () => Promise.resolve(url === openapiUrl ? openAPIV3 : fakeManifest), - }), - ) as any; - - const manifest = await toolService.getToolManifest(manifestUrl); - - expect(manifest).toMatchSnapshot(); - }); - - it('should return error on openAPIInvalid', async () => { - const openapiUrl = 'http://fake-url.com/openapiUrl.json'; - const manifestUrl = 'http://fake-url.com/manifest.json'; - const fakeManifest = { - $schema: '../node_modules/@lobehub/chat-plugin-sdk/schema.json', - api: [], - openapi: openapiUrl, - author: 'LobeHub', - createAt: '2023-08-12', - homepage: 'https://github.com/lobehub/chat-plugin-realtime-weather', - identifier: 'realtime-weather', - meta: { - avatar: '🌈', - tags: ['weather', 'realtime'], - title: 'Realtime Weather', - description: 'Get realtime weather information', - }, - ui: { - url: 'https://realtime-weather.chat-plugin.lobehub.com/iframe', - height: 310, - }, - version: '1', - }; - - global.fetch = vi.fn((url) => - Promise.resolve({ - ok: true, - headers: new Headers({ 'content-type': 'application/json' }), - json: () => Promise.resolve(url === openapiUrl ? [] : fakeManifest), - }), - ) as any; - - try { - await toolService.getToolManifest(manifestUrl); - } catch (e) { - expect(e).toEqual(new TypeError('openAPIInvalid')); - } - }); - }); }); it('can parse the OpenAI plugin', async () => { diff --git a/src/store/tool/slices/oldStore/action.test.ts b/src/store/tool/slices/oldStore/action.test.ts index 3d2e967037..07833a2881 100644 --- a/src/store/tool/slices/oldStore/action.test.ts +++ b/src/store/tool/slices/oldStore/action.test.ts @@ -161,137 +161,28 @@ describe('useToolStore:pluginStore', () => { }); describe('installPlugin', () => { - it('should install a plugin with valid manifest', async () => { - const pluginIdentifier = 'plugin1'; - - const originalUpdateInstallLoadingState = useToolStore.getState().updateInstallLoadingState; - const updateInstallLoadingStateMock = vi.fn(); - - act(() => { - useToolStore.setState({ - updateInstallLoadingState: updateInstallLoadingStateMock, - }); - }); - - const pluginManifestMock = { - $schema: '../node_modules/@lobehub/chat-plugin-sdk/schema.json', - api: [ - { - url: 'https://realtime-weather.chat-plugin.lobehub.com/api/v1', - name: 'fetchCurrentWeather', - description: '获取当前天气情况', - parameters: { - properties: { - city: { - description: '城市名称', - type: 'string', - }, - }, - required: ['city'], - type: 'object', - }, - }, - ], - author: 'LobeHub', - createAt: '2023-08-12', - homepage: 'https://github.com/lobehub/chat-plugin-realtime-weather', - identifier: 'realtime-weather', - meta: { - avatar: '🌈', - tags: ['weather', 'realtime'], - title: 'Realtime Weather', - description: 'Get realtime weather information', - }, - ui: { - url: 'https://realtime-weather.chat-plugin.lobehub.com/iframe', - height: 310, - }, - version: '1', - }; - (toolService.getToolManifest as Mock).mockResolvedValue(pluginManifestMock); - - await act(async () => { - await useToolStore.getState().installPlugin(pluginIdentifier); - }); - - // Then - expect(toolService.getToolManifest).toHaveBeenCalled(); - expect(notification.error).not.toHaveBeenCalled(); - expect(updateInstallLoadingStateMock).toHaveBeenCalledTimes(2); - expect(pluginService.installPlugin).toHaveBeenCalledWith({ - identifier: 'plugin1', - type: 'plugin', - manifest: pluginManifestMock, - }); - - act(() => { - useToolStore.setState({ - updateInstallLoadingState: originalUpdateInstallLoadingState, - }); - }); - }); - - it('should throw error with no error', async () => { - // Given - - const error = new TypeError('noManifest'); - - // Mock necessary modules and functions - (toolService.getToolManifest as Mock).mockRejectedValue(error); - - useToolStore.setState({ - oldPluginItems: [ - { - identifier: 'plugin1', - title: 'plugin1', - avatar: '🍏', - } as DiscoverPluginItem, - ], - }); - + it('should be deprecated and do nothing', async () => { + // Old plugin system has been deprecated await act(async () => { await useToolStore.getState().installPlugin('plugin1'); }); - expect(notification.error).toHaveBeenCalledWith({ - description: 'error.noManifest', - message: 'error.installError', - }); + // Should not call any service + expect(toolService.getToolManifest).not.toHaveBeenCalled(); + expect(pluginService.installPlugin).not.toHaveBeenCalled(); + expect(notification.error).not.toHaveBeenCalled(); }); }); describe('installPlugins', () => { - it('should install multiple plugins', async () => { - // Given - act(() => { - useToolStore.setState({ - oldPluginItems: [ - { - identifier: 'plugin1', - title: 'plugin1', - avatar: '🍏', - manifest: 'https://abc.com/manifest.json', - } as DiscoverPluginItem, - { - identifier: 'plugin2', - title: 'plugin2', - avatar: '🍏', - manifest: 'https://abc.com/manifest.json', - } as DiscoverPluginItem, - ], - }); - }); - - const plugins = ['plugin1', 'plugin2']; - - (toolService.getToolManifest as Mock).mockResolvedValue(pluginManifestMock); - - // When + it('should be deprecated and do nothing', async () => { + // Old plugin system has been deprecated await act(async () => { - await useToolStore.getState().installPlugins(plugins); + await useToolStore.getState().installPlugins(['plugin1', 'plugin2']); }); - expect(pluginService.installPlugin).toHaveBeenCalledTimes(2); + // Should not call any service + expect(pluginService.installPlugin).not.toHaveBeenCalled(); }); }); diff --git a/src/store/tool/slices/oldStore/action.ts b/src/store/tool/slices/oldStore/action.ts index f33fd5e589..1e2b5fedfd 100644 --- a/src/store/tool/slices/oldStore/action.ts +++ b/src/store/tool/slices/oldStore/action.ts @@ -1,29 +1,23 @@ import { type LobeTool } from '@lobechat/types'; import { uniqBy } from 'es-toolkit/compat'; -import { t } from 'i18next'; import { produce } from 'immer'; import { type SWRResponse } from 'swr'; import useSWR from 'swr'; -import { notification } from '@/components/AntdStaticMethods'; import { mutate } from '@/libs/swr'; import { pluginService } from '@/services/plugin'; import { toolService } from '@/services/tool'; import { globalHelpers } from '@/store/global/helpers'; -import { pluginStoreSelectors } from '@/store/tool/selectors'; import { type StoreSetter } from '@/store/types'; import { type DiscoverPluginItem, type PluginListResponse, type PluginQueryParams, } from '@/types/discover'; -import { type PluginInstallError } from '@/types/tool/plugin'; -import { sleep } from '@/utils/sleep'; import { setNamespace } from '@/utils/storeDebug'; import { type ToolStore } from '../../store'; import { type PluginInstallProgress, type PluginStoreState } from './initialState'; -import { PluginInstallStep } from './initialState'; const n = setNamespace('pluginStore'); @@ -44,107 +38,21 @@ export class PluginStoreActionImpl { } installOldPlugin = async ( - name: string, - type: 'plugin' | 'customPlugin' = 'plugin', + _name: string, + _type: 'plugin' | 'customPlugin' = 'plugin', ): Promise => { - const plugin = pluginStoreSelectors.getPluginById(name)(this.#get()); - if (!plugin) return; - - const { updateInstallLoadingState, refreshPlugins, updatePluginInstallProgress } = this.#get(); - - try { - // Start installation process - updateInstallLoadingState(name, true); - - // Step 1: Fetch plugin manifest - updatePluginInstallProgress(name, { - progress: 25, - step: PluginInstallStep.FETCHING_MANIFEST, - }); - - const data = await toolService.getToolManifest(plugin.manifest); - - // Step 2: Install plugin - updatePluginInstallProgress(name, { - progress: 60, - step: PluginInstallStep.INSTALLING_PLUGIN, - }); - - await pluginService.installPlugin({ identifier: plugin.identifier, manifest: data, type }); - - updatePluginInstallProgress(name, { - progress: 85, - step: PluginInstallStep.INSTALLING_PLUGIN, - }); - - await refreshPlugins(); - - // Step 4: Complete installation - updatePluginInstallProgress(name, { - progress: 100, - step: PluginInstallStep.COMPLETED, - }); - - // Briefly show completion status then clear progress - await sleep(1000); - - updatePluginInstallProgress(name, undefined); - updateInstallLoadingState(name, undefined); - } catch (error) { - console.error(error); - - const err = error as PluginInstallError; - - // Set error state - updatePluginInstallProgress(name, { - error: err.message, - progress: 0, - step: PluginInstallStep.ERROR, - }); - - updateInstallLoadingState(name, undefined); - - notification.error({ - description: t(`error.${err.message}`, { ns: 'plugin' }), - message: t('error.installError', { name: plugin.title, ns: 'plugin' }), - }); - } + // Old plugin system has been deprecated, skip installation silently }; installPlugin = async ( - name: string, - type: 'plugin' | 'customPlugin' = 'plugin', + _name: string, + _type: 'plugin' | 'customPlugin' = 'plugin', ): Promise => { - const plugin = pluginStoreSelectors.getPluginById(name)(this.#get()); - if (!plugin) return; - - const { updateInstallLoadingState, refreshPlugins } = this.#get(); - try { - updateInstallLoadingState(name, true); - const data = await toolService.getToolManifest(plugin.manifest); - - await pluginService.installPlugin({ identifier: plugin.identifier, manifest: data, type }); - await refreshPlugins(); - - updateInstallLoadingState(name, undefined); - } catch (error) { - console.error(error); - - const err = error as PluginInstallError; - - updateInstallLoadingState(name, undefined); - - notification.error({ - description: t(`error.${err.message}`, { ns: 'plugin' }), - message: t('error.installError', { name: plugin.title, ns: 'plugin' }), - }); - } + // Old plugin system has been deprecated, skip installation silently }; - installPlugins = async (plugins: string[]): Promise => { - const { installPlugin } = this.#get(); - - await Promise.all(plugins.map((identifier) => installPlugin(identifier))); + installPlugins = async (_plugins: string[]): Promise => { + // Old plugin system has been deprecated, skip installation silently }; loadMorePlugins = (): void => { diff --git a/src/store/tool/slices/plugin/action.test.ts b/src/store/tool/slices/plugin/action.test.ts index 9a6e07279f..98ee622feb 100644 --- a/src/store/tool/slices/plugin/action.test.ts +++ b/src/store/tool/slices/plugin/action.test.ts @@ -3,7 +3,6 @@ import { act, renderHook } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { pluginService } from '@/services/plugin'; -import { type DiscoverPluginItem } from '@/types/discover'; import { merge } from '@/utils/merge'; import { useToolStore } from '../../store'; @@ -22,24 +21,8 @@ beforeEach(() => { describe('useToolStore:plugin', () => { describe('checkPluginsIsInstalled', () => { - it('should not perform any operations if the plugin list is empty', async () => { - const installPluginsMock = vi.fn(); - useToolStore.setState({ - loadPluginStore: vi.fn(), - installPlugins: installPluginsMock, - }); - - const { result } = renderHook(() => useToolStore()); - - await act(async () => { - await result.current.checkPluginsIsInstalled([]); - }); - - expect(installPluginsMock).not.toHaveBeenCalled(); - }); - - it('should load the plugin store and install plugins if necessary', async () => { - const plugins = ['plugin1', 'plugin2']; + it('should be deprecated and do nothing', async () => { + // Old plugin system has been deprecated const loadPluginStoreMock = vi.fn(); const installPluginsMock = vi.fn(); useToolStore.setState({ @@ -50,32 +33,12 @@ describe('useToolStore:plugin', () => { const { result } = renderHook(() => useToolStore()); await act(async () => { - await result.current.checkPluginsIsInstalled(plugins); - }); - - expect(loadPluginStoreMock).toHaveBeenCalled(); - expect(installPluginsMock).toHaveBeenCalledWith(plugins); - }); - - it('should not load the plugin store and install plugins', async () => { - const plugins = ['plugin1', 'plugin2']; - const loadPluginStoreMock = vi.fn(); - const installPluginsMock = vi.fn(); - useToolStore.setState({ - loadPluginStore: loadPluginStoreMock, - installPlugins: installPluginsMock, - installedPlugins: [{ identifier: 'abc' }] as LobeTool[], - oldPluginItems: [{ identifier: 'abc' }] as DiscoverPluginItem[], - }); - - const { result } = renderHook(() => useToolStore()); - - await act(async () => { - await result.current.checkPluginsIsInstalled(plugins); + await result.current.checkPluginsIsInstalled(['plugin1', 'plugin2']); }); + // Should not call any methods since old plugin system is deprecated expect(loadPluginStoreMock).not.toHaveBeenCalled(); - expect(installPluginsMock).toHaveBeenCalledWith(plugins); + expect(installPluginsMock).not.toHaveBeenCalled(); }); }); diff --git a/src/store/tool/slices/plugin/action.ts b/src/store/tool/slices/plugin/action.ts index a531485560..be4f8d0ebb 100644 --- a/src/store/tool/slices/plugin/action.ts +++ b/src/store/tool/slices/plugin/action.ts @@ -8,7 +8,6 @@ import { type StoreSetter } from '@/store/types'; import { merge } from '@/utils/merge'; import { type ToolStore } from '../../store'; -import { pluginStoreSelectors } from '../oldStore/selectors'; import { pluginSelectors } from './selectors'; /** @@ -29,19 +28,8 @@ export class PluginActionImpl { this.#get = get; } - checkPluginsIsInstalled = async (plugins: string[]): Promise => { - // if there is no plugins, just skip. - if (plugins.length === 0) return; - - const { loadPluginStore, installPlugins } = this.#get(); - - // check if the store is empty - // if it is, we need to load the plugin store - if (pluginStoreSelectors.onlinePluginStore(this.#get()).length === 0) { - await loadPluginStore(); - } - - await installPlugins(plugins); + checkPluginsIsInstalled = async (_plugins: string[]): Promise => { + // Old plugin system has been deprecated, skip auto-installation }; removeAllPlugins = async (): Promise => {