@lobehub/lobehub 2.0.0-next.310 → 2.0.0-next.312

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 (118) hide show
  1. package/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  2. package/CHANGELOG.md +58 -0
  3. package/changelog/v1.json +21 -0
  4. package/docs/development/basic/chat-api.mdx +0 -1
  5. package/docs/development/basic/chat-api.zh-CN.mdx +0 -1
  6. package/e2e/README.md +1 -1
  7. package/e2e/src/features/community/detail-pages.feature +2 -2
  8. package/e2e/src/features/community/interactions.feature +5 -5
  9. package/e2e/src/features/community/smoke.feature +1 -1
  10. package/e2e/src/steps/community/detail-pages.steps.ts +6 -4
  11. package/e2e/src/steps/community/interactions.steps.ts +3 -3
  12. package/package.json +1 -1
  13. package/packages/builtin-tool-agent-builder/src/systemRole.ts +9 -0
  14. package/packages/model-runtime/src/core/BaseAI.ts +0 -2
  15. package/packages/model-runtime/src/core/ModelRuntime.test.ts +0 -37
  16. package/packages/model-runtime/src/core/ModelRuntime.ts +0 -5
  17. package/packages/model-runtime/src/core/RouterRuntime/baseRuntimeMap.ts +4 -0
  18. package/packages/model-runtime/src/core/RouterRuntime/createRuntime.test.ts +325 -200
  19. package/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts +205 -64
  20. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +0 -14
  21. package/packages/model-runtime/src/providers/aihubmix/index.test.ts +14 -20
  22. package/packages/model-runtime/src/types/index.ts +0 -1
  23. package/packages/model-runtime/src/utils/createError.test.ts +0 -20
  24. package/packages/model-runtime/src/utils/createError.ts +0 -1
  25. package/public/favicon-32x-32-error.ico +0 -0
  26. package/public/favicon-32x32-done-dev.ico +0 -0
  27. package/public/favicon-32x32-done.ico +0 -0
  28. package/public/favicon-32x32-error-dev.ico +0 -0
  29. package/public/favicon-32x32-progress-dev.ico +0 -0
  30. package/public/favicon-32x32-progress.ico +0 -0
  31. package/public/favicon-done-dev.ico +0 -0
  32. package/public/favicon-done.ico +0 -0
  33. package/public/favicon-error-dev.ico +0 -0
  34. package/public/favicon-error.ico +0 -0
  35. package/public/favicon-progress-dev.ico +0 -0
  36. package/public/favicon-progress.ico +0 -0
  37. package/src/app/(backend)/market/agent/[[...segments]]/route.ts +3 -33
  38. package/src/app/(backend)/market/oidc/[[...segments]]/route.ts +5 -6
  39. package/src/app/(backend)/market/social/[[...segments]]/route.ts +5 -52
  40. package/src/app/(backend)/market/user/[username]/route.ts +3 -9
  41. package/src/app/(backend)/market/user/me/route.ts +3 -34
  42. package/src/app/[variants]/(main)/agent/profile/features/Header/AgentPublishButton/PublishResultModal.tsx +1 -1
  43. package/src/app/[variants]/(main)/community/(detail)/_layout/Header.tsx +15 -3
  44. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Overview/TagList.tsx +1 -1
  45. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Related/index.tsx +2 -2
  46. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/SystemRole/TagList.tsx +1 -1
  47. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/SystemRole/index.tsx +1 -1
  48. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Header.tsx +2 -2
  49. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/ActionButton/AddAgent.tsx +1 -1
  50. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/ActionButton/index.tsx +1 -1
  51. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/Related/index.tsx +2 -2
  52. package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/StatusPage/index.tsx +2 -2
  53. package/src/app/[variants]/(main)/community/(detail)/user/features/UserAgentCard.tsx +2 -2
  54. package/src/app/[variants]/(main)/community/(detail)/user/features/UserFavoriteAgents.tsx +1 -1
  55. package/src/app/[variants]/(main)/community/(list)/(home)/index.tsx +2 -2
  56. package/src/app/[variants]/(main)/community/(list)/(home)/loading.tsx +1 -1
  57. package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/Client.tsx +5 -1
  58. package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/Category/index.tsx +1 -1
  59. package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/List/Item.tsx +1 -1
  60. package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/MarketSourceSwitch.tsx +1 -1
  61. package/src/app/[variants]/(main)/community/_layout/Sidebar/Header/Nav.tsx +2 -2
  62. package/src/app/[variants]/(main)/home/features/CommunityAgents/List.tsx +1 -1
  63. package/src/app/[variants]/(main)/home/features/CommunityAgents/index.tsx +1 -1
  64. package/src/app/[variants]/(mobile)/_layout/index.tsx +1 -1
  65. package/src/app/[variants]/(mobile)/router/mobileRouter.config.tsx +6 -6
  66. package/src/app/[variants]/router/desktopRouter.config.tsx +8 -8
  67. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
  68. package/src/features/ChatMiniMap/useMinimapData.ts +1 -1
  69. package/src/features/CommandMenu/SearchResults.tsx +1 -1
  70. package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +20 -2
  71. package/src/features/Conversation/store/slices/virtuaList/action.ts +9 -0
  72. package/src/features/Electron/navigation/routeMetadata.ts +1 -1
  73. package/src/layout/GlobalProvider/FaviconProvider.tsx +92 -0
  74. package/src/layout/GlobalProvider/index.tsx +15 -11
  75. package/src/libs/next/config/define-config.ts +1 -1
  76. package/src/libs/trpc/lambda/middleware/marketSDK.ts +14 -23
  77. package/src/libs/trusted-client/index.ts +1 -1
  78. package/src/server/routers/lambda/market/index.ts +5 -0
  79. package/src/server/routers/lambda/market/oidc.ts +41 -61
  80. package/src/server/routers/tools/market.ts +12 -44
  81. package/src/server/services/agentRuntime/AgentRuntimeService.test.ts +7 -0
  82. package/src/server/services/agentRuntime/AgentRuntimeService.ts +1 -1
  83. package/src/server/services/aiAgent/__tests__/execAgent.threadId.test.ts +7 -0
  84. package/src/server/services/aiAgent/__tests__/execGroupSubAgentTask.test.ts +7 -0
  85. package/src/server/services/aiAgent/index.ts +9 -96
  86. package/src/server/services/discover/index.ts +11 -16
  87. package/src/server/services/market/index.ts +485 -0
  88. package/src/server/services/toolExecution/builtin.ts +11 -17
  89. package/src/server/services/toolExecution/index.ts +6 -2
  90. package/src/server/sitemap.test.ts +5 -5
  91. package/src/server/sitemap.ts +3 -3
  92. package/src/services/codeInterpreter.ts +0 -13
  93. package/packages/model-runtime/src/types/textToImage.ts +0 -36
  94. package/src/server/services/lobehubSkill/index.ts +0 -109
  95. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/DetailProvider.tsx +0 -0
  96. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/Block.tsx +0 -0
  97. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/Knowledge.tsx +0 -0
  98. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/KnowledgeItem.tsx +0 -0
  99. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/PluginItem.tsx +0 -0
  100. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/Plugins.tsx +0 -0
  101. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Capabilities/index.tsx +0 -0
  102. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Nav.tsx +0 -0
  103. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Overview/index.tsx +0 -0
  104. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/Versions/index.tsx +0 -0
  105. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Details/index.tsx +0 -0
  106. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/Related/Item.tsx +0 -0
  107. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/Summary/index.tsx +0 -0
  108. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/TocList/index.tsx +0 -0
  109. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/features/Sidebar/index.tsx +0 -0
  110. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/index.tsx +0 -0
  111. /package/src/app/[variants]/(main)/community/(detail)/{assistant → agent}/loading.tsx +0 -0
  112. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/_layout/index.tsx +0 -0
  113. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/_layout/style.ts +0 -0
  114. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/Category/useCategory.tsx +0 -0
  115. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/List/TokenTag.tsx +0 -0
  116. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/features/List/index.tsx +0 -0
  117. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/index.tsx +0 -0
  118. /package/src/app/[variants]/(main)/community/(list)/{assistant → agent}/loading.tsx +0 -0
@@ -11,7 +11,6 @@ import type {
11
11
  } from '@lobechat/types';
12
12
  import { ThreadStatus, ThreadType } from '@lobechat/types';
13
13
  import { nanoid } from '@lobechat/utils';
14
- import { MarketSDK } from '@lobehub/market-sdk';
15
14
  import debug from 'debug';
16
15
 
17
16
  import { LOADING_FLAT } from '@/const/message';
@@ -20,8 +19,6 @@ import { MessageModel } from '@/database/models/message';
20
19
  import { PluginModel } from '@/database/models/plugin';
21
20
  import { ThreadModel } from '@/database/models/thread';
22
21
  import { TopicModel } from '@/database/models/topic';
23
- import { UserModel } from '@/database/models/user';
24
- import { generateTrustedClientToken } from '@/libs/trusted-client';
25
22
  import {
26
23
  type ServerAgentToolsContext,
27
24
  createServerAgentToolsEngine,
@@ -30,6 +27,7 @@ import {
30
27
  import { AgentService } from '@/server/services/agent';
31
28
  import { AgentRuntimeService } from '@/server/services/agentRuntime';
32
29
  import type { StepLifecycleCallbacks } from '@/server/services/agentRuntime/types';
30
+ import { MarketService } from '@/server/services/market';
33
31
 
34
32
  const log = debug('lobe-server:ai-agent-service');
35
33
 
@@ -88,6 +86,7 @@ export class AiAgentService {
88
86
  private readonly threadModel: ThreadModel;
89
87
  private readonly topicModel: TopicModel;
90
88
  private readonly agentRuntimeService: AgentRuntimeService;
89
+ private readonly marketService: MarketService;
91
90
 
92
91
  constructor(db: LobeChatDatabase, userId: string) {
93
92
  this.userId = userId;
@@ -99,6 +98,7 @@ export class AiAgentService {
99
98
  this.threadModel = new ThreadModel(db, userId);
100
99
  this.topicModel = new TopicModel(db, userId);
101
100
  this.agentRuntimeService = new AgentRuntimeService(db, userId);
101
+ this.marketService = new MarketService({ userInfo: { userId } });
102
102
  }
103
103
 
104
104
  /**
@@ -190,7 +190,12 @@ export class AiAgentService {
190
190
  };
191
191
 
192
192
  // 5. Fetch LobeHub Skills manifests (temporary solution until LOBE-3517 is implemented)
193
- const lobehubSkillManifests = await this.fetchLobehubSkillManifests();
193
+ let lobehubSkillManifests: LobeToolManifest[] = [];
194
+ try {
195
+ lobehubSkillManifests = await this.marketService.getLobehubSkillManifests();
196
+ } catch (error) {
197
+ log('execAgent: failed to fetch lobehub skill manifests: %O', error);
198
+ }
194
199
  log('execAgent: got %d lobehub skill manifests', lobehubSkillManifests.length);
195
200
 
196
201
  // 6. Create tools using Server AgentToolsEngine
@@ -736,98 +741,6 @@ export class AiAgentService {
736
741
  };
737
742
  }
738
743
 
739
- /**
740
- * Fetch LobeHub Skills manifests from Market API
741
- * This is a temporary solution until LOBE-3517 is implemented (store skills in DB)
742
- */
743
- private async fetchLobehubSkillManifests(): Promise<LobeToolManifest[]> {
744
- try {
745
- // 1. Get user info for trusted client token
746
- const user = await UserModel.findById(this.db, this.userId);
747
- if (!user?.email) {
748
- log('fetchLobehubSkillManifests: user email not found, skipping');
749
- return [];
750
- }
751
-
752
- // 2. Generate trusted client token
753
- const trustedClientToken = generateTrustedClientToken({
754
- email: user.email,
755
- name: user.fullName || user.firstName || undefined,
756
- userId: this.userId,
757
- });
758
-
759
- if (!trustedClientToken) {
760
- log('fetchLobehubSkillManifests: trusted client not configured, skipping');
761
- return [];
762
- }
763
-
764
- // 3. Create MarketSDK instance
765
- const marketSDK = new MarketSDK({
766
- baseURL: process.env.NEXT_PUBLIC_MARKET_BASE_URL,
767
- trustedClientToken,
768
- });
769
-
770
- // 4. Get user's connected skills
771
- const { connections } = await marketSDK.connect.listConnections();
772
- if (!connections || connections.length === 0) {
773
- log('fetchLobehubSkillManifests: no connected skills found');
774
- return [];
775
- }
776
-
777
- log('fetchLobehubSkillManifests: found %d connected skills', connections.length);
778
-
779
- // 5. Fetch tools for each connection and build manifests
780
- const manifests: LobeToolManifest[] = [];
781
-
782
- for (const connection of connections) {
783
- try {
784
- // Connection returns providerId (e.g., 'twitter', 'linear'), not numeric id
785
- const providerId = (connection as any).providerId;
786
- if (!providerId) {
787
- log('fetchLobehubSkillManifests: connection missing providerId: %O', connection);
788
- continue;
789
- }
790
- const providerName =
791
- (connection as any).providerName || (connection as any).name || providerId;
792
- const icon = (connection as any).icon;
793
-
794
- const { tools } = await marketSDK.skills.listTools(providerId);
795
- if (!tools || tools.length === 0) continue;
796
-
797
- const manifest: LobeToolManifest = {
798
- api: tools.map((tool: any) => ({
799
- description: tool.description || '',
800
- name: tool.name,
801
- parameters: tool.inputSchema || { properties: {}, type: 'object' },
802
- })),
803
- identifier: providerId,
804
- meta: {
805
- avatar: icon || '🔗',
806
- description: `LobeHub Skill: ${providerName}`,
807
- tags: ['lobehub-skill', providerId],
808
- title: providerName,
809
- },
810
- type: 'builtin',
811
- };
812
-
813
- manifests.push(manifest);
814
- log(
815
- 'fetchLobehubSkillManifests: built manifest for %s with %d tools',
816
- providerId,
817
- tools.length,
818
- );
819
- } catch (error) {
820
- log('fetchLobehubSkillManifests: failed to fetch tools for connection: %O', error);
821
- }
822
- }
823
-
824
- return manifests;
825
- } catch (error) {
826
- log('fetchLobehubSkillManifests: error fetching skills: %O', error);
827
- return [];
828
- }
829
- }
830
-
831
744
  /**
832
745
  * Calculate total tokens from AgentState usage object
833
746
  * AgentState.usage is of type Usage from @lobechat/agent-runtime
@@ -62,10 +62,11 @@ import { cloneDeep, countBy, isString, merge, uniq, uniqBy } from 'es-toolkit/co
62
62
  import matter from 'gray-matter';
63
63
  import urlJoin from 'url-join';
64
64
 
65
- import { type TrustedClientUserInfo, generateTrustedClientToken } from '@/libs/trusted-client';
65
+ import { type TrustedClientUserInfo } from '@/libs/trusted-client';
66
66
  import { normalizeLocale } from '@/locales/resources';
67
67
  import { AssistantStore } from '@/server/modules/AssistantStore';
68
68
  import { PluginStore } from '@/server/modules/PluginStore';
69
+ import { MarketService } from '@/server/services/market';
69
70
 
70
71
  const log = debug('lobe-server:discover');
71
72
 
@@ -84,18 +85,14 @@ export class DiscoverService {
84
85
  constructor(options: DiscoverServiceOptions = {}) {
85
86
  const { accessToken, userInfo } = options;
86
87
 
87
- // Generate trusted client token if user info is available
88
- const trustedClientToken = userInfo ? generateTrustedClientToken(userInfo) : undefined;
88
+ // Use MarketService to initialize MarketSDK
89
+ const marketService = new MarketService({ accessToken, userInfo });
90
+ this.market = marketService.market;
89
91
 
90
- this.market = new MarketSDK({
91
- accessToken,
92
- baseURL: process.env.NEXT_PUBLIC_MARKET_BASE_URL,
93
- trustedClientToken,
94
- });
95
92
  log(
96
- 'DiscoverService initialized with market baseURL: %s, hasTrustedToken: %s, userId: %s',
93
+ 'DiscoverService initialized with market baseURL: %s, hasAuth: %s, userId: %s',
97
94
  process.env.NEXT_PUBLIC_MARKET_BASE_URL,
98
- !!trustedClientToken,
95
+ !!(accessToken || userInfo),
99
96
  userInfo?.userId,
100
97
  );
101
98
  }
@@ -135,14 +132,12 @@ export class DiscoverService {
135
132
  }
136
133
 
137
134
  async fetchM2MToken(params: { clientId: string; clientSecret: string }) {
138
- // 使用传入的客户端凭证创建新的 MarketSDK 实例
139
- const tokenMarket = new MarketSDK({
140
- baseURL: process.env.NEXT_PUBLIC_MARKET_BASE_URL,
141
- clientId: params.clientId,
142
- clientSecret: params.clientSecret,
135
+ // Use MarketService with M2M credentials
136
+ const marketService = new MarketService({
137
+ clientCredentials: params,
143
138
  });
144
139
 
145
- const tokenInfo = await tokenMarket.fetchM2MToken();
140
+ const tokenInfo = await marketService.fetchM2MToken();
146
141
 
147
142
  return {
148
143
  accessToken: tokenInfo.accessToken,
@@ -0,0 +1,485 @@
1
+ import type { LobeToolManifest } from '@lobechat/context-engine';
2
+ import { MarketSDK } from '@lobehub/market-sdk';
3
+ import debug from 'debug';
4
+ import { type NextRequest } from 'next/server';
5
+
6
+ import {
7
+ type TrustedClientUserInfo,
8
+ generateTrustedClientToken,
9
+ getTrustedClientTokenForSession,
10
+ } from '@/libs/trusted-client';
11
+
12
+ const log = debug('lobe-server:market-service');
13
+
14
+ const MARKET_BASE_URL = process.env.NEXT_PUBLIC_MARKET_BASE_URL || 'https://market.lobehub.com';
15
+
16
+ // ============================== Helper Functions ==============================
17
+
18
+ /**
19
+ * Extract access token from Authorization header
20
+ */
21
+ export function extractAccessToken(req: NextRequest): string | undefined {
22
+ const authHeader = req.headers.get('authorization');
23
+ if (authHeader?.startsWith('Bearer ')) {
24
+ return authHeader.slice(7);
25
+ }
26
+ return undefined;
27
+ }
28
+
29
+ export interface LobehubSkillExecuteParams {
30
+ args: Record<string, any>;
31
+ provider: string;
32
+ toolName: string;
33
+ }
34
+
35
+ export interface LobehubSkillExecuteResult {
36
+ content: string;
37
+ error?: { code: string; message?: string };
38
+ success: boolean;
39
+ }
40
+
41
+ export interface MarketServiceOptions {
42
+ /** Access token from OIDC flow (user token) */
43
+ accessToken?: string;
44
+ /** Client credentials for M2M authentication */
45
+ clientCredentials?: {
46
+ clientId: string;
47
+ clientSecret: string;
48
+ };
49
+ /** Pre-generated trusted client token (alternative to userInfo) */
50
+ trustedClientToken?: string;
51
+ /** User info for generating trusted client token */
52
+ userInfo?: TrustedClientUserInfo;
53
+ }
54
+
55
+ /**
56
+ * Market Service
57
+ *
58
+ * Provides a unified interface to MarketSDK with business logic encapsulation.
59
+ * This service wraps MarketSDK methods to avoid repetition across the codebase.
60
+ *
61
+ * Usage:
62
+ * ```typescript
63
+ * // From Next.js request (API Routes) - recommended
64
+ * const marketService = await MarketService.createFromRequest(req);
65
+ * await marketService.submitFeedback({ ... });
66
+ *
67
+ * // With user authentication
68
+ * const service = new MarketService({ accessToken, userInfo });
69
+ *
70
+ * // With trusted client only
71
+ * const service = new MarketService({ userInfo });
72
+ *
73
+ * // M2M authentication
74
+ * const service = new MarketService({ clientCredentials: { clientId, clientSecret } });
75
+ *
76
+ * // Public endpoints (no auth)
77
+ * const service = new MarketService();
78
+ * ```
79
+ */
80
+ export class MarketService {
81
+ market: MarketSDK;
82
+
83
+ constructor(options: MarketServiceOptions = {}) {
84
+ const { accessToken, userInfo, clientCredentials, trustedClientToken } = options;
85
+
86
+ // Use provided trustedClientToken or generate from userInfo
87
+ const resolvedTrustedClientToken =
88
+ trustedClientToken || (userInfo ? generateTrustedClientToken(userInfo) : undefined);
89
+
90
+ this.market = new MarketSDK({
91
+ accessToken,
92
+ baseURL: MARKET_BASE_URL,
93
+ clientId: clientCredentials?.clientId,
94
+ clientSecret: clientCredentials?.clientSecret,
95
+ trustedClientToken: resolvedTrustedClientToken,
96
+ });
97
+
98
+ log(
99
+ 'MarketService initialized: baseURL=%s, hasAccessToken=%s, hasTrustedToken=%s, hasClientCredentials=%s',
100
+ MARKET_BASE_URL,
101
+ !!accessToken,
102
+ !!resolvedTrustedClientToken,
103
+ !!clientCredentials,
104
+ );
105
+ }
106
+
107
+ // ============================== Factory Methods ==============================
108
+
109
+ /**
110
+ * Create MarketService from Next.js request (server-side only)
111
+ * Extracts accessToken from Authorization header and trustedClientToken from session
112
+ */
113
+ static async createFromRequest(req: NextRequest): Promise<MarketService> {
114
+ const accessToken = extractAccessToken(req);
115
+ const trustedClientToken = await getTrustedClientTokenForSession();
116
+
117
+ return new MarketService({
118
+ accessToken,
119
+ trustedClientToken,
120
+ });
121
+ }
122
+
123
+ // ============================== Feedback Methods ==============================
124
+
125
+ /**
126
+ * Submit feedback to LobeHub
127
+ */
128
+ async submitFeedback(params: {
129
+ clientInfo?: {
130
+ language?: string;
131
+ timezone?: string;
132
+ url?: string;
133
+ userAgent?: string;
134
+ };
135
+ email?: string;
136
+ message: string;
137
+ screenshotUrl?: string;
138
+ title: string;
139
+ }) {
140
+ const { title, message, email, screenshotUrl, clientInfo } = params;
141
+
142
+ // Build message with screenshot if available
143
+ let feedbackMessage = message;
144
+ if (screenshotUrl) {
145
+ feedbackMessage += `\n\n**Screenshot**: ${screenshotUrl}`;
146
+ }
147
+
148
+ return this.market.feedback.submitFeedback({
149
+ clientInfo,
150
+ email: email || '',
151
+ message: feedbackMessage,
152
+ title,
153
+ });
154
+ }
155
+
156
+ // ============================== Auth Methods ==============================
157
+
158
+ /**
159
+ * Exchange OAuth authorization code for tokens
160
+ */
161
+ async exchangeAuthorizationCode(params: {
162
+ clientId: string;
163
+ code: string;
164
+ codeVerifier: string;
165
+ redirectUri: string;
166
+ }) {
167
+ return this.market.auth.exchangeOAuthToken({
168
+ clientId: params.clientId,
169
+ code: params.code,
170
+ codeVerifier: params.codeVerifier,
171
+ grantType: 'authorization_code',
172
+ redirectUri: params.redirectUri,
173
+ });
174
+ }
175
+
176
+ /**
177
+ * Get OAuth handoff information
178
+ */
179
+ async getOAuthHandoff(id: string) {
180
+ return this.market.auth.getOAuthHandoff(id);
181
+ }
182
+
183
+ /**
184
+ * Get user info from token
185
+ */
186
+ async getUserInfo(token: string) {
187
+ return this.market.auth.getUserInfo(token);
188
+ }
189
+
190
+ /**
191
+ * Get user info with trusted client token (server-side)
192
+ */
193
+ async getUserInfoWithTrustedClient() {
194
+ const userInfoUrl = `${MARKET_BASE_URL}/lobehub-oidc/userinfo`;
195
+ const response = await fetch(userInfoUrl, {
196
+ // @ts-ignore
197
+ headers: this.market.headers,
198
+ method: 'GET',
199
+ });
200
+
201
+ if (!response.ok) {
202
+ throw new Error('Failed to get user info');
203
+ }
204
+
205
+ return response.json();
206
+ }
207
+
208
+ /**
209
+ * Refresh OAuth token
210
+ */
211
+ async refreshToken(params: { clientId: string; refreshToken: string }) {
212
+ return this.market.auth.exchangeOAuthToken({
213
+ clientId: params.clientId,
214
+ grantType: 'refresh_token',
215
+ refreshToken: params.refreshToken,
216
+ });
217
+ }
218
+
219
+ // ============================== Client Methods ==============================
220
+
221
+ /**
222
+ * Register client for M2M authentication
223
+ */
224
+ async registerClient(params: {
225
+ clientName: string;
226
+ clientType: string;
227
+ deviceId: string;
228
+ platform?: string;
229
+ version?: string;
230
+ }) {
231
+ // @ts-ignore
232
+ return this.market.registerClient(params);
233
+ }
234
+
235
+ /**
236
+ * Fetch M2M token with client credentials
237
+ */
238
+ async fetchM2MToken() {
239
+ return this.market.fetchM2MToken();
240
+ }
241
+
242
+ // ============================== Skills Methods ==============================
243
+
244
+ /**
245
+ * List available tools for a provider
246
+ */
247
+ async listSkillTools(providerId: string) {
248
+ return this.market.skills.listTools(providerId);
249
+ }
250
+
251
+ /**
252
+ * Call a skill tool
253
+ */
254
+ async callSkillTool(provider: string, params: { args: Record<string, any>; tool: string }) {
255
+ return this.market.skills.callTool(provider, params);
256
+ }
257
+
258
+ /**
259
+ * List user's connected skills
260
+ */
261
+ async listSkillConnections() {
262
+ return this.market.connect.listConnections();
263
+ }
264
+
265
+ // ============================== Plugin Methods ==============================
266
+
267
+ /**
268
+ * Call cloud MCP endpoint
269
+ */
270
+ async callCloudMcpEndpoint(
271
+ params: {
272
+ apiParams: Record<string, any>;
273
+ identifier: string;
274
+ toolName: string;
275
+ },
276
+ options?: {
277
+ headers?: Record<string, string>;
278
+ },
279
+ ) {
280
+ return this.market.plugins.callCloudGateway(params, options);
281
+ }
282
+
283
+ /**
284
+ * Get plugin manifest
285
+ */
286
+ async getPluginManifest(params: {
287
+ identifier: string;
288
+ install?: boolean;
289
+ locale?: string;
290
+ version?: string;
291
+ }) {
292
+ return this.market.plugins.getPluginManifest(params);
293
+ }
294
+
295
+ /**
296
+ * Report plugin installation
297
+ */
298
+ async reportPluginInstallation(params: any) {
299
+ return this.market.plugins.reportInstallation(params);
300
+ }
301
+
302
+ /**
303
+ * Report plugin call
304
+ */
305
+ async reportPluginCall(params: any) {
306
+ return this.market.plugins.reportCall(params);
307
+ }
308
+
309
+ /**
310
+ * Create plugin event
311
+ */
312
+ async createPluginEvent(params: any) {
313
+ return this.market.plugins.createEvent(params);
314
+ }
315
+
316
+ // ============================== Agent Methods ==============================
317
+
318
+ /**
319
+ * Get agent detail
320
+ */
321
+ async getAgentDetail(identifier: string, options?: { locale?: string; version?: string }) {
322
+ return this.market.agents.getAgentDetail(identifier, options);
323
+ }
324
+
325
+ /**
326
+ * Get agent list
327
+ */
328
+ async getAgentList(params?: any) {
329
+ return this.market.agents.getAgentList(params);
330
+ }
331
+
332
+ /**
333
+ * Increase agent install count
334
+ */
335
+ async increaseAgentInstallCount(identifier: string) {
336
+ return this.market.agents.increaseInstallCount(identifier);
337
+ }
338
+
339
+ /**
340
+ * Create agent event
341
+ */
342
+ async createAgentEvent(params: any) {
343
+ return this.market.agents.createEvent(params);
344
+ }
345
+
346
+ // ============================== Agent Group Methods ==============================
347
+
348
+ /**
349
+ * Get agent group detail
350
+ */
351
+ async getAgentGroupDetail(identifier: string, options?: { locale?: string; version?: number }) {
352
+ return this.market.agentGroups.getAgentGroupDetail(identifier, options);
353
+ }
354
+
355
+ /**
356
+ * Get agent group list
357
+ */
358
+ async getAgentGroupList(params?: any) {
359
+ return this.market.agentGroups.getAgentGroupList(params);
360
+ }
361
+
362
+ // ============================== User Methods ==============================
363
+
364
+ /**
365
+ * Get user profile by username
366
+ */
367
+ async getUserProfile(username: string, options?: { locale?: string }) {
368
+ return this.market.user.getUserInfo(username, options);
369
+ }
370
+
371
+ // ============================== Skills Methods ==============================
372
+
373
+ /**
374
+ * Execute a LobeHub Skill tool
375
+ * @param params - The skill execution parameters (provider, toolName, args)
376
+ * @returns Execution result with content and success status
377
+ */
378
+ async executeLobehubSkill(params: LobehubSkillExecuteParams): Promise<LobehubSkillExecuteResult> {
379
+ const { provider, toolName, args } = params;
380
+
381
+ log('executeLobehubSkill: %s/%s with args: %O', provider, toolName, args);
382
+
383
+ try {
384
+ const response = await this.market.skills.callTool(provider, {
385
+ args,
386
+ tool: toolName,
387
+ });
388
+
389
+ log('executeLobehubSkill: response: %O', response);
390
+
391
+ return {
392
+ content: typeof response.data === 'string' ? response.data : JSON.stringify(response.data),
393
+ success: response.success,
394
+ };
395
+ } catch (error) {
396
+ const err = error as Error;
397
+ console.error('MarketService.executeLobehubSkill error %s/%s: %O', provider, toolName, err);
398
+
399
+ return {
400
+ content: err.message,
401
+ error: { code: 'LOBEHUB_SKILL_ERROR', message: err.message },
402
+ success: false,
403
+ };
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Fetch LobeHub Skills manifests from Market API
409
+ * Gets user's connected skills and builds tool manifests for agent execution
410
+ *
411
+ * @returns Array of tool manifests for connected skills
412
+ */
413
+ async getLobehubSkillManifests(): Promise<LobeToolManifest[]> {
414
+ try {
415
+ // 1. Get user's connected skills
416
+ const { connections } = await this.market.connect.listConnections();
417
+ if (!connections || connections.length === 0) {
418
+ log('getLobehubSkillManifests: no connected skills found');
419
+ return [];
420
+ }
421
+
422
+ log('getLobehubSkillManifests: found %d connected skills', connections.length);
423
+
424
+ // 2. Fetch tools for each connection and build manifests
425
+ const manifests: LobeToolManifest[] = [];
426
+
427
+ for (const connection of connections) {
428
+ try {
429
+ // Connection returns providerId (e.g., 'twitter', 'linear'), not numeric id
430
+ const providerId = (connection as any).providerId;
431
+ if (!providerId) {
432
+ log('getLobehubSkillManifests: connection missing providerId: %O', connection);
433
+ continue;
434
+ }
435
+ const providerName =
436
+ (connection as any).providerName || (connection as any).name || providerId;
437
+ const icon = (connection as any).icon;
438
+
439
+ const { tools } = await this.market.skills.listTools(providerId);
440
+ if (!tools || tools.length === 0) continue;
441
+
442
+ const manifest: LobeToolManifest = {
443
+ api: tools.map((tool: any) => ({
444
+ description: tool.description || '',
445
+ name: tool.name,
446
+ parameters: tool.inputSchema || { properties: {}, type: 'object' },
447
+ })),
448
+ identifier: providerId,
449
+ meta: {
450
+ avatar: icon || '🔗',
451
+ description: `LobeHub Skill: ${providerName}`,
452
+ tags: ['lobehub-skill', providerId],
453
+ title: providerName,
454
+ },
455
+ type: 'builtin',
456
+ };
457
+
458
+ manifests.push(manifest);
459
+ log(
460
+ 'getLobehubSkillManifests: built manifest for %s with %d tools',
461
+ providerId,
462
+ tools.length,
463
+ );
464
+ } catch (error) {
465
+ log('getLobehubSkillManifests: failed to fetch tools for connection: %O', error);
466
+ }
467
+ }
468
+
469
+ return manifests;
470
+ } catch (error) {
471
+ log('getLobehubSkillManifests: error fetching skills: %O', error);
472
+ return [];
473
+ }
474
+ }
475
+
476
+ // ============================== Direct SDK Access ==============================
477
+
478
+ /**
479
+ * Get MarketSDK instance for advanced usage
480
+ * Use this when you need direct access to SDK methods not wrapped by this service
481
+ */
482
+ getSDK(): MarketSDK {
483
+ return this.market;
484
+ }
485
+ }