@lobehub/lobehub 2.0.0-next.50 → 2.0.0-next.52

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 (171) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/apps/desktop/src/main/controllers/LocalFileCtr.ts +25 -5
  3. package/apps/desktop/src/main/controllers/ShellCommandCtr.ts +242 -0
  4. package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +4 -1
  5. package/apps/desktop/src/main/controllers/__tests__/ShellCommandCtr.test.ts +499 -0
  6. package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +357 -0
  7. package/apps/desktop/src/main/modules/fileSearch/impl/macOS.ts +30 -22
  8. package/changelog/v1.json +18 -0
  9. package/locales/ar/chat.json +20 -0
  10. package/locales/ar/common.json +1 -0
  11. package/locales/ar/components.json +6 -0
  12. package/locales/ar/models.json +119 -126
  13. package/locales/ar/plugin.json +2 -1
  14. package/locales/bg-BG/chat.json +20 -0
  15. package/locales/bg-BG/common.json +1 -0
  16. package/locales/bg-BG/components.json +6 -0
  17. package/locales/bg-BG/models.json +104 -132
  18. package/locales/bg-BG/plugin.json +2 -1
  19. package/locales/de-DE/chat.json +20 -0
  20. package/locales/de-DE/common.json +1 -0
  21. package/locales/de-DE/components.json +6 -0
  22. package/locales/de-DE/models.json +119 -126
  23. package/locales/de-DE/plugin.json +2 -1
  24. package/locales/en-US/chat.json +20 -0
  25. package/locales/en-US/common.json +1 -0
  26. package/locales/en-US/components.json +6 -0
  27. package/locales/en-US/models.json +167 -126
  28. package/locales/en-US/plugin.json +2 -1
  29. package/locales/es-ES/chat.json +20 -0
  30. package/locales/es-ES/common.json +1 -0
  31. package/locales/es-ES/components.json +6 -0
  32. package/locales/es-ES/models.json +119 -126
  33. package/locales/es-ES/plugin.json +2 -1
  34. package/locales/fa-IR/chat.json +20 -0
  35. package/locales/fa-IR/common.json +1 -0
  36. package/locales/fa-IR/components.json +6 -0
  37. package/locales/fa-IR/models.json +119 -126
  38. package/locales/fa-IR/plugin.json +2 -1
  39. package/locales/fr-FR/chat.json +20 -0
  40. package/locales/fr-FR/common.json +1 -0
  41. package/locales/fr-FR/components.json +6 -0
  42. package/locales/fr-FR/models.json +119 -126
  43. package/locales/fr-FR/plugin.json +2 -1
  44. package/locales/it-IT/chat.json +20 -0
  45. package/locales/it-IT/common.json +1 -0
  46. package/locales/it-IT/components.json +6 -0
  47. package/locales/it-IT/models.json +119 -126
  48. package/locales/it-IT/plugin.json +2 -1
  49. package/locales/ja-JP/chat.json +20 -0
  50. package/locales/ja-JP/common.json +1 -0
  51. package/locales/ja-JP/components.json +6 -0
  52. package/locales/ja-JP/models.json +119 -126
  53. package/locales/ja-JP/plugin.json +2 -1
  54. package/locales/ko-KR/chat.json +20 -0
  55. package/locales/ko-KR/common.json +1 -0
  56. package/locales/ko-KR/components.json +6 -0
  57. package/locales/ko-KR/models.json +119 -126
  58. package/locales/ko-KR/plugin.json +2 -1
  59. package/locales/nl-NL/chat.json +20 -0
  60. package/locales/nl-NL/common.json +1 -0
  61. package/locales/nl-NL/components.json +6 -0
  62. package/locales/nl-NL/models.json +119 -126
  63. package/locales/nl-NL/plugin.json +2 -1
  64. package/locales/pl-PL/chat.json +20 -0
  65. package/locales/pl-PL/common.json +1 -0
  66. package/locales/pl-PL/components.json +6 -0
  67. package/locales/pl-PL/models.json +119 -126
  68. package/locales/pl-PL/plugin.json +2 -1
  69. package/locales/pt-BR/chat.json +20 -0
  70. package/locales/pt-BR/common.json +1 -0
  71. package/locales/pt-BR/components.json +6 -0
  72. package/locales/pt-BR/models.json +119 -126
  73. package/locales/pt-BR/plugin.json +2 -1
  74. package/locales/ru-RU/chat.json +20 -0
  75. package/locales/ru-RU/common.json +1 -0
  76. package/locales/ru-RU/components.json +6 -0
  77. package/locales/ru-RU/models.json +119 -126
  78. package/locales/ru-RU/plugin.json +2 -1
  79. package/locales/tr-TR/chat.json +20 -0
  80. package/locales/tr-TR/common.json +1 -0
  81. package/locales/tr-TR/components.json +6 -0
  82. package/locales/tr-TR/models.json +119 -126
  83. package/locales/tr-TR/plugin.json +2 -1
  84. package/locales/vi-VN/chat.json +20 -0
  85. package/locales/vi-VN/common.json +1 -0
  86. package/locales/vi-VN/components.json +6 -0
  87. package/locales/vi-VN/models.json +119 -126
  88. package/locales/vi-VN/plugin.json +2 -1
  89. package/locales/zh-CN/chat.json +20 -0
  90. package/locales/zh-CN/common.json +1 -0
  91. package/locales/zh-CN/components.json +6 -0
  92. package/locales/zh-CN/models.json +173 -80
  93. package/locales/zh-CN/plugin.json +2 -1
  94. package/locales/zh-TW/chat.json +20 -0
  95. package/locales/zh-TW/common.json +1 -0
  96. package/locales/zh-TW/components.json +6 -0
  97. package/locales/zh-TW/models.json +119 -126
  98. package/locales/zh-TW/plugin.json +2 -1
  99. package/package.json +1 -1
  100. package/packages/agent-runtime/src/core/InterventionChecker.ts +1 -1
  101. package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +23 -23
  102. package/packages/agent-runtime/src/types/state.ts +7 -1
  103. package/packages/const/src/settings/tool.ts +1 -5
  104. package/packages/electron-client-ipc/src/types/localSystem.ts +26 -2
  105. package/packages/file-loaders/src/loaders/docx/index.ts +1 -1
  106. package/packages/model-bank/src/aiModels/wenxin.ts +1348 -291
  107. package/packages/model-runtime/src/core/contextBuilders/openai.test.ts +58 -0
  108. package/packages/model-runtime/src/core/contextBuilders/openai.ts +24 -10
  109. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +3 -2
  110. package/packages/model-runtime/src/providers/openai/index.test.ts +44 -0
  111. package/packages/model-runtime/src/providers/wenxin/index.ts +22 -1
  112. package/packages/model-runtime/src/utils/modelParse.ts +6 -0
  113. package/packages/types/src/tool/builtin.ts +15 -4
  114. package/packages/types/src/tool/intervention.ts +32 -2
  115. package/packages/types/src/user/settings/tool.ts +3 -27
  116. package/src/config/modelProviders/wenxin.ts +2 -3
  117. package/src/features/Conversation/MarkdownElements/remarkPlugins/__snapshots__/createRemarkSelfClosingTagPlugin.test.ts.snap +133 -0
  118. package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.test.ts +48 -0
  119. package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.ts +2 -1
  120. package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx +3 -3
  121. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/Fallback.tsx +98 -0
  122. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/ModeSelector.tsx +5 -6
  123. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/index.tsx +40 -36
  124. package/src/features/Conversation/Messages/Group/Tool/Render/LoadingPlaceholder/index.tsx +3 -3
  125. package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +25 -18
  126. package/src/features/LocalFile/LocalFile.tsx +55 -5
  127. package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +10 -4
  128. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +2 -2
  129. package/src/locales/default/components.ts +6 -0
  130. package/src/locales/default/plugin.ts +2 -1
  131. package/src/services/chat/chat.test.ts +1 -0
  132. package/src/services/electron/localFileService.ts +4 -0
  133. package/src/store/aiInfra/slices/aiProvider/__tests__/selectors.test.ts +62 -0
  134. package/src/store/aiInfra/slices/aiProvider/selectors.ts +1 -1
  135. package/src/store/chat/agents/GeneralChatAgent.ts +26 -1
  136. package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +173 -0
  137. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +8 -40
  138. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +91 -34
  139. package/src/store/user/selectors.ts +1 -0
  140. package/src/store/user/slices/settings/action.ts +12 -0
  141. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +0 -7
  142. package/src/store/user/slices/settings/selectors/index.ts +1 -0
  143. package/src/store/user/slices/settings/selectors/settings.test.ts +0 -37
  144. package/src/store/user/slices/settings/selectors/settings.ts +0 -5
  145. package/src/store/user/slices/settings/selectors/toolIntervention.ts +17 -0
  146. package/src/tools/code-interpreter/Render/index.tsx +1 -1
  147. package/src/tools/interventions.ts +32 -0
  148. package/src/tools/local-system/Intervention/RunCommand/index.tsx +56 -0
  149. package/src/tools/local-system/Placeholder/ListFiles.tsx +3 -5
  150. package/src/tools/local-system/Placeholder/SearchFiles.tsx +2 -5
  151. package/src/tools/local-system/Render/ListFiles/index.tsx +16 -21
  152. package/src/tools/local-system/Render/RenameLocalFile/index.tsx +15 -20
  153. package/src/tools/local-system/Render/RunCommand/index.tsx +103 -27
  154. package/src/tools/local-system/Render/SearchFiles/SearchQuery/index.tsx +0 -1
  155. package/src/tools/local-system/Render/SearchFiles/index.tsx +15 -20
  156. package/src/tools/local-system/Render/WriteFile/index.tsx +2 -8
  157. package/src/tools/local-system/index.ts +184 -4
  158. package/src/tools/local-system/systemRole.ts +62 -8
  159. package/src/tools/placeholders.ts +39 -8
  160. package/src/tools/renders.ts +56 -9
  161. package/src/tools/web-browsing/Placeholder/{PageContent.tsx → CrawlMultiPages.tsx} +4 -1
  162. package/src/tools/web-browsing/Placeholder/CrawlSinglePage.tsx +12 -0
  163. package/src/tools/web-browsing/Placeholder/Search.tsx +4 -4
  164. package/src/tools/web-browsing/Render/CrawlMultiPages.tsx +15 -0
  165. package/src/tools/web-browsing/Render/CrawlSinglePage.tsx +15 -0
  166. package/src/tools/web-browsing/Render/Search/index.tsx +39 -44
  167. package/packages/database/migrations/0044_add_tool_intervention.sql +0 -1
  168. package/src/tools/local-system/Placeholder/index.tsx +0 -25
  169. package/src/tools/local-system/Render/index.tsx +0 -40
  170. package/src/tools/web-browsing/Placeholder/index.tsx +0 -40
  171. package/src/tools/web-browsing/Render/index.tsx +0 -57
@@ -0,0 +1,357 @@
1
+ import path from 'node:path';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { MacOSSearchServiceImpl } from '../impl/macOS';
5
+
6
+ /**
7
+ * macOS File Search Integration Tests
8
+ *
9
+ * These tests run against the real macOS Spotlight service
10
+ * using files in the current repository.
11
+ *
12
+ * Run with: bunx vitest run 'macOS.integration.test'
13
+ */
14
+
15
+ // Get repository root path (assumes test runs from apps/desktop)
16
+ const repoRoot = path.resolve(__dirname, '../../../../..');
17
+
18
+ describe.skipIf(process.platform !== 'darwin')('MacOSSearchServiceImpl Integration', () => {
19
+ const searchService = new MacOSSearchServiceImpl();
20
+
21
+ describe('checkSearchServiceStatus', () => {
22
+ it('should verify Spotlight is available on macOS', async () => {
23
+ const isAvailable = await searchService.checkSearchServiceStatus();
24
+
25
+ expect(isAvailable).toBe(true);
26
+ });
27
+ });
28
+
29
+ describe('search for known repository files', () => {
30
+ it('should find package.json in repo root', async () => {
31
+ const results = await searchService.search({
32
+ keywords: 'package.json',
33
+ limit: 10,
34
+ onlyIn: repoRoot,
35
+ });
36
+
37
+ expect(results.length).toBeGreaterThan(0);
38
+
39
+ // Should find at least one package.json
40
+ const packageJson = results.find((r) => r.name === 'package.json');
41
+ expect(packageJson).toBeDefined();
42
+ expect(packageJson!.type).toBe('json');
43
+ expect(packageJson!.path).toContain(repoRoot);
44
+ });
45
+
46
+ it('should find README files', async () => {
47
+ const results = await searchService.search({
48
+ keywords: 'README',
49
+ limit: 10,
50
+ onlyIn: repoRoot,
51
+ });
52
+ expect(results.length).toBeGreaterThan(0);
53
+
54
+ // Should contain markdown files
55
+ const mdFile = results.find((r) => r.type === 'md');
56
+ expect(mdFile).toBeDefined();
57
+ expect(mdFile!.name).toMatch(/README/i);
58
+ });
59
+
60
+ it('should find TypeScript files', async () => {
61
+ const results = await searchService.search({
62
+ keywords: 'macOS',
63
+ limit: 10,
64
+ onlyIn: repoRoot,
65
+ });
66
+
67
+ expect(results.length).toBeGreaterThan(0);
68
+
69
+ // Should find the macOS.ts implementation file
70
+ const macOSFile = results.find((r) => r.name.includes('macOS') && r.type === 'ts');
71
+ expect(macOSFile).toBeDefined();
72
+ expect(macOSFile!.contentType).toBe('code');
73
+ });
74
+
75
+ it('should find files in apps/desktop directory', async () => {
76
+ const desktopPath = path.join(repoRoot, 'apps/desktop');
77
+
78
+ const results = await searchService.search({
79
+ keywords: 'src',
80
+ limit: 20,
81
+ onlyIn: desktopPath,
82
+ });
83
+
84
+ // Spotlight indexing may not be complete for this directory
85
+ // so we make the test lenient
86
+ if (results.length > 0) {
87
+ // All results should be within apps/desktop
88
+ results.forEach((result) => {
89
+ expect(result.path).toContain('apps/desktop');
90
+ });
91
+ } else {
92
+ // eslint-disable-next-line no-console
93
+ console.warn(
94
+ '⚠️ No results found in apps/desktop - Spotlight indexing may not be complete',
95
+ );
96
+ }
97
+
98
+ // At minimum, verify the search completed without error
99
+ expect(Array.isArray(results)).toBe(true);
100
+ });
101
+
102
+ it('should find test files', async () => {
103
+ const results = await searchService.search({
104
+ keywords: 'test.ts',
105
+ limit: 10,
106
+ onlyIn: repoRoot,
107
+ });
108
+
109
+ expect(results.length).toBeGreaterThan(0);
110
+
111
+ // Should find test files
112
+ const testFile = results.find((r) => r.name.endsWith('.test.ts'));
113
+ expect(testFile).toBeDefined();
114
+ expect(testFile!.path).toContain('__tests__');
115
+ });
116
+ });
117
+
118
+ describe('search with filters', () => {
119
+ it('should respect limit parameter', async () => {
120
+ const limit = 3;
121
+ const results = await searchService.search({
122
+ keywords: 'src',
123
+ limit,
124
+ onlyIn: repoRoot,
125
+ });
126
+
127
+ expect(results.length).toBeLessThanOrEqual(limit);
128
+ });
129
+
130
+ it('should search in specific subdirectory only', async () => {
131
+ const srcPath = path.join(repoRoot, 'apps/desktop/src');
132
+
133
+ const results = await searchService.search({
134
+ keywords: 'index',
135
+ limit: 10,
136
+ onlyIn: srcPath,
137
+ });
138
+
139
+ // All results should be within the specified directory
140
+ results.forEach((result) => {
141
+ expect(result.path).toContain('apps/desktop/src');
142
+ });
143
+ });
144
+
145
+ it('should return empty array for non-existent keywords', async () => {
146
+ const results = await searchService.search({
147
+ keywords: 'xyzabc123unlikely-keyword-that-does-not-exist-12345',
148
+ limit: 5,
149
+ onlyIn: repoRoot,
150
+ });
151
+
152
+ expect(results).toEqual([]);
153
+ });
154
+ });
155
+
156
+ describe('file type detection', () => {
157
+ it('should correctly identify TypeScript files', async () => {
158
+ const results = await searchService.search({
159
+ keywords: 'LocalFileCtr',
160
+ limit: 5,
161
+ onlyIn: repoRoot,
162
+ });
163
+
164
+ const tsFile = results.find((r) => r.name === 'LocalFileCtr.ts');
165
+ if (tsFile) {
166
+ expect(tsFile.type).toBe('ts');
167
+ expect(tsFile.contentType).toBe('code');
168
+ expect(tsFile.isDirectory).toBe(false);
169
+ }
170
+ });
171
+
172
+ it('should correctly identify JSON files', async () => {
173
+ const results = await searchService.search({
174
+ keywords: 'tsconfig',
175
+ limit: 5,
176
+ onlyIn: repoRoot,
177
+ });
178
+
179
+ const jsonFile = results.find((r) => r.name.includes('tsconfig') && r.type === 'json');
180
+ if (jsonFile) {
181
+ expect(jsonFile.type).toBe('json');
182
+ expect(jsonFile.contentType).toBe('code');
183
+ expect(jsonFile.size).toBeGreaterThan(0);
184
+ }
185
+ });
186
+
187
+ it('should correctly identify directories', async () => {
188
+ const results = await searchService.search({
189
+ keywords: '__tests__',
190
+ limit: 10,
191
+ onlyIn: repoRoot,
192
+ });
193
+
194
+ const testDir = results.find((r) => r.name === '__tests__' && r.isDirectory);
195
+ if (testDir) {
196
+ expect(testDir.isDirectory).toBe(true);
197
+ expect(testDir.type).toBe('');
198
+ }
199
+ });
200
+
201
+ it('should correctly identify markdown files', async () => {
202
+ const results = await searchService.search({
203
+ keywords: 'CLAUDE.md',
204
+ limit: 5,
205
+ onlyIn: repoRoot,
206
+ });
207
+
208
+ const mdFile = results.find((r) => r.name === 'CLAUDE.md');
209
+ if (mdFile) {
210
+ expect(mdFile.type).toBe('md');
211
+ expect(mdFile.contentType).toBe('text');
212
+ }
213
+ });
214
+ });
215
+
216
+ describe('file metadata', () => {
217
+ it('should return valid file metadata', async () => {
218
+ const results = await searchService.search({
219
+ keywords: 'package.json',
220
+ limit: 1,
221
+ onlyIn: repoRoot,
222
+ });
223
+
224
+ expect(results.length).toBeGreaterThan(0);
225
+
226
+ const file = results[0];
227
+
228
+ // Verify all metadata fields are present
229
+ expect(file.path).toBeTruthy();
230
+ expect(file.name).toBeTruthy();
231
+ expect(typeof file.isDirectory).toBe('boolean');
232
+ expect(typeof file.size).toBe('number');
233
+ expect(file.size).toBeGreaterThanOrEqual(0);
234
+ expect(file.type).toBeDefined();
235
+ expect(file.contentType).toBeDefined();
236
+ expect(file.modifiedTime).toBeInstanceOf(Date);
237
+ expect(file.createdTime).toBeInstanceOf(Date);
238
+ expect(file.lastAccessTime).toBeInstanceOf(Date);
239
+
240
+ // Dates should be valid
241
+ expect(file.modifiedTime.getTime()).toBeGreaterThan(0);
242
+ expect(file.createdTime.getTime()).toBeGreaterThan(0);
243
+ });
244
+
245
+ it('should handle files with different extensions', async () => {
246
+ const testCases = [
247
+ { keyword: '.ts', expectedType: 'ts', expectedContentType: 'code' },
248
+ { keyword: '.json', expectedType: 'json', expectedContentType: 'code' },
249
+ { keyword: '.txt', expectedType: 'txt', expectedContentType: 'text' },
250
+ ];
251
+
252
+ for (const { keyword, expectedType, expectedContentType } of testCases) {
253
+ const results = await searchService.search({
254
+ keywords: keyword,
255
+ limit: 5,
256
+ onlyIn: repoRoot,
257
+ });
258
+
259
+ if (results.length > 0) {
260
+ const file = results.find((r) => r.type === expectedType);
261
+ if (file) {
262
+ expect(file.type).toBe(expectedType);
263
+ expect(file.contentType).toBe(expectedContentType);
264
+ }
265
+ }
266
+ }
267
+ });
268
+ });
269
+
270
+ describe('search accuracy after fix', () => {
271
+ it('should use fuzzy matching instead of exact phrase', async () => {
272
+ // Test the fix: keywords should do fuzzy matching, not exact phrase
273
+ // Before fix: "local file" would only match exact phrase "local file"
274
+ // After fix: "local file" should match "LocalFileCtr" (contains "local" and "file")
275
+
276
+ const results = await searchService.search({
277
+ keywords: 'LocalFile',
278
+ limit: 10,
279
+ onlyIn: repoRoot,
280
+ });
281
+
282
+ expect(results.length).toBeGreaterThan(0);
283
+
284
+ // Should find LocalFileCtr.ts or similar files
285
+ const found = results.some(
286
+ (r) => r.name.includes('LocalFile') || r.name.includes('localFile'),
287
+ );
288
+ expect(found).toBe(true);
289
+ });
290
+
291
+ it('should handle paths with spaces correctly', async () => {
292
+ // Test the fix: command args should be properly split
293
+ // This test verifies spawn receives correct arguments array
294
+
295
+ const pathWithSpaces = repoRoot; // May contain spaces in CI or certain setups
296
+ const results = await searchService.search({
297
+ keywords: 'test',
298
+ limit: 5,
299
+ onlyIn: pathWithSpaces,
300
+ });
301
+
302
+ // Should not throw error even if path contains spaces
303
+ expect(Array.isArray(results)).toBe(true);
304
+ });
305
+
306
+ it('should search case-insensitively', async () => {
307
+ // The "cd" flag in kMDItemFSName makes it case-insensitive
308
+
309
+ const lowerResults = await searchService.search({
310
+ keywords: 'readme',
311
+ limit: 5,
312
+ onlyIn: repoRoot,
313
+ });
314
+
315
+ const upperResults = await searchService.search({
316
+ keywords: 'README',
317
+ limit: 5,
318
+ onlyIn: repoRoot,
319
+ });
320
+
321
+ // Both searches should find similar files
322
+ expect(lowerResults.length).toBeGreaterThan(0);
323
+ expect(upperResults.length).toBeGreaterThan(0);
324
+ });
325
+ });
326
+
327
+ describe('error handling', () => {
328
+ it('should handle non-existent directory gracefully', async () => {
329
+ const nonExistentPath = path.join(repoRoot, 'this-directory-does-not-exist-12345');
330
+
331
+ const results = await searchService.search({
332
+ keywords: 'test',
333
+ limit: 5,
334
+ onlyIn: nonExistentPath,
335
+ });
336
+
337
+ // Should return empty array instead of throwing
338
+ expect(results).toEqual([]);
339
+ });
340
+ });
341
+
342
+ describe('updateSearchIndex', () => {
343
+ it.skip('should handle index update request', async () => {
344
+ // Index update requires elevated permissions, may fail in restricted environments
345
+ const result = await searchService.updateSearchIndex(repoRoot);
346
+
347
+ // Should return boolean (true if succeeded, false if failed)
348
+ expect(typeof result).toBe('boolean');
349
+ }, 15000); // Index update can take time
350
+ });
351
+ });
352
+
353
+ // Skip message for non-macOS platforms
354
+ if (process.platform !== 'darwin') {
355
+ // eslint-disable-next-line no-console
356
+ console.log('⏭️ Skipping macOS integration tests on', process.platform, '(only runs on darwin)');
357
+ }
@@ -23,12 +23,11 @@ export class MacOSSearchServiceImpl extends FileSearchImpl {
23
23
  */
24
24
  async search(options: SearchOptions): Promise<FileResult[]> {
25
25
  // Build the command first, regardless of execution method
26
- const command = this.buildSearchCommand(options);
27
- logger.debug(`Executing command: ${command}`);
26
+ const { cmd, args, commandString } = this.buildSearchCommand(options);
27
+ logger.debug(`Executing command: ${commandString}`);
28
28
 
29
29
  // Use spawn for both live and non-live updates to handle large outputs
30
30
  return new Promise((resolve, reject) => {
31
- const [cmd, ...args] = command.split(' ');
32
31
  const childProcess = spawn(cmd, args);
33
32
 
34
33
  let results: string[] = []; // Store raw file paths
@@ -137,31 +136,39 @@ export class MacOSSearchServiceImpl extends FileSearchImpl {
137
136
  /**
138
137
  * Build mdfind command string
139
138
  * @param options Search options
140
- * @returns Complete command string
139
+ * @returns Command components (cmd, args array, and command string for logging)
141
140
  */
142
- private buildSearchCommand(options: SearchOptions): string {
143
- // Basic command
144
- let command = 'mdfind';
145
-
146
- // Add options
147
- const mdFindOptions: string[] = [];
141
+ private buildSearchCommand(options: SearchOptions): {
142
+ args: string[];
143
+ cmd: string;
144
+ commandString: string;
145
+ } {
146
+ // Command and arguments array
147
+ const cmd = 'mdfind';
148
+ const args: string[] = [];
148
149
 
149
150
  // macOS mdfind doesn't support -limit parameter, we'll limit results in post-processing
150
151
 
151
152
  // Search in specific directory
152
153
  if (options.onlyIn) {
153
- mdFindOptions.push(`-onlyin "${options.onlyIn}"`);
154
+ args.push('-onlyin', options.onlyIn);
154
155
  }
155
156
 
156
157
  // Live update
157
158
  if (options.liveUpdate) {
158
- mdFindOptions.push('-live');
159
+ args.push('-live');
159
160
  }
160
161
 
161
162
  // Detailed metadata
162
163
  if (options.detailed) {
163
- mdFindOptions.push(
164
- '-attr kMDItemDisplayName kMDItemContentType kMDItemKind kMDItemFSSize kMDItemFSCreationDate kMDItemFSContentChangeDate',
164
+ args.push(
165
+ '-attr',
166
+ 'kMDItemDisplayName',
167
+ 'kMDItemContentType',
168
+ 'kMDItemKind',
169
+ 'kMDItemFSSize',
170
+ 'kMDItemFSCreationDate',
171
+ 'kMDItemFSContentChangeDate',
165
172
  );
166
173
  }
167
174
 
@@ -171,9 +178,10 @@ export class MacOSSearchServiceImpl extends FileSearchImpl {
171
178
  // Basic query
172
179
  if (options.keywords) {
173
180
  // If the query string doesn't use Spotlight query syntax (doesn't contain kMDItem properties),
174
- // treat it as plain text search
181
+ // treat it as a flexible name search rather than exact phrase match
175
182
  if (!options.keywords.includes('kMDItem')) {
176
- queryExpression = `"${options.keywords.replaceAll('"', '\\"')}"`;
183
+ // Use kMDItemFSName for filename matching with wildcards for better flexibility
184
+ queryExpression = `kMDItemFSName == "*${options.keywords.replaceAll('"', '\\"')}*"cd`;
177
185
  } else {
178
186
  queryExpression = options.keywords;
179
187
  }
@@ -244,15 +252,15 @@ export class MacOSSearchServiceImpl extends FileSearchImpl {
244
252
  }
245
253
  }
246
254
 
247
- // Combine complete command
248
- if (mdFindOptions.length > 0) {
249
- command += ' ' + mdFindOptions.join(' ');
255
+ // Add query expression to args
256
+ if (queryExpression) {
257
+ args.push(queryExpression);
250
258
  }
251
259
 
252
- // Finally add query expression
253
- command += ` ${queryExpression}`;
260
+ // Build command string for logging
261
+ const commandString = `${cmd} ${args.map((arg) => (arg.includes(' ') || arg.includes('*') ? `"${arg}"` : arg)).join(' ')}`;
254
262
 
255
- return command;
263
+ return { args, cmd, commandString };
256
264
  }
257
265
 
258
266
  /**
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Filter out reasoning fields from messages in ChatCompletion API."
6
+ ]
7
+ },
8
+ "date": "2025-11-13",
9
+ "version": "2.0.0-next.52"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Update ERNIE-5.0-Thinking-Preview model."
15
+ ]
16
+ },
17
+ "date": "2025-11-13",
18
+ "version": "2.0.0-next.51"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "fixes": [
@@ -369,6 +369,26 @@
369
369
  "remained": "متبقي",
370
370
  "used": "مستخدم"
371
371
  },
372
+ "tool": {
373
+ "intervention": {
374
+ "approve": "الموافقة",
375
+ "approveAndRemember": "الموافقة والتذكر",
376
+ "approveOnce": "الموافقة لمرة واحدة فقط",
377
+ "mode": {
378
+ "allowList": "قائمة السماح",
379
+ "allowListDesc": "تنفيذ الأدوات المعتمدة فقط تلقائيًا",
380
+ "autoRun": "الموافقة التلقائية",
381
+ "autoRunDesc": "الموافقة تلقائيًا على تنفيذ جميع الأدوات",
382
+ "manual": "يدوي",
383
+ "manualDesc": "يتطلب الموافقة اليدوية في كل مرة يتم فيها الاستدعاء"
384
+ },
385
+ "reject": "رفض",
386
+ "rejectReasonPlaceholder": "إدخال سبب الرفض سيساعد الوكيل على الفهم وتحسين الإجراءات المستقبلية",
387
+ "rejectTitle": "رفض استدعاء الأداة هذه المرة",
388
+ "rejectedWithReason": "تم رفض استدعاء الأداة هذه المرة بشكل يدوي: {{reason}}",
389
+ "toolRejected": "تم رفض استدعاء الأداة هذه المرة بشكل يدوي"
390
+ }
391
+ },
372
392
  "topic": {
373
393
  "checkOpenNewTopic": "هل ترغب في فتح موضوع جديد؟",
374
394
  "checkSaveCurrentMessages": "هل ترغب في حفظ الدردشة الحالية كموضوع؟",
@@ -135,6 +135,7 @@
135
135
  }
136
136
  },
137
137
  "close": "إغلاق",
138
+ "confirm": "تأكيد",
138
139
  "contact": "اتصل بنا",
139
140
  "copy": "نسخ",
140
141
  "copyFail": "فشل في النسخ",
@@ -106,6 +106,12 @@
106
106
  "keyPlaceholder": "المفتاح",
107
107
  "valuePlaceholder": "القيمة"
108
108
  },
109
+ "LocalFile": {
110
+ "action": {
111
+ "open": "فتح",
112
+ "showInFolder": "عرض في المجلد"
113
+ }
114
+ },
109
115
  "MaxTokenSlider": {
110
116
  "unlimited": "غير محدود"
111
117
  },