@lobehub/lobehub 2.0.0-next.296 → 2.0.0-next.297
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.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +2 -2
- package/packages/types/package.json +1 -1
- package/packages/types/src/discover/assistants.ts +4 -0
- package/packages/types/src/discover/groupAgents.ts +196 -0
- package/packages/types/src/discover/index.ts +5 -1
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/DetailProvider.tsx +19 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Members/index.tsx +137 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Nav.tsx +88 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Overview/index.tsx +213 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Related/index.tsx +85 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/SystemRole/TagList.tsx +20 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/SystemRole/index.tsx +71 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/Versions/index.tsx +119 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/index.tsx +51 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Header.tsx +253 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/AddGroupAgent.tsx +222 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/index.tsx +34 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/Summary/index.tsx +42 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/index.tsx +41 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/StatusPage/index.tsx +104 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/index.tsx +103 -0
- package/src/app/[variants]/(main)/community/(detail)/group_agent/loading.tsx +1 -0
- package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +7 -1
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserContent.tsx +2 -0
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupCard.tsx +186 -0
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupList.tsx +59 -0
- package/src/app/[variants]/(main)/community/(detail)/user/index.tsx +3 -1
- package/src/app/[variants]/(main)/community/(list)/assistant/features/List/Item.tsx +26 -8
- package/src/app/[variants]/(main)/community/(list)/assistant/index.tsx +1 -0
- package/src/app/[variants]/(main)/group/profile/features/GroupProfile/index.tsx +2 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/AgentPublishButton/PublishResultModal.tsx +2 -1
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/GroupForkConfirmModal.tsx +60 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/GroupPublishResultModal.tsx +62 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/PublishButton.tsx +122 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/index.tsx +46 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/types.ts +12 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/useMarketGroupPublish.ts +211 -0
- package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/utils.ts +22 -0
- package/src/app/[variants]/router/desktopRouter.config.tsx +7 -0
- package/src/locales/default/setting.ts +12 -0
- package/src/server/routers/lambda/market/agentGroup.ts +296 -0
- package/src/server/routers/lambda/market/index.ts +134 -4
- package/src/server/services/discover/index.ts +123 -7
- package/src/services/discover.ts +55 -0
- package/src/store/discover/slices/groupAgent/action.ts +80 -0
- package/src/store/discover/store.ts +3 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
|
2
|
+
import debug from 'debug';
|
|
3
|
+
import { customAlphabet } from 'nanoid/non-secure';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
|
7
|
+
import { marketSDK, marketUserInfo, serverDatabase } from '@/libs/trpc/lambda/middleware';
|
|
8
|
+
import { type TrustedClientUserInfo, generateTrustedClientToken } from '@/libs/trusted-client';
|
|
9
|
+
|
|
10
|
+
const MARKET_BASE_URL = process.env.NEXT_PUBLIC_MARKET_BASE_URL || 'https://market.lobehub.com';
|
|
11
|
+
|
|
12
|
+
interface MarketUserInfo {
|
|
13
|
+
accountId: number;
|
|
14
|
+
sub: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const log = debug('lambda-router:market:agent-group');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate a market identifier (8-character lowercase alphanumeric string)
|
|
21
|
+
* Format: [a-z0-9]{8}
|
|
22
|
+
*/
|
|
23
|
+
const generateMarketIdentifier = () => {
|
|
24
|
+
const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
|
|
25
|
+
const generate = customAlphabet(alphabet, 8);
|
|
26
|
+
return generate();
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
interface FetchMarketUserInfoOptions {
|
|
30
|
+
accessToken?: string;
|
|
31
|
+
userInfo?: TrustedClientUserInfo;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fetch Market user info using either trustedClientToken or accessToken
|
|
36
|
+
* Returns the Market accountId which is different from LobeChat userId
|
|
37
|
+
*/
|
|
38
|
+
const fetchMarketUserInfo = async (
|
|
39
|
+
options: FetchMarketUserInfoOptions,
|
|
40
|
+
): Promise<MarketUserInfo | null> => {
|
|
41
|
+
const { userInfo, accessToken } = options;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const userInfoUrl = `${MARKET_BASE_URL}/lobehub-oidc/userinfo`;
|
|
45
|
+
const headers: Record<string, string> = {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (userInfo) {
|
|
50
|
+
const trustedClientToken = generateTrustedClientToken(userInfo);
|
|
51
|
+
if (trustedClientToken) {
|
|
52
|
+
headers['x-lobe-trust-token'] = trustedClientToken;
|
|
53
|
+
log('Using trustedClientToken for user info fetch');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!headers['x-lobe-trust-token'] && accessToken) {
|
|
58
|
+
headers['Authorization'] = `Bearer ${accessToken}`;
|
|
59
|
+
log('Using accessToken for user info fetch');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!headers['x-lobe-trust-token'] && !headers['Authorization']) {
|
|
63
|
+
log('No authentication method available for fetching user info');
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const response = await fetch(userInfoUrl, {
|
|
68
|
+
headers,
|
|
69
|
+
method: 'GET',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
log('Failed to fetch Market user info: %s %s', response.status, response.statusText);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (await response.json()) as MarketUserInfo;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
log('Error fetching Market user info: %O', error);
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Authenticated procedure for agent group management
|
|
85
|
+
const agentGroupProcedure = authedProcedure
|
|
86
|
+
.use(serverDatabase)
|
|
87
|
+
.use(marketUserInfo)
|
|
88
|
+
.use(marketSDK)
|
|
89
|
+
.use(async ({ ctx, next }) => {
|
|
90
|
+
const { UserModel } = await import('@/database/models/user');
|
|
91
|
+
const userModel = new UserModel(ctx.serverDB, ctx.userId);
|
|
92
|
+
|
|
93
|
+
let marketOidcAccessToken: string | undefined;
|
|
94
|
+
try {
|
|
95
|
+
const userState = await userModel.getUserState(async () => ({}));
|
|
96
|
+
marketOidcAccessToken = userState.settings?.market?.accessToken;
|
|
97
|
+
log('marketOidcAccessToken from DB exists=%s', !!marketOidcAccessToken);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
log('Failed to get marketOidcAccessToken from DB: %O', error);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return next({
|
|
103
|
+
ctx: {
|
|
104
|
+
marketOidcAccessToken,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Schema definitions
|
|
110
|
+
const memberAgentSchema = z.object({
|
|
111
|
+
avatar: z.string().nullish(),
|
|
112
|
+
category: z.string().optional(),
|
|
113
|
+
config: z.record(z.any()),
|
|
114
|
+
description: z.string(),
|
|
115
|
+
displayOrder: z.number().optional(),
|
|
116
|
+
identifier: z.string(),
|
|
117
|
+
name: z.string(),
|
|
118
|
+
role: z.enum(['supervisor', 'participant']),
|
|
119
|
+
url: z.string(),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const publishOrCreateGroupSchema = z.object({
|
|
123
|
+
avatar: z.string().nullish(),
|
|
124
|
+
backgroundColor: z.string().nullish(),
|
|
125
|
+
category: z.string().optional(),
|
|
126
|
+
changelog: z.string().optional(),
|
|
127
|
+
config: z
|
|
128
|
+
.object({
|
|
129
|
+
allowDM: z.boolean().optional(),
|
|
130
|
+
openingMessage: z.string().optional(),
|
|
131
|
+
openingQuestions: z.array(z.string()).optional(),
|
|
132
|
+
revealDM: z.boolean().optional(),
|
|
133
|
+
systemPrompt: z.string().optional(),
|
|
134
|
+
})
|
|
135
|
+
.optional(),
|
|
136
|
+
description: z.string(),
|
|
137
|
+
identifier: z.string().optional(),
|
|
138
|
+
memberAgents: z.array(memberAgentSchema),
|
|
139
|
+
name: z.string(),
|
|
140
|
+
visibility: z.enum(['public', 'private', 'internal']).optional(),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
export const agentGroupRouter = router({
|
|
144
|
+
/**
|
|
145
|
+
* Check if current user owns the specified group
|
|
146
|
+
*/
|
|
147
|
+
checkOwnership: agentGroupProcedure
|
|
148
|
+
.input(z.object({ identifier: z.string() }))
|
|
149
|
+
.query(async ({ input, ctx }) => {
|
|
150
|
+
log('checkOwnership input: %O', input);
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const groupDetail = await ctx.marketSDK.agentGroups.getAgentGroupDetail(input.identifier);
|
|
154
|
+
|
|
155
|
+
if (!groupDetail) {
|
|
156
|
+
return {
|
|
157
|
+
exists: false,
|
|
158
|
+
isOwner: false,
|
|
159
|
+
originalGroup: null,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
|
|
164
|
+
const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
|
|
165
|
+
let currentAccountId: number | null = null;
|
|
166
|
+
|
|
167
|
+
const marketUserInfoResult = await fetchMarketUserInfo({ accessToken, userInfo });
|
|
168
|
+
currentAccountId = marketUserInfoResult?.accountId ?? null;
|
|
169
|
+
|
|
170
|
+
const ownerId = groupDetail.group.ownerId;
|
|
171
|
+
const isOwner = currentAccountId !== null && `${ownerId}` === `${currentAccountId}`;
|
|
172
|
+
|
|
173
|
+
log(
|
|
174
|
+
'checkOwnership result: isOwner=%s, currentAccountId=%s, ownerId=%s',
|
|
175
|
+
isOwner,
|
|
176
|
+
currentAccountId,
|
|
177
|
+
ownerId,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
exists: true,
|
|
182
|
+
isOwner,
|
|
183
|
+
originalGroup: isOwner
|
|
184
|
+
? null
|
|
185
|
+
: {
|
|
186
|
+
// TODO: Add author info from group detail
|
|
187
|
+
author: undefined,
|
|
188
|
+
avatar: groupDetail.group.avatar,
|
|
189
|
+
identifier: groupDetail.group.identifier,
|
|
190
|
+
name: groupDetail.group.name,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
} catch (error) {
|
|
194
|
+
log('Error checking ownership: %O', error);
|
|
195
|
+
return {
|
|
196
|
+
exists: false,
|
|
197
|
+
isOwner: false,
|
|
198
|
+
originalGroup: null,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}),
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Unified publish or create agent group flow
|
|
205
|
+
* 1. Check if identifier exists and if current user is owner
|
|
206
|
+
* 2. If not owner or no identifier, create new group
|
|
207
|
+
* 3. Create new version for the group if updating
|
|
208
|
+
*/
|
|
209
|
+
publishOrCreate: agentGroupProcedure
|
|
210
|
+
.input(publishOrCreateGroupSchema)
|
|
211
|
+
.mutation(async ({ input, ctx }) => {
|
|
212
|
+
log('publishOrCreate input: %O', input);
|
|
213
|
+
|
|
214
|
+
const { identifier: inputIdentifier, name, memberAgents, ...groupData } = input;
|
|
215
|
+
let finalIdentifier = inputIdentifier;
|
|
216
|
+
let isNewGroup = false;
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
// Step 1: Check ownership if identifier is provided
|
|
220
|
+
if (inputIdentifier) {
|
|
221
|
+
try {
|
|
222
|
+
const groupDetail =
|
|
223
|
+
await ctx.marketSDK.agentGroups.getAgentGroupDetail(inputIdentifier);
|
|
224
|
+
log('Group detail for ownership check: ownerId=%s', groupDetail?.group.ownerId);
|
|
225
|
+
|
|
226
|
+
const userInfo = ctx.marketUserInfo as TrustedClientUserInfo | undefined;
|
|
227
|
+
const accessToken = (ctx as { marketOidcAccessToken?: string }).marketOidcAccessToken;
|
|
228
|
+
let currentAccountId: number | null = null;
|
|
229
|
+
|
|
230
|
+
const marketUserInfoResult = await fetchMarketUserInfo({ accessToken, userInfo });
|
|
231
|
+
currentAccountId = marketUserInfoResult?.accountId ?? null;
|
|
232
|
+
log('Market user info: accountId=%s', currentAccountId);
|
|
233
|
+
|
|
234
|
+
const ownerId = groupDetail?.group.ownerId;
|
|
235
|
+
|
|
236
|
+
log('Ownership check: currentAccountId=%s, ownerId=%s', currentAccountId, ownerId);
|
|
237
|
+
|
|
238
|
+
if (!currentAccountId || `${ownerId}` !== `${currentAccountId}`) {
|
|
239
|
+
// Not the owner, need to create a new group
|
|
240
|
+
log('User is not owner, will create new group');
|
|
241
|
+
finalIdentifier = undefined;
|
|
242
|
+
isNewGroup = true;
|
|
243
|
+
}
|
|
244
|
+
} catch (detailError) {
|
|
245
|
+
// Group not found or error, create new
|
|
246
|
+
log('Group not found or error, will create new: %O', detailError);
|
|
247
|
+
finalIdentifier = undefined;
|
|
248
|
+
isNewGroup = true;
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
isNewGroup = true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Step 2: Create new group or update existing
|
|
255
|
+
if (!finalIdentifier || isNewGroup) {
|
|
256
|
+
// Generate a unique 8-character identifier
|
|
257
|
+
finalIdentifier = generateMarketIdentifier();
|
|
258
|
+
isNewGroup = true;
|
|
259
|
+
|
|
260
|
+
log('Creating new group with identifier: %s', finalIdentifier);
|
|
261
|
+
|
|
262
|
+
await ctx.marketSDK.agentGroups.createAgentGroup({
|
|
263
|
+
...groupData,
|
|
264
|
+
identifier: finalIdentifier,
|
|
265
|
+
// @ts-ignore
|
|
266
|
+
memberAgents,
|
|
267
|
+
name,
|
|
268
|
+
});
|
|
269
|
+
} else {
|
|
270
|
+
// Update existing group - create new version
|
|
271
|
+
log('Creating new version for group: %s', finalIdentifier);
|
|
272
|
+
|
|
273
|
+
await ctx.marketSDK.agentGroups.createAgentGroupVersion({
|
|
274
|
+
...groupData,
|
|
275
|
+
identifier: finalIdentifier,
|
|
276
|
+
// @ts-ignore
|
|
277
|
+
memberAgents,
|
|
278
|
+
name,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
identifier: finalIdentifier,
|
|
284
|
+
isNewGroup,
|
|
285
|
+
success: true,
|
|
286
|
+
};
|
|
287
|
+
} catch (error) {
|
|
288
|
+
log('Error in publishOrCreate: %O', error);
|
|
289
|
+
throw new TRPCError({
|
|
290
|
+
cause: error,
|
|
291
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
292
|
+
message: error instanceof Error ? error.message : 'Failed to publish group',
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}),
|
|
296
|
+
});
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from '@/types/discover';
|
|
18
18
|
|
|
19
19
|
import { agentRouter } from './agent';
|
|
20
|
+
import { agentGroupRouter } from './agentGroup';
|
|
20
21
|
import { oidcRouter } from './oidc';
|
|
21
22
|
import { socialRouter } from './social';
|
|
22
23
|
import { userRouter } from './user';
|
|
@@ -44,6 +45,9 @@ export const marketRouter = router({
|
|
|
44
45
|
// ============================== Agent Management (authenticated) ==============================
|
|
45
46
|
agent: agentRouter,
|
|
46
47
|
|
|
48
|
+
// ============================== Agent Group Management (authenticated) ==============================
|
|
49
|
+
agentGroup: agentGroupRouter,
|
|
50
|
+
|
|
47
51
|
// ============================== Assistant Market ==============================
|
|
48
52
|
getAssistantCategories: marketProcedure
|
|
49
53
|
.input(
|
|
@@ -120,6 +124,7 @@ export const marketRouter = router({
|
|
|
120
124
|
.object({
|
|
121
125
|
category: z.string().optional(),
|
|
122
126
|
connectionType: z.nativeEnum(McpConnectionType).optional(),
|
|
127
|
+
includeAgentGroup: z.boolean().optional(),
|
|
123
128
|
locale: z.string().optional(),
|
|
124
129
|
order: z.enum(['asc', 'desc']).optional(),
|
|
125
130
|
ownerId: z.string().optional(),
|
|
@@ -145,6 +150,95 @@ export const marketRouter = router({
|
|
|
145
150
|
}
|
|
146
151
|
}),
|
|
147
152
|
|
|
153
|
+
// ============================== Group Agent Market (Discovery) ==============================
|
|
154
|
+
getGroupAgentCategories: marketProcedure
|
|
155
|
+
.input(
|
|
156
|
+
z
|
|
157
|
+
.object({
|
|
158
|
+
locale: z.string().optional(),
|
|
159
|
+
q: z.string().optional(),
|
|
160
|
+
})
|
|
161
|
+
.optional(),
|
|
162
|
+
)
|
|
163
|
+
.query(async ({ input, ctx }) => {
|
|
164
|
+
log('getGroupAgentCategories input: %O', input);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
return await ctx.discoverService.getGroupAgentCategories(input);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
log('Error fetching group agent categories: %O', error);
|
|
170
|
+
throw new TRPCError({
|
|
171
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
172
|
+
message: 'Failed to fetch group agent categories',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}),
|
|
176
|
+
|
|
177
|
+
getGroupAgentDetail: marketProcedure
|
|
178
|
+
.input(
|
|
179
|
+
z.object({
|
|
180
|
+
identifier: z.string(),
|
|
181
|
+
locale: z.string().optional(),
|
|
182
|
+
version: z.string().optional(),
|
|
183
|
+
}),
|
|
184
|
+
)
|
|
185
|
+
.query(async ({ input, ctx }) => {
|
|
186
|
+
log('getGroupAgentDetail input: %O', input);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
return await ctx.discoverService.getGroupAgentDetail(input);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
log('Error fetching group agent detail: %O', error);
|
|
192
|
+
throw new TRPCError({
|
|
193
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
194
|
+
message: 'Failed to fetch group agent detail',
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}),
|
|
198
|
+
|
|
199
|
+
getGroupAgentIdentifiers: marketProcedure.query(async ({ ctx }) => {
|
|
200
|
+
log('getGroupAgentIdentifiers called');
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
return await ctx.discoverService.getGroupAgentIdentifiers();
|
|
204
|
+
} catch (error) {
|
|
205
|
+
log('Error fetching group agent identifiers: %O', error);
|
|
206
|
+
throw new TRPCError({
|
|
207
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
208
|
+
message: 'Failed to fetch group agent identifiers',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}),
|
|
212
|
+
|
|
213
|
+
getGroupAgentList: marketProcedure
|
|
214
|
+
.input(
|
|
215
|
+
z
|
|
216
|
+
.object({
|
|
217
|
+
category: z.string().optional(),
|
|
218
|
+
locale: z.string().optional(),
|
|
219
|
+
order: z.enum(['asc', 'desc']).optional(),
|
|
220
|
+
ownerId: z.string().optional(),
|
|
221
|
+
page: z.number().optional(),
|
|
222
|
+
pageSize: z.number().optional(),
|
|
223
|
+
q: z.string().optional(),
|
|
224
|
+
sort: z.enum(['createdAt', 'updatedAt', 'name', 'recommended']).optional(),
|
|
225
|
+
})
|
|
226
|
+
.optional(),
|
|
227
|
+
)
|
|
228
|
+
.query(async ({ input, ctx }) => {
|
|
229
|
+
log('getGroupAgentList input: %O', input);
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
return await ctx.discoverService.getGroupAgentList(input);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
log('Error fetching group agent list: %O', error);
|
|
235
|
+
throw new TRPCError({
|
|
236
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
237
|
+
message: 'Failed to fetch group agent list',
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}),
|
|
241
|
+
|
|
148
242
|
getLegacyPluginList: marketProcedure
|
|
149
243
|
.input(
|
|
150
244
|
z
|
|
@@ -167,7 +261,7 @@ export const marketRouter = router({
|
|
|
167
261
|
}),
|
|
168
262
|
|
|
169
263
|
// ============================== MCP Market ==============================
|
|
170
|
-
getMcpCategories: marketProcedure
|
|
264
|
+
getMcpCategories: marketProcedure
|
|
171
265
|
.input(
|
|
172
266
|
z
|
|
173
267
|
.object({
|
|
@@ -351,7 +445,7 @@ getMcpCategories: marketProcedure
|
|
|
351
445
|
}),
|
|
352
446
|
|
|
353
447
|
// ============================== Plugin Market ==============================
|
|
354
|
-
getPluginCategories: marketProcedure
|
|
448
|
+
getPluginCategories: marketProcedure
|
|
355
449
|
.input(
|
|
356
450
|
z
|
|
357
451
|
.object({
|
|
@@ -439,7 +533,7 @@ getPluginCategories: marketProcedure
|
|
|
439
533
|
}),
|
|
440
534
|
|
|
441
535
|
// ============================== Providers ==============================
|
|
442
|
-
getProviderDetail: marketProcedure
|
|
536
|
+
getProviderDetail: marketProcedure
|
|
443
537
|
.input(
|
|
444
538
|
z.object({
|
|
445
539
|
identifier: z.string(),
|
|
@@ -503,7 +597,7 @@ getProviderDetail: marketProcedure
|
|
|
503
597
|
}),
|
|
504
598
|
|
|
505
599
|
// ============================== User Profile ==============================
|
|
506
|
-
getUserInfo: marketProcedure
|
|
600
|
+
getUserInfo: marketProcedure
|
|
507
601
|
.input(
|
|
508
602
|
z.object({
|
|
509
603
|
locale: z.string().optional(),
|
|
@@ -675,6 +769,42 @@ getUserInfo: marketProcedure
|
|
|
675
769
|
}
|
|
676
770
|
}),
|
|
677
771
|
|
|
772
|
+
reportGroupAgentEvent: marketProcedure
|
|
773
|
+
.input(
|
|
774
|
+
z.object({
|
|
775
|
+
event: z.enum(['add', 'chat', 'click']),
|
|
776
|
+
identifier: z.string(),
|
|
777
|
+
source: z.string().optional(),
|
|
778
|
+
}),
|
|
779
|
+
)
|
|
780
|
+
.mutation(async ({ input, ctx }) => {
|
|
781
|
+
log('reportGroupAgentEvent input: %O', input);
|
|
782
|
+
|
|
783
|
+
try {
|
|
784
|
+
await ctx.discoverService.createGroupAgentEvent(input);
|
|
785
|
+
return { success: true };
|
|
786
|
+
} catch (error) {
|
|
787
|
+
console.error('Error reporting Group Agent event: %O', error);
|
|
788
|
+
return { success: false };
|
|
789
|
+
}
|
|
790
|
+
}),
|
|
791
|
+
|
|
792
|
+
reportGroupAgentInstall: marketProcedure
|
|
793
|
+
.input(
|
|
794
|
+
z.object({
|
|
795
|
+
identifier: z.string(),
|
|
796
|
+
}),
|
|
797
|
+
)
|
|
798
|
+
.mutation(async ({ input, ctx }) => {
|
|
799
|
+
log('reportGroupAgentInstall input: %O', input);
|
|
800
|
+
try {
|
|
801
|
+
await ctx.discoverService.increaseGroupAgentInstallCount(input.identifier);
|
|
802
|
+
return { success: true };
|
|
803
|
+
} catch (error) {
|
|
804
|
+
log('Error reporting group agent installation: %O', error);
|
|
805
|
+
return { success: false };
|
|
806
|
+
}
|
|
807
|
+
}),
|
|
678
808
|
|
|
679
809
|
reportMcpEvent: marketProcedure
|
|
680
810
|
.input(
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
type ModelQueryParams,
|
|
34
34
|
ModelSorts,
|
|
35
35
|
type PluginListResponse,
|
|
36
|
-
type PluginQueryParams
|
|
36
|
+
type PluginQueryParams,
|
|
37
37
|
PluginSorts,
|
|
38
38
|
type ProviderListResponse,
|
|
39
39
|
type ProviderQueryParams,
|
|
@@ -718,6 +718,7 @@ export class DiscoverService {
|
|
|
718
718
|
q,
|
|
719
719
|
sort = AssistantSorts.CreatedAt,
|
|
720
720
|
ownerId,
|
|
721
|
+
includeAgentGroup,
|
|
721
722
|
} = rest;
|
|
722
723
|
|
|
723
724
|
try {
|
|
@@ -740,9 +741,10 @@ export class DiscoverService {
|
|
|
740
741
|
}
|
|
741
742
|
}
|
|
742
743
|
|
|
743
|
-
// @ts-ignore
|
|
744
744
|
const data = await this.market.agents.getAgentList({
|
|
745
745
|
category,
|
|
746
|
+
// includeAgentGroup may not be in SDK type definition yet, using 'as any'
|
|
747
|
+
includeAgentGroup,
|
|
746
748
|
locale: normalizedLocale,
|
|
747
749
|
order,
|
|
748
750
|
ownerId,
|
|
@@ -752,7 +754,7 @@ export class DiscoverService {
|
|
|
752
754
|
sort: apiSort,
|
|
753
755
|
status: 'published',
|
|
754
756
|
visibility: 'public',
|
|
755
|
-
});
|
|
757
|
+
} as any);
|
|
756
758
|
|
|
757
759
|
const transformedItems: DiscoverAssistantItem[] = (data.items || []).map((item: any) => {
|
|
758
760
|
const normalizedAuthor = this.normalizeAuthorField(item.author);
|
|
@@ -773,6 +775,7 @@ export class DiscoverService {
|
|
|
773
775
|
tags: item.tags || [],
|
|
774
776
|
title: item.name || item.identifier,
|
|
775
777
|
tokenUsage: item.tokenUsage || 0,
|
|
778
|
+
type: item.type,
|
|
776
779
|
userName: normalizedAuthor.userName,
|
|
777
780
|
};
|
|
778
781
|
});
|
|
@@ -922,7 +925,6 @@ export class DiscoverService {
|
|
|
922
925
|
await this.market.plugins.createEvent(params);
|
|
923
926
|
};
|
|
924
927
|
|
|
925
|
-
|
|
926
928
|
/**
|
|
927
929
|
* report plugin call result to marketplace
|
|
928
930
|
*/
|
|
@@ -1735,14 +1737,18 @@ export class DiscoverService {
|
|
|
1735
1737
|
|
|
1736
1738
|
try {
|
|
1737
1739
|
// Call Market SDK to get user info
|
|
1738
|
-
const response
|
|
1740
|
+
const response = (await this.market.user.getUserInfo(username, {
|
|
1741
|
+
locale,
|
|
1742
|
+
})) as UserInfoResponse & {
|
|
1743
|
+
agentGroups?: any[];
|
|
1744
|
+
};
|
|
1739
1745
|
|
|
1740
1746
|
if (!response?.user) {
|
|
1741
1747
|
log('getUserInfo: user not found for username=%s', username);
|
|
1742
1748
|
return undefined;
|
|
1743
1749
|
}
|
|
1744
1750
|
|
|
1745
|
-
const { user, agents } = response;
|
|
1751
|
+
const { user, agents, agentGroups } = response;
|
|
1746
1752
|
|
|
1747
1753
|
// Transform agents to DiscoverAssistantItem format
|
|
1748
1754
|
const transformedAgents: DiscoverAssistantItem[] = (agents || []).map((agent: any) => ({
|
|
@@ -1763,7 +1769,27 @@ export class DiscoverService {
|
|
|
1763
1769
|
tokenUsage: agent.tokenUsage || 0,
|
|
1764
1770
|
}));
|
|
1765
1771
|
|
|
1772
|
+
// Transform agentGroups to DiscoverGroupAgentItem format
|
|
1773
|
+
const transformedAgentGroups = (agentGroups || []).map((group: any) => ({
|
|
1774
|
+
author: user.displayName || user.userName || user.namespace || '',
|
|
1775
|
+
avatar: group.avatar || '👥',
|
|
1776
|
+
category: group.category as any,
|
|
1777
|
+
createdAt: group.createdAt,
|
|
1778
|
+
description: group.description || '',
|
|
1779
|
+
homepage: `https://lobehub.com/discover/group_agent/${group.identifier}`,
|
|
1780
|
+
identifier: group.identifier,
|
|
1781
|
+
installCount: group.installCount || 0,
|
|
1782
|
+
isFeatured: group.isFeatured || false,
|
|
1783
|
+
isOfficial: group.isOfficial || false,
|
|
1784
|
+
memberCount: 0, // Will be populated from memberAgents in detail view
|
|
1785
|
+
schemaVersion: 1,
|
|
1786
|
+
tags: group.tags || [],
|
|
1787
|
+
title: group.name || group.identifier,
|
|
1788
|
+
updatedAt: group.updatedAt,
|
|
1789
|
+
}));
|
|
1790
|
+
|
|
1766
1791
|
const result: DiscoverUserProfile = {
|
|
1792
|
+
agentGroups: transformedAgentGroups,
|
|
1767
1793
|
agents: transformedAgents,
|
|
1768
1794
|
user: {
|
|
1769
1795
|
avatarUrl: user.avatarUrl || null,
|
|
@@ -1781,11 +1807,101 @@ export class DiscoverService {
|
|
|
1781
1807
|
},
|
|
1782
1808
|
};
|
|
1783
1809
|
|
|
1784
|
-
log(
|
|
1810
|
+
log(
|
|
1811
|
+
'getUserInfo: returning user profile with %d agents and %d groups',
|
|
1812
|
+
result.agents.length,
|
|
1813
|
+
result.agentGroups?.length || 0,
|
|
1814
|
+
);
|
|
1785
1815
|
return result;
|
|
1786
1816
|
} catch (error) {
|
|
1787
1817
|
log('getUserInfo: error fetching user info: %O', error);
|
|
1788
1818
|
return undefined;
|
|
1789
1819
|
}
|
|
1790
1820
|
};
|
|
1821
|
+
|
|
1822
|
+
// ============================== Group Agent Market Methods ==============================
|
|
1823
|
+
|
|
1824
|
+
getGroupAgentCategories = async (params?: CategoryListQuery) => {
|
|
1825
|
+
try {
|
|
1826
|
+
// TODO: SDK method not yet available, using fallback
|
|
1827
|
+
const response = await (this.market.agentGroups as any).getAgentGroupCategories?.(params);
|
|
1828
|
+
return response || { items: [] };
|
|
1829
|
+
} catch (error) {
|
|
1830
|
+
log('getGroupAgentCategories: error: %O', error);
|
|
1831
|
+
return { items: [] };
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1835
|
+
getGroupAgentDetail = async (params: {
|
|
1836
|
+
identifier: string;
|
|
1837
|
+
locale?: string;
|
|
1838
|
+
version?: string;
|
|
1839
|
+
}) => {
|
|
1840
|
+
try {
|
|
1841
|
+
const response = await this.market.agentGroups.getAgentGroupDetail(params.identifier, {
|
|
1842
|
+
locale: params.locale,
|
|
1843
|
+
version: params.version ? Number(params.version) : undefined,
|
|
1844
|
+
});
|
|
1845
|
+
return response;
|
|
1846
|
+
} catch (error) {
|
|
1847
|
+
log('getGroupAgentDetail: error: %O', error);
|
|
1848
|
+
throw error;
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
|
|
1852
|
+
getGroupAgentIdentifiers = async () => {
|
|
1853
|
+
try {
|
|
1854
|
+
// TODO: SDK method not yet available, using fallback
|
|
1855
|
+
const response = await (this.market.agentGroups as any).getAgentGroupIdentifiers?.();
|
|
1856
|
+
return response || { identifiers: [] };
|
|
1857
|
+
} catch (error) {
|
|
1858
|
+
log('getGroupAgentIdentifiers: error: %O', error);
|
|
1859
|
+
return { identifiers: [] };
|
|
1860
|
+
}
|
|
1861
|
+
};
|
|
1862
|
+
|
|
1863
|
+
getGroupAgentList = async (params?: {
|
|
1864
|
+
category?: string;
|
|
1865
|
+
locale?: string;
|
|
1866
|
+
order?: 'asc' | 'desc';
|
|
1867
|
+
ownerId?: string;
|
|
1868
|
+
page?: number;
|
|
1869
|
+
pageSize?: number;
|
|
1870
|
+
q?: string;
|
|
1871
|
+
sort?: 'createdAt' | 'updatedAt' | 'name' | 'recommended';
|
|
1872
|
+
}) => {
|
|
1873
|
+
try {
|
|
1874
|
+
const response = await this.market.agentGroups.getAgentGroupList({
|
|
1875
|
+
...params,
|
|
1876
|
+
status: 'published' as any,
|
|
1877
|
+
visibility: 'public' as any,
|
|
1878
|
+
});
|
|
1879
|
+
return response;
|
|
1880
|
+
} catch (error) {
|
|
1881
|
+
log('getGroupAgentList: error: %O', error);
|
|
1882
|
+
return { currentPage: 1, items: [], totalCount: 0, totalPages: 1 };
|
|
1883
|
+
}
|
|
1884
|
+
};
|
|
1885
|
+
|
|
1886
|
+
createGroupAgentEvent = async (params: {
|
|
1887
|
+
event: 'add' | 'chat' | 'click';
|
|
1888
|
+
identifier: string;
|
|
1889
|
+
source?: string;
|
|
1890
|
+
}) => {
|
|
1891
|
+
try {
|
|
1892
|
+
// TODO: SDK method not yet available
|
|
1893
|
+
await (this.market.agentGroups as any).createAgentGroupEvent?.(params);
|
|
1894
|
+
} catch (error) {
|
|
1895
|
+
log('createGroupAgentEvent: error: %O', error);
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
|
|
1899
|
+
increaseGroupAgentInstallCount = async (identifier: string) => {
|
|
1900
|
+
try {
|
|
1901
|
+
// TODO: SDK method not yet available
|
|
1902
|
+
await (this.market.agentGroups as any).increaseInstallCount?.(identifier);
|
|
1903
|
+
} catch (error) {
|
|
1904
|
+
log('increaseGroupAgentInstallCount: error: %O', error);
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1791
1907
|
}
|