@elizaos/client 1.5.5-alpha.10

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 (209) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +350 -0
  3. package/dist/assets/empty-module-CLMscLYw.js +1 -0
  4. package/dist/assets/main-BBZ_3lkn.css +5999 -0
  5. package/dist/assets/main-C5zNUkXH.js +7 -0
  6. package/dist/assets/main-Dz64ENQg.js +614 -0
  7. package/dist/assets/react-vendor-DM5m98rr.js +545 -0
  8. package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
  9. package/dist/elizaos-avatar.png +0 -0
  10. package/dist/elizaos-icon.png +0 -0
  11. package/dist/elizaos-logo-light.png +0 -0
  12. package/dist/elizaos.webp +0 -0
  13. package/dist/favicon.ico +0 -0
  14. package/dist/images/agents/agent1.png +0 -0
  15. package/dist/images/agents/agent2.png +0 -0
  16. package/dist/images/agents/agent3.png +0 -0
  17. package/dist/images/agents/agent4.png +0 -0
  18. package/dist/images/agents/agent5.png +0 -0
  19. package/dist/index.html +14 -0
  20. package/index.html +24 -0
  21. package/package.json +159 -0
  22. package/postcss.config.js +3 -0
  23. package/public/elizaos-avatar.png +0 -0
  24. package/public/elizaos-icon.png +0 -0
  25. package/public/elizaos-logo-light.png +0 -0
  26. package/public/elizaos.webp +0 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/images/agents/agent1.png +0 -0
  29. package/public/images/agents/agent2.png +0 -0
  30. package/public/images/agents/agent3.png +0 -0
  31. package/public/images/agents/agent4.png +0 -0
  32. package/public/images/agents/agent5.png +0 -0
  33. package/src/App.tsx +222 -0
  34. package/src/components/AgentDetailsPanel.tsx +147 -0
  35. package/src/components/ChatInputArea.tsx +196 -0
  36. package/src/components/ChatMessageListComponent.tsx +139 -0
  37. package/src/components/actionTool.tsx +186 -0
  38. package/src/components/add-agent-card.tsx +77 -0
  39. package/src/components/agent-action-viewer.tsx +816 -0
  40. package/src/components/agent-avatar-stack.tsx +121 -0
  41. package/src/components/agent-card.cy.tsx +259 -0
  42. package/src/components/agent-card.tsx +177 -0
  43. package/src/components/agent-creator.tsx +142 -0
  44. package/src/components/agent-log-viewer.tsx +645 -0
  45. package/src/components/agent-memory-edit-overlay.tsx +461 -0
  46. package/src/components/agent-memory-viewer.tsx +504 -0
  47. package/src/components/agent-settings.tsx +270 -0
  48. package/src/components/agent-sidebar.tsx +178 -0
  49. package/src/components/api-key-dialog.tsx +113 -0
  50. package/src/components/app-sidebar.tsx +685 -0
  51. package/src/components/array-input.tsx +116 -0
  52. package/src/components/audio-recorder.tsx +292 -0
  53. package/src/components/avatar-panel.tsx +141 -0
  54. package/src/components/character-form.tsx +1138 -0
  55. package/src/components/chat.tsx +1813 -0
  56. package/src/components/combobox.tsx +187 -0
  57. package/src/components/confirmation-dialog.tsx +59 -0
  58. package/src/components/connection-error-banner.tsx +101 -0
  59. package/src/components/connection-status.cy.tsx +73 -0
  60. package/src/components/connection-status.tsx +155 -0
  61. package/src/components/copy-button.tsx +35 -0
  62. package/src/components/delete-button.tsx +24 -0
  63. package/src/components/env-settings.tsx +261 -0
  64. package/src/components/group-card.tsx +160 -0
  65. package/src/components/group-panel.tsx +543 -0
  66. package/src/components/input-copy.tsx +21 -0
  67. package/src/components/logs-page.tsx +41 -0
  68. package/src/components/media-content.tsx +385 -0
  69. package/src/components/memory-graph.tsx +170 -0
  70. package/src/components/missing-secrets-dialog.tsx +72 -0
  71. package/src/components/onboarding-tour.tsx +247 -0
  72. package/src/components/page-title.tsx +8 -0
  73. package/src/components/plugins-panel.tsx +383 -0
  74. package/src/components/profile-card.tsx +66 -0
  75. package/src/components/profile-overlay.tsx +283 -0
  76. package/src/components/retry-button.tsx +28 -0
  77. package/src/components/secret-panel.tsx +1505 -0
  78. package/src/components/server-management.tsx +264 -0
  79. package/src/components/split-button.tsx +148 -0
  80. package/src/components/stop-agent-button.tsx +99 -0
  81. package/src/components/ui/alert-dialog.cy.tsx +333 -0
  82. package/src/components/ui/alert-dialog.tsx +115 -0
  83. package/src/components/ui/alert.tsx +49 -0
  84. package/src/components/ui/avatar.cy.tsx +180 -0
  85. package/src/components/ui/avatar.tsx +57 -0
  86. package/src/components/ui/badge.cy.tsx +146 -0
  87. package/src/components/ui/badge.tsx +43 -0
  88. package/src/components/ui/button.cy.tsx +177 -0
  89. package/src/components/ui/button.tsx +56 -0
  90. package/src/components/ui/card.cy.tsx +160 -0
  91. package/src/components/ui/card.tsx +73 -0
  92. package/src/components/ui/chat/animated-markdown.tsx +59 -0
  93. package/src/components/ui/chat/chat-bubble.tsx +178 -0
  94. package/src/components/ui/chat/chat-container.tsx +51 -0
  95. package/src/components/ui/chat/chat-input.cy.tsx +169 -0
  96. package/src/components/ui/chat/chat-input.tsx +47 -0
  97. package/src/components/ui/chat/chat-message-list.tsx +61 -0
  98. package/src/components/ui/chat/chat-tts-button.tsx +199 -0
  99. package/src/components/ui/chat/code-block.tsx +79 -0
  100. package/src/components/ui/chat/expandable-chat.tsx +131 -0
  101. package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
  102. package/src/components/ui/chat/markdown.tsx +209 -0
  103. package/src/components/ui/chat/message-loading.tsx +48 -0
  104. package/src/components/ui/checkbox.cy.tsx +170 -0
  105. package/src/components/ui/checkbox.tsx +30 -0
  106. package/src/components/ui/collapsible.cy.tsx +283 -0
  107. package/src/components/ui/collapsible.tsx +9 -0
  108. package/src/components/ui/command.cy.tsx +313 -0
  109. package/src/components/ui/command.tsx +143 -0
  110. package/src/components/ui/dialog.cy.tsx +279 -0
  111. package/src/components/ui/dialog.tsx +104 -0
  112. package/src/components/ui/dropdown-menu.cy.tsx +273 -0
  113. package/src/components/ui/dropdown-menu.tsx +281 -0
  114. package/src/components/ui/input.cy.tsx +82 -0
  115. package/src/components/ui/input.tsx +27 -0
  116. package/src/components/ui/label.cy.tsx +157 -0
  117. package/src/components/ui/label.tsx +19 -0
  118. package/src/components/ui/resizable.tsx +42 -0
  119. package/src/components/ui/scroll-area.cy.tsx +242 -0
  120. package/src/components/ui/scroll-area.tsx +46 -0
  121. package/src/components/ui/select.cy.tsx +277 -0
  122. package/src/components/ui/select.tsx +155 -0
  123. package/src/components/ui/separator.cy.tsx +145 -0
  124. package/src/components/ui/separator.tsx +29 -0
  125. package/src/components/ui/sheet.cy.tsx +324 -0
  126. package/src/components/ui/sheet.tsx +119 -0
  127. package/src/components/ui/sidebar.tsx +734 -0
  128. package/src/components/ui/skeleton.cy.tsx +149 -0
  129. package/src/components/ui/skeleton.tsx +17 -0
  130. package/src/components/ui/split-button.cy.tsx +274 -0
  131. package/src/components/ui/split-button.tsx +112 -0
  132. package/src/components/ui/switch.tsx +28 -0
  133. package/src/components/ui/tabs.cy.tsx +271 -0
  134. package/src/components/ui/tabs.tsx +53 -0
  135. package/src/components/ui/textarea.cy.tsx +136 -0
  136. package/src/components/ui/textarea.tsx +26 -0
  137. package/src/components/ui/toast.cy.tsx +209 -0
  138. package/src/components/ui/toast.tsx +126 -0
  139. package/src/components/ui/toaster.tsx +29 -0
  140. package/src/components/ui/tooltip.cy.tsx +244 -0
  141. package/src/components/ui/tooltip.tsx +30 -0
  142. package/src/config/agent-templates.ts +349 -0
  143. package/src/config/voice-models.ts +181 -0
  144. package/src/constants.ts +23 -0
  145. package/src/context/AuthContext.tsx +44 -0
  146. package/src/context/ConnectionContext.tsx +194 -0
  147. package/src/entry.tsx +9 -0
  148. package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
  149. package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
  150. package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
  151. package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
  152. package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
  153. package/src/hooks/use-agent-management.ts +130 -0
  154. package/src/hooks/use-agent-tab-state.ts +74 -0
  155. package/src/hooks/use-agent-update.ts +469 -0
  156. package/src/hooks/use-character-convert.ts +138 -0
  157. package/src/hooks/use-confirmation.ts +55 -0
  158. package/src/hooks/use-delete-agent.ts +123 -0
  159. package/src/hooks/use-dm-channels.ts +198 -0
  160. package/src/hooks/use-elevenlabs-voices.ts +83 -0
  161. package/src/hooks/use-file-upload.ts +224 -0
  162. package/src/hooks/use-mobile.tsx +19 -0
  163. package/src/hooks/use-onboarding.tsx +49 -0
  164. package/src/hooks/use-panel-width-state.ts +147 -0
  165. package/src/hooks/use-partial-update.ts +288 -0
  166. package/src/hooks/use-plugin-details.ts +462 -0
  167. package/src/hooks/use-plugins.ts +119 -0
  168. package/src/hooks/use-query-hooks.ts +1263 -0
  169. package/src/hooks/use-server-agents.ts +62 -0
  170. package/src/hooks/use-server-version.tsx +47 -0
  171. package/src/hooks/use-sidebar-state.ts +50 -0
  172. package/src/hooks/use-socket-chat.ts +264 -0
  173. package/src/hooks/use-toast.ts +260 -0
  174. package/src/hooks/use-version.tsx +64 -0
  175. package/src/index.css +146 -0
  176. package/src/lib/api-client-config.ts +53 -0
  177. package/src/lib/api-type-mappers.ts +196 -0
  178. package/src/lib/export-utils.ts +123 -0
  179. package/src/lib/logger.ts +19 -0
  180. package/src/lib/media-utils.ts +170 -0
  181. package/src/lib/pca.test.ts +17 -0
  182. package/src/lib/pca.ts +52 -0
  183. package/src/lib/socketio-manager.ts +664 -0
  184. package/src/lib/utils.ts +168 -0
  185. package/src/main.tsx +16 -0
  186. package/src/mocks/empty-module.ts +12 -0
  187. package/src/mocks/node-module.ts +57 -0
  188. package/src/polyfills.ts +37 -0
  189. package/src/routes/agent-detail.tsx +30 -0
  190. package/src/routes/agent-list.tsx +27 -0
  191. package/src/routes/agent-settings.tsx +48 -0
  192. package/src/routes/character-detail.tsx +52 -0
  193. package/src/routes/character-form.tsx +79 -0
  194. package/src/routes/character-list.tsx +38 -0
  195. package/src/routes/chat.tsx +128 -0
  196. package/src/routes/createAgent.tsx +13 -0
  197. package/src/routes/group-new.tsx +50 -0
  198. package/src/routes/group.tsx +29 -0
  199. package/src/routes/home.tsx +218 -0
  200. package/src/routes/not-found.tsx +71 -0
  201. package/src/test/setup.ts +154 -0
  202. package/src/types/crypto-browserify.d.ts +4 -0
  203. package/src/types/index.ts +13 -0
  204. package/src/types/rooms.ts +8 -0
  205. package/src/types.ts +84 -0
  206. package/src/vite-env.d.ts +40 -0
  207. package/tailwind.config.ts +90 -0
  208. package/tsconfig.json +10 -0
  209. package/vite.config.ts +102 -0
@@ -0,0 +1,462 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { useMemo } from 'react';
3
+ import clientLogger from '@/lib/logger';
4
+
5
+ // Registry configuration - same as in use-plugins.ts
6
+ const REGISTRY_ORG = 'elizaos-plugins';
7
+ const REGISTRY_REPO = 'registry';
8
+ const REGISTRY_URL = `https://raw.githubusercontent.com/${REGISTRY_ORG}/${REGISTRY_REPO}/refs/heads/main/generated-registry.json`;
9
+
10
+ // Define the structure of plugin secrets requirements
11
+ interface PluginSecret {
12
+ name: string;
13
+ description?: string;
14
+ required: boolean;
15
+ example?: string;
16
+ }
17
+
18
+ interface PluginPackageJson {
19
+ name: string;
20
+ version: string;
21
+ description?: string;
22
+ elizaos?: {
23
+ secrets?: PluginSecret[];
24
+ requiredSecrets?: string[]; // Legacy format - just array of secret names
25
+ };
26
+ agentConfig?: {
27
+ pluginType?: string;
28
+ pluginParameters?: Record<
29
+ string,
30
+ {
31
+ type: string;
32
+ description?: string;
33
+ required: boolean;
34
+ sensitive?: boolean;
35
+ example?: string;
36
+ }
37
+ >;
38
+ };
39
+ }
40
+
41
+ interface PluginDetails {
42
+ name: string;
43
+ requiredSecrets: PluginSecret[];
44
+ }
45
+
46
+ // Core plugins that are part of the monorepo and don't need external fetching
47
+ const CORE_PLUGINS = ['@elizaos/plugin-bootstrap', '@elizaos/plugin-sql'];
48
+
49
+ // Registry types (same as in use-plugins.ts)
50
+ interface GitVersionInfo {
51
+ version: string | null;
52
+ branch: string | null;
53
+ }
54
+
55
+ interface PluginGitInfo {
56
+ repo: string;
57
+ v0: GitVersionInfo;
58
+ v1: GitVersionInfo;
59
+ }
60
+
61
+ interface PluginNpmInfo {
62
+ repo: string;
63
+ v0: string | null;
64
+ v1: string | null;
65
+ }
66
+
67
+ interface PluginSupport {
68
+ v0: boolean;
69
+ v1: boolean;
70
+ }
71
+
72
+ interface PluginInfo {
73
+ git: PluginGitInfo;
74
+ npm: PluginNpmInfo;
75
+ supports: PluginSupport;
76
+ }
77
+
78
+ interface RegistryResponse {
79
+ lastUpdatedAt: string;
80
+ registry: Record<string, PluginInfo>;
81
+ }
82
+
83
+ /**
84
+ * Fetch the plugin registry to get GitHub repo information
85
+ */
86
+ async function fetchPluginRegistry(): Promise<RegistryResponse | null> {
87
+ try {
88
+ const response = await fetch(REGISTRY_URL);
89
+ if (!response.ok) {
90
+ throw new Error(`Failed to fetch registry: ${response.status}`);
91
+ }
92
+ return await response.json();
93
+ } catch (error) {
94
+ clientLogger.error('Failed to fetch plugin registry:', error);
95
+ return null;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Convert plugin name for registry lookup - handles both @elizaos and @elizaos-plugins formats
101
+ */
102
+ function getRegistryPluginName(pluginName: string): string {
103
+ // If already in @elizaos-plugins format, return as is
104
+ if (pluginName.startsWith('@elizaos-plugins/')) {
105
+ return pluginName;
106
+ }
107
+ // Convert @elizaos to @elizaos-plugins format
108
+ return pluginName.replace('@elizaos/', '@elizaos-plugins/');
109
+ }
110
+
111
+ /**
112
+ * Check if a plugin is a core plugin that doesn't need external fetching
113
+ */
114
+ function isCorePlugin(pluginName: string): boolean {
115
+ return CORE_PLUGINS.includes(pluginName);
116
+ }
117
+
118
+ /**
119
+ * Get GitHub repo path from registry data
120
+ */
121
+ function getGitHubRepoPath(
122
+ pluginName: string,
123
+ registryData: RegistryResponse | null
124
+ ): string | null {
125
+ // Skip core plugins
126
+ if (isCorePlugin(pluginName)) {
127
+ return null;
128
+ }
129
+
130
+ if (!registryData) {
131
+ clientLogger.warn(`No registry data available for ${pluginName}`);
132
+ return null;
133
+ }
134
+
135
+ // Try both naming conventions
136
+ const registryName = getRegistryPluginName(pluginName);
137
+ const alternativeName = pluginName; // Try the original name as well
138
+
139
+ clientLogger.debug(
140
+ `Looking for ${pluginName} in registry as ${registryName} or ${alternativeName}`
141
+ );
142
+
143
+ // Try primary registry name first
144
+ let pluginInfo = registryData.registry[registryName];
145
+
146
+ // If not found, try the alternative name
147
+ if (!pluginInfo && registryName !== alternativeName) {
148
+ pluginInfo = registryData.registry[alternativeName];
149
+ if (pluginInfo) {
150
+ clientLogger.debug(`Found plugin under alternative name: ${alternativeName}`);
151
+ }
152
+ }
153
+
154
+ if (!pluginInfo?.git?.repo) {
155
+ clientLogger.warn(
156
+ `No GitHub repo found in registry for plugin: ${pluginName} (tried ${registryName} and ${alternativeName})`
157
+ );
158
+ return null;
159
+ }
160
+
161
+ clientLogger.debug(`Found repo info for ${pluginName}: ${pluginInfo.git.repo}`);
162
+
163
+ // Get the appropriate branch/version
164
+ const gitInfo = pluginInfo.git.v1.branch ? pluginInfo.git.v1 : pluginInfo.git.v0;
165
+ const branch = gitInfo?.branch || 'main'; // Default to 'main' if no branch info
166
+
167
+ if (!gitInfo?.branch) {
168
+ clientLogger.warn(`No branch information found for plugin: ${pluginName}`);
169
+ // Don't return null here - we'll try default branches in fetchPluginPackageJson
170
+ } else {
171
+ clientLogger.debug(`Branch for ${pluginName}: ${branch}`);
172
+ }
173
+
174
+ // Extract owner/repo from the git URL
175
+ let ownerRepo: string;
176
+
177
+ const repoMatch = pluginInfo.git.repo.match(/github\.com[:/]([^/]+\/[^/.]+)(\.git)?$/);
178
+ if (repoMatch) {
179
+ ownerRepo = repoMatch[1];
180
+ } else {
181
+ // Try to parse as a simple owner/repo format (e.g., "elizaos-plugins/plugin-google-genai")
182
+ const simpleMatch = pluginInfo.git.repo.match(/^([^/]+\/[^/.]+)$/);
183
+ if (simpleMatch) {
184
+ ownerRepo = simpleMatch[1];
185
+ } else {
186
+ clientLogger.warn(`Could not parse GitHub repo URL: ${pluginInfo.git.repo}`);
187
+ return null;
188
+ }
189
+ }
190
+
191
+ clientLogger.debug(`Parsed owner/repo for ${pluginName}: ${ownerRepo}`);
192
+
193
+ // Return a simple path without packages subdirectory
194
+ // fetchPluginPackageJson will try various combinations
195
+ const finalPath = `${ownerRepo}/${branch}`;
196
+ clientLogger.debug(`Initial path for ${pluginName}: ${finalPath}`);
197
+
198
+ return finalPath;
199
+ }
200
+
201
+ /**
202
+ * Fetches package.json for a single plugin from GitHub
203
+ */
204
+ async function fetchPluginPackageJson(
205
+ pluginName: string,
206
+ repoPath: string | null
207
+ ): Promise<PluginPackageJson | null> {
208
+ // Skip core plugins
209
+ if (isCorePlugin(pluginName)) {
210
+ return null;
211
+ }
212
+
213
+ if (!repoPath) {
214
+ clientLogger.warn(`No repo path available for plugin: ${pluginName}`);
215
+ return null;
216
+ }
217
+
218
+ clientLogger.debug(
219
+ `Starting package.json fetch for ${pluginName} with initial path: ${repoPath}`
220
+ );
221
+
222
+ // Extract the base repo path
223
+ const pathParts = repoPath.split('/');
224
+ const owner = pathParts[0];
225
+ const repo = pathParts[1];
226
+ const branch = pathParts[2] || 'main';
227
+ const packageName = pluginName.replace('@elizaos/', '');
228
+
229
+ clientLogger.debug(
230
+ `Extracted parts - owner: ${owner}, repo: ${repo}, branch: ${branch}, packageName: ${packageName}`
231
+ );
232
+
233
+ // Try multiple possible paths for package.json
234
+ // Prioritize root-level (standalone repos) over monorepo structure
235
+ const possiblePaths = [
236
+ // Try with the provided branch first at root level
237
+ `${owner}/${repo}/${branch}`,
238
+ // Try common branch names at root level
239
+ `${owner}/${repo}/main`,
240
+ `${owner}/${repo}/master`,
241
+ `${owner}/${repo}/1.x`,
242
+ `${owner}/${repo}/v1`,
243
+ `${owner}/${repo}/v2`,
244
+ // Only try packages subdirectory as a last resort for monorepo structure
245
+ `${owner}/${repo}/${branch}/packages/${packageName}`,
246
+ `${owner}/${repo}/main/packages/${packageName}`,
247
+ `${owner}/${repo}/master/packages/${packageName}`,
248
+ ];
249
+
250
+ // Remove duplicates while preserving order
251
+ const uniquePaths = [...new Set(possiblePaths)];
252
+
253
+ clientLogger.debug(`Will try ${uniquePaths.length} unique paths for ${pluginName}:`, uniquePaths);
254
+
255
+ for (const path of uniquePaths) {
256
+ try {
257
+ const url = `https://raw.githubusercontent.com/${path}/package.json`;
258
+ clientLogger.debug(`Trying URL for ${pluginName}: ${url}`);
259
+
260
+ const response = await fetch(url);
261
+
262
+ if (response.ok) {
263
+ const packageJson: PluginPackageJson = await response.json();
264
+ clientLogger.debug(`✅ Found package.json for ${pluginName} at ${url}`);
265
+ return packageJson;
266
+ } else {
267
+ clientLogger.debug(`❌ Failed to fetch ${pluginName} from ${url}: HTTP ${response.status}`);
268
+ }
269
+ } catch (error) {
270
+ // Silently continue to next path
271
+ clientLogger.debug(
272
+ `❌ Error fetching ${pluginName} from ${path}: ${error instanceof Error ? error.message : String(error)}`
273
+ );
274
+ }
275
+ }
276
+
277
+ // If we couldn't find package.json in any location, log a warning but don't throw
278
+ clientLogger.warn(
279
+ `Could not find package.json for ${pluginName} in any of the expected locations`
280
+ );
281
+ return null;
282
+ }
283
+
284
+ /**
285
+ * Extract required secrets from package.json
286
+ */
287
+ function extractRequiredSecrets(
288
+ pluginName: string,
289
+ packageJson: PluginPackageJson | null
290
+ ): PluginSecret[] {
291
+ // Core plugins don't have required secrets
292
+ if (isCorePlugin(pluginName)) {
293
+ return [];
294
+ }
295
+
296
+ if (!packageJson) {
297
+ return [];
298
+ }
299
+
300
+ // First, try to get secrets from agentConfig.pluginParameters (new format)
301
+ if (packageJson?.agentConfig?.pluginParameters) {
302
+ const secrets: PluginSecret[] = [];
303
+ for (const [name, config] of Object.entries(packageJson.agentConfig.pluginParameters)) {
304
+ // Fix linter error: only check for boolean true since required is typed as boolean
305
+ if (config.required === true) {
306
+ secrets.push({
307
+ name,
308
+ description: config.description,
309
+ required: true,
310
+ example: config.example,
311
+ });
312
+ }
313
+ }
314
+ // If we found agentConfig.pluginParameters, return the results even if empty
315
+ // This means the plugin explicitly defined its parameters and we should respect that
316
+ return secrets;
317
+ }
318
+
319
+ // Try to get secrets from elizaos.secrets
320
+ if (packageJson?.elizaos?.secrets) {
321
+ return packageJson.elizaos.secrets.filter((secret) => secret.required);
322
+ }
323
+
324
+ // Legacy format - convert string array to PluginSecret array
325
+ if (packageJson?.elizaos?.requiredSecrets) {
326
+ return packageJson.elizaos.requiredSecrets.map((secretName) => ({
327
+ name: secretName,
328
+ required: true,
329
+ }));
330
+ }
331
+
332
+ // No fallback secrets - if we can't find explicit configuration, don't assume anything
333
+ return [];
334
+ }
335
+
336
+ /**
337
+ * Hook to fetch plugin details including required secrets
338
+ */
339
+ export function usePluginDetails(pluginNames: string[]) {
340
+ // Create a stable key for the query to prevent infinite loops
341
+ const stablePluginNames = useMemo(() => {
342
+ return [...pluginNames].sort().join(',');
343
+ }, [pluginNames]);
344
+
345
+ return useQuery({
346
+ queryKey: ['plugin-details', stablePluginNames],
347
+ queryFn: async () => {
348
+ clientLogger.debug('[usePluginDetails] Starting fetch for plugins:', pluginNames);
349
+ const details: PluginDetails[] = [];
350
+
351
+ // Separate core plugins from external plugins
352
+ const corePlugins = pluginNames.filter(isCorePlugin);
353
+ const externalPlugins = pluginNames.filter((name) => !isCorePlugin(name));
354
+
355
+ clientLogger.debug('[usePluginDetails] Core plugins:', corePlugins);
356
+ clientLogger.debug('[usePluginDetails] External plugins:', externalPlugins);
357
+
358
+ // Add core plugins with empty secrets
359
+ corePlugins.forEach((name) => {
360
+ details.push({
361
+ name,
362
+ requiredSecrets: [],
363
+ });
364
+ });
365
+
366
+ // Only fetch registry if we have external plugins
367
+ if (externalPlugins.length > 0) {
368
+ // Fetch the registry to get repo information
369
+ const registryData = await fetchPluginRegistry();
370
+ clientLogger.debug(
371
+ '[usePluginDetails] Registry data fetched:',
372
+ registryData ? 'success' : 'failed'
373
+ );
374
+
375
+ // Log available plugins in registry for debugging
376
+ if (registryData) {
377
+ const availablePlugins = Object.keys(registryData.registry);
378
+ clientLogger.debug('[usePluginDetails] Available plugins in registry:', availablePlugins);
379
+ }
380
+
381
+ // Fetch package.json for each external plugin in parallel
382
+ const packageJsonPromises = externalPlugins.map(async (name) => {
383
+ const repoPath = getGitHubRepoPath(name, registryData);
384
+ const packageJson = await fetchPluginPackageJson(name, repoPath);
385
+ return { name, packageJson };
386
+ });
387
+
388
+ const results = await Promise.all(packageJsonPromises);
389
+
390
+ // Extract required secrets for each plugin
391
+ for (const { name, packageJson } of results) {
392
+ const requiredSecrets = extractRequiredSecrets(name, packageJson);
393
+ details.push({
394
+ name,
395
+ requiredSecrets,
396
+ });
397
+ }
398
+ }
399
+
400
+ clientLogger.debug('[usePluginDetails] Final details:', details);
401
+ return details;
402
+ },
403
+ enabled: pluginNames.length > 0,
404
+ staleTime: 5 * 60 * 1000, // Cache for 5 minutes
405
+ retry: 2, // Retry failed requests
406
+ refetchOnMount: false, // Prevent unnecessary refetches
407
+ refetchOnWindowFocus: false, // Prevent refetches on window focus
408
+ });
409
+ }
410
+
411
+ /**
412
+ * Hook to get all required secrets for a list of plugins
413
+ */
414
+ export function useRequiredSecrets(pluginNames: string[]) {
415
+ const { data: pluginDetails, isLoading, error } = usePluginDetails(pluginNames);
416
+
417
+ const requiredSecrets = useMemo(() => {
418
+ if (!pluginDetails) return [];
419
+
420
+ return pluginDetails.reduce(
421
+ (acc, plugin) => {
422
+ plugin.requiredSecrets.forEach((secret) => {
423
+ // Avoid duplicates
424
+ if (!acc.find((s) => s.name === secret.name)) {
425
+ acc.push({
426
+ ...secret,
427
+ plugin: plugin.name,
428
+ });
429
+ }
430
+ });
431
+ return acc;
432
+ },
433
+ [] as (PluginSecret & { plugin: string })[]
434
+ );
435
+ }, [pluginDetails]);
436
+
437
+ return {
438
+ requiredSecrets,
439
+ isLoading,
440
+ error,
441
+ };
442
+ }
443
+
444
+ /**
445
+ * Check if all required secrets are provided
446
+ */
447
+ export function validateRequiredSecrets(
448
+ requiredSecrets: PluginSecret[],
449
+ providedSecrets: Record<string, string | null>
450
+ ): { isValid: boolean; missingSecrets: string[] } {
451
+ const missingSecrets = requiredSecrets
452
+ .filter((secret) => {
453
+ const value = providedSecrets[secret.name];
454
+ return !value || value.trim() === '';
455
+ })
456
+ .map((secret) => secret.name);
457
+
458
+ return {
459
+ isValid: missingSecrets.length === 0,
460
+ missingSecrets,
461
+ };
462
+ }
@@ -0,0 +1,119 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { createElizaClient } from '@/lib/api-client-config';
3
+ import clientLogger from '@/lib/logger';
4
+
5
+ // Registry configuration - centralized for maintainability
6
+ const REGISTRY_ORG = 'elizaos-plugins';
7
+ const REGISTRY_REPO = 'registry';
8
+ const REGISTRY_URL = `https://raw.githubusercontent.com/${REGISTRY_ORG}/${REGISTRY_REPO}/refs/heads/main/generated-registry.json`;
9
+
10
+ interface GitVersionInfo {
11
+ version: string | null;
12
+ branch: string | null;
13
+ }
14
+
15
+ interface PluginGitInfo {
16
+ repo: string;
17
+ v0: GitVersionInfo;
18
+ v1: GitVersionInfo;
19
+ }
20
+
21
+ interface PluginNpmInfo {
22
+ repo: string;
23
+ v0: string | null;
24
+ v1: string | null;
25
+ }
26
+
27
+ interface PluginSupport {
28
+ v0: boolean;
29
+ v1: boolean;
30
+ }
31
+
32
+ interface PluginInfo {
33
+ git: PluginGitInfo;
34
+ npm: PluginNpmInfo;
35
+ supports: PluginSupport;
36
+ }
37
+
38
+ interface RegistryResponse {
39
+ lastUpdatedAt: string;
40
+ registry: Record<string, PluginInfo>;
41
+ }
42
+
43
+ /**
44
+ * Function to fetch plugins data from the registry API and merge with agent plugins.
45
+ * @returns {UseQueryResult<PluginEntry[]>} Query result containing array of plugin entries
46
+ */
47
+ export function usePlugins() {
48
+ return useQuery({
49
+ queryKey: ['plugins'],
50
+ queryFn: async () => {
51
+ try {
52
+ // Fetch plugins from registry and agent data in parallel
53
+ const elizaClient = createElizaClient();
54
+ const [registryResponse, agentsResponse] = await Promise.all([
55
+ fetch(REGISTRY_URL),
56
+ elizaClient.agents.listAgents(),
57
+ ]);
58
+
59
+ // Process registry data
60
+ const registryData: RegistryResponse = await registryResponse.json();
61
+
62
+ // Extract plugin names from registry that support v1 and are plugins
63
+ const registryPlugins = Object.entries(registryData.registry || {})
64
+ .filter(([name, data]: [string, PluginInfo]) => {
65
+ // Check if it's a plugin and has v1 support
66
+ const isPlugin = name.includes('plugin');
67
+ const hasV1Support = data.supports.v1 === true;
68
+ const hasV1Version =
69
+ data.npm.v1 !== null || (data.git.v1.version !== null && data.git.v1.branch !== null);
70
+
71
+ return isPlugin && hasV1Support && hasV1Version;
72
+ })
73
+ .map(([name]) => name.replace(/^@elizaos-plugins\//, '@elizaos/'))
74
+ .sort();
75
+
76
+ // Process agent plugins from the parallel fetch
77
+ let agentPlugins: string[] = [];
78
+ try {
79
+ const agents = agentsResponse?.agents || [];
80
+ if (agents.length > 0) {
81
+ // Get plugins from the first active agent
82
+ const activeAgent = agents.find((agent) => agent.status === 'active');
83
+ if (activeAgent && activeAgent.id) {
84
+ const agentDetailResponse = await elizaClient.agents.getAgent(activeAgent.id);
85
+
86
+ // Extract plugins from the agent response
87
+ const plugins = (agentDetailResponse as any)?.plugins;
88
+ if (Array.isArray(plugins)) {
89
+ agentPlugins = plugins;
90
+ }
91
+ }
92
+ }
93
+ } catch (agentError) {
94
+ clientLogger.warn('Could not fetch agent plugins:', agentError);
95
+ }
96
+
97
+ // Merge registry plugins with agent plugins and remove duplicates
98
+ const allPlugins = [...new Set([...registryPlugins, ...agentPlugins])];
99
+ return allPlugins.sort();
100
+ } catch (error) {
101
+ clientLogger.error('Failed to fetch from registry, falling back to basic list:', error);
102
+
103
+ // Return fallback plugins with basic info
104
+ return [
105
+ '@elizaos/plugin-bootstrap',
106
+ '@elizaos/plugin-evm',
107
+ '@elizaos/plugin-discord',
108
+ '@elizaos/plugin-elevenlabs',
109
+ '@elizaos/plugin-anthropic',
110
+ '@elizaos/plugin-browser',
111
+ '@elizaos/plugin-farcaster',
112
+ '@elizaos/plugin-groq',
113
+ ]
114
+ .filter((name) => name.includes('plugin'))
115
+ .sort();
116
+ }
117
+ },
118
+ });
119
+ }