@lobehub/chat 1.129.2 → 1.129.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.cursor/rules/project-introduce.mdc +4 -4
  2. package/.cursor/rules/react-component.mdc +2 -2
  3. package/.cursor/rules/typescript.mdc +57 -5
  4. package/.vscode/settings.json +3 -1
  5. package/AGENTS.md +2 -5
  6. package/CHANGELOG.md +50 -0
  7. package/changelog/v1.json +18 -0
  8. package/package.json +2 -2
  9. package/packages/agent-runtime/package.json +1 -1
  10. package/packages/context-engine/package.json +1 -1
  11. package/packages/database/package.json +1 -1
  12. package/packages/electron-server-ipc/package.json +1 -1
  13. package/packages/file-loaders/package.json +1 -1
  14. package/packages/model-bank/package.json +2 -2
  15. package/packages/model-runtime/package.json +1 -1
  16. package/packages/model-runtime/src/core/RouterRuntime/baseRuntimeMap.ts +2 -0
  17. package/packages/model-runtime/src/core/RouterRuntime/createRuntime.test.ts +89 -64
  18. package/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts +58 -40
  19. package/packages/model-runtime/src/providers/newapi/index.ts +17 -2
  20. package/packages/model-runtime/src/providers/qwen/createImage.test.ts +110 -0
  21. package/packages/model-runtime/src/providers/qwen/createImage.ts +100 -3
  22. package/packages/prompts/package.json +1 -1
  23. package/packages/utils/package.json +4 -2
  24. package/packages/utils/src/client/index.ts +2 -0
  25. package/packages/utils/src/client/sanitize.test.ts +108 -0
  26. package/packages/utils/src/client/sanitize.ts +33 -0
  27. package/packages/web-crawler/package.json +1 -1
  28. package/src/features/PluginStore/InstalledList/List/Item/Action.tsx +12 -2
  29. package/src/features/PluginStore/McpList/List/Action.tsx +12 -2
  30. package/src/features/PluginStore/PluginList/List/Action.tsx +12 -2
  31. package/src/features/Portal/Artifacts/Body/Renderer/SVG.tsx +7 -3
  32. package/src/server/modules/EdgeConfig/index.ts +3 -19
  33. package/src/server/modules/EdgeConfig/types.ts +9 -0
  34. /package/packages/utils/src/{clipboard.ts → client/clipboard.ts} +0 -0
@@ -7,6 +7,7 @@ import { Flexbox } from 'react-layout-kit';
7
7
 
8
8
  import PluginDetailModal from '@/features/PluginDetailModal';
9
9
  import { useAgentStore } from '@/store/agent';
10
+ import { agentSelectors } from '@/store/agent/selectors';
10
11
  import { useServerConfigStore } from '@/store/serverConfig';
11
12
  import { pluginHelpers, useToolStore } from '@/store/tool';
12
13
  import { pluginSelectors, pluginStoreSelectors } from '@/store/tool/selectors';
@@ -36,7 +37,10 @@ const Actions = memo<ActionsProps>(({ identifier, type, isMCP }) => {
36
37
  const { t } = useTranslation('plugin');
37
38
  const [open, setOpen] = useState(false);
38
39
  const plugin = useToolStore(pluginSelectors.getToolManifestById(identifier));
39
- const togglePlugin = useAgentStore((s) => s.togglePlugin);
40
+ const [togglePlugin, isPluginEnabledInAgent] = useAgentStore((s) => [
41
+ s.togglePlugin,
42
+ agentSelectors.currentAgentPlugins(s).includes(identifier),
43
+ ]);
40
44
  const { modal } = App.useApp();
41
45
  const [tab, setTab] = useState('info');
42
46
  const hasSettings = pluginHelpers.isSettingSchemaNonEmpty(plugin?.settings);
@@ -90,7 +94,13 @@ const Actions = memo<ActionsProps>(({ identifier, type, isMCP }) => {
90
94
  modal.confirm({
91
95
  centered: true,
92
96
  okButtonProps: { danger: true },
93
- onOk: async () => unInstallPlugin(identifier),
97
+ onOk: async () => {
98
+ // If plugin is enabled in current agent, disable it first
99
+ if (isPluginEnabledInAgent) {
100
+ await togglePlugin(identifier, false);
101
+ }
102
+ await unInstallPlugin(identifier);
103
+ },
94
104
  title: t('store.actions.confirmUninstall'),
95
105
  type: 'error',
96
106
  });
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
8
  import { useAgentStore } from '@/store/agent';
9
+ import { agentSelectors } from '@/store/agent/selectors';
9
10
  import { useToolStore } from '@/store/tool';
10
11
  import { mcpStoreSelectors, pluginSelectors } from '@/store/tool/selectors';
11
12
 
@@ -24,7 +25,10 @@ const Actions = memo<ActionsProps>(({ identifier }) => {
24
25
  ]);
25
26
 
26
27
  const { t } = useTranslation('plugin');
27
- const togglePlugin = useAgentStore((s) => s.togglePlugin);
28
+ const [togglePlugin, isPluginEnabledInAgent] = useAgentStore((s) => [
29
+ s.togglePlugin,
30
+ agentSelectors.currentAgentPlugins(s).includes(identifier),
31
+ ]);
28
32
  const { modal } = App.useApp();
29
33
 
30
34
  return (
@@ -42,7 +46,13 @@ const Actions = memo<ActionsProps>(({ identifier }) => {
42
46
  modal.confirm({
43
47
  centered: true,
44
48
  okButtonProps: { danger: true },
45
- onOk: async () => unInstallPlugin(identifier),
49
+ onOk: async () => {
50
+ // If plugin is enabled in current agent, disable it first
51
+ if (isPluginEnabledInAgent) {
52
+ await togglePlugin(identifier, false);
53
+ }
54
+ await unInstallPlugin(identifier);
55
+ },
46
56
  title: t('store.actions.confirmUninstall'),
47
57
  type: 'error',
48
58
  });
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
8
  import { useAgentStore } from '@/store/agent';
9
+ import { agentSelectors } from '@/store/agent/selectors';
9
10
  import { useToolStore } from '@/store/tool';
10
11
  import { pluginSelectors, pluginStoreSelectors } from '@/store/tool/selectors';
11
12
 
@@ -22,7 +23,10 @@ const Actions = memo<ActionsProps>(({ identifier }) => {
22
23
  ]);
23
24
 
24
25
  const { t } = useTranslation('plugin');
25
- const togglePlugin = useAgentStore((s) => s.togglePlugin);
26
+ const [togglePlugin, isPluginEnabledInAgent] = useAgentStore((s) => [
27
+ s.togglePlugin,
28
+ agentSelectors.currentAgentPlugins(s).includes(identifier),
29
+ ]);
26
30
  const { modal } = App.useApp();
27
31
 
28
32
  return (
@@ -40,7 +44,13 @@ const Actions = memo<ActionsProps>(({ identifier }) => {
40
44
  modal.confirm({
41
45
  centered: true,
42
46
  okButtonProps: { danger: true },
43
- onOk: async () => unInstallPlugin(identifier),
47
+ onOk: async () => {
48
+ // If plugin is enabled in current agent, disable it first
49
+ if (isPluginEnabledInAgent) {
50
+ await togglePlugin(identifier, false);
51
+ }
52
+ await unInstallPlugin(identifier);
53
+ },
44
54
  title: t('store.actions.confirmUninstall'),
45
55
  type: 'error',
46
56
  });
@@ -1,15 +1,16 @@
1
+ import { copyImageToClipboard, sanitizeSVGContent } from '@lobechat/utils/client';
1
2
  import { Button, Dropdown, Tooltip } from '@lobehub/ui';
2
3
  import { App, Space } from 'antd';
3
4
  import { css, cx } from 'antd-style';
4
5
  import { CopyIcon, DownloadIcon } from 'lucide-react';
5
6
  import { domToPng } from 'modern-screenshot';
7
+ import { useMemo } from 'react';
6
8
  import { useTranslation } from 'react-i18next';
7
9
  import { Center, Flexbox } from 'react-layout-kit';
8
10
 
9
11
  import { BRANDING_NAME } from '@/const/branding';
10
12
  import { useChatStore } from '@/store/chat';
11
13
  import { chatPortalSelectors } from '@/store/chat/selectors';
12
- import { copyImageToClipboard } from '@/utils/clipboard';
13
14
 
14
15
  const svgContainer = css`
15
16
  width: 100%;
@@ -36,6 +37,9 @@ const SVGRenderer = ({ content }: SVGRendererProps) => {
36
37
  const { t } = useTranslation('portal');
37
38
  const { message } = App.useApp();
38
39
 
40
+ // Sanitize SVG content to prevent XSS attacks
41
+ const sanitizedContent = useMemo(() => sanitizeSVGContent(content), [content]);
42
+
39
43
  const generatePng = async () => {
40
44
  return domToPng(document.querySelector(`#${DOM_ID}`) as HTMLDivElement, {
41
45
  features: {
@@ -50,7 +54,7 @@ const SVGRenderer = ({ content }: SVGRendererProps) => {
50
54
  let dataUrl = '';
51
55
  if (type === 'png') dataUrl = await generatePng();
52
56
  else if (type === 'svg') {
53
- const blob = new Blob([content], { type: 'image/svg+xml' });
57
+ const blob = new Blob([sanitizedContent], { type: 'image/svg+xml' });
54
58
 
55
59
  dataUrl = URL.createObjectURL(blob);
56
60
  }
@@ -73,7 +77,7 @@ const SVGRenderer = ({ content }: SVGRendererProps) => {
73
77
  >
74
78
  <Center
75
79
  className={cx(svgContainer)}
76
- dangerouslySetInnerHTML={{ __html: content }}
80
+ dangerouslySetInnerHTML={{ __html: sanitizedContent }}
77
81
  id={DOM_ID}
78
82
  />
79
83
  <Flexbox className={cx(actions)}>
@@ -2,16 +2,7 @@ import { EdgeConfigClient, createClient } from '@vercel/edge-config';
2
2
 
3
3
  import { appEnv } from '@/envs/app';
4
4
 
5
- const EdgeConfigKeys = {
6
- /**
7
- * Assistant whitelist
8
- */
9
- AssistantBlacklist: 'assistant_blacklist',
10
- /**
11
- * Assistant whitelist
12
- */
13
- AssistantWhitelist: 'assistant_whitelist',
14
- };
5
+ import { EdgeConfigData } from './types';
15
6
 
16
7
  export class EdgeConfig {
17
8
  get client(): EdgeConfigClient {
@@ -30,14 +21,7 @@ export class EdgeConfig {
30
21
 
31
22
  getAgentRestrictions = async () => {
32
23
  const { assistant_blacklist: blacklist, assistant_whitelist: whitelist } =
33
- await this.client.getAll([
34
- EdgeConfigKeys.AssistantWhitelist,
35
- EdgeConfigKeys.AssistantBlacklist,
36
- ]);
37
-
38
- return { blacklist, whitelist } as {
39
- blacklist: string[] | undefined;
40
- whitelist: string[] | undefined;
41
- };
24
+ await this.client.getAll<EdgeConfigData>(['assistant_whitelist', 'assistant_blacklist']);
25
+ return { blacklist, whitelist };
42
26
  };
43
27
  }
@@ -0,0 +1,9 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
2
+
3
+ /**
4
+ * EdgeConfig 完整配置类型
5
+ */
6
+ export interface EdgeConfigData {
7
+ assistant_blacklist?: string[];
8
+ assistant_whitelist?: string[];
9
+ }