@lobehub/lobehub 2.0.0-next.145 → 2.0.0-next.147

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 (29) hide show
  1. package/.github/workflows/test.yml +3 -0
  2. package/CHANGELOG.md +50 -0
  3. package/apps/desktop/package.json +2 -0
  4. package/apps/desktop/src/main/controllers/__tests__/UploadFileCtr.test.ts +8 -12
  5. package/apps/desktop/src/main/core/infrastructure/__tests__/ProtocolManager.test.ts +1 -0
  6. package/apps/desktop/src/main/core/ui/__tests__/Tray.test.ts +2 -2
  7. package/apps/desktop/src/main/utils/__tests__/file-system.test.ts +1 -1
  8. package/apps/desktop/src/main/utils/__tests__/logger.test.ts +7 -7
  9. package/apps/desktop/src/main/utils/next-electron-rsc.ts +3 -1
  10. package/apps/desktop/src/preload/invoke.test.ts +4 -2
  11. package/apps/desktop/src/preload/routeInterceptor.test.ts +54 -9
  12. package/apps/desktop/src/preload/streamer.test.ts +32 -31
  13. package/changelog/v1.json +18 -0
  14. package/docs/development/database-schema.dbml +2 -1
  15. package/package.json +3 -3
  16. package/packages/database/migrations/0056_update_agent_slug_index.sql +2 -0
  17. package/packages/database/migrations/meta/0056_snapshot.json +8411 -0
  18. package/packages/database/migrations/meta/_journal.json +7 -0
  19. package/packages/database/src/core/migrations.json +11 -2
  20. package/packages/database/src/models/session.ts +2 -1
  21. package/packages/database/src/schemas/agent.ts +2 -3
  22. package/packages/electron-client-ipc/src/events/system.ts +1 -3
  23. package/packages/electron-client-ipc/src/types/system.ts +1 -0
  24. package/src/auth.ts +11 -2
  25. package/src/envs/auth.ts +12 -0
  26. package/src/libs/better-auth/constants.ts +7 -1
  27. package/src/libs/better-auth/sso/index.ts +2 -0
  28. package/src/libs/better-auth/sso/providers/apple.ts +33 -0
  29. package/src/libs/mcp/__tests__/__snapshots__/index.test.ts.snap +9 -0
@@ -145,6 +145,9 @@ jobs:
145
145
  env:
146
146
  NODE_OPTIONS: --max-old-space-size=6144
147
147
 
148
+ - name: Typecheck Desktop
149
+ run: pnpm typecheck
150
+ working-directory: apps/desktop
148
151
 
149
152
  - name: Test Desktop Client
150
153
  run: pnpm test
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.147](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.146...v2.0.0-next.147)
6
+
7
+ <sup>Released on **2025-12-02**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Support apple sso auth.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Support apple sso auth, closes [#10563](https://github.com/lobehub/lobe-chat/issues/10563) ([2e50313](https://github.com/lobehub/lobe-chat/commit/2e50313))
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 2.0.0-next.146](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.145...v2.0.0-next.146)
31
+
32
+ <sup>Released on **2025-12-02**</sup>
33
+
34
+ #### ♻ Code Refactoring
35
+
36
+ - **misc**: Refactor agent slug schema.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Code refactoring
44
+
45
+ - **misc**: Refactor agent slug schema, closes [#10561](https://github.com/lobehub/lobe-chat/issues/10561) ([0d609d1](https://github.com/lobehub/lobe-chat/commit/0d609d1))
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 2.0.0-next.145](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.144...v2.0.0-next.145)
6
56
 
7
57
  <sup>Released on **2025-12-02**</sup>
@@ -65,6 +65,7 @@
65
65
  "happy-dom": "^20.0.11",
66
66
  "http-proxy-agent": "^7.0.2",
67
67
  "https-proxy-agent": "^7.0.6",
68
+ "i18next": "^25.6.3",
68
69
  "just-diff": "^6.0.2",
69
70
  "lodash": "^4.17.21",
70
71
  "lodash-es": "^4.17.21",
@@ -74,6 +75,7 @@
74
75
  "tsx": "^4.20.6",
75
76
  "typescript": "^5.9.3",
76
77
  "undici": "^7.16.0",
78
+ "uuid": "^13.0.0",
77
79
  "vite": "^7.2.4",
78
80
  "vitest": "^3.2.4"
79
81
  },
@@ -34,10 +34,9 @@ describe('UploadFileCtr', () => {
34
34
  const params = {
35
35
  hash: 'abc123',
36
36
  path: '/test/file.txt',
37
- dataType: 'base64' as const,
38
- data: 'dGVzdCBjb250ZW50',
37
+ content: new ArrayBuffer(16),
39
38
  filename: 'file.txt',
40
- fileType: 'txt',
39
+ type: 'text/plain',
41
40
  };
42
41
  const expectedResult = { id: 'file-id-123', url: '/files/file-id-123' };
43
42
  mockFileService.uploadFile.mockResolvedValue(expectedResult);
@@ -52,10 +51,9 @@ describe('UploadFileCtr', () => {
52
51
  const params = {
53
52
  hash: 'abc123',
54
53
  path: '/test/file.txt',
55
- dataType: 'base64' as const,
56
- data: 'dGVzdCBjb250ZW50',
54
+ content: new ArrayBuffer(16),
57
55
  filename: 'file.txt',
58
- fileType: 'txt',
56
+ type: 'text/plain',
59
57
  };
60
58
  const error = new Error('Upload failed');
61
59
  mockFileService.uploadFile.mockRejectedValue(error);
@@ -139,10 +137,9 @@ describe('UploadFileCtr', () => {
139
137
  const params = {
140
138
  hash: 'xyz789',
141
139
  path: '/test/newfile.txt',
142
- dataType: 'base64' as const,
143
- data: 'bmV3IGZpbGUgY29udGVudA==',
140
+ content: 'bmV3IGZpbGUgY29udGVudA==',
144
141
  filename: 'newfile.txt',
145
- fileType: 'txt',
142
+ type: 'text/plain',
146
143
  };
147
144
  const expectedResult = { id: 'new-file-id', url: '/files/new-file-id' };
148
145
  mockFileService.uploadFile.mockResolvedValue(expectedResult);
@@ -157,10 +154,9 @@ describe('UploadFileCtr', () => {
157
154
  const params = {
158
155
  hash: 'xyz789',
159
156
  path: '/test/newfile.txt',
160
- dataType: 'base64' as const,
161
- data: 'bmV3IGZpbGUgY29udGVudA==',
157
+ content: 'bmV3IGZpbGUgY29udGVudA==',
162
158
  filename: 'newfile.txt',
163
- fileType: 'txt',
159
+ type: 'text/plain',
164
160
  };
165
161
  const error = new Error('Create failed');
166
162
  mockFileService.uploadFile.mockRejectedValue(error);
@@ -315,6 +315,7 @@ describe('ProtocolManager', () => {
315
315
  it('should show main window and dispatch to handler', async () => {
316
316
  vi.mocked(parseProtocolUrl).mockReturnValue({
317
317
  action: 'install',
318
+ originalUrl: 'lobehub://plugin/install?url=https://example.com',
318
319
  params: { url: 'https://example.com' },
319
320
  urlType: 'plugin',
320
321
  });
@@ -242,7 +242,7 @@ describe('Tray', () => {
242
242
  const templateArg = vi.mocked(Menu.buildFromTemplate).mock.calls[0][0];
243
243
  const showMainWindowItem = templateArg.find((item: any) => item.label === 'Show Main Window');
244
244
 
245
- showMainWindowItem?.click?.();
245
+ showMainWindowItem?.click?.(null as any, null as any, null as any);
246
246
 
247
247
  expect(mockApp.browserManager.showMainWindow).toHaveBeenCalled();
248
248
  });
@@ -253,7 +253,7 @@ describe('Tray', () => {
253
253
  const templateArg = vi.mocked(Menu.buildFromTemplate).mock.calls[0][0];
254
254
  const quitItem = templateArg.find((item: any) => item.label === 'Quit');
255
255
 
256
- quitItem?.click?.();
256
+ quitItem?.click?.(null as any, null as any, null as any);
257
257
 
258
258
  expect(app.quit).toHaveBeenCalled();
259
259
  });
@@ -81,7 +81,7 @@ describe('file-system', () => {
81
81
  vi.mocked(statSync).mockImplementation(() => {
82
82
  throw new Error('ENOENT: no such file or directory');
83
83
  });
84
- vi.mocked(mkdirSync).mockImplementation(() => {});
84
+ vi.mocked(mkdirSync).mockImplementation(() => undefined);
85
85
 
86
86
  makeSureDirExist(dir);
87
87
 
@@ -27,7 +27,7 @@ describe('logger', () => {
27
27
  });
28
28
 
29
29
  afterEach(() => {
30
- delete process.env.NODE_ENV;
30
+ delete (process.env as NodeJS.ProcessEnv & { NODE_ENV?: string }).NODE_ENV;
31
31
  delete process.env.DEBUG_VERBOSE;
32
32
  });
33
33
 
@@ -73,7 +73,7 @@ describe('logger', () => {
73
73
 
74
74
  describe('logger.error', () => {
75
75
  it('should use electronLog.error in production', () => {
76
- process.env.NODE_ENV = 'production';
76
+ (process.env as NodeJS.ProcessEnv & { NODE_ENV?: string }).NODE_ENV = 'production';
77
77
  const logger = createLogger('test:error');
78
78
  logger.error('error message', { error: 'details' });
79
79
 
@@ -82,7 +82,7 @@ describe('logger', () => {
82
82
  });
83
83
 
84
84
  it('should use console.error in development', () => {
85
- process.env.NODE_ENV = 'development';
85
+ (process.env as NodeJS.ProcessEnv & { NODE_ENV?: string }).NODE_ENV = 'development';
86
86
  const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
87
87
  const logger = createLogger('test:error');
88
88
  logger.error('error message', { error: 'details' });
@@ -107,7 +107,7 @@ describe('logger', () => {
107
107
 
108
108
  describe('logger.info', () => {
109
109
  it('should use electronLog.info with namespace in production', () => {
110
- process.env.NODE_ENV = 'production';
110
+ (process.env as NodeJS.ProcessEnv & { NODE_ENV?: string }).NODE_ENV = 'production';
111
111
  const logger = createLogger('test:info');
112
112
  logger.info('info message', { data: 'value' });
113
113
 
@@ -118,7 +118,7 @@ describe('logger', () => {
118
118
  });
119
119
 
120
120
  it('should use debug logger in development', () => {
121
- process.env.NODE_ENV = 'development';
121
+ (process.env as NodeJS.ProcessEnv & { NODE_ENV?: string }).NODE_ENV = 'development';
122
122
  const logger = createLogger('test:info');
123
123
  logger.info('info message', { data: 'value' });
124
124
 
@@ -162,7 +162,7 @@ describe('logger', () => {
162
162
 
163
163
  describe('logger.warn', () => {
164
164
  it('should use electronLog.warn in production', () => {
165
- process.env.NODE_ENV = 'production';
165
+ (process.env as NodeJS.ProcessEnv & { NODE_ENV?: string }).NODE_ENV = 'production';
166
166
  const logger = createLogger('test:warn');
167
167
  logger.warn('warn message', { warning: 'details' });
168
168
 
@@ -171,7 +171,7 @@ describe('logger', () => {
171
171
  });
172
172
 
173
173
  it('should not use electronLog.warn in development', () => {
174
- process.env.NODE_ENV = 'development';
174
+ (process.env as NodeJS.ProcessEnv & { NODE_ENV?: string }).NODE_ENV = 'development';
175
175
  const logger = createLogger('test:warn');
176
176
  logger.warn('warn message');
177
177
 
@@ -1,7 +1,9 @@
1
1
  // copy from https://github.com/kirill-konshin/next-electron-rsc
2
2
  import { serialize as serializeCookie } from 'cookie';
3
3
  import { type Protocol, type Session } from 'electron';
4
+ // @ts-ignore
4
5
  import type { NextConfig } from 'next';
6
+ // @ts-ignore
5
7
  import type NextNodeServer from 'next/dist/server/next-server';
6
8
  import assert from 'node:assert';
7
9
  import { IncomingMessage, ServerResponse } from 'node:http';
@@ -204,7 +206,7 @@ export function createHandler({
204
206
  logger.info('Initializing Next.js app for production');
205
207
 
206
208
  // https://github.com/lobehub/lobe-chat/pull/9851
207
- // @ts-expect-error
209
+ // @ts-ignore
208
210
  // noinspection JSConstantReassignment
209
211
  process.env.NODE_ENV = 'production';
210
212
  const next = require(resolve.sync('next', { basedir: standaloneDir }));
@@ -47,7 +47,8 @@ describe('invoke', () => {
47
47
  const expectedResult = { processed: true };
48
48
  mockIpcRendererInvoke.mockResolvedValue(expectedResult);
49
49
 
50
- const result = await invoke('someEvent' as ClientDispatchEventKey, param1, param2, param3);
50
+ // Use 'as any' to bypass type checking for testing multiple parameters
51
+ const result = await (invoke as any)('someEvent', param1, param2, param3);
51
52
 
52
53
  expect(mockIpcRendererInvoke).toHaveBeenCalledWith('someEvent', param1, param2, param3);
53
54
  expect(mockIpcRendererInvoke).toHaveBeenCalledTimes(1);
@@ -109,7 +110,8 @@ describe('invoke', () => {
109
110
  const expectedResponse: TestResponse = { message: 'success', status: 200 };
110
111
  mockIpcRendererInvoke.mockResolvedValue(expectedResponse);
111
112
 
112
- const result = await invoke('testEvent' as ClientDispatchEventKey);
113
+ // Use 'as any' to bypass type checking for testing with mock event
114
+ const result = (await (invoke as any)('testEvent')) as TestResponse;
113
115
 
114
116
  expect(result).toEqual(expectedResponse);
115
117
  expect(typeof result.message).toBe('string');
@@ -110,7 +110,12 @@ describe('setupRouteInterceptors', () => {
110
110
  it('should intercept internal link matching route pattern', async () => {
111
111
  setupRouteInterceptors();
112
112
 
113
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
113
+ const matchedRoute = {
114
+ description: 'Developer Tools',
115
+ enabled: true,
116
+ pathPrefix: '/desktop/devtools',
117
+ targetWindow: 'devtools',
118
+ };
114
119
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
115
120
 
116
121
  const link = document.createElement('a');
@@ -142,7 +147,12 @@ describe('setupRouteInterceptors', () => {
142
147
  pathname: '/desktop/devtools/console',
143
148
  });
144
149
 
145
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
150
+ const matchedRoute = {
151
+ description: 'Developer Tools',
152
+ enabled: true,
153
+ pathPrefix: '/desktop/devtools',
154
+ targetWindow: 'devtools',
155
+ };
146
156
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
147
157
 
148
158
  const link = document.createElement('a');
@@ -183,7 +193,12 @@ describe('setupRouteInterceptors', () => {
183
193
  it('should intercept pushState for matched routes', () => {
184
194
  setupRouteInterceptors();
185
195
 
186
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
196
+ const matchedRoute = {
197
+ description: 'Developer Tools',
198
+ enabled: true,
199
+ pathPrefix: '/desktop/devtools',
200
+ targetWindow: 'devtools',
201
+ };
187
202
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
188
203
 
189
204
  const originalLength = history.length;
@@ -208,7 +223,12 @@ describe('setupRouteInterceptors', () => {
208
223
  pathname: '/desktop/devtools/console',
209
224
  });
210
225
 
211
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
226
+ const matchedRoute = {
227
+ description: 'Developer Tools',
228
+ enabled: true,
229
+ pathPrefix: '/desktop/devtools',
230
+ targetWindow: 'devtools',
231
+ };
212
232
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
213
233
 
214
234
  history.pushState({}, '', '/desktop/devtools/network');
@@ -248,7 +268,12 @@ describe('setupRouteInterceptors', () => {
248
268
  it('should intercept replaceState for matched routes', () => {
249
269
  setupRouteInterceptors();
250
270
 
251
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
271
+ const matchedRoute = {
272
+ description: 'Developer Tools',
273
+ enabled: true,
274
+ pathPrefix: '/desktop/devtools',
275
+ targetWindow: 'devtools',
276
+ };
252
277
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
253
278
 
254
279
  history.replaceState({}, '', '/desktop/devtools');
@@ -270,7 +295,12 @@ describe('setupRouteInterceptors', () => {
270
295
  pathname: '/desktop/devtools/console',
271
296
  });
272
297
 
273
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
298
+ const matchedRoute = {
299
+ description: 'Developer Tools',
300
+ enabled: true,
301
+ pathPrefix: '/desktop/devtools',
302
+ targetWindow: 'devtools',
303
+ };
274
304
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
275
305
 
276
306
  history.replaceState({}, '', '/desktop/devtools/network');
@@ -296,7 +326,12 @@ describe('setupRouteInterceptors', () => {
296
326
  setupRouteInterceptors();
297
327
 
298
328
  // First trigger a route interception to add path to preventedPaths
299
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
329
+ const matchedRoute = {
330
+ description: 'Developer Tools',
331
+ enabled: true,
332
+ pathPrefix: '/desktop/devtools',
333
+ targetWindow: 'devtools',
334
+ };
300
335
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
301
336
  history.pushState({}, '', '/desktop/devtools');
302
337
 
@@ -338,7 +373,12 @@ describe('setupRouteInterceptors', () => {
338
373
 
339
374
  setupRouteInterceptors();
340
375
 
341
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
376
+ const matchedRoute = {
377
+ description: 'Developer Tools',
378
+ enabled: true,
379
+ pathPrefix: '/desktop/devtools',
380
+ targetWindow: 'devtools',
381
+ };
342
382
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
343
383
 
344
384
  history.pushState({}, '', '/desktop/devtools');
@@ -358,7 +398,12 @@ describe('setupRouteInterceptors', () => {
358
398
 
359
399
  setupRouteInterceptors();
360
400
 
361
- const matchedRoute = { pathPrefix: '/desktop/devtools', targetWindow: 'devtools' };
401
+ const matchedRoute = {
402
+ description: 'Developer Tools',
403
+ enabled: true,
404
+ pathPrefix: '/desktop/devtools',
405
+ targetWindow: 'devtools',
406
+ };
362
407
  vi.mocked(findMatchingRoute).mockReturnValue(matchedRoute);
363
408
 
364
409
  history.pushState({}, '', '/desktop/devtools');
@@ -30,9 +30,9 @@ describe('onStreamInvoke', () => {
30
30
 
31
31
  it('should set up stream listeners and send start event', () => {
32
32
  const params: ProxyTRPCRequestParams = {
33
- input: { query: 'test' },
34
- path: 'test.endpoint',
35
- type: 'query',
33
+ headers: { 'content-type': 'application/json' },
34
+ method: 'POST',
35
+ urlPath: '/trpc/lambda/test.endpoint',
36
36
  };
37
37
 
38
38
  const callbacks = {
@@ -78,9 +78,9 @@ describe('onStreamInvoke', () => {
78
78
  };
79
79
 
80
80
  const params: ProxyTRPCRequestParams = {
81
- input: {},
82
- path: 'test',
83
- type: 'query',
81
+ headers: {},
82
+ method: 'GET',
83
+ urlPath: '/trpc/test',
84
84
  };
85
85
 
86
86
  onStreamInvoke(params, callbacks);
@@ -106,9 +106,9 @@ describe('onStreamInvoke', () => {
106
106
  };
107
107
 
108
108
  const params: ProxyTRPCRequestParams = {
109
- input: {},
110
- path: 'test',
111
- type: 'query',
109
+ headers: {},
110
+ method: 'GET',
111
+ urlPath: '/trpc/test',
112
112
  };
113
113
 
114
114
  onStreamInvoke(params, callbacks);
@@ -138,9 +138,9 @@ describe('onStreamInvoke', () => {
138
138
  };
139
139
 
140
140
  const params: ProxyTRPCRequestParams = {
141
- input: {},
142
- path: 'test',
143
- type: 'query',
141
+ headers: {},
142
+ method: 'GET',
143
+ urlPath: '/trpc/test',
144
144
  };
145
145
 
146
146
  onStreamInvoke(params, callbacks);
@@ -179,9 +179,9 @@ describe('onStreamInvoke', () => {
179
179
  };
180
180
 
181
181
  const params: ProxyTRPCRequestParams = {
182
- input: {},
183
- path: 'test',
184
- type: 'query',
182
+ headers: {},
183
+ method: 'GET',
184
+ urlPath: '/trpc/test',
185
185
  };
186
186
 
187
187
  onStreamInvoke(params, callbacks);
@@ -221,9 +221,9 @@ describe('onStreamInvoke', () => {
221
221
  };
222
222
 
223
223
  const params: ProxyTRPCRequestParams = {
224
- input: {},
225
- path: 'test',
226
- type: 'query',
224
+ headers: {},
225
+ method: 'GET',
226
+ urlPath: '/trpc/test',
227
227
  };
228
228
 
229
229
  const cleanup = onStreamInvoke(params, callbacks);
@@ -255,9 +255,9 @@ describe('onStreamInvoke', () => {
255
255
  };
256
256
 
257
257
  const params: ProxyTRPCRequestParams = {
258
- input: {},
259
- path: 'test',
260
- type: 'query',
258
+ headers: {},
259
+ method: 'GET',
260
+ urlPath: '/trpc/test',
261
261
  };
262
262
 
263
263
  onStreamInvoke(params, callbacks);
@@ -290,13 +290,14 @@ describe('onStreamInvoke', () => {
290
290
  };
291
291
 
292
292
  const params: ProxyTRPCRequestParams = {
293
- input: {
293
+ body: JSON.stringify({
294
294
  filters: { active: true },
295
295
  query: 'complex query',
296
296
  sort: { field: 'date', order: 'desc' },
297
- },
298
- path: 'complex.nested.endpoint',
299
- type: 'mutation',
297
+ }),
298
+ headers: { 'content-type': 'application/json', 'x-custom-header': 'value' },
299
+ method: 'POST',
300
+ urlPath: '/trpc/lambda/complex.nested.endpoint',
300
301
  };
301
302
 
302
303
  onStreamInvoke(params, callbacks);
@@ -316,9 +317,9 @@ describe('onStreamInvoke', () => {
316
317
  };
317
318
 
318
319
  const params: ProxyTRPCRequestParams = {
319
- input: {},
320
- path: 'test',
321
- type: 'query',
320
+ headers: {},
321
+ method: 'GET',
322
+ urlPath: '/trpc/test',
322
323
  };
323
324
 
324
325
  const cleanup = onStreamInvoke(params, callbacks);
@@ -346,9 +347,9 @@ describe('onStreamInvoke', () => {
346
347
  };
347
348
 
348
349
  const params: ProxyTRPCRequestParams = {
349
- input: {},
350
- path: 'test',
351
- type: 'query',
350
+ headers: {},
351
+ method: 'GET',
352
+ urlPath: '/trpc/test',
352
353
  };
353
354
 
354
355
  onStreamInvoke(params, callbacks);
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Support apple sso auth."
6
+ ]
7
+ },
8
+ "date": "2025-12-02",
9
+ "version": "2.0.0-next.147"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Refactor agent slug schema."
15
+ ]
16
+ },
17
+ "date": "2025-12-02",
18
+ "version": "2.0.0-next.146"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "features": [
@@ -1,6 +1,6 @@
1
1
  table agents {
2
2
  id text [pk, not null]
3
- slug varchar(100) [unique]
3
+ slug varchar(100)
4
4
  title varchar(255)
5
5
  description varchar(1000)
6
6
  tags jsonb [default: `[]`]
@@ -27,6 +27,7 @@ table agents {
27
27
 
28
28
  indexes {
29
29
  (client_id, user_id) [name: 'client_id_user_id_unique', unique]
30
+ (slug, user_id) [name: 'agents_slug_user_id_unique', unique]
30
31
  title [name: 'agents_title_idx']
31
32
  description [name: 'agents_description_idx']
32
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.145",
3
+ "version": "2.0.0-next.147",
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",
@@ -171,10 +171,10 @@
171
171
  "@lobehub/chat-plugins-gateway": "^1.9.0",
172
172
  "@lobehub/editor": "^1.23.1",
173
173
  "@lobehub/icons": "^2.43.1",
174
- "@lobehub/market-sdk": "^0.23.0",
174
+ "@lobehub/market-sdk": "^0.23.2",
175
175
  "@lobehub/tts": "^2.0.1",
176
176
  "@lobehub/ui": "^2.18.3",
177
- "@modelcontextprotocol/sdk": "^1.23.0",
177
+ "@modelcontextprotocol/sdk": "^1.24.0",
178
178
  "@neondatabase/serverless": "^1.0.2",
179
179
  "@next/third-parties": "^16.0.5",
180
180
  "@opentelemetry/exporter-jaeger": "^2.2.0",
@@ -0,0 +1,2 @@
1
+ ALTER TABLE "agents" DROP CONSTRAINT "agents_slug_unique";--> statement-breakpoint
2
+ CREATE UNIQUE INDEX IF NOT EXISTS "agents_slug_user_id_unique" ON "agents" USING btree ("slug","user_id");