@lobehub/lobehub 2.0.0-next.263 → 2.0.0-next.265

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 (33) hide show
  1. package/.github/workflows/manual-build-desktop.yml +16 -37
  2. package/CHANGELOG.md +52 -0
  3. package/apps/desktop/native-deps.config.mjs +19 -3
  4. package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +13 -0
  5. package/apps/desktop/src/main/core/browser/Browser.ts +14 -0
  6. package/apps/desktop/src/main/core/browser/__tests__/Browser.test.ts +32 -0
  7. package/apps/desktop/src/main/utils/permissions.ts +86 -22
  8. package/changelog/v1.json +18 -0
  9. package/package.json +2 -2
  10. package/packages/database/src/models/__tests__/agent.test.ts +165 -4
  11. package/packages/database/src/models/agent.ts +46 -0
  12. package/packages/database/src/repositories/agentGroup/index.test.ts +498 -0
  13. package/packages/database/src/repositories/agentGroup/index.ts +150 -0
  14. package/packages/database/src/repositories/home/__tests__/index.test.ts +113 -1
  15. package/packages/database/src/repositories/home/index.ts +48 -67
  16. package/pnpm-workspace.yaml +1 -0
  17. package/src/app/[variants]/(main)/agent/features/Conversation/MainChatInput/index.tsx +2 -2
  18. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentGroupItem/index.tsx +2 -6
  19. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentGroupItem/useDropdownMenu.tsx +100 -0
  20. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +2 -4
  21. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/useDropdownMenu.tsx +149 -0
  22. package/src/app/[variants]/(main)/home/_layout/hooks/index.ts +0 -1
  23. package/src/app/[variants]/(main)/home/features/InputArea/index.tsx +1 -1
  24. package/src/features/ChatInput/InputEditor/index.tsx +1 -0
  25. package/src/features/EditorCanvas/DiffAllToolbar.tsx +1 -1
  26. package/src/server/routers/lambda/agent.ts +15 -0
  27. package/src/server/routers/lambda/agentGroup.ts +16 -0
  28. package/src/services/agent.ts +11 -0
  29. package/src/services/chatGroup/index.ts +11 -0
  30. package/src/store/home/slices/sidebarUI/action.test.ts +23 -22
  31. package/src/store/home/slices/sidebarUI/action.ts +37 -9
  32. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/Item/useDropdownMenu.tsx +0 -62
  33. package/src/app/[variants]/(main)/home/_layout/hooks/useSessionItemMenuItems.tsx +0 -238
@@ -44,28 +44,6 @@ env:
44
44
  BUN_VERSION: 1.2.23
45
45
 
46
46
  jobs:
47
- test:
48
- name: Code quality check
49
- runs-on: ubuntu-latest
50
- steps:
51
- - name: Checkout base
52
- uses: actions/checkout@v6
53
- with:
54
- fetch-depth: 0
55
-
56
- - name: Setup Node & Bun
57
- uses: ./.github/actions/setup-node-bun
58
- with:
59
- node-version: ${{ env.NODE_VERSION }}
60
- bun-version: ${{ env.BUN_VERSION }}
61
- package-manager-cache: 'false'
62
-
63
- - name: Install deps
64
- run: bun i
65
-
66
- - name: Lint
67
- run: bun run lint
68
-
69
47
  version:
70
48
  name: Determine version
71
49
  runs-on: ubuntu-latest
@@ -106,7 +84,7 @@ jobs:
106
84
  echo "🚦 Release Version: ${{ steps.set_version.outputs.version }}"
107
85
 
108
86
  build-macos:
109
- needs: [version, test]
87
+ needs: [version]
110
88
  name: Build Desktop App (macOS)
111
89
  if: inputs.build_macos
112
90
  runs-on: ${{ matrix.os }}
@@ -126,10 +104,10 @@ jobs:
126
104
 
127
105
  # node-linker=hoisted 模式将可以确保 asar 压缩可用
128
106
  - name: Install dependencies
129
- run: pnpm install --node-linker=hoisted
130
-
131
- - name: Install deps on Desktop
132
- run: npm run install-isolated --prefix=./apps/desktop
107
+ run: |
108
+ pnpm install --node-linker=hoisted &
109
+ npm run install-isolated --prefix=./apps/desktop &
110
+ wait
133
111
 
134
112
  - name: Set package version
135
113
  run: npm run workflow:set-desktop-version ${{ needs.version.outputs.version }} ${{ inputs.channel }}
@@ -187,7 +165,7 @@ jobs:
187
165
  retention-days: 5
188
166
 
189
167
  build-windows:
190
- needs: [version, test]
168
+ needs: [version]
191
169
  name: Build Desktop App (Windows)
192
170
  if: inputs.build_windows
193
171
  runs-on: windows-2025
@@ -203,10 +181,11 @@ jobs:
203
181
  package-manager-cache: 'false'
204
182
 
205
183
  - name: Install dependencies
206
- run: pnpm install --node-linker=hoisted
207
-
208
- - name: Install deps on Desktop
209
- run: npm run install-isolated --prefix=./apps/desktop
184
+ shell: pwsh
185
+ run: |
186
+ $job1 = Start-Job -ScriptBlock { pnpm install --node-linker=hoisted }
187
+ $job2 = Start-Job -ScriptBlock { npm run install-isolated --prefix=./apps/desktop }
188
+ $job1, $job2 | Wait-Job | Receive-Job
210
189
 
211
190
  - name: Set package version
212
191
  run: npm run workflow:set-desktop-version ${{ needs.version.outputs.version }} ${{ inputs.channel }}
@@ -240,7 +219,7 @@ jobs:
240
219
  retention-days: 5
241
220
 
242
221
  build-linux:
243
- needs: [version, test]
222
+ needs: [version]
244
223
  name: Build Desktop App (Linux)
245
224
  if: inputs.build_linux
246
225
  runs-on: ubuntu-latest
@@ -256,10 +235,10 @@ jobs:
256
235
  package-manager-cache: 'false'
257
236
 
258
237
  - name: Install dependencies
259
- run: pnpm install --node-linker=hoisted
260
-
261
- - name: Install deps on Desktop
262
- run: npm run install-isolated --prefix=./apps/desktop
238
+ run: |
239
+ pnpm install --node-linker=hoisted &
240
+ npm run install-isolated --prefix=./apps/desktop &
241
+ wait
263
242
 
264
243
  - name: Set package version
265
244
  run: npm run workflow:set-desktop-version ${{ needs.version.outputs.version }} ${{ inputs.channel }}
package/CHANGELOG.md CHANGED
@@ -2,6 +2,58 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.265](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.264...v2.0.0-next.265)
6
+
7
+ <sup>Released on **2026-01-11**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix duplicate agent and group, Fix Windows desktop build error with macOS native module, force plain text paste in ChatInput editor.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix duplicate agent and group, closes [#11411](https://github.com/lobehub/lobe-chat/issues/11411) ([bc8aea4](https://github.com/lobehub/lobe-chat/commit/bc8aea4))
21
+ - **misc**: Fix Windows desktop build error with macOS native module, closes [#11417](https://github.com/lobehub/lobe-chat/issues/11417) ([67a8114](https://github.com/lobehub/lobe-chat/commit/67a8114))
22
+ - **misc**: Force plain text paste in ChatInput editor, closes [#11414](https://github.com/lobehub/lobe-chat/issues/11414) ([70daf13](https://github.com/lobehub/lobe-chat/commit/70daf13))
23
+
24
+ </details>
25
+
26
+ <div align="right">
27
+
28
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
29
+
30
+ </div>
31
+
32
+ ## [Version 2.0.0-next.264](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.263...v2.0.0-next.264)
33
+
34
+ <sup>Released on **2026-01-11**</sup>
35
+
36
+ #### 🐛 Bug Fixes
37
+
38
+ - **misc**: Handle will-prevent-unload event to allow app quit.
39
+
40
+ <br/>
41
+
42
+ <details>
43
+ <summary><kbd>Improvements and Fixes</kbd></summary>
44
+
45
+ #### What's fixed
46
+
47
+ - **misc**: Handle will-prevent-unload event to allow app quit, closes [#11406](https://github.com/lobehub/lobe-chat/issues/11406) ([cbeb013](https://github.com/lobehub/lobe-chat/commit/cbeb013))
48
+
49
+ </details>
50
+
51
+ <div align="right">
52
+
53
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
54
+
55
+ </div>
56
+
5
57
  ## [Version 2.0.0-next.263](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.262...v2.0.0-next.263)
6
58
 
7
59
  <sup>Released on **2026-01-11**</sup>
@@ -8,19 +8,31 @@
8
8
  *
9
9
  * This module automatically resolves the full dependency tree.
10
10
  */
11
-
12
11
  import fs from 'node:fs';
12
+ import os from 'node:os';
13
13
  import path from 'node:path';
14
14
  import { fileURLToPath } from 'node:url';
15
15
 
16
16
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
17
 
18
+ /**
19
+ * Get the current target platform
20
+ * During build, electron-builder sets npm_config_platform
21
+ * Falls back to os.platform() for development
22
+ */
23
+ function getTargetPlatform() {
24
+ return process.env.npm_config_platform || os.platform();
25
+ }
26
+ const isDarwin = getTargetPlatform() === 'darwin';
18
27
  /**
19
28
  * List of native modules that need special handling
20
29
  * Only add the top-level native modules here - dependencies are resolved automatically
30
+ *
31
+ * Platform-specific modules are only included when building for their target platform
21
32
  */
22
33
  export const nativeModules = [
23
- 'node-mac-permissions',
34
+ // macOS-only native modules
35
+ ...(isDarwin ? ['node-mac-permissions'] : []),
24
36
  // Add more native modules here as needed
25
37
  // e.g., 'better-sqlite3', 'sharp', etc.
26
38
  ];
@@ -32,7 +44,11 @@ export const nativeModules = [
32
44
  * @param {string} nodeModulesPath - Path to node_modules directory
33
45
  * @returns {Set<string>} Set of all dependencies
34
46
  */
35
- function resolveDependencies(moduleName, visited = new Set(), nodeModulesPath = path.join(__dirname, 'node_modules')) {
47
+ function resolveDependencies(
48
+ moduleName,
49
+ visited = new Set(),
50
+ nodeModulesPath = path.join(__dirname, 'node_modules'),
51
+ ) {
36
52
  if (visited.has(moduleName)) {
37
53
  return visited;
38
54
  }
@@ -4,6 +4,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
4
  import type { App } from '@/core/App';
5
5
  import type { IpcContext } from '@/utils/ipc';
6
6
  import { IpcHandler } from '@/utils/ipc/base';
7
+ import {
8
+ __resetMacPermissionsModuleCache,
9
+ __setMacPermissionsModule,
10
+ } from '@/utils/permissions';
7
11
 
8
12
  import SystemController from '../SystemCtr';
9
13
 
@@ -131,6 +135,9 @@ describe('SystemController', () => {
131
135
  ipcHandlers.clear();
132
136
  ipcMainHandleMock.mockClear();
133
137
  (IpcHandler.getInstance() as any).registeredChannels?.clear();
138
+ // Reset and inject mock permissions module for testing
139
+ __resetMacPermissionsModuleCache();
140
+ __setMacPermissionsModule(permissionsMock as any);
134
141
  controller = new SystemController(mockApp);
135
142
  });
136
143
 
@@ -169,6 +176,8 @@ describe('SystemController', () => {
169
176
  it('should return true on non-macOS when requesting accessibility access', async () => {
170
177
  const { macOS } = await import('electron-is');
171
178
  vi.mocked(macOS).mockReturnValue(false);
179
+ // Clear the injected module to simulate non-macOS behavior
180
+ __setMacPermissionsModule(null);
172
181
 
173
182
  const result = await invokeIpc('system.requestAccessibilityAccess');
174
183
 
@@ -177,6 +186,7 @@ describe('SystemController', () => {
177
186
 
178
187
  // Reset
179
188
  vi.mocked(macOS).mockReturnValue(true);
189
+ __setMacPermissionsModule(permissionsMock as any);
180
190
  });
181
191
  });
182
192
 
@@ -226,6 +236,8 @@ describe('SystemController', () => {
226
236
  const { macOS } = await import('electron-is');
227
237
  const { shell } = await import('electron');
228
238
  vi.mocked(macOS).mockReturnValue(false);
239
+ // Clear the injected module to simulate non-macOS behavior
240
+ __setMacPermissionsModule(null);
229
241
 
230
242
  const result = await invokeIpc('system.requestMicrophoneAccess');
231
243
 
@@ -235,6 +247,7 @@ describe('SystemController', () => {
235
247
 
236
248
  // Reset
237
249
  vi.mocked(macOS).mockReturnValue(true);
250
+ __setMacPermissionsModule(permissionsMock as any);
238
251
  });
239
252
  });
240
253
 
@@ -184,6 +184,20 @@ export default class Browser {
184
184
  this.setupReadyToShowListener(browserWindow);
185
185
  this.setupCloseListener(browserWindow);
186
186
  this.setupFocusListener(browserWindow);
187
+ this.setupWillPreventUnloadListener(browserWindow);
188
+ }
189
+
190
+ private setupWillPreventUnloadListener(browserWindow: BrowserWindow): void {
191
+ logger.debug(`[${this.identifier}] Setting up 'will-prevent-unload' event listener.`);
192
+ browserWindow.webContents.on('will-prevent-unload', (event) => {
193
+ logger.debug(
194
+ `[${this.identifier}] 'will-prevent-unload' fired. isQuiting: ${this.app.isQuiting}`,
195
+ );
196
+ if (this.app.isQuiting) {
197
+ logger.info(`[${this.identifier}] App is quitting, ignoring beforeunload cancellation.`);
198
+ event.preventDefault();
199
+ }
200
+ });
187
201
  }
188
202
 
189
203
  private setupReadyToShowListener(browserWindow: BrowserWindow): void {
@@ -40,6 +40,7 @@ const { mockBrowserWindow, mockNativeTheme, mockIpcMain, mockScreen, MockBrowser
40
40
  onHeadersReceived: vi.fn(),
41
41
  },
42
42
  },
43
+ on: vi.fn(),
43
44
  },
44
45
  };
45
46
 
@@ -646,4 +647,35 @@ describe('Browser', () => {
646
647
  expect(mockBrowserWindow.setBackgroundColor).not.toHaveBeenCalled();
647
648
  });
648
649
  });
650
+
651
+ describe('will-prevent-unload event handling', () => {
652
+ let willPreventUnloadHandler: (e: any) => void;
653
+
654
+ beforeEach(() => {
655
+ // Get the will-prevent-unload handler registered during initialization
656
+ willPreventUnloadHandler = mockBrowserWindow.webContents.on.mock.calls.find(
657
+ (call) => call[0] === 'will-prevent-unload',
658
+ )?.[1];
659
+ });
660
+
661
+ it('should call preventDefault when app is quitting', () => {
662
+ (mockApp as any).isQuiting = true;
663
+ const mockEvent = { preventDefault: vi.fn() };
664
+
665
+ expect(willPreventUnloadHandler).toBeDefined();
666
+ willPreventUnloadHandler(mockEvent);
667
+
668
+ expect(mockEvent.preventDefault).toHaveBeenCalled();
669
+ });
670
+
671
+ it('should not call preventDefault when app is not quitting', () => {
672
+ (mockApp as any).isQuiting = false;
673
+ const mockEvent = { preventDefault: vi.fn() };
674
+
675
+ expect(willPreventUnloadHandler).toBeDefined();
676
+ willPreventUnloadHandler(mockEvent);
677
+
678
+ expect(mockEvent.preventDefault).not.toHaveBeenCalled();
679
+ });
680
+ });
649
681
  });
@@ -1,22 +1,80 @@
1
1
  /**
2
2
  * Unified macOS Permission Management using node-mac-permissions
3
3
  * @see https://github.com/codebytere/node-mac-permissions
4
+ *
5
+ * IMPORTANT: node-mac-permissions is a macOS-only native module.
6
+ * It must be dynamically imported to prevent loading errors on Windows/Linux.
4
7
  */
5
8
  import { shell } from 'electron';
6
9
  import { macOS } from 'electron-is';
7
- import {
8
- askForAccessibilityAccess,
9
- askForCameraAccess,
10
- askForFullDiskAccess,
11
- askForMicrophoneAccess,
12
- askForScreenCaptureAccess,
13
- getAuthStatus,
14
- type AuthType,
15
- type PermissionType,
16
- } from 'node-mac-permissions';
17
10
 
18
11
  import { createLogger } from './logger';
19
12
 
13
+ // Type definitions - use module types when available, fallback to local definition
14
+ // Note: We don't import the module statically, so we need local type definitions
15
+ type AuthType =
16
+ | 'accessibility'
17
+ | 'calendar'
18
+ | 'camera'
19
+ | 'contacts'
20
+ | 'full-disk-access'
21
+ | 'input-monitoring'
22
+ | 'location'
23
+ | 'microphone'
24
+ | 'reminders'
25
+ | 'screen'
26
+ | 'speech-recognition';
27
+
28
+ type PermissionType = 'authorized' | 'denied' | 'not determined' | 'restricted';
29
+
30
+ // Lazy-loaded module cache
31
+ let macPermissionsModule: typeof import('node-mac-permissions') | null = null;
32
+
33
+ // Test injection override (set via __setMacPermissionsModule for testing)
34
+ let testModuleOverride: typeof import('node-mac-permissions') | null = null;
35
+
36
+ /**
37
+ * Lazily load the node-mac-permissions module (macOS only)
38
+ * Returns null on non-macOS platforms
39
+ */
40
+ function getMacPermissionsModule(): typeof import('node-mac-permissions') | null {
41
+ // Allow test injection to override the module
42
+ if (testModuleOverride) {
43
+ return testModuleOverride;
44
+ }
45
+
46
+ if (!macOS()) {
47
+ return null;
48
+ }
49
+
50
+ if (!macPermissionsModule) {
51
+ // Dynamic require to prevent module loading on non-macOS platforms
52
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
53
+ macPermissionsModule = require('node-mac-permissions');
54
+ }
55
+
56
+ return macPermissionsModule;
57
+ }
58
+
59
+ /**
60
+ * Reset the module cache (for testing purposes)
61
+ * @internal
62
+ */
63
+ export function __resetMacPermissionsModuleCache(): void {
64
+ macPermissionsModule = null;
65
+ testModuleOverride = null;
66
+ }
67
+
68
+ /**
69
+ * Set the mac permissions module (for testing purposes)
70
+ * @internal
71
+ */
72
+ export function __setMacPermissionsModule(
73
+ module: typeof import('node-mac-permissions') | null,
74
+ ): void {
75
+ testModuleOverride = module;
76
+ }
77
+
20
78
  const logger = createLogger('utils:permissions');
21
79
 
22
80
  /**
@@ -42,12 +100,13 @@ function normalizeStatus(status: PermissionType | 'not determined'): PermissionS
42
100
  * Get the authorization status for a specific permission type
43
101
  */
44
102
  export function getPermissionStatus(type: AuthType): PermissionStatus {
45
- if (!macOS()) {
103
+ const macPermissions = getMacPermissionsModule();
104
+ if (!macPermissions) {
46
105
  logger.debug(`[Permission] Not macOS, returning granted for ${type}`);
47
106
  return 'granted';
48
107
  }
49
108
 
50
- const status = getAuthStatus(type);
109
+ const status = macPermissions.getAuthStatus(type);
51
110
  const normalized = normalizeStatus(status);
52
111
  logger.info(`[Permission] ${type} status: ${normalized}`);
53
112
  return normalized;
@@ -65,13 +124,14 @@ export function getAccessibilityStatus(): PermissionStatus {
65
124
  * Opens System Preferences to the Accessibility pane
66
125
  */
67
126
  export function requestAccessibilityAccess(): boolean {
68
- if (!macOS()) {
127
+ const macPermissions = getMacPermissionsModule();
128
+ if (!macPermissions) {
69
129
  logger.info('[Accessibility] Not macOS, returning true');
70
130
  return true;
71
131
  }
72
132
 
73
133
  logger.info('[Accessibility] Requesting accessibility access...');
74
- askForAccessibilityAccess();
134
+ macPermissions.askForAccessibilityAccess();
75
135
 
76
136
  // Check the status after requesting
77
137
  const status = getPermissionStatus('accessibility');
@@ -90,7 +150,8 @@ export function getMicrophoneStatus(): PermissionStatus {
90
150
  * Shows the system permission dialog if not determined
91
151
  */
92
152
  export async function requestMicrophoneAccess(): Promise<boolean> {
93
- if (!macOS()) {
153
+ const macPermissions = getMacPermissionsModule();
154
+ if (!macPermissions) {
94
155
  logger.info('[Microphone] Not macOS, returning true');
95
156
  return true;
96
157
  }
@@ -106,7 +167,7 @@ export async function requestMicrophoneAccess(): Promise<boolean> {
106
167
  if (currentStatus === 'not-determined') {
107
168
  logger.info('[Microphone] Status is not-determined, requesting access...');
108
169
  try {
109
- const result = await askForMicrophoneAccess();
170
+ const result = await macPermissions.askForMicrophoneAccess();
110
171
  logger.info(`[Microphone] askForMicrophoneAccess result: ${result}`);
111
172
  return result === 'authorized';
112
173
  } catch (error) {
@@ -135,7 +196,8 @@ export function getCameraStatus(): PermissionStatus {
135
196
  * Shows the system permission dialog if not determined
136
197
  */
137
198
  export async function requestCameraAccess(): Promise<boolean> {
138
- if (!macOS()) {
199
+ const macPermissions = getMacPermissionsModule();
200
+ if (!macPermissions) {
139
201
  logger.info('[Camera] Not macOS, returning true');
140
202
  return true;
141
203
  }
@@ -151,7 +213,7 @@ export async function requestCameraAccess(): Promise<boolean> {
151
213
  if (currentStatus === 'not-determined') {
152
214
  logger.info('[Camera] Status is not-determined, requesting access...');
153
215
  try {
154
- const result = await askForCameraAccess();
216
+ const result = await macPermissions.askForCameraAccess();
155
217
  logger.info(`[Camera] askForCameraAccess result: ${result}`);
156
218
  return result === 'authorized';
157
219
  } catch (error) {
@@ -181,7 +243,8 @@ export function getScreenCaptureStatus(): PermissionStatus {
181
243
  * @param openPreferences - Whether to open System Preferences (default: true)
182
244
  */
183
245
  export async function requestScreenCaptureAccess(openPreferences = true): Promise<boolean> {
184
- if (!macOS()) {
246
+ const macPermissions = getMacPermissionsModule();
247
+ if (!macPermissions) {
185
248
  logger.info('[Screen] Not macOS, returning true');
186
249
  return true;
187
250
  }
@@ -196,7 +259,7 @@ export async function requestScreenCaptureAccess(openPreferences = true): Promis
196
259
 
197
260
  // Request screen capture access - this will prompt the user or open settings
198
261
  logger.info('[Screen] Requesting screen capture access...');
199
- askForScreenCaptureAccess(openPreferences);
262
+ macPermissions.askForScreenCaptureAccess(openPreferences);
200
263
 
201
264
  // Check the status after requesting
202
265
  const newStatus = getPermissionStatus('screen');
@@ -218,13 +281,14 @@ export function getFullDiskAccessStatus(): PermissionStatus {
218
281
  * user must manually add the app in System Settings
219
282
  */
220
283
  export function requestFullDiskAccess(): void {
221
- if (!macOS()) {
284
+ const macPermissions = getMacPermissionsModule();
285
+ if (!macPermissions) {
222
286
  logger.info('[FullDiskAccess] Not macOS, skipping');
223
287
  return;
224
288
  }
225
289
 
226
290
  logger.info('[FullDiskAccess] Opening Full Disk Access settings...');
227
- askForFullDiskAccess();
291
+ macPermissions.askForFullDiskAccess();
228
292
  }
229
293
 
230
294
  /**
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Fix duplicate agent and group, Fix Windows desktop build error with macOS native module, force plain text paste in ChatInput editor."
6
+ ]
7
+ },
8
+ "date": "2026-01-11",
9
+ "version": "2.0.0-next.265"
10
+ },
11
+ {
12
+ "children": {
13
+ "fixes": [
14
+ "Handle will-prevent-unload event to allow app quit."
15
+ ]
16
+ },
17
+ "date": "2026-01-11",
18
+ "version": "2.0.0-next.264"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "improvements": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.263",
3
+ "version": "2.0.0-next.265",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -202,7 +202,7 @@
202
202
  "@lobehub/chat-plugin-sdk": "^1.32.4",
203
203
  "@lobehub/chat-plugins-gateway": "^1.9.0",
204
204
  "@lobehub/desktop-ipc-typings": "workspace:*",
205
- "@lobehub/editor": "^3.8.0",
205
+ "@lobehub/editor": "^3.11.0",
206
206
  "@lobehub/icons": "^4.0.2",
207
207
  "@lobehub/market-sdk": "0.28.1",
208
208
  "@lobehub/tts": "^4.0.2",