@lobehub/lobehub 2.0.0-next.37 → 2.0.0-next.39

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 (127) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/apps/desktop/src/main/modules/networkProxy/__tests__/dispatcher.test.ts +401 -0
  3. package/apps/desktop/src/main/modules/networkProxy/__tests__/tester.test.ts +531 -0
  4. package/apps/desktop/src/main/modules/networkProxy/__tests__/urlBuilder.test.ts +349 -0
  5. package/apps/desktop/src/main/modules/networkProxy/__tests__/validator.test.ts +492 -0
  6. package/changelog/v1.json +14 -0
  7. package/locales/ar/auth.json +45 -1
  8. package/locales/ar/modelProvider.json +13 -1
  9. package/locales/bg-BG/auth.json +45 -1
  10. package/locales/bg-BG/modelProvider.json +13 -1
  11. package/locales/de-DE/auth.json +45 -1
  12. package/locales/de-DE/modelProvider.json +13 -1
  13. package/locales/en-US/auth.json +45 -1
  14. package/locales/en-US/modelProvider.json +13 -1
  15. package/locales/es-ES/auth.json +45 -1
  16. package/locales/es-ES/modelProvider.json +13 -1
  17. package/locales/fa-IR/auth.json +45 -1
  18. package/locales/fa-IR/modelProvider.json +13 -1
  19. package/locales/fr-FR/auth.json +45 -1
  20. package/locales/fr-FR/modelProvider.json +13 -1
  21. package/locales/it-IT/auth.json +45 -1
  22. package/locales/it-IT/modelProvider.json +13 -1
  23. package/locales/ja-JP/auth.json +45 -1
  24. package/locales/ja-JP/modelProvider.json +13 -1
  25. package/locales/ko-KR/auth.json +45 -1
  26. package/locales/ko-KR/modelProvider.json +13 -1
  27. package/locales/nl-NL/auth.json +45 -1
  28. package/locales/nl-NL/modelProvider.json +13 -1
  29. package/locales/pl-PL/auth.json +45 -1
  30. package/locales/pl-PL/modelProvider.json +13 -1
  31. package/locales/pt-BR/auth.json +45 -1
  32. package/locales/pt-BR/modelProvider.json +13 -1
  33. package/locales/ru-RU/auth.json +45 -1
  34. package/locales/ru-RU/modelProvider.json +13 -1
  35. package/locales/tr-TR/auth.json +45 -1
  36. package/locales/tr-TR/modelProvider.json +13 -1
  37. package/locales/vi-VN/auth.json +45 -1
  38. package/locales/vi-VN/modelProvider.json +13 -1
  39. package/locales/zh-CN/auth.json +45 -1
  40. package/locales/zh-CN/modelProvider.json +13 -1
  41. package/locales/zh-TW/auth.json +45 -1
  42. package/locales/zh-TW/modelProvider.json +13 -1
  43. package/package.json +1 -1
  44. package/packages/context-engine/src/processors/MessageCleanup.ts +1 -0
  45. package/packages/context-engine/src/processors/__tests__/MessageCleanup.test.ts +28 -0
  46. package/packages/obervability-otel/package.json +3 -1
  47. package/packages/obervability-otel/src/api.ts +2 -0
  48. package/packages/obervability-otel/src/trpc/convention.ts +16 -0
  49. package/packages/obervability-otel/src/trpc/index.test.ts +38 -0
  50. package/packages/obervability-otel/src/trpc/index.ts +62 -0
  51. package/packages/obervability-otel/src/trpc/metrics.ts +31 -0
  52. package/packages/types/src/usage/usageRecord.ts +54 -0
  53. package/packages/web-crawler/src/crawImpl/browserless.ts +1 -1
  54. package/packages/web-crawler/src/crawImpl/naive.ts +9 -9
  55. package/packages/web-crawler/src/crawler.ts +5 -5
  56. package/packages/web-crawler/src/urlRules.ts +13 -13
  57. package/packages/web-crawler/src/utils/appUrlRules.ts +5 -5
  58. package/src/app/[variants]/(main)/profile/hooks/useCategory.tsx +10 -1
  59. package/src/app/[variants]/(main)/profile/usage/Client.tsx +114 -0
  60. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/ActiveModels/ModelTable.tsx +175 -0
  61. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/ActiveModels/index.tsx +126 -0
  62. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/MonthSpend.tsx +53 -0
  63. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/TodaySpend.tsx +67 -0
  64. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/index.tsx +19 -0
  65. package/src/app/[variants]/(main)/profile/usage/features/UsageTable.tsx +145 -0
  66. package/src/app/[variants]/(main)/profile/usage/features/UsageTrends.tsx +107 -0
  67. package/src/app/[variants]/(main)/profile/usage/features/components/UsageBarChart.tsx +48 -0
  68. package/src/app/[variants]/(main)/profile/usage/page.tsx +23 -0
  69. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +3 -3
  70. package/src/features/Conversation/Messages/Group/Actions/WithoutContentId.tsx +37 -14
  71. package/src/features/Conversation/Messages/Group/Error/index.tsx +1 -1
  72. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +13 -35
  73. package/src/features/Conversation/Messages/Group/GroupItem.tsx +43 -0
  74. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +1 -2
  75. package/src/features/Conversation/Messages/Group/Tool/Render/CustomRender.tsx +1 -1
  76. package/src/features/Conversation/Messages/Group/Tool/index.tsx +0 -2
  77. package/src/features/Conversation/Messages/Group/index.tsx +7 -2
  78. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +3 -0
  79. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +21 -7
  80. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -1
  81. package/src/features/PluginsUI/Render/MCPType/index.tsx +52 -0
  82. package/src/features/PluginsUI/Render/StandaloneType/Iframe.tsx +2 -2
  83. package/src/features/PluginsUI/Render/index.tsx +17 -0
  84. package/src/libs/mcp/client.ts +3 -2
  85. package/src/libs/mcp/types.ts +71 -0
  86. package/src/libs/trpc/lambda/index.ts +5 -2
  87. package/src/libs/trpc/middleware/openTelemetry.ts +141 -0
  88. package/src/locales/default/auth.ts +44 -0
  89. package/src/locales/default/chat.ts +1 -0
  90. package/src/server/routers/desktop/mcp.ts +1 -3
  91. package/src/server/routers/lambda/index.ts +2 -0
  92. package/src/server/routers/lambda/usage.ts +36 -0
  93. package/src/server/routers/tools/mcp.ts +1 -3
  94. package/src/server/services/mcp/index.test.ts +28 -15
  95. package/src/server/services/mcp/index.ts +29 -18
  96. package/src/server/services/usage/index.test.ts +310 -0
  97. package/src/server/services/usage/index.ts +164 -0
  98. package/src/services/chat/contextEngineering.test.ts +4 -0
  99. package/src/services/mcp.test.ts +7 -1
  100. package/src/services/mcp.ts +13 -12
  101. package/src/services/usage.ts +13 -0
  102. package/src/store/chat/agents/createAgentExecutors.ts +2 -3
  103. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +40 -1
  104. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +13 -5
  105. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +3 -3
  106. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +6 -6
  107. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +2 -2
  108. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
  109. package/src/store/chat/slices/builtinTool/actions/search.ts +6 -6
  110. package/src/store/chat/slices/message/actions/publicApi.ts +19 -1
  111. package/src/store/chat/slices/message/initialState.ts +5 -0
  112. package/src/store/chat/slices/message/selectors/chat.test.ts +22 -602
  113. package/src/store/chat/slices/message/selectors/chat.ts +0 -2
  114. package/src/store/chat/slices/message/selectors/dbMessage.test.ts +51 -0
  115. package/src/store/chat/slices/message/selectors/displayMessage.test.ts +818 -0
  116. package/src/store/chat/slices/message/selectors/displayMessage.ts +52 -1
  117. package/src/store/chat/slices/message/selectors/messageState.ts +2 -0
  118. package/src/store/chat/slices/plugin/action.test.ts +4 -4
  119. package/src/store/chat/slices/plugin/actions/index.ts +39 -0
  120. package/src/store/chat/slices/plugin/actions/internals.ts +83 -0
  121. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +188 -0
  122. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +213 -0
  123. package/src/store/chat/slices/plugin/actions/publicApi.ts +115 -0
  124. package/src/store/chat/slices/plugin/actions/workflow.ts +121 -0
  125. package/src/store/chat/store.ts +1 -1
  126. package/src/store/global/initialState.ts +1 -0
  127. package/src/store/chat/slices/plugin/action.ts +0 -539
@@ -0,0 +1,492 @@
1
+ import { NetworkProxySettings } from '@lobechat/electron-client-ipc';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { ProxyConfigValidator } from '../validator';
5
+
6
+ describe('ProxyConfigValidator', () => {
7
+ const validConfig: NetworkProxySettings = {
8
+ enableProxy: true,
9
+ proxyType: 'http',
10
+ proxyServer: 'proxy.example.com',
11
+ proxyPort: '8080',
12
+ proxyRequireAuth: false,
13
+ proxyBypass: 'localhost,127.0.0.1,::1',
14
+ };
15
+
16
+ describe('validate', () => {
17
+ describe('disabled proxy', () => {
18
+ it('should validate successfully when proxy is disabled', () => {
19
+ const config: NetworkProxySettings = {
20
+ ...validConfig,
21
+ enableProxy: false,
22
+ };
23
+
24
+ const result = ProxyConfigValidator.validate(config);
25
+
26
+ expect(result.isValid).toBe(true);
27
+ expect(result.errors).toHaveLength(0);
28
+ });
29
+
30
+ it('should skip validation for disabled proxy even with invalid fields', () => {
31
+ const config: NetworkProxySettings = {
32
+ enableProxy: false,
33
+ proxyType: 'invalid' as any,
34
+ proxyServer: '',
35
+ proxyPort: 'invalid',
36
+ proxyRequireAuth: false,
37
+ proxyBypass: 'localhost',
38
+ };
39
+
40
+ const result = ProxyConfigValidator.validate(config);
41
+
42
+ expect(result.isValid).toBe(true);
43
+ expect(result.errors).toHaveLength(0);
44
+ });
45
+ });
46
+
47
+ describe('proxy type validation', () => {
48
+ it('should accept http proxy type', () => {
49
+ const config: NetworkProxySettings = {
50
+ ...validConfig,
51
+ proxyType: 'http',
52
+ };
53
+
54
+ const result = ProxyConfigValidator.validate(config);
55
+
56
+ expect(result.isValid).toBe(true);
57
+ expect(result.errors).toHaveLength(0);
58
+ });
59
+
60
+ it('should accept https proxy type', () => {
61
+ const config: NetworkProxySettings = {
62
+ ...validConfig,
63
+ proxyType: 'https',
64
+ };
65
+
66
+ const result = ProxyConfigValidator.validate(config);
67
+
68
+ expect(result.isValid).toBe(true);
69
+ expect(result.errors).toHaveLength(0);
70
+ });
71
+
72
+ it('should accept socks5 proxy type', () => {
73
+ const config: NetworkProxySettings = {
74
+ ...validConfig,
75
+ proxyType: 'socks5',
76
+ };
77
+
78
+ const result = ProxyConfigValidator.validate(config);
79
+
80
+ expect(result.isValid).toBe(true);
81
+ expect(result.errors).toHaveLength(0);
82
+ });
83
+
84
+ it('should reject unsupported proxy type', () => {
85
+ const config: NetworkProxySettings = {
86
+ ...validConfig,
87
+ proxyType: 'socks4' as any,
88
+ };
89
+
90
+ const result = ProxyConfigValidator.validate(config);
91
+
92
+ expect(result.isValid).toBe(false);
93
+ expect(result.errors).toHaveLength(1);
94
+ expect(result.errors[0]).toContain('Unsupported proxy type');
95
+ expect(result.errors[0]).toContain('socks4');
96
+ });
97
+
98
+ it('should reject invalid proxy type', () => {
99
+ const config: NetworkProxySettings = {
100
+ ...validConfig,
101
+ proxyType: 'ftp' as any,
102
+ };
103
+
104
+ const result = ProxyConfigValidator.validate(config);
105
+
106
+ expect(result.isValid).toBe(false);
107
+ expect(result.errors[0]).toContain('Supported types: http, https, socks5');
108
+ });
109
+ });
110
+
111
+ describe('proxy server validation', () => {
112
+ it('should accept valid domain name', () => {
113
+ const config: NetworkProxySettings = {
114
+ ...validConfig,
115
+ proxyServer: 'proxy.example.com',
116
+ };
117
+
118
+ const result = ProxyConfigValidator.validate(config);
119
+
120
+ expect(result.isValid).toBe(true);
121
+ expect(result.errors).toHaveLength(0);
122
+ });
123
+
124
+ it('should accept valid IPv4 address', () => {
125
+ const config: NetworkProxySettings = {
126
+ ...validConfig,
127
+ proxyServer: '192.168.1.1',
128
+ };
129
+
130
+ const result = ProxyConfigValidator.validate(config);
131
+
132
+ expect(result.isValid).toBe(true);
133
+ expect(result.errors).toHaveLength(0);
134
+ });
135
+
136
+ it('should accept localhost', () => {
137
+ const config: NetworkProxySettings = {
138
+ ...validConfig,
139
+ proxyServer: 'localhost',
140
+ };
141
+
142
+ const result = ProxyConfigValidator.validate(config);
143
+
144
+ expect(result.isValid).toBe(true);
145
+ expect(result.errors).toHaveLength(0);
146
+ });
147
+
148
+ it('should accept subdomain', () => {
149
+ const config: NetworkProxySettings = {
150
+ ...validConfig,
151
+ proxyServer: 'proxy.subdomain.example.com',
152
+ };
153
+
154
+ const result = ProxyConfigValidator.validate(config);
155
+
156
+ expect(result.isValid).toBe(true);
157
+ expect(result.errors).toHaveLength(0);
158
+ });
159
+
160
+ it('should reject empty proxy server', () => {
161
+ const config: NetworkProxySettings = {
162
+ ...validConfig,
163
+ proxyServer: '',
164
+ };
165
+
166
+ const result = ProxyConfigValidator.validate(config);
167
+
168
+ expect(result.isValid).toBe(false);
169
+ expect(result.errors).toContain('Proxy server is required when proxy is enabled');
170
+ });
171
+
172
+ it('should reject whitespace-only proxy server', () => {
173
+ const config: NetworkProxySettings = {
174
+ ...validConfig,
175
+ proxyServer: ' ',
176
+ };
177
+
178
+ const result = ProxyConfigValidator.validate(config);
179
+
180
+ expect(result.isValid).toBe(false);
181
+ expect(result.errors).toContain('Proxy server is required when proxy is enabled');
182
+ });
183
+
184
+ it('should reject invalid domain format', () => {
185
+ const config: NetworkProxySettings = {
186
+ ...validConfig,
187
+ proxyServer: 'invalid..domain',
188
+ };
189
+
190
+ const result = ProxyConfigValidator.validate(config);
191
+
192
+ expect(result.isValid).toBe(false);
193
+ expect(result.errors).toContain('Invalid proxy server format');
194
+ });
195
+
196
+ it('should reject domain starting with hyphen', () => {
197
+ const config: NetworkProxySettings = {
198
+ ...validConfig,
199
+ proxyServer: '-proxy.com',
200
+ };
201
+
202
+ const result = ProxyConfigValidator.validate(config);
203
+
204
+ expect(result.isValid).toBe(false);
205
+ expect(result.errors).toContain('Invalid proxy server format');
206
+ });
207
+
208
+ it('should reject domain with invalid characters', () => {
209
+ const config: NetworkProxySettings = {
210
+ ...validConfig,
211
+ proxyServer: 'proxy@example.com',
212
+ };
213
+
214
+ const result = ProxyConfigValidator.validate(config);
215
+
216
+ expect(result.isValid).toBe(false);
217
+ expect(result.errors).toContain('Invalid proxy server format');
218
+ });
219
+ });
220
+
221
+ describe('proxy port validation', () => {
222
+ it('should accept valid port 1', () => {
223
+ const config: NetworkProxySettings = {
224
+ ...validConfig,
225
+ proxyPort: '1',
226
+ };
227
+
228
+ const result = ProxyConfigValidator.validate(config);
229
+
230
+ expect(result.isValid).toBe(true);
231
+ expect(result.errors).toHaveLength(0);
232
+ });
233
+
234
+ it('should accept valid port 65535', () => {
235
+ const config: NetworkProxySettings = {
236
+ ...validConfig,
237
+ proxyPort: '65535',
238
+ };
239
+
240
+ const result = ProxyConfigValidator.validate(config);
241
+
242
+ expect(result.isValid).toBe(true);
243
+ expect(result.errors).toHaveLength(0);
244
+ });
245
+
246
+ it('should accept common proxy port 8080', () => {
247
+ const config: NetworkProxySettings = {
248
+ ...validConfig,
249
+ proxyPort: '8080',
250
+ };
251
+
252
+ const result = ProxyConfigValidator.validate(config);
253
+
254
+ expect(result.isValid).toBe(true);
255
+ expect(result.errors).toHaveLength(0);
256
+ });
257
+
258
+ it('should reject empty proxy port', () => {
259
+ const config: NetworkProxySettings = {
260
+ ...validConfig,
261
+ proxyPort: '',
262
+ };
263
+
264
+ const result = ProxyConfigValidator.validate(config);
265
+
266
+ expect(result.isValid).toBe(false);
267
+ expect(result.errors).toContain('Proxy port is required when proxy is enabled');
268
+ });
269
+
270
+ it('should reject whitespace-only proxy port', () => {
271
+ const config: NetworkProxySettings = {
272
+ ...validConfig,
273
+ proxyPort: ' ',
274
+ };
275
+
276
+ const result = ProxyConfigValidator.validate(config);
277
+
278
+ expect(result.isValid).toBe(false);
279
+ expect(result.errors).toContain('Proxy port is required when proxy is enabled');
280
+ });
281
+
282
+ it('should reject port 0', () => {
283
+ const config: NetworkProxySettings = {
284
+ ...validConfig,
285
+ proxyPort: '0',
286
+ };
287
+
288
+ const result = ProxyConfigValidator.validate(config);
289
+
290
+ expect(result.isValid).toBe(false);
291
+ expect(result.errors).toContain('Proxy port must be a valid number between 1 and 65535');
292
+ });
293
+
294
+ it('should reject port above 65535', () => {
295
+ const config: NetworkProxySettings = {
296
+ ...validConfig,
297
+ proxyPort: '65536',
298
+ };
299
+
300
+ const result = ProxyConfigValidator.validate(config);
301
+
302
+ expect(result.isValid).toBe(false);
303
+ expect(result.errors).toContain('Proxy port must be a valid number between 1 and 65535');
304
+ });
305
+
306
+ it('should reject negative port', () => {
307
+ const config: NetworkProxySettings = {
308
+ ...validConfig,
309
+ proxyPort: '-1',
310
+ };
311
+
312
+ const result = ProxyConfigValidator.validate(config);
313
+
314
+ expect(result.isValid).toBe(false);
315
+ expect(result.errors).toContain('Proxy port must be a valid number between 1 and 65535');
316
+ });
317
+
318
+ it('should reject non-numeric port', () => {
319
+ const config: NetworkProxySettings = {
320
+ ...validConfig,
321
+ proxyPort: 'abc',
322
+ };
323
+
324
+ const result = ProxyConfigValidator.validate(config);
325
+
326
+ expect(result.isValid).toBe(false);
327
+ expect(result.errors).toContain('Proxy port must be a valid number between 1 and 65535');
328
+ });
329
+ });
330
+
331
+ describe('authentication validation', () => {
332
+ it('should validate successfully with auth disabled', () => {
333
+ const config: NetworkProxySettings = {
334
+ ...validConfig,
335
+ proxyRequireAuth: false,
336
+ };
337
+
338
+ const result = ProxyConfigValidator.validate(config);
339
+
340
+ expect(result.isValid).toBe(true);
341
+ expect(result.errors).toHaveLength(0);
342
+ });
343
+
344
+ it('should validate successfully with auth enabled and credentials provided', () => {
345
+ const config: NetworkProxySettings = {
346
+ ...validConfig,
347
+ proxyRequireAuth: true,
348
+ proxyUsername: 'testuser',
349
+ proxyPassword: 'testpass',
350
+ };
351
+
352
+ const result = ProxyConfigValidator.validate(config);
353
+
354
+ expect(result.isValid).toBe(true);
355
+ expect(result.errors).toHaveLength(0);
356
+ });
357
+
358
+ it('should reject when auth is enabled but username is missing', () => {
359
+ const config: NetworkProxySettings = {
360
+ ...validConfig,
361
+ proxyRequireAuth: true,
362
+ proxyUsername: '',
363
+ proxyPassword: 'testpass',
364
+ };
365
+
366
+ const result = ProxyConfigValidator.validate(config);
367
+
368
+ expect(result.isValid).toBe(false);
369
+ expect(result.errors).toContain(
370
+ 'Proxy username is required when authentication is enabled',
371
+ );
372
+ });
373
+
374
+ it('should reject when auth is enabled but username is whitespace', () => {
375
+ const config: NetworkProxySettings = {
376
+ ...validConfig,
377
+ proxyRequireAuth: true,
378
+ proxyUsername: ' ',
379
+ proxyPassword: 'testpass',
380
+ };
381
+
382
+ const result = ProxyConfigValidator.validate(config);
383
+
384
+ expect(result.isValid).toBe(false);
385
+ expect(result.errors).toContain(
386
+ 'Proxy username is required when authentication is enabled',
387
+ );
388
+ });
389
+
390
+ it('should reject when auth is enabled but password is missing', () => {
391
+ const config: NetworkProxySettings = {
392
+ ...validConfig,
393
+ proxyRequireAuth: true,
394
+ proxyUsername: 'testuser',
395
+ proxyPassword: '',
396
+ };
397
+
398
+ const result = ProxyConfigValidator.validate(config);
399
+
400
+ expect(result.isValid).toBe(false);
401
+ expect(result.errors).toContain(
402
+ 'Proxy password is required when authentication is enabled',
403
+ );
404
+ });
405
+
406
+ it('should reject when auth is enabled but password is whitespace', () => {
407
+ const config: NetworkProxySettings = {
408
+ ...validConfig,
409
+ proxyRequireAuth: true,
410
+ proxyUsername: 'testuser',
411
+ proxyPassword: ' ',
412
+ };
413
+
414
+ const result = ProxyConfigValidator.validate(config);
415
+
416
+ expect(result.isValid).toBe(false);
417
+ expect(result.errors).toContain(
418
+ 'Proxy password is required when authentication is enabled',
419
+ );
420
+ });
421
+
422
+ it('should reject when auth is enabled but both username and password are missing', () => {
423
+ const config: NetworkProxySettings = {
424
+ ...validConfig,
425
+ proxyRequireAuth: true,
426
+ proxyUsername: '',
427
+ proxyPassword: '',
428
+ };
429
+
430
+ const result = ProxyConfigValidator.validate(config);
431
+
432
+ expect(result.isValid).toBe(false);
433
+ expect(result.errors).toHaveLength(2);
434
+ expect(result.errors).toContain(
435
+ 'Proxy username is required when authentication is enabled',
436
+ );
437
+ expect(result.errors).toContain(
438
+ 'Proxy password is required when authentication is enabled',
439
+ );
440
+ });
441
+
442
+ it('should allow missing credentials when auth is disabled', () => {
443
+ const config: NetworkProxySettings = {
444
+ ...validConfig,
445
+ proxyRequireAuth: false,
446
+ proxyUsername: undefined,
447
+ proxyPassword: undefined,
448
+ };
449
+
450
+ const result = ProxyConfigValidator.validate(config);
451
+
452
+ expect(result.isValid).toBe(true);
453
+ expect(result.errors).toHaveLength(0);
454
+ });
455
+ });
456
+
457
+ describe('multiple validation errors', () => {
458
+ it('should collect all validation errors', () => {
459
+ const config: NetworkProxySettings = {
460
+ enableProxy: true,
461
+ proxyType: 'invalid' as any,
462
+ proxyServer: '',
463
+ proxyPort: 'abc',
464
+ proxyRequireAuth: true,
465
+ proxyUsername: '',
466
+ proxyPassword: '',
467
+ proxyBypass: 'localhost',
468
+ };
469
+
470
+ const result = ProxyConfigValidator.validate(config);
471
+
472
+ expect(result.isValid).toBe(false);
473
+ expect(result.errors.length).toBeGreaterThan(1);
474
+ });
475
+
476
+ it('should collect errors for invalid server and port', () => {
477
+ const config: NetworkProxySettings = {
478
+ ...validConfig,
479
+ proxyServer: 'invalid..domain',
480
+ proxyPort: '99999',
481
+ };
482
+
483
+ const result = ProxyConfigValidator.validate(config);
484
+
485
+ expect(result.isValid).toBe(false);
486
+ expect(result.errors).toHaveLength(2);
487
+ expect(result.errors).toContain('Invalid proxy server format');
488
+ expect(result.errors).toContain('Proxy port must be a valid number between 1 and 65535');
489
+ });
490
+ });
491
+ });
492
+ });
package/changelog/v1.json CHANGED
@@ -1,4 +1,18 @@
1
1
  [
2
+ {
3
+ "children": {},
4
+ "date": "2025-11-08",
5
+ "version": "2.0.0-next.39"
6
+ },
7
+ {
8
+ "children": {
9
+ "improvements": [
10
+ "Update i18n."
11
+ ]
12
+ },
13
+ "date": "2025-11-08",
14
+ "version": "2.0.0-next.38"
15
+ },
2
16
  {
3
17
  "children": {
4
18
  "fixes": [
@@ -145,6 +145,50 @@
145
145
  "apikey": "إدارة مفاتيح API",
146
146
  "profile": "الملف الشخصي",
147
147
  "security": "الأمان",
148
- "stats": "الإحصائيات"
148
+ "stats": "الإحصائيات",
149
+ "usage": "إحصاءات الاستخدام"
150
+ },
151
+ "usage": {
152
+ "activeModels": {
153
+ "modelTable": "قائمة النماذج",
154
+ "models": "النماذج النشطة",
155
+ "providerTable": "قائمة المزودين",
156
+ "providers": "المزودون النشطون",
157
+ "table": {
158
+ "calls": "عدد الاستدعاءات",
159
+ "model": "النموذج",
160
+ "provider": "المزود",
161
+ "spend": "التكلفة"
162
+ }
163
+ },
164
+ "cards": {
165
+ "month": {
166
+ "modelCalls": "استدعاءات النموذج",
167
+ "title": "إنفاق هذا الشهر"
168
+ },
169
+ "today": {
170
+ "title": "إنفاق اليوم",
171
+ "yesterday": "أمس"
172
+ }
173
+ },
174
+ "table": {
175
+ "actions": "إجراءات",
176
+ "createdAt": "وقت الاستخدام",
177
+ "inputTokens": "رموز الإدخال",
178
+ "model": "النموذج",
179
+ "outputTokens": "رموز الإخراج",
180
+ "spend": "التكلفة",
181
+ "tps": "TPS",
182
+ "ttft": "TTFT",
183
+ "type": "نوع الاستدعاء"
184
+ },
185
+ "trends": {
186
+ "spend": "المبلغ",
187
+ "tokens": "الرموز"
188
+ },
189
+ "welcome": {
190
+ "model": "النموذج",
191
+ "provider": "المزود"
192
+ }
149
193
  }
150
194
  }
@@ -199,6 +199,12 @@
199
199
  "all": "الكل",
200
200
  "list": {
201
201
  "disabled": "غير مفعل",
202
+ "disabledActions": {
203
+ "sort": "طريقة الترتيب",
204
+ "sortAlphabetical": "ترتيب أبجديًا",
205
+ "sortAlphabeticalDesc": "ترتيب أبجدي عكسي",
206
+ "sortDefault": "الترتيب الافتراضي"
207
+ },
202
208
  "enabled": "مفعل"
203
209
  },
204
210
  "notFound": "لم يتم العثور على نتائج البحث",
@@ -391,7 +397,13 @@
391
397
  "addNew": "إضافة نموذج",
392
398
  "disabled": "غير مفعل",
393
399
  "disabledActions": {
394
- "showMore": "عرض الكل"
400
+ "showMore": "عرض الكل",
401
+ "sort": "طريقة الترتيب",
402
+ "sortAlphabetical": "ترتيب أبجديًا",
403
+ "sortAlphabeticalDesc": "ترتيب أبجدي عكسي",
404
+ "sortDefault": "الترتيب الافتراضي",
405
+ "sortReleasedAt": "ترتيب حسب أقدم تاريخ إصدار",
406
+ "sortReleasedAtDesc": "ترتيب حسب أحدث تاريخ إصدار"
395
407
  },
396
408
  "empty": {
397
409
  "desc": "يرجى إنشاء نموذج مخصص أو سحب نموذج للبدء في الاستخدام",
@@ -145,6 +145,50 @@
145
145
  "apikey": "Управление на API ключове",
146
146
  "profile": "Профил",
147
147
  "security": "Сигурност",
148
- "stats": "Статистика"
148
+ "stats": "Статистика",
149
+ "usage": "Статистика за използване"
150
+ },
151
+ "usage": {
152
+ "activeModels": {
153
+ "modelTable": "Списък с модели",
154
+ "models": "Активни модели",
155
+ "providerTable": "Списък с доставчици",
156
+ "providers": "Активни доставчици",
157
+ "table": {
158
+ "calls": "Брой извиквания",
159
+ "model": "Модел",
160
+ "provider": "Доставчик",
161
+ "spend": "Разходи"
162
+ }
163
+ },
164
+ "cards": {
165
+ "month": {
166
+ "modelCalls": "Извиквания на модела",
167
+ "title": "Разходи за този месец"
168
+ },
169
+ "today": {
170
+ "title": "Разходи за днес",
171
+ "yesterday": "Вчера"
172
+ }
173
+ },
174
+ "table": {
175
+ "actions": "Действия",
176
+ "createdAt": "Време на използване",
177
+ "inputTokens": "Входни токени",
178
+ "model": "Модел",
179
+ "outputTokens": "Изходни токени",
180
+ "spend": "Разходи",
181
+ "tps": "TPS",
182
+ "ttft": "TTFT",
183
+ "type": "Тип извикване"
184
+ },
185
+ "trends": {
186
+ "spend": "Сума",
187
+ "tokens": "Токени"
188
+ },
189
+ "welcome": {
190
+ "model": "Модел",
191
+ "provider": "Доставчик"
192
+ }
149
193
  }
150
194
  }
@@ -199,6 +199,12 @@
199
199
  "all": "Всички",
200
200
  "list": {
201
201
  "disabled": "Неактивиран",
202
+ "disabledActions": {
203
+ "sort": "Сортиране",
204
+ "sortAlphabetical": "Сортиране по азбучен ред",
205
+ "sortAlphabeticalDesc": "Сортиране по обратен азбучен ред",
206
+ "sortDefault": "Сортиране по подразбиране"
207
+ },
202
208
  "enabled": "Активиран"
203
209
  },
204
210
  "notFound": "Не са намерени резултати от търсенето",
@@ -391,7 +397,13 @@
391
397
  "addNew": "Добавяне на модел",
392
398
  "disabled": "Неактивен",
393
399
  "disabledActions": {
394
- "showMore": "Покажи всичко"
400
+ "showMore": "Покажи всичко",
401
+ "sort": "Сортиране",
402
+ "sortAlphabetical": "Сортиране по азбучен ред",
403
+ "sortAlphabeticalDesc": "Сортиране по обратен азбучен ред",
404
+ "sortDefault": "Сортиране по подразбиране",
405
+ "sortReleasedAt": "Сортиране по най-ранна дата на публикуване",
406
+ "sortReleasedAtDesc": "Сортиране по най-нова дата на публикуване"
395
407
  },
396
408
  "empty": {
397
409
  "desc": "Моля, създайте персонализиран модел или изтеглете модел, за да започнете да го използвате",