@lobehub/chat 1.139.4 → 1.140.0

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 (136) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/Dockerfile +3 -0
  3. package/Dockerfile.database +3 -0
  4. package/Dockerfile.pglite +3 -0
  5. package/apps/desktop/src/main/core/App.ts +26 -1
  6. package/apps/desktop/src/main/core/__tests__/App.test.ts +282 -0
  7. package/changelog/v1.json +14 -0
  8. package/docs/development/basic/comfyui-development.mdx +1009 -0
  9. package/docs/development/basic/comfyui-development.zh-CN.mdx +998 -0
  10. package/docs/self-hosting/environment-variables/model-provider.mdx +52 -0
  11. package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +52 -0
  12. package/docs/usage/providers/comfyui.mdx +816 -0
  13. package/docs/usage/providers/comfyui.zh-CN.mdx +816 -0
  14. package/locales/en-US/modelProvider.json +52 -0
  15. package/locales/zh-CN/modelProvider.json +52 -0
  16. package/locales/zh-CN/models.json +52 -0
  17. package/locales/zh-CN/providers.json +3 -0
  18. package/package.json +2 -1
  19. package/packages/model-bank/package.json +1 -0
  20. package/packages/model-bank/src/aiModels/comfyui.ts +335 -0
  21. package/packages/model-bank/src/aiModels/index.ts +3 -0
  22. package/packages/model-bank/src/const/modelProvider.ts +1 -0
  23. package/packages/model-bank/src/standard-parameters/index.ts +38 -0
  24. package/packages/model-runtime/src/core/ModelRuntime.ts +8 -1
  25. package/packages/model-runtime/src/index.ts +2 -0
  26. package/packages/model-runtime/src/providers/comfyui/__tests__/index.test.ts +521 -0
  27. package/packages/model-runtime/src/providers/comfyui/auth/AuthManager.ts +116 -0
  28. package/packages/model-runtime/src/providers/comfyui/index.ts +180 -0
  29. package/packages/model-runtime/src/runtimeMap.ts +2 -0
  30. package/packages/model-runtime/src/types/error.ts +8 -0
  31. package/packages/model-runtime/src/types/image.ts +9 -0
  32. package/packages/model-runtime/src/utils/comfyuiErrorParser.test.ts +369 -0
  33. package/packages/model-runtime/src/utils/comfyuiErrorParser.ts +266 -0
  34. package/packages/model-runtime/src/utils/modelParse.test.ts +10 -1
  35. package/packages/model-runtime/src/utils/modelParse.ts +7 -0
  36. package/packages/types/src/aiProvider.ts +11 -1
  37. package/packages/types/src/asyncTask.ts +4 -0
  38. package/packages/types/src/auth.ts +8 -0
  39. package/packages/types/src/user/settings/keyVaults.ts +10 -0
  40. package/packages/utils/src/base64.test.ts +117 -0
  41. package/packages/utils/src/base64.ts +44 -0
  42. package/packages/utils/src/index.ts +2 -0
  43. package/src/app/(backend)/webapi/create-image/comfyui/route.ts +98 -0
  44. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +1 -0
  45. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ErrorState.tsx +42 -6
  46. package/src/app/[variants]/(main)/settings/provider/detail/comfyui/index.tsx +138 -0
  47. package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +4 -0
  48. package/src/components/InvalidAPIKey/APIKeyForm/ComfyUIForm.tsx +251 -0
  49. package/src/components/InvalidAPIKey/APIKeyForm/__tests__/ComfyUIForm.test.tsx +137 -0
  50. package/src/components/InvalidAPIKey/APIKeyForm/index.tsx +10 -1
  51. package/src/config/modelProviders/comfyui.ts +40 -0
  52. package/src/config/modelProviders/index.ts +3 -0
  53. package/src/envs/llm.ts +16 -0
  54. package/src/features/Conversation/Error/index.tsx +3 -1
  55. package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +1 -1
  56. package/src/features/Conversation/Messages/User/Actions.tsx +2 -3
  57. package/src/locales/default/error.ts +14 -0
  58. package/src/locales/default/modelProvider.ts +52 -0
  59. package/src/server/globalConfig/genServerAiProviderConfig.ts +1 -1
  60. package/src/server/modules/ModelRuntime/index.test.ts +66 -0
  61. package/src/server/modules/ModelRuntime/index.ts +35 -0
  62. package/src/server/routers/async/image.ts +87 -8
  63. package/src/server/routers/lambda/comfyui.ts +96 -0
  64. package/src/server/routers/lambda/index.ts +2 -0
  65. package/src/server/services/comfyui/__tests__/config/constants.test.ts +146 -0
  66. package/src/server/services/comfyui/__tests__/config/modelRegistry.test.ts +277 -0
  67. package/src/server/services/comfyui/__tests__/config/promptToolConst.test.ts +357 -0
  68. package/src/server/services/comfyui/__tests__/config/systemComponents.test.ts +137 -0
  69. package/src/server/services/comfyui/__tests__/core/comfyUIAuthService.test.ts +146 -0
  70. package/src/server/services/comfyui/__tests__/core/comfyUIConnectionService.test.ts +287 -0
  71. package/src/server/services/comfyui/__tests__/core/comfyuiClient.test.ts +666 -0
  72. package/src/server/services/comfyui/__tests__/core/errorHandler.test.ts +230 -0
  73. package/src/server/services/comfyui/__tests__/core/errorHandling.test.ts +134 -0
  74. package/src/server/services/comfyui/__tests__/core/imageService.test.ts +528 -0
  75. package/src/server/services/comfyui/__tests__/core/modelResolver.test.ts +454 -0
  76. package/src/server/services/comfyui/__tests__/core/workflowBuilder.test.ts +294 -0
  77. package/src/server/services/comfyui/__tests__/fixtures/parameters.fixture.ts +140 -0
  78. package/src/server/services/comfyui/__tests__/fixtures/supported.fixture.ts +97 -0
  79. package/src/server/services/comfyui/__tests__/fixtures/testModels.ts +64 -0
  80. package/src/server/services/comfyui/__tests__/helpers/mockContext.ts +98 -0
  81. package/src/server/services/comfyui/__tests__/helpers/realConfigData.ts +80 -0
  82. package/src/server/services/comfyui/__tests__/helpers/testSetup.ts +219 -0
  83. package/src/server/services/comfyui/__tests__/integration/parameterMapping.test.ts +138 -0
  84. package/src/server/services/comfyui/__tests__/integration/parameterTransformation.test.ts +88 -0
  85. package/src/server/services/comfyui/__tests__/integration/serviceIntegration.test.ts +160 -0
  86. package/src/server/services/comfyui/__tests__/setup/unifiedMocks.ts +48 -0
  87. package/src/server/services/comfyui/__tests__/utils/cacheManager.test.ts +571 -0
  88. package/src/server/services/comfyui/__tests__/utils/componentInfo.test.ts +329 -0
  89. package/src/server/services/comfyui/__tests__/utils/imageResizer.test.ts +424 -0
  90. package/src/server/services/comfyui/__tests__/utils/promptSplitter.test.ts +191 -0
  91. package/src/server/services/comfyui/__tests__/utils/weightDType.test.ts +192 -0
  92. package/src/server/services/comfyui/__tests__/utils/workflowDetector.test.ts +507 -0
  93. package/src/server/services/comfyui/__tests__/workflows/flux-kontext.test.ts +381 -0
  94. package/src/server/services/comfyui/__tests__/workflows/simple-sd.test.ts +558 -0
  95. package/src/server/services/comfyui/__tests__/workflows/unified-workflows.test.ts +392 -0
  96. package/src/server/services/comfyui/config/constants.ts +110 -0
  97. package/src/server/services/comfyui/config/fluxModelRegistry.ts +843 -0
  98. package/src/server/services/comfyui/config/modelRegistry.ts +48 -0
  99. package/src/server/services/comfyui/config/promptToolConst.ts +624 -0
  100. package/src/server/services/comfyui/config/sdModelRegistry.ts +508 -0
  101. package/src/server/services/comfyui/config/systemComponents.ts +385 -0
  102. package/src/server/services/comfyui/config/workflowRegistry.ts +70 -0
  103. package/src/server/services/comfyui/core/comfyUIAuthService.ts +145 -0
  104. package/src/server/services/comfyui/core/comfyUIClientService.ts +249 -0
  105. package/src/server/services/comfyui/core/comfyUIConnectionService.ts +136 -0
  106. package/src/server/services/comfyui/core/errorHandlerService.ts +538 -0
  107. package/src/server/services/comfyui/core/imageService.ts +272 -0
  108. package/src/server/services/comfyui/core/modelResolverService.ts +290 -0
  109. package/src/server/services/comfyui/core/workflowBuilderService.ts +79 -0
  110. package/src/server/services/comfyui/errors/base.ts +21 -0
  111. package/src/server/services/comfyui/errors/configError.ts +26 -0
  112. package/src/server/services/comfyui/errors/index.ts +29 -0
  113. package/src/server/services/comfyui/errors/modelResolverError.ts +42 -0
  114. package/src/server/services/comfyui/errors/servicesError.ts +42 -0
  115. package/src/server/services/comfyui/errors/typeGuards.ts +12 -0
  116. package/src/server/services/comfyui/errors/utilsError.ts +34 -0
  117. package/src/server/services/comfyui/errors/workflowError.ts +26 -0
  118. package/src/server/services/comfyui/types/index.ts +42 -0
  119. package/src/server/services/comfyui/utils/cacheManager.ts +92 -0
  120. package/src/server/services/comfyui/utils/componentInfo.ts +86 -0
  121. package/src/server/services/comfyui/utils/imageResizer.ts +173 -0
  122. package/src/server/services/comfyui/utils/promptSplitter.ts +132 -0
  123. package/src/server/services/comfyui/utils/staticModelLookup.ts +138 -0
  124. package/src/server/services/comfyui/utils/weightDType.ts +18 -0
  125. package/src/server/services/comfyui/utils/workflowDetector.ts +60 -0
  126. package/src/server/services/comfyui/utils/workflowUtils.ts +73 -0
  127. package/src/server/services/comfyui/workflows/flux-dev.ts +234 -0
  128. package/src/server/services/comfyui/workflows/flux-kontext.ts +308 -0
  129. package/src/server/services/comfyui/workflows/flux-schnell.ts +169 -0
  130. package/src/server/services/comfyui/workflows/index.ts +5 -0
  131. package/src/server/services/comfyui/workflows/sd35.ts +227 -0
  132. package/src/server/services/comfyui/workflows/simple-sd.ts +273 -0
  133. package/src/server/services/generation/index.test.ts +39 -4
  134. package/src/server/services/generation/index.ts +37 -5
  135. package/src/services/_auth.ts +15 -2
  136. package/src/store/user/slices/modelList/selectors/keyVaults.ts +3 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 1.140.0](https://github.com/lobehub/lobe-chat/compare/v1.139.5...v1.140.0)
6
+
7
+ <sup>Released on **2025-10-21**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Add ComfyUI integration Phase1(RFC-128).
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Add ComfyUI integration Phase1(RFC-128), closes [#9043](https://github.com/lobehub/lobe-chat/issues/9043) ([15ffe28](https://github.com/lobehub/lobe-chat/commit/15ffe28))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 1.139.5](https://github.com/lobehub/lobe-chat/compare/v1.139.4...v1.139.5)
31
+
32
+ <sup>Released on **2025-10-21**</sup>
33
+
34
+ #### 🐛 Bug Fixes
35
+
36
+ - **desktop**: Fix desktop open error in some edge cases.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### What's fixed
44
+
45
+ - **desktop**: Fix desktop open error in some edge cases, closes [#9813](https://github.com/lobehub/lobe-chat/issues/9813) ([6334f62](https://github.com/lobehub/lobe-chat/commit/6334f62))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 1.139.4](https://github.com/lobehub/lobe-chat/compare/v1.139.3...v1.139.4)
6
56
 
7
57
  <sup>Released on **2025-10-21**</sup>
package/Dockerfile CHANGED
@@ -165,6 +165,9 @@ ENV \
165
165
  CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
166
166
  # Cohere
167
167
  COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
168
+ # ComfyUI
169
+ COMFYUI_BASE_URL="" COMFYUI_AUTH_TYPE="" \
170
+ COMFYUI_API_KEY="" COMFYUI_USERNAME="" COMFYUI_PASSWORD="" COMFYUI_CUSTOM_HEADERS="" \
168
171
  # DeepSeek
169
172
  DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
170
173
  # Fireworks AI
@@ -218,6 +218,9 @@ ENV \
218
218
  CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
219
219
  # Cohere
220
220
  COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
221
+ # ComfyUI
222
+ COMFYUI_BASE_URL="" COMFYUI_AUTH_TYPE="" \
223
+ COMFYUI_API_KEY="" COMFYUI_USERNAME="" COMFYUI_PASSWORD="" COMFYUI_CUSTOM_HEADERS="" \
221
224
  # DeepSeek
222
225
  DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
223
226
  # Fireworks AI
package/Dockerfile.pglite CHANGED
@@ -167,6 +167,9 @@ ENV \
167
167
  CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
168
168
  # Cohere
169
169
  COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
170
+ # ComfyUI
171
+ COMFYUI_BASE_URL="" COMFYUI_AUTH_TYPE="" \
172
+ COMFYUI_API_KEY="" COMFYUI_USERNAME="" COMFYUI_PASSWORD="" COMFYUI_CUSTOM_HEADERS="" \
170
173
  # DeepSeek
171
174
  DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
172
175
  # Fireworks AI
@@ -1,11 +1,12 @@
1
1
  import { ElectronIPCEventHandler, ElectronIPCServer } from '@lobechat/electron-server-ipc';
2
2
  import { Session, app, ipcMain, protocol } from 'electron';
3
3
  import { macOS, windows } from 'electron-is';
4
+ import { pathExistsSync, remove } from 'fs-extra';
4
5
  import os from 'node:os';
5
6
  import { join } from 'node:path';
6
7
 
7
8
  import { name } from '@/../../package.json';
8
- import { buildDir, nextStandaloneDir } from '@/const/dir';
9
+ import { buildDir, LOCAL_DATABASE_DIR, nextStandaloneDir } from '@/const/dir';
9
10
  import { isDev } from '@/const/env';
10
11
  import { IControlModule } from '@/controllers';
11
12
  import { IServiceModule } from '@/services';
@@ -129,6 +130,9 @@ export class App {
129
130
 
130
131
  this.initDevBranding();
131
132
 
133
+ // Clean up stale database lock file before starting IPC server
134
+ await this.cleanupDatabaseLock();
135
+
132
136
  // ==============
133
137
  await this.ipcServer.start();
134
138
  logger.debug('IPC server started');
@@ -371,6 +375,27 @@ export class App {
371
375
  }
372
376
  };
373
377
 
378
+ /**
379
+ * Clean up stale database lock file from previous crashes or abnormal exits
380
+ */
381
+ private cleanupDatabaseLock = async () => {
382
+ try {
383
+ const dbPath = join(this.appStoragePath, LOCAL_DATABASE_DIR);
384
+ const lockPath = `${dbPath}.lock`;
385
+
386
+ if (pathExistsSync(lockPath)) {
387
+ logger.info(`Cleaning up stale database lock file: ${lockPath}`);
388
+ await remove(lockPath);
389
+ logger.info('Database lock file removed successfully');
390
+ } else {
391
+ logger.debug('No database lock file found, skipping cleanup');
392
+ }
393
+ } catch (error) {
394
+ logger.error('Failed to cleanup database lock file:', error);
395
+ // Non-fatal error, allow application to continue
396
+ }
397
+ };
398
+
374
399
  private registerNextHandler() {
375
400
  logger.debug('Registering Next.js handler');
376
401
  const handler = createHandler({
@@ -0,0 +1,282 @@
1
+ import { app } from 'electron';
2
+ import { pathExistsSync, remove } from 'fs-extra';
3
+ import { join } from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+
6
+ import { LOCAL_DATABASE_DIR } from '@/const/dir';
7
+
8
+ // Mock electron modules
9
+ vi.mock('electron', () => ({
10
+ app: {
11
+ getAppPath: vi.fn(() => '/mock/app/path'),
12
+ getLocale: vi.fn(() => 'en-US'),
13
+ getPath: vi.fn(() => '/mock/user/path'),
14
+ requestSingleInstanceLock: vi.fn(() => true),
15
+ whenReady: vi.fn(() => Promise.resolve()),
16
+ on: vi.fn(),
17
+ commandLine: {
18
+ appendSwitch: vi.fn(),
19
+ },
20
+ dock: {
21
+ setIcon: vi.fn(),
22
+ },
23
+ exit: vi.fn(),
24
+ },
25
+ ipcMain: {
26
+ handle: vi.fn(),
27
+ },
28
+ nativeTheme: {
29
+ on: vi.fn(),
30
+ shouldUseDarkColors: false,
31
+ },
32
+ protocol: {
33
+ registerSchemesAsPrivileged: vi.fn(),
34
+ },
35
+ }));
36
+
37
+ // Mock logger
38
+ vi.mock('@/utils/logger', () => ({
39
+ createLogger: () => ({
40
+ debug: vi.fn(),
41
+ info: vi.fn(),
42
+ warn: vi.fn(),
43
+ error: vi.fn(),
44
+ }),
45
+ }));
46
+
47
+ // Mock fs-extra module
48
+ vi.mock('fs-extra', async () => {
49
+ const actual = await vi.importActual('fs-extra');
50
+ return {
51
+ ...actual,
52
+ pathExistsSync: vi.fn(),
53
+ remove: vi.fn(),
54
+ };
55
+ });
56
+
57
+ // Mock common/routes
58
+ vi.mock('~common/routes', () => ({
59
+ findMatchingRoute: vi.fn(),
60
+ extractSubPath: vi.fn(),
61
+ }));
62
+
63
+ // Mock other dependencies
64
+ vi.mock('electron-is', () => ({
65
+ macOS: vi.fn(() => false),
66
+ windows: vi.fn(() => false),
67
+ }));
68
+
69
+ vi.mock('fix-path', () => ({
70
+ default: vi.fn(),
71
+ }));
72
+
73
+ vi.mock('@/const/env', () => ({
74
+ isDev: false,
75
+ }));
76
+
77
+ vi.mock('@/const/dir', () => ({
78
+ buildDir: '/mock/build',
79
+ nextStandaloneDir: '/mock/standalone',
80
+ LOCAL_DATABASE_DIR: 'lobehub-local-db',
81
+ appStorageDir: '/mock/storage/path',
82
+ userDataDir: '/mock/user/data',
83
+ DB_SCHEMA_HASH_FILENAME: 'lobehub-local-db-schema-hash',
84
+ FILE_STORAGE_DIR: 'file-storage',
85
+ INSTALL_PLUGINS_DIR: 'plugins',
86
+ LOCAL_STORAGE_URL_PREFIX: '/lobe-desktop-file',
87
+ }));
88
+
89
+ vi.mock('@lobechat/electron-server-ipc', () => ({
90
+ ElectronIPCServer: vi.fn().mockImplementation(() => ({
91
+ start: vi.fn().mockResolvedValue(undefined),
92
+ })),
93
+ }));
94
+
95
+ // Mock all infrastructure managers
96
+ vi.mock('../infrastructure/I18nManager', () => ({
97
+ I18nManager: vi.fn().mockImplementation(() => ({
98
+ init: vi.fn().mockResolvedValue(undefined),
99
+ })),
100
+ }));
101
+
102
+ vi.mock('../infrastructure/StoreManager', () => ({
103
+ StoreManager: vi.fn().mockImplementation(() => ({
104
+ get: vi.fn((key) => {
105
+ if (key === 'storagePath') return '/mock/storage/path';
106
+ return undefined;
107
+ }),
108
+ set: vi.fn(),
109
+ })),
110
+ }));
111
+
112
+ vi.mock('../infrastructure/StaticFileServerManager', () => ({
113
+ StaticFileServerManager: vi.fn().mockImplementation(() => ({
114
+ initialize: vi.fn().mockResolvedValue(undefined),
115
+ destroy: vi.fn(),
116
+ })),
117
+ }));
118
+
119
+ vi.mock('../infrastructure/UpdaterManager', () => ({
120
+ UpdaterManager: vi.fn().mockImplementation(() => ({
121
+ initialize: vi.fn().mockResolvedValue(undefined),
122
+ })),
123
+ }));
124
+
125
+ vi.mock('../infrastructure/ProtocolManager', () => ({
126
+ ProtocolManager: vi.fn().mockImplementation(() => ({
127
+ initialize: vi.fn(),
128
+ processPendingUrls: vi.fn().mockResolvedValue(undefined),
129
+ })),
130
+ }));
131
+
132
+ vi.mock('../browser/BrowserManager', () => ({
133
+ BrowserManager: vi.fn().mockImplementation(() => ({
134
+ initializeBrowsers: vi.fn(),
135
+ getIdentifierByWebContents: vi.fn(),
136
+ })),
137
+ }));
138
+
139
+ vi.mock('../ui/MenuManager', () => ({
140
+ MenuManager: vi.fn().mockImplementation(() => ({
141
+ initialize: vi.fn(),
142
+ })),
143
+ }));
144
+
145
+ vi.mock('../ui/ShortcutManager', () => ({
146
+ ShortcutManager: vi.fn().mockImplementation(() => ({
147
+ initialize: vi.fn(),
148
+ })),
149
+ }));
150
+
151
+ vi.mock('../ui/TrayManager', () => ({
152
+ TrayManager: vi.fn().mockImplementation(() => ({
153
+ initializeTrays: vi.fn(),
154
+ destroyAll: vi.fn(),
155
+ })),
156
+ }));
157
+
158
+ vi.mock('@/utils/next-electron-rsc', () => ({
159
+ createHandler: vi.fn(() => ({
160
+ createInterceptor: vi.fn(),
161
+ registerCustomHandler: vi.fn(),
162
+ })),
163
+ }));
164
+
165
+ // Mock controllers and services
166
+ vi.mock('../../controllers/*Ctr.ts', () => ({}));
167
+ vi.mock('../../services/*Srv.ts', () => ({}));
168
+
169
+ // Import after mocks are set up
170
+ import { App } from '../App';
171
+
172
+ describe('App - Database Lock Cleanup', () => {
173
+ let appInstance: App;
174
+ let mockLockPath: string;
175
+
176
+ beforeEach(() => {
177
+ vi.clearAllMocks();
178
+
179
+ // Mock glob imports to return empty arrays
180
+ (import.meta as any).glob = vi.fn(() => ({}));
181
+
182
+ mockLockPath = join('/mock/storage/path', LOCAL_DATABASE_DIR) + '.lock';
183
+ });
184
+
185
+ afterEach(() => {
186
+ vi.clearAllMocks();
187
+ });
188
+
189
+ describe('bootstrap - database lock cleanup', () => {
190
+ it('should remove stale lock file if it exists during bootstrap', async () => {
191
+ // Setup: simulate existing lock file
192
+ vi.mocked(pathExistsSync).mockReturnValue(true);
193
+ vi.mocked(remove).mockResolvedValue(undefined);
194
+
195
+ // Create app instance
196
+ appInstance = new App();
197
+
198
+ // Call bootstrap which should trigger cleanup
199
+ await appInstance.bootstrap();
200
+
201
+ // Verify: lock file check was called
202
+ expect(pathExistsSync).toHaveBeenCalledWith(mockLockPath);
203
+
204
+ // Verify: lock file was removed
205
+ expect(remove).toHaveBeenCalledWith(mockLockPath);
206
+ });
207
+
208
+ it('should not attempt to remove lock file if it does not exist', async () => {
209
+ // Setup: no lock file exists
210
+ vi.mocked(pathExistsSync).mockReturnValue(false);
211
+
212
+ // Create app instance
213
+ appInstance = new App();
214
+
215
+ // Call bootstrap
216
+ await appInstance.bootstrap();
217
+
218
+ // Verify: lock file check was called
219
+ expect(pathExistsSync).toHaveBeenCalledWith(mockLockPath);
220
+
221
+ // Verify: remove was NOT called since file doesn't exist
222
+ expect(remove).not.toHaveBeenCalled();
223
+ });
224
+
225
+ it('should continue bootstrap even if lock cleanup fails', async () => {
226
+ // Setup: simulate lock file exists but cleanup fails
227
+ vi.mocked(pathExistsSync).mockReturnValue(true);
228
+ vi.mocked(remove).mockRejectedValue(new Error('Permission denied'));
229
+
230
+ // Create app instance
231
+ appInstance = new App();
232
+
233
+ // Bootstrap should not throw even if cleanup fails
234
+ await expect(appInstance.bootstrap()).resolves.not.toThrow();
235
+
236
+ // Verify: cleanup was attempted
237
+ expect(pathExistsSync).toHaveBeenCalledWith(mockLockPath);
238
+ expect(remove).toHaveBeenCalledWith(mockLockPath);
239
+ });
240
+
241
+ it('should clean up lock file before starting IPC server', async () => {
242
+ // Setup
243
+ vi.mocked(pathExistsSync).mockReturnValue(true);
244
+ const callOrder: string[] = [];
245
+
246
+ vi.mocked(remove).mockImplementation(async () => {
247
+ callOrder.push('remove');
248
+ });
249
+
250
+ // Mock IPC server start to track call order
251
+ const { ElectronIPCServer } = await import('@lobechat/electron-server-ipc');
252
+ const mockStart = vi.fn().mockImplementation(() => {
253
+ callOrder.push('ipcServer.start');
254
+ return Promise.resolve();
255
+ });
256
+
257
+ vi.mocked(ElectronIPCServer).mockImplementation(
258
+ () =>
259
+ ({
260
+ start: mockStart,
261
+ }) as any,
262
+ );
263
+
264
+ // Create app instance and bootstrap
265
+ appInstance = new App();
266
+ await appInstance.bootstrap();
267
+
268
+ // Verify: cleanup happens before IPC server starts
269
+ expect(callOrder).toEqual(['remove', 'ipcServer.start']);
270
+ });
271
+ });
272
+
273
+ describe('appStoragePath', () => {
274
+ it('should return storage path from store manager', () => {
275
+ appInstance = new App();
276
+
277
+ const storagePath = appInstance.appStoragePath;
278
+
279
+ expect(storagePath).toBe('/mock/storage/path');
280
+ });
281
+ });
282
+ });
package/changelog/v1.json CHANGED
@@ -1,4 +1,18 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Add ComfyUI integration Phase1(RFC-128)."
6
+ ]
7
+ },
8
+ "date": "2025-10-21",
9
+ "version": "1.140.0"
10
+ },
11
+ {
12
+ "children": {},
13
+ "date": "2025-10-21",
14
+ "version": "1.139.5"
15
+ },
2
16
  {
3
17
  "children": {
4
18
  "fixes": [