@elizaos/app-core 2.0.0-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 (399) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +90 -0
  3. package/src/App.tsx +472 -0
  4. package/src/actions/character.test.ts +139 -0
  5. package/src/actions/character.ts +152 -0
  6. package/src/actions/chat-helpers.ts +100 -0
  7. package/src/actions/cloud.ts +59 -0
  8. package/src/actions/index.ts +12 -0
  9. package/src/actions/lifecycle.ts +175 -0
  10. package/src/actions/onboarding.ts +46 -0
  11. package/src/actions/triggers.ts +190 -0
  12. package/src/ambient.d.ts +16 -0
  13. package/src/api/client.ts +5516 -0
  14. package/src/api/index.ts +1 -0
  15. package/src/autonomy/index.ts +477 -0
  16. package/src/bridge/capacitor-bridge.ts +295 -0
  17. package/src/bridge/electrobun-rpc.ts +58 -0
  18. package/src/bridge/electrobun-runtime.ts +28 -0
  19. package/src/bridge/index.ts +5 -0
  20. package/src/bridge/native-plugins.ts +134 -0
  21. package/src/bridge/plugin-bridge.ts +352 -0
  22. package/src/bridge/storage-bridge.ts +162 -0
  23. package/src/chat/index.ts +250 -0
  24. package/src/coding/index.ts +43 -0
  25. package/src/components/AdvancedPageView.tsx +362 -0
  26. package/src/components/AgentActivityBox.tsx +49 -0
  27. package/src/components/ApiKeyConfig.tsx +224 -0
  28. package/src/components/AppsPageView.tsx +52 -0
  29. package/src/components/AppsView.tsx +293 -0
  30. package/src/components/AvatarLoader.tsx +86 -0
  31. package/src/components/AvatarSelector.tsx +223 -0
  32. package/src/components/BscTradePanel.tsx +549 -0
  33. package/src/components/BugReportModal.tsx +499 -0
  34. package/src/components/CharacterView.tsx +1645 -0
  35. package/src/components/ChatAvatar.test.ts +96 -0
  36. package/src/components/ChatAvatar.tsx +147 -0
  37. package/src/components/ChatComposer.tsx +330 -0
  38. package/src/components/ChatMessage.tsx +448 -0
  39. package/src/components/ChatModalView.test.tsx +118 -0
  40. package/src/components/ChatModalView.tsx +125 -0
  41. package/src/components/ChatView.tsx +992 -0
  42. package/src/components/CloudSourceControls.tsx +80 -0
  43. package/src/components/CodingAgentSettingsSection.tsx +536 -0
  44. package/src/components/CommandPalette.tsx +284 -0
  45. package/src/components/CompanionSceneHost.tsx +497 -0
  46. package/src/components/CompanionShell.tsx +31 -0
  47. package/src/components/CompanionView.tsx +109 -0
  48. package/src/components/ConfigPageView.tsx +758 -0
  49. package/src/components/ConfigSaveFooter.tsx +41 -0
  50. package/src/components/ConfirmModal.tsx +379 -0
  51. package/src/components/ConnectionFailedBanner.tsx +91 -0
  52. package/src/components/ConnectorsPageView.tsx +13 -0
  53. package/src/components/ConversationsSidebar.tsx +279 -0
  54. package/src/components/CustomActionEditor.tsx +1125 -0
  55. package/src/components/CustomActionsPanel.tsx +288 -0
  56. package/src/components/CustomActionsView.tsx +322 -0
  57. package/src/components/DatabasePageView.tsx +55 -0
  58. package/src/components/DatabaseView.tsx +814 -0
  59. package/src/components/ElizaCloudDashboard.tsx +1696 -0
  60. package/src/components/EmotePicker.tsx +529 -0
  61. package/src/components/ErrorBoundary.tsx +76 -0
  62. package/src/components/FineTuningView.tsx +1077 -0
  63. package/src/components/GameView.tsx +552 -0
  64. package/src/components/GameViewOverlay.tsx +133 -0
  65. package/src/components/GlobalEmoteOverlay.tsx +155 -0
  66. package/src/components/Header.test.tsx +413 -0
  67. package/src/components/Header.tsx +403 -0
  68. package/src/components/HeartbeatsView.tsx +1003 -0
  69. package/src/components/InventoryView.tsx +385 -0
  70. package/src/components/KnowledgeView.tsx +1128 -0
  71. package/src/components/LanguageDropdown.tsx +188 -0
  72. package/src/components/LifoMonitorPanel.tsx +196 -0
  73. package/src/components/LifoSandboxView.tsx +499 -0
  74. package/src/components/LoadingScreen.tsx +77 -0
  75. package/src/components/LogsPageView.tsx +17 -0
  76. package/src/components/LogsView.tsx +239 -0
  77. package/src/components/MediaGalleryView.tsx +433 -0
  78. package/src/components/MediaSettingsSection.tsx +893 -0
  79. package/src/components/MessageContent.tsx +815 -0
  80. package/src/components/OnboardingWizard.test.tsx +107 -0
  81. package/src/components/OnboardingWizard.tsx +189 -0
  82. package/src/components/PairingView.tsx +110 -0
  83. package/src/components/PermissionsSection.tsx +1186 -0
  84. package/src/components/PluginsPageView.tsx +9 -0
  85. package/src/components/PluginsView.tsx +3157 -0
  86. package/src/components/ProviderSwitcher.tsx +908 -0
  87. package/src/components/RestartBanner.tsx +76 -0
  88. package/src/components/RuntimeView.tsx +460 -0
  89. package/src/components/SaveCommandModal.tsx +211 -0
  90. package/src/components/SecretsView.tsx +569 -0
  91. package/src/components/SettingsView.tsx +825 -0
  92. package/src/components/ShellOverlays.tsx +41 -0
  93. package/src/components/ShortcutsOverlay.tsx +155 -0
  94. package/src/components/SkillsView.tsx +1435 -0
  95. package/src/components/StartupFailureView.tsx +63 -0
  96. package/src/components/StreamView.tsx +483 -0
  97. package/src/components/StripeEmbeddedCheckout.tsx +155 -0
  98. package/src/components/SubscriptionStatus.tsx +640 -0
  99. package/src/components/SystemWarningBanner.tsx +71 -0
  100. package/src/components/ThemeToggle.tsx +100 -0
  101. package/src/components/TrajectoriesView.tsx +526 -0
  102. package/src/components/TrajectoryDetailView.tsx +426 -0
  103. package/src/components/TriggersView.tsx +1 -0
  104. package/src/components/VectorBrowserView.tsx +1633 -0
  105. package/src/components/VoiceConfigView.tsx +675 -0
  106. package/src/components/VrmStage.test.ts +219 -0
  107. package/src/components/VrmStage.tsx +432 -0
  108. package/src/components/WhatsAppQrOverlay.tsx +230 -0
  109. package/src/components/__tests__/chainConfig.test.ts +220 -0
  110. package/src/components/apps/AppDetailPane.tsx +242 -0
  111. package/src/components/apps/AppsCatalogGrid.tsx +137 -0
  112. package/src/components/apps/extensions/HyperscapeAppDetailPanel.tsx +577 -0
  113. package/src/components/apps/extensions/registry.ts +16 -0
  114. package/src/components/apps/extensions/types.ts +9 -0
  115. package/src/components/apps/helpers.ts +44 -0
  116. package/src/components/avatar/VrmAnimationLoader.test.ts +164 -0
  117. package/src/components/avatar/VrmAnimationLoader.ts +151 -0
  118. package/src/components/avatar/VrmBlinkController.ts +118 -0
  119. package/src/components/avatar/VrmCameraManager.ts +407 -0
  120. package/src/components/avatar/VrmEngine.ts +2678 -0
  121. package/src/components/avatar/VrmFootShadow.ts +96 -0
  122. package/src/components/avatar/VrmViewer.tsx +421 -0
  123. package/src/components/avatar/__tests__/VrmCameraManager.test.ts +168 -0
  124. package/src/components/avatar/__tests__/VrmEngine.test.ts +1574 -0
  125. package/src/components/avatar/mixamoVRMRigMap.ts +62 -0
  126. package/src/components/avatar/retargetMixamoFbxToVrm.ts +144 -0
  127. package/src/components/avatar/retargetMixamoGltfToVrm.ts +119 -0
  128. package/src/components/chainConfig.ts +380 -0
  129. package/src/components/companion/CompanionHeader.tsx +47 -0
  130. package/src/components/companion/CompanionSceneHost.tsx +1 -0
  131. package/src/components/companion/VrmStage.tsx +2 -0
  132. package/src/components/companion/__tests__/walletUtils.test.ts +742 -0
  133. package/src/components/companion/walletUtils.ts +290 -0
  134. package/src/components/companion-shell-styles.test.ts +142 -0
  135. package/src/components/companion-shell-styles.ts +270 -0
  136. package/src/components/confirm-delete-control.tsx +69 -0
  137. package/src/components/conversations/ConversationListItem.tsx +185 -0
  138. package/src/components/conversations/conversation-utils.ts +151 -0
  139. package/src/components/format.ts +131 -0
  140. package/src/components/index.ts +94 -0
  141. package/src/components/inventory/CopyableAddress.tsx +41 -0
  142. package/src/components/inventory/InventoryToolbar.tsx +142 -0
  143. package/src/components/inventory/NftGrid.tsx +99 -0
  144. package/src/components/inventory/TokenLogo.tsx +71 -0
  145. package/src/components/inventory/TokensTable.tsx +216 -0
  146. package/src/components/inventory/constants.ts +170 -0
  147. package/src/components/inventory/index.ts +29 -0
  148. package/src/components/inventory/media-url.test.ts +38 -0
  149. package/src/components/inventory/media-url.ts +36 -0
  150. package/src/components/inventory/useInventoryData.ts +460 -0
  151. package/src/components/knowledge-upload-image.ts +215 -0
  152. package/src/components/labels.ts +46 -0
  153. package/src/components/onboarding/ActivateStep.tsx +30 -0
  154. package/src/components/onboarding/ConnectionStep.tsx +1530 -0
  155. package/src/components/onboarding/IdentityStep.tsx +147 -0
  156. package/src/components/onboarding/OnboardingPanel.tsx +39 -0
  157. package/src/components/onboarding/OnboardingStepNav.tsx +31 -0
  158. package/src/components/onboarding/PermissionsStep.tsx +20 -0
  159. package/src/components/onboarding/RpcStep.tsx +402 -0
  160. package/src/components/onboarding/WakeUpStep.tsx +184 -0
  161. package/src/components/permissions/PermissionIcon.tsx +25 -0
  162. package/src/components/permissions/StreamingPermissions.tsx +413 -0
  163. package/src/components/plugins/showcase-data.ts +481 -0
  164. package/src/components/shared/ShellHeaderControls.tsx +193 -0
  165. package/src/components/shared-companion-scene-context.ts +15 -0
  166. package/src/components/skeletons.tsx +88 -0
  167. package/src/components/stream/ActivityFeed.tsx +113 -0
  168. package/src/components/stream/AvatarPip.tsx +10 -0
  169. package/src/components/stream/ChatContent.tsx +126 -0
  170. package/src/components/stream/ChatTicker.tsx +55 -0
  171. package/src/components/stream/IdleContent.tsx +73 -0
  172. package/src/components/stream/StatusBar.tsx +469 -0
  173. package/src/components/stream/StreamSettings.tsx +506 -0
  174. package/src/components/stream/StreamTerminal.tsx +94 -0
  175. package/src/components/stream/StreamVoiceConfig.tsx +160 -0
  176. package/src/components/stream/helpers.ts +134 -0
  177. package/src/components/stream/overlays/OverlayLayer.tsx +75 -0
  178. package/src/components/stream/overlays/built-in/ActionTickerWidget.tsx +64 -0
  179. package/src/components/stream/overlays/built-in/AlertPopupWidget.tsx +87 -0
  180. package/src/components/stream/overlays/built-in/BrandingWidget.tsx +51 -0
  181. package/src/components/stream/overlays/built-in/CustomHtmlWidget.tsx +105 -0
  182. package/src/components/stream/overlays/built-in/PeonGlassWidget.tsx +265 -0
  183. package/src/components/stream/overlays/built-in/PeonHudWidget.tsx +247 -0
  184. package/src/components/stream/overlays/built-in/PeonSakuraWidget.tsx +278 -0
  185. package/src/components/stream/overlays/built-in/ThoughtBubbleWidget.tsx +77 -0
  186. package/src/components/stream/overlays/built-in/ViewerCountWidget.tsx +46 -0
  187. package/src/components/stream/overlays/built-in/index.ts +13 -0
  188. package/src/components/stream/overlays/registry.ts +22 -0
  189. package/src/components/stream/overlays/types.ts +90 -0
  190. package/src/components/stream/overlays/useOverlayLayout.ts +218 -0
  191. package/src/components/trajectory-format.ts +50 -0
  192. package/src/components/ui-badges.tsx +109 -0
  193. package/src/components/ui-switch.tsx +57 -0
  194. package/src/components/vector-browser-three.ts +27 -0
  195. package/src/config/config-catalog.ts +1092 -0
  196. package/src/config/config-field.tsx +1901 -0
  197. package/src/config/config-renderer.tsx +730 -0
  198. package/src/config/index.ts +11 -0
  199. package/src/config/ui-renderer.tsx +1751 -0
  200. package/src/config/ui-spec.ts +256 -0
  201. package/src/events/index.ts +89 -0
  202. package/src/hooks/index.ts +13 -0
  203. package/src/hooks/useBugReport.tsx +43 -0
  204. package/src/hooks/useCanvasWindow.ts +372 -0
  205. package/src/hooks/useChatAvatarVoice.ts +111 -0
  206. package/src/hooks/useContextMenu.ts +127 -0
  207. package/src/hooks/useKeyboardShortcuts.ts +86 -0
  208. package/src/hooks/useLifoSync.ts +143 -0
  209. package/src/hooks/useMemoryMonitor.ts +334 -0
  210. package/src/hooks/useRenderGuard.ts +43 -0
  211. package/src/hooks/useRetakeCapture.ts +67 -0
  212. package/src/hooks/useStreamPopoutNavigation.ts +27 -0
  213. package/src/hooks/useTimeout.ts +37 -0
  214. package/src/hooks/useVoiceChat.ts +1441 -0
  215. package/src/hooks/useWhatsAppPairing.ts +123 -0
  216. package/src/i18n/index.ts +76 -0
  217. package/src/i18n/locales/en.json +1194 -0
  218. package/src/i18n/locales/es.json +1194 -0
  219. package/src/i18n/locales/ko.json +1194 -0
  220. package/src/i18n/locales/pt.json +1194 -0
  221. package/src/i18n/locales/zh-CN.json +1194 -0
  222. package/src/i18n/messages.ts +21 -0
  223. package/src/index.ts +6 -0
  224. package/src/navigation/index.ts +282 -0
  225. package/src/navigation.test.ts +189 -0
  226. package/src/onboarding-config.test.ts +104 -0
  227. package/src/onboarding-config.ts +114 -0
  228. package/src/platform/browser-launch.test.ts +94 -0
  229. package/src/platform/browser-launch.ts +149 -0
  230. package/src/platform/index.ts +58 -0
  231. package/src/platform/init.ts +236 -0
  232. package/src/platform/lifo.ts +215 -0
  233. package/src/providers/index.ts +99 -0
  234. package/src/state/AppContext.tsx +5846 -0
  235. package/src/state/index.ts +6 -0
  236. package/src/state/internal.ts +86 -0
  237. package/src/state/onboarding-resume.test.ts +135 -0
  238. package/src/state/onboarding-resume.ts +263 -0
  239. package/src/state/parsers.test.ts +124 -0
  240. package/src/state/parsers.ts +308 -0
  241. package/src/state/persistence.ts +321 -0
  242. package/src/state/shell-routing.ts +32 -0
  243. package/src/state/types.ts +701 -0
  244. package/src/state/ui-preferences.ts +3 -0
  245. package/src/state/useApp.ts +23 -0
  246. package/src/state/vrm.ts +76 -0
  247. package/src/stories/AppMockProvider.tsx +32 -0
  248. package/src/stories/ChatEmptyState.stories.tsx +27 -0
  249. package/src/stories/ChatMessage.stories.tsx +115 -0
  250. package/src/stories/CompanionHeader.stories.tsx +74 -0
  251. package/src/stories/CompanionView.stories.tsx +33 -0
  252. package/src/stories/ConversationListItem.stories.tsx +102 -0
  253. package/src/stories/TypingIndicator.stories.tsx +28 -0
  254. package/src/styles/anime.css +6324 -0
  255. package/src/styles/base.css +196 -0
  256. package/src/styles/onboarding-game.css +738 -0
  257. package/src/styles/styles.css +2087 -0
  258. package/src/styles/xterm.css +241 -0
  259. package/src/types/index.ts +715 -0
  260. package/src/types/react-test-renderer.d.ts +45 -0
  261. package/src/utils/asset-url.ts +110 -0
  262. package/src/utils/assistant-text.ts +172 -0
  263. package/src/utils/clipboard.ts +41 -0
  264. package/src/utils/desktop-dialogs.ts +80 -0
  265. package/src/utils/index.ts +6 -0
  266. package/src/utils/number-parsing.ts +125 -0
  267. package/src/utils/openExternalUrl.ts +20 -0
  268. package/src/utils/spoken-text.ts +65 -0
  269. package/src/utils/streaming-text.ts +120 -0
  270. package/src/voice/index.ts +1 -0
  271. package/src/voice/types.ts +197 -0
  272. package/src/wallet-rpc.ts +176 -0
  273. package/test/app/AppContext.pty-sessions.test.tsx +143 -0
  274. package/test/app/MessageContent.test.tsx +326 -0
  275. package/test/app/PermissionsOnboarding.test.tsx +356 -0
  276. package/test/app/PermissionsSection.test.tsx +573 -0
  277. package/test/app/advanced-trajectory-fine-tuning.e2e.test.ts +393 -0
  278. package/test/app/agent-activity-box.test.tsx +132 -0
  279. package/test/app/agent-transfer-lock.test.ts +274 -0
  280. package/test/app/api-client-electron-fallback.test.ts +139 -0
  281. package/test/app/api-client-timeout.test.ts +75 -0
  282. package/test/app/api-client-ws.test.ts +98 -0
  283. package/test/app/api-client.ws-max-reconnect.test.ts +139 -0
  284. package/test/app/api-client.ws-reconnect.test.ts +157 -0
  285. package/test/app/app-context-autonomy-events.test.ts +478 -0
  286. package/test/app/apps-page-view.test.ts +114 -0
  287. package/test/app/apps-view.test.ts +769 -0
  288. package/test/app/autonomous-workflows.e2e.test.ts +765 -0
  289. package/test/app/autonomy-events.test.ts +150 -0
  290. package/test/app/avatar-selector.test.tsx +52 -0
  291. package/test/app/bsc-trade-panel.test.tsx +134 -0
  292. package/test/app/bug-report-modal.test.tsx +353 -0
  293. package/test/app/character-customization.e2e.test.ts +1199 -0
  294. package/test/app/chat-advanced-features.e2e.test.ts +706 -0
  295. package/test/app/chat-composer.test.tsx +181 -0
  296. package/test/app/chat-language-header.test.ts +64 -0
  297. package/test/app/chat-message.test.tsx +222 -0
  298. package/test/app/chat-modal-view.test.tsx +191 -0
  299. package/test/app/chat-routine-filter.test.ts +96 -0
  300. package/test/app/chat-send-lock.test.ts +1465 -0
  301. package/test/app/chat-stream-api-client.test.tsx +390 -0
  302. package/test/app/chat-view-game-modal.test.tsx +661 -0
  303. package/test/app/chat-view.test.tsx +877 -0
  304. package/test/app/cloud-api.e2e.test.ts +258 -0
  305. package/test/app/cloud-login-flow.e2e.test.ts +494 -0
  306. package/test/app/cloud-login-lock.test.ts +411 -0
  307. package/test/app/command-palette.test.tsx +184 -0
  308. package/test/app/command-registry.test.ts +75 -0
  309. package/test/app/companion-greeting-wave.test.tsx +425 -0
  310. package/test/app/companion-stale-conversation.test.tsx +447 -0
  311. package/test/app/companion-view.test.tsx +686 -0
  312. package/test/app/confirm-delete-control.test.ts +79 -0
  313. package/test/app/confirm-modal.test.tsx +219 -0
  314. package/test/app/connectors-ui.e2e.test.ts +508 -0
  315. package/test/app/conversations-sidebar-game-modal.test.tsx +260 -0
  316. package/test/app/conversations-sidebar.test.tsx +160 -0
  317. package/test/app/custom-actions-smoke.test.ts +387 -0
  318. package/test/app/custom-avatar-api-client.test.ts +207 -0
  319. package/test/app/desktop-utils.test.ts +145 -0
  320. package/test/app/electrobun-rpc-bridge.test.ts +83 -0
  321. package/test/app/events.test.ts +88 -0
  322. package/test/app/export-import-flows.e2e.test.ts +700 -0
  323. package/test/app/fine-tuning-view.test.ts +471 -0
  324. package/test/app/game-view-auth-session.test.tsx +186 -0
  325. package/test/app/game-view.test.ts +444 -0
  326. package/test/app/global-emote-overlay.test.tsx +106 -0
  327. package/test/app/header-status.test.tsx +149 -0
  328. package/test/app/i18n.test.ts +152 -0
  329. package/test/app/inventory-bsc-view.test.ts +940 -0
  330. package/test/app/knowledge-ui.e2e.test.ts +762 -0
  331. package/test/app/knowledge-upload-helpers.test.ts +124 -0
  332. package/test/app/lifecycle-lock.test.ts +267 -0
  333. package/test/app/lifo-popout-utils.test.ts +208 -0
  334. package/test/app/lifo-safe-endpoint.test.ts +34 -0
  335. package/test/app/loading-screen.test.tsx +45 -0
  336. package/test/app/memory-monitor.test.ts +332 -0
  337. package/test/app/navigation.test.tsx +22 -0
  338. package/test/app/onboarding-finish-lock.test.ts +663 -0
  339. package/test/app/onboarding-language.test.tsx +160 -0
  340. package/test/app/onboarding-steps.test.tsx +375 -0
  341. package/test/app/open-external-url.test.ts +65 -0
  342. package/test/app/pages-navigation-smoke.e2e.test.ts +633 -0
  343. package/test/app/pairing-lock.test.ts +260 -0
  344. package/test/app/pairing-view.test.tsx +74 -0
  345. package/test/app/permissions-section.test.ts +432 -0
  346. package/test/app/plugin-bridge.test.ts +109 -0
  347. package/test/app/plugins-ui.e2e.test.ts +605 -0
  348. package/test/app/plugins-view-game-modal.test.tsx +650 -0
  349. package/test/app/plugins-view-toggle-restart.test.ts +129 -0
  350. package/test/app/provider-dropdown-default.test.tsx +302 -0
  351. package/test/app/restart-banner.test.tsx +197 -0
  352. package/test/app/retake-capture.test.ts +84 -0
  353. package/test/app/sandbox-api-client.test.ts +108 -0
  354. package/test/app/save-command-modal.test.tsx +109 -0
  355. package/test/app/secrets-view.test.tsx +92 -0
  356. package/test/app/settings-control-styles.test.tsx +142 -0
  357. package/test/app/settings-reset.e2e.test.ts +726 -0
  358. package/test/app/settings-sections.e2e.test.ts +614 -0
  359. package/test/app/shared-format.test.ts +44 -0
  360. package/test/app/shared-switch.test.ts +69 -0
  361. package/test/app/shell-mode-switching.e2e.test.ts +829 -0
  362. package/test/app/shell-mode-tab-memory.test.tsx +58 -0
  363. package/test/app/shell-overlays.test.tsx +50 -0
  364. package/test/app/shortcuts-overlay.test.tsx +111 -0
  365. package/test/app/sse-interruption.test.ts +122 -0
  366. package/test/app/startup-asset-missing.e2e.test.ts +126 -0
  367. package/test/app/startup-backend-missing.e2e.test.ts +118 -0
  368. package/test/app/startup-chat.e2e.test.ts +305 -0
  369. package/test/app/startup-conversation-restore.test.tsx +344 -0
  370. package/test/app/startup-failure-view.test.tsx +103 -0
  371. package/test/app/startup-onboarding.e2e.test.ts +618 -0
  372. package/test/app/startup-timeout.test.tsx +80 -0
  373. package/test/app/startup-token-401.e2e.test.ts +103 -0
  374. package/test/app/stream-helpers.test.ts +46 -0
  375. package/test/app/stream-popout-navigation.test.tsx +41 -0
  376. package/test/app/stream-status-bar.test.tsx +89 -0
  377. package/test/app/theme-toggle.test.tsx +33 -0
  378. package/test/app/training-api-client.test.ts +128 -0
  379. package/test/app/trajectories-view.test.tsx +220 -0
  380. package/test/app/triggers-api-client.test.ts +77 -0
  381. package/test/app/triggers-navigation.test.ts +113 -0
  382. package/test/app/triggers-view.e2e.test.ts +674 -0
  383. package/test/app/update-channel-lock.test.ts +259 -0
  384. package/test/app/vector-browser.async-cleanup.test.tsx +367 -0
  385. package/test/app/vector-browser.e2e.test.ts +653 -0
  386. package/test/app/vrm-stage.test.tsx +351 -0
  387. package/test/app/vrm-viewer.test.tsx +298 -0
  388. package/test/app/wallet-api-save-lock.test.ts +298 -0
  389. package/test/app/wallet-hooks.test.ts +405 -0
  390. package/test/app/wallet-ui-flows.e2e.test.ts +556 -0
  391. package/test/avatar/asset-url.test.ts +90 -0
  392. package/test/avatar/avatar-selector.test.ts +173 -0
  393. package/test/avatar/mixamo-vrm-rig-map.test.ts +111 -0
  394. package/test/avatar/voice-chat-streaming-text.test.ts +96 -0
  395. package/test/avatar/voice-chat.test.ts +391 -0
  396. package/test/ui/command-palette-commands.test.ts +57 -0
  397. package/test/ui/ui-renderer.test.ts +39 -0
  398. package/tsconfig.build.json +19 -0
  399. package/tsconfig.json +20 -0
@@ -0,0 +1,758 @@
1
+ /**
2
+ * Config page — agent-level configuration.
3
+ *
4
+ * Sections:
5
+ * 1. Wallet & RPC providers
6
+ * 2. Secrets (modal)
7
+ */
8
+
9
+ import { useCallback, useEffect, useState } from "react";
10
+ import {
11
+ WALLET_RPC_PROVIDER_OPTIONS,
12
+ type WalletRpcSelections,
13
+ } from "@elizaos/autonomous/contracts/wallet";
14
+ import { client } from "../api";
15
+ import {
16
+ ConfigRenderer,
17
+ defaultRegistry,
18
+ type JsonSchemaObject,
19
+ } from "../config";
20
+ import { useApp } from "../state";
21
+ import type { ConfigUiHint } from "../types";
22
+ import {
23
+ buildWalletRpcUpdateRequest,
24
+ resolveInitialWalletRpcSelections,
25
+ } from "../wallet-rpc";
26
+ import { SecretsView } from "./SecretsView";
27
+
28
+ type RpcProviderOption<T extends string> = {
29
+ id: T;
30
+ label: string;
31
+ };
32
+
33
+ type RpcFieldDefinition = {
34
+ configKey: string;
35
+ label: string;
36
+ isSet: boolean;
37
+ };
38
+
39
+ type RpcFieldGroup = ReadonlyArray<RpcFieldDefinition>;
40
+
41
+ type RpcSectionConfigMap = Record<string, RpcFieldGroup>;
42
+
43
+ const EVM_RPC_OPTIONS = WALLET_RPC_PROVIDER_OPTIONS.evm;
44
+ const BSC_RPC_OPTIONS = WALLET_RPC_PROVIDER_OPTIONS.bsc;
45
+ const SOLANA_RPC_OPTIONS = WALLET_RPC_PROVIDER_OPTIONS.solana;
46
+
47
+ type CloudRpcStatusProps = {
48
+ connected: boolean;
49
+ credits: number | null;
50
+ creditsLow: boolean;
51
+ creditsCritical: boolean;
52
+ topUpUrl: string | null;
53
+ loginBusy: boolean;
54
+ onLogin: () => void;
55
+ };
56
+
57
+ function CloudRpcStatus({
58
+ connected,
59
+ credits,
60
+ creditsLow,
61
+ creditsCritical,
62
+ loginBusy,
63
+ onLogin,
64
+ }: CloudRpcStatusProps) {
65
+ const { t, setState, setTab } = useApp();
66
+ if (connected) {
67
+ return (
68
+ <div className="flex items-center gap-2 text-xs">
69
+ <span className="inline-block w-2 h-2 rounded-full bg-[var(--ok,#16a34a)]" />
70
+ <span className="font-semibold">
71
+ {t("configpageview.ConnectedToElizaC")}
72
+ </span>
73
+ {credits !== null && (
74
+ <span className="text-[var(--muted)] ml-auto">
75
+ {t("configpageview.Credits")}{" "}
76
+ <span
77
+ className={
78
+ creditsCritical
79
+ ? "text-[var(--danger,#e74c3c)] font-bold"
80
+ : creditsLow
81
+ ? "rounded-md bg-[var(--warn-subtle)] px-1.5 py-0.5 text-[var(--text)] font-bold"
82
+ : ""
83
+ }
84
+ >
85
+ ${credits.toFixed(2)}
86
+ </span>
87
+ <button
88
+ type="button"
89
+ onClick={() => {
90
+ setState("cloudDashboardView", "billing");
91
+ setTab("settings");
92
+ }}
93
+ className="ml-1.5 text-[10px] text-[var(--text)] underline decoration-[var(--accent)] underline-offset-2 hover:opacity-80 bg-transparent border-0 p-0 cursor-pointer"
94
+ >
95
+ {t("configpageview.TopUp")}
96
+ </button>
97
+ </span>
98
+ )}
99
+ </div>
100
+ );
101
+ }
102
+
103
+ return (
104
+ <div className="flex items-center justify-between gap-2">
105
+ <div className="flex items-center gap-2 text-xs">
106
+ <span className="inline-block w-2 h-2 rounded-full bg-[var(--muted)]" />
107
+ <span className="text-[var(--muted)]">
108
+ {t("configpageview.RequiresElizaCloud")}
109
+ </span>
110
+ </div>
111
+ <button
112
+ type="button"
113
+ className="btn text-xs py-[3px] px-3 !mt-0 font-bold"
114
+ onClick={() => void onLogin()}
115
+ disabled={loginBusy}
116
+ >
117
+ {loginBusy ? "Connecting..." : "Log in"}
118
+ </button>
119
+ </div>
120
+ );
121
+ }
122
+
123
+ function buildRpcRendererConfig(
124
+ selectedProvider: string,
125
+ providerConfigs: RpcSectionConfigMap,
126
+ rpcFieldValues: Record<string, string>,
127
+ ) {
128
+ const fields = providerConfigs[selectedProvider];
129
+ if (!fields?.length) return null;
130
+
131
+ const props: {
132
+ schema: JsonSchemaObject;
133
+ hints: Record<string, ConfigUiHint>;
134
+ values: Record<string, unknown>;
135
+ setKeys: Set<string>;
136
+ } = {
137
+ schema: {
138
+ type: "object",
139
+ properties: {},
140
+ required: [],
141
+ },
142
+ hints: {},
143
+ values: {},
144
+ setKeys: new Set<string>(),
145
+ };
146
+
147
+ for (const field of fields) {
148
+ props.schema.properties[field.configKey] = {
149
+ type: "string",
150
+ description: field.label,
151
+ };
152
+ props.hints[field.configKey] = {
153
+ label: field.label,
154
+ sensitive: true,
155
+ placeholder: field.isSet
156
+ ? "Already set — leave blank to keep"
157
+ : "Enter API key",
158
+ width: "full",
159
+ };
160
+ if (rpcFieldValues[field.configKey] !== undefined) {
161
+ props.values[field.configKey] = rpcFieldValues[field.configKey];
162
+ }
163
+ if (field.isSet) {
164
+ props.setKeys.add(field.configKey);
165
+ }
166
+ }
167
+
168
+ return props;
169
+ }
170
+
171
+ type RpcSectionCloudProps = CloudRpcStatusProps;
172
+
173
+ type RpcSectionProps<T extends string> = {
174
+ title: string;
175
+ description: string;
176
+ options: readonly RpcProviderOption<T>[];
177
+ selectedProvider: T;
178
+ onSelect: (provider: T) => void;
179
+ providerConfigs: RpcSectionConfigMap;
180
+ rpcFieldValues: Record<string, string>;
181
+ onRpcFieldChange: (key: string, value: unknown) => void;
182
+ cloud: RpcSectionCloudProps;
183
+ containerClassName: string;
184
+ };
185
+
186
+ function RpcConfigSection<T extends string>({
187
+ title,
188
+ description,
189
+ options,
190
+ selectedProvider,
191
+ onSelect,
192
+ providerConfigs,
193
+ rpcFieldValues,
194
+ onRpcFieldChange,
195
+ cloud,
196
+ containerClassName,
197
+ }: RpcSectionProps<T>) {
198
+ const rpcConfig = buildRpcRendererConfig(
199
+ selectedProvider,
200
+ providerConfigs,
201
+ rpcFieldValues,
202
+ );
203
+
204
+ return (
205
+ <div>
206
+ <div className="text-xs font-bold mb-1">{title}</div>
207
+ <div className="text-[11px] text-[var(--muted)] mb-2">{description}</div>
208
+
209
+ {renderRpcProviderButtons(
210
+ options,
211
+ selectedProvider,
212
+ onSelect,
213
+ containerClassName,
214
+ (key: string) => {
215
+ // hack to get t function without breaking hook rules
216
+ return key === "elizaclouddashboard.ElizaCloud" ? "Eliza Cloud" : key;
217
+ },
218
+ )}
219
+
220
+ <div className="mt-3">
221
+ {selectedProvider === "eliza-cloud" ? (
222
+ <CloudRpcStatus
223
+ connected={cloud.connected}
224
+ credits={cloud.credits}
225
+ creditsLow={cloud.creditsLow}
226
+ creditsCritical={cloud.creditsCritical}
227
+ topUpUrl={cloud.topUpUrl}
228
+ loginBusy={cloud.loginBusy}
229
+ onLogin={() => void cloud.onLogin()}
230
+ />
231
+ ) : rpcConfig ? (
232
+ <ConfigRenderer
233
+ schema={rpcConfig.schema}
234
+ hints={rpcConfig.hints}
235
+ values={rpcConfig.values}
236
+ setKeys={rpcConfig.setKeys}
237
+ registry={defaultRegistry}
238
+ onChange={onRpcFieldChange}
239
+ />
240
+ ) : null}
241
+ </div>
242
+ </div>
243
+ );
244
+ }
245
+
246
+ function renderRpcProviderButtons<T extends string>(
247
+ options: readonly RpcProviderOption<T>[],
248
+ selectedProvider: T,
249
+ onSelect: (provider: T) => void,
250
+ containerClassName: string,
251
+ tFallback?: (key: string) => string,
252
+ ) {
253
+ return (
254
+ <div className={containerClassName}>
255
+ {options.map((provider) => {
256
+ const active = selectedProvider === provider.id;
257
+ return (
258
+ <button
259
+ type="button"
260
+ key={provider.id}
261
+ className={`flex min-h-[44px] items-center justify-center rounded-lg border px-3 py-2 text-center text-xs font-semibold leading-tight shadow-sm transition-colors ${
262
+ active
263
+ ? "border-[var(--accent)] bg-[var(--accent)] text-[var(--accent-foreground)]"
264
+ : "border-[var(--border)] bg-[var(--card)] text-[var(--text)] hover:border-[var(--accent)] hover:bg-[var(--bg-hover)]"
265
+ }`}
266
+ onClick={() => onSelect(provider.id)}
267
+ >
268
+ <div className="leading-tight">
269
+ {provider.id === "eliza-cloud" && tFallback
270
+ ? "Eliza Cloud"
271
+ : provider.label}
272
+ </div>
273
+ </button>
274
+ );
275
+ })}
276
+ </div>
277
+ );
278
+ }
279
+
280
+ /* ── Cloud services toggle section ───────────────────────────────────── */
281
+
282
+ type CloudServiceKey = "inference" | "rpc" | "media" | "tts" | "embeddings";
283
+
284
+ const CLOUD_SERVICE_DEFS: {
285
+ key: CloudServiceKey;
286
+ label: string;
287
+ description: string;
288
+ }[] = [
289
+ {
290
+ key: "inference",
291
+ label: "Model Inference",
292
+ description:
293
+ "Use Eliza Cloud for LLM calls. Turn off to use your own API keys (Anthropic, OpenAI, etc.)",
294
+ },
295
+ {
296
+ key: "rpc",
297
+ label: "Blockchain RPC",
298
+ description: "Use Eliza Cloud RPC endpoints for EVM, BSC, and Solana",
299
+ },
300
+ {
301
+ key: "media",
302
+ label: "Media Generation",
303
+ description: "Use Eliza Cloud for image, video, audio, and vision",
304
+ },
305
+ {
306
+ key: "tts",
307
+ label: "Text-to-Speech",
308
+ description: "Use Eliza Cloud for TTS voice synthesis",
309
+ },
310
+ {
311
+ key: "embeddings",
312
+ label: "Embeddings",
313
+ description: "Use Eliza Cloud for text embedding generation",
314
+ },
315
+ ];
316
+
317
+ function CloudServicesSection() {
318
+ const { t } = useApp();
319
+ const [services, setServices] = useState<Record<CloudServiceKey, boolean>>({
320
+ inference: true,
321
+ rpc: true,
322
+ media: true,
323
+ tts: true,
324
+ embeddings: true,
325
+ });
326
+ const [saving, setSaving] = useState(false);
327
+ const [loaded, setLoaded] = useState(false);
328
+ const [needsRestart, setNeedsRestart] = useState(false);
329
+
330
+ // Load current config on mount
331
+ useEffect(() => {
332
+ let cancelled = false;
333
+ client
334
+ .getConfig()
335
+ .then((cfg) => {
336
+ if (cancelled) return;
337
+ const cloud = cfg.cloud as
338
+ | { services?: Record<string, boolean> }
339
+ | undefined;
340
+ if (cloud?.services) {
341
+ setServices((prev) => ({
342
+ ...prev,
343
+ ...Object.fromEntries(
344
+ Object.entries(cloud.services ?? {}).filter(
345
+ ([, v]) => typeof v === "boolean",
346
+ ),
347
+ ),
348
+ }));
349
+ }
350
+ setLoaded(true);
351
+ })
352
+ .catch(() => setLoaded(true));
353
+ return () => {
354
+ cancelled = true;
355
+ };
356
+ }, []);
357
+
358
+ const handleToggle = useCallback(
359
+ async (key: CloudServiceKey) => {
360
+ const newValue = !services[key];
361
+ const updated = { ...services, [key]: newValue };
362
+ setServices(updated);
363
+ setSaving(true);
364
+
365
+ // Also set inferenceMode based on inference toggle
366
+ const inferenceMode = updated.inference ? "cloud" : "byok";
367
+
368
+ try {
369
+ await client.updateConfig({
370
+ cloud: { services: updated, inferenceMode },
371
+ });
372
+ setNeedsRestart(true);
373
+ } catch (err) {
374
+ // Revert on error
375
+ setServices(services);
376
+ console.error("[config] Failed to save cloud services:", err);
377
+ } finally {
378
+ setSaving(false);
379
+ }
380
+ },
381
+ [services],
382
+ );
383
+
384
+ if (!loaded) return null;
385
+
386
+ return (
387
+ <div className="p-4 border border-[var(--border)] bg-[var(--card)] mt-4">
388
+ <div className="flex items-center justify-between mb-3">
389
+ <div className="font-bold text-sm">
390
+ {t("configpageview.CloudServices")}
391
+ </div>
392
+ {needsRestart && (
393
+ <span className="rounded-full border border-[var(--warn)] bg-[var(--warn-subtle)] px-2 py-0.5 text-[11px] font-medium text-[var(--text)]">
394
+ {t("configpageview.RestartRequiredFor")}
395
+ </span>
396
+ )}
397
+ </div>
398
+ <p className="text-[12px] text-[var(--muted)] mb-4">
399
+ {t("configpageview.ChooseWhichElizaCl")}
400
+ </p>
401
+ <div className="space-y-2">
402
+ {CLOUD_SERVICE_DEFS.map(({ key, label, description }) => (
403
+ <div
404
+ key={key}
405
+ className="flex items-center justify-between p-2.5 border border-[var(--border)] rounded cursor-pointer hover:border-[var(--accent)] transition-colors"
406
+ >
407
+ <div className="flex-1 min-w-0 mr-3">
408
+ <div
409
+ className="text-[13px] font-medium"
410
+ id={`cloud-service-${key}`}
411
+ >
412
+ {label}
413
+ </div>
414
+ <div className="text-[11px] text-[var(--muted)] mt-0.5">
415
+ {description}
416
+ </div>
417
+ </div>
418
+ <button
419
+ type="button"
420
+ role="switch"
421
+ aria-checked={services[key]}
422
+ aria-labelledby={`cloud-service-${key}`}
423
+ disabled={saving}
424
+ onClick={() => void handleToggle(key)}
425
+ className={`relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors cursor-pointer ${
426
+ services[key] ? "bg-[var(--accent)]" : "bg-[var(--border)]"
427
+ }`}
428
+ >
429
+ <span
430
+ className={`pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transform transition-transform ${
431
+ services[key] ? "translate-x-4" : "translate-x-0"
432
+ }`}
433
+ />
434
+ </button>
435
+ </div>
436
+ ))}
437
+ </div>
438
+ </div>
439
+ );
440
+ }
441
+
442
+ /* ── ConfigPageView ──────────────────────────────────────────────────── */
443
+
444
+ export function ConfigPageView({ embedded = false }: { embedded?: boolean }) {
445
+ const {
446
+ t,
447
+ elizaCloudConnected,
448
+ elizaCloudCredits,
449
+ elizaCloudCreditsLow,
450
+ elizaCloudCreditsCritical,
451
+ elizaCloudTopUpUrl,
452
+ elizaCloudLoginBusy,
453
+ walletConfig,
454
+ walletApiKeySaving,
455
+ handleWalletApiKeySave,
456
+ handleCloudLogin,
457
+ } = useApp();
458
+
459
+ const [secretsOpen, setSecretsOpen] = useState(false);
460
+
461
+ /* ── RPC provider field values ─────────────────────────────────────── */
462
+ const [rpcFieldValues, setRpcFieldValues] = useState<Record<string, string>>(
463
+ {},
464
+ );
465
+
466
+ const handleRpcFieldChange = useCallback((key: string, value: unknown) => {
467
+ setRpcFieldValues((prev) => ({ ...prev, [key]: String(value ?? "") }));
468
+ }, []);
469
+
470
+ /* ── RPC provider selection state ──────────────────────────────────── */
471
+ const [selectedEvmRpc, setSelectedEvmRpc] =
472
+ useState<WalletRpcSelections["evm"]>("eliza-cloud");
473
+ const [selectedBscRpc, setSelectedBscRpc] =
474
+ useState<WalletRpcSelections["bsc"]>("eliza-cloud");
475
+ const [selectedSolanaRpc, setSelectedSolanaRpc] =
476
+ useState<WalletRpcSelections["solana"]>("eliza-cloud");
477
+
478
+ useEffect(() => {
479
+ const initialSelections = resolveInitialWalletRpcSelections(walletConfig);
480
+ setSelectedEvmRpc(initialSelections.evm);
481
+ setSelectedBscRpc(initialSelections.bsc);
482
+ setSelectedSolanaRpc(initialSelections.solana);
483
+ }, [walletConfig]);
484
+
485
+ const handleWalletSaveAll = useCallback(() => {
486
+ const config = buildWalletRpcUpdateRequest({
487
+ walletConfig,
488
+ rpcFieldValues,
489
+ selectedProviders: {
490
+ evm: selectedEvmRpc,
491
+ bsc: selectedBscRpc,
492
+ solana: selectedSolanaRpc,
493
+ },
494
+ });
495
+ void handleWalletApiKeySave(config);
496
+ }, [
497
+ handleWalletApiKeySave,
498
+ rpcFieldValues,
499
+ selectedBscRpc,
500
+ selectedEvmRpc,
501
+ selectedSolanaRpc,
502
+ walletConfig,
503
+ ]);
504
+
505
+ const evmRpcConfigs: RpcSectionConfigMap = {
506
+ alchemy: [
507
+ {
508
+ configKey: "ALCHEMY_API_KEY",
509
+ label: "Alchemy API Key",
510
+ isSet: walletConfig?.alchemyKeySet ?? false,
511
+ },
512
+ ],
513
+ infura: [
514
+ {
515
+ configKey: "INFURA_API_KEY",
516
+ label: "Infura API Key",
517
+ isSet: walletConfig?.infuraKeySet ?? false,
518
+ },
519
+ ],
520
+ ankr: [
521
+ {
522
+ configKey: "ANKR_API_KEY",
523
+ label: "Ankr API Key",
524
+ isSet: walletConfig?.ankrKeySet ?? false,
525
+ },
526
+ ],
527
+ };
528
+
529
+ const bscRpcConfigs: RpcSectionConfigMap = {
530
+ alchemy: [
531
+ {
532
+ configKey: "ALCHEMY_API_KEY",
533
+ label: "Alchemy API Key",
534
+ isSet: walletConfig?.alchemyKeySet ?? false,
535
+ },
536
+ ],
537
+ ankr: [
538
+ {
539
+ configKey: "ANKR_API_KEY",
540
+ label: "Ankr API Key",
541
+ isSet: walletConfig?.ankrKeySet ?? false,
542
+ },
543
+ ],
544
+ nodereal: [
545
+ {
546
+ configKey: "NODEREAL_BSC_RPC_URL",
547
+ label: "NodeReal BSC RPC URL",
548
+ isSet: walletConfig?.nodeRealBscRpcSet ?? false,
549
+ },
550
+ ],
551
+ quicknode: [
552
+ {
553
+ configKey: "QUICKNODE_BSC_RPC_URL",
554
+ label: "QuickNode BSC RPC URL",
555
+ isSet: walletConfig?.quickNodeBscRpcSet ?? false,
556
+ },
557
+ ],
558
+ };
559
+
560
+ const solanaRpcConfigs: RpcSectionConfigMap = {
561
+ "helius-birdeye": [
562
+ {
563
+ configKey: "HELIUS_API_KEY",
564
+ label: "Helius API Key",
565
+ isSet: walletConfig?.heliusKeySet ?? false,
566
+ },
567
+ {
568
+ configKey: "BIRDEYE_API_KEY",
569
+ label: "Birdeye API Key",
570
+ isSet: walletConfig?.birdeyeKeySet ?? false,
571
+ },
572
+ ],
573
+ };
574
+
575
+ const cloudStatusProps = {
576
+ connected: elizaCloudConnected,
577
+ credits: elizaCloudCredits,
578
+ creditsLow: elizaCloudCreditsLow,
579
+ creditsCritical: elizaCloudCreditsCritical,
580
+ topUpUrl: elizaCloudTopUpUrl,
581
+ loginBusy: elizaCloudLoginBusy,
582
+ onLogin: () => void handleCloudLogin(),
583
+ };
584
+
585
+ const legacyRpcChains = walletConfig?.legacyCustomChains ?? [];
586
+ const legacyRpcWarning =
587
+ legacyRpcChains.length > 0
588
+ ? `Legacy raw RPC is still active for ${legacyRpcChains.join(", ")}. Re-save a supported provider selection to migrate fully.`
589
+ : null;
590
+
591
+ return (
592
+ <div>
593
+ {!embedded && (
594
+ <>
595
+ <h2 className="text-lg font-bold mb-1">
596
+ {t("configpageview.Config")}
597
+ </h2>
598
+ <p className="text-[13px] text-[var(--muted)] mb-5">
599
+ {t("configpageview.WalletProvidersAnd")}
600
+ </p>
601
+ </>
602
+ )}
603
+
604
+ {/* ═══════════════════════════════════════════════════════════════
605
+ 1. WALLET & RPC
606
+ ═══════════════════════════════════════════════════════════════ */}
607
+ <div className="p-4 border border-[var(--border)] bg-[var(--card)]">
608
+ <div className="flex items-center justify-between mb-4">
609
+ <div className="font-bold text-sm">
610
+ {t("configpageview.WalletAmpRPC")}
611
+ </div>
612
+ <button
613
+ type="button"
614
+ className="settings-button flex items-center gap-1.5 text-[12px] text-[var(--muted)] hover:text-[var(--txt)] bg-transparent border border-[var(--border)] rounded cursor-pointer transition-colors hover:border-[var(--accent)]"
615
+ onClick={() => setSecretsOpen(true)}
616
+ title={t("configpageview.SecretsVault1")}
617
+ >
618
+ <svg
619
+ width="13"
620
+ height="13"
621
+ viewBox="0 0 24 24"
622
+ fill="none"
623
+ stroke="currentColor"
624
+ strokeWidth="2"
625
+ strokeLinecap="round"
626
+ strokeLinejoin="round"
627
+ >
628
+ <title>{t("configpageview.SecretsVault")}</title>
629
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
630
+ <path d="M7 11V7a5 5 0 0 1 10 0v4" />
631
+ </svg>
632
+
633
+ {t("configpageview.Secrets")}
634
+ </button>
635
+ </div>
636
+
637
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
638
+ {/* BSC */}
639
+ <RpcConfigSection
640
+ title={t("configpageview.BSC")}
641
+ description="BNB Smart Chain — trading and market feed"
642
+ options={BSC_RPC_OPTIONS}
643
+ selectedProvider={selectedBscRpc}
644
+ onSelect={setSelectedBscRpc}
645
+ providerConfigs={bscRpcConfigs}
646
+ rpcFieldValues={rpcFieldValues}
647
+ onRpcFieldChange={handleRpcFieldChange}
648
+ cloud={cloudStatusProps}
649
+ containerClassName="grid grid-cols-2 md:grid-cols-5 gap-1.5"
650
+ />
651
+
652
+ {/* EVM */}
653
+ <RpcConfigSection
654
+ title={t("configpageview.EVM")}
655
+ description="Ethereum, Base, Arbitrum, Optimism, Polygon"
656
+ options={EVM_RPC_OPTIONS}
657
+ selectedProvider={selectedEvmRpc}
658
+ onSelect={setSelectedEvmRpc}
659
+ providerConfigs={evmRpcConfigs}
660
+ rpcFieldValues={rpcFieldValues}
661
+ onRpcFieldChange={handleRpcFieldChange}
662
+ cloud={cloudStatusProps}
663
+ containerClassName="grid grid-cols-4 gap-1.5"
664
+ />
665
+
666
+ {/* Solana */}
667
+ <RpcConfigSection
668
+ title={t("configpageview.Solana")}
669
+ description="Solana mainnet tokens and NFTs"
670
+ options={SOLANA_RPC_OPTIONS}
671
+ selectedProvider={selectedSolanaRpc}
672
+ onSelect={setSelectedSolanaRpc}
673
+ providerConfigs={solanaRpcConfigs}
674
+ rpcFieldValues={rpcFieldValues}
675
+ onRpcFieldChange={handleRpcFieldChange}
676
+ cloud={cloudStatusProps}
677
+ containerClassName="grid grid-cols-2 gap-1.5"
678
+ />
679
+ </div>
680
+
681
+ {legacyRpcWarning && (
682
+ <div className="mt-4 rounded-lg border border-[var(--warn)] bg-[var(--warn-subtle)] px-3 py-2 text-[11px] text-[var(--text)]">
683
+ {legacyRpcWarning}
684
+ </div>
685
+ )}
686
+
687
+ <div className="flex justify-end mt-4">
688
+ <button
689
+ type="button"
690
+ className="btn text-[11px] py-1 px-3.5 !mt-0"
691
+ onClick={handleWalletSaveAll}
692
+ disabled={walletApiKeySaving}
693
+ >
694
+ {walletApiKeySaving ? "Saving..." : "Save"}
695
+ </button>
696
+ </div>
697
+ </div>
698
+
699
+ {/* ═══════════════════════════════════════════════════════════════
700
+ 2. CLOUD SERVICES
701
+ ═══════════════════════════════════════════════════════════════ */}
702
+ {elizaCloudConnected && <CloudServicesSection />}
703
+
704
+ {/* ── Secrets modal ── */}
705
+ {secretsOpen && (
706
+ <div
707
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
708
+ onClick={(e) => {
709
+ if (e.target === e.currentTarget) setSecretsOpen(false);
710
+ }}
711
+ onKeyDown={(e) => {
712
+ if (e.key === "Escape" || e.key === "Enter" || e.key === " ") {
713
+ e.preventDefault();
714
+ setSecretsOpen(false);
715
+ }
716
+ }}
717
+ role="dialog"
718
+ aria-modal="true"
719
+ >
720
+ <div className="w-full max-w-2xl max-h-[80vh] border border-[var(--border)] bg-[var(--card)] p-5 shadow-lg flex flex-col">
721
+ <div className="flex items-center justify-between mb-4 flex-shrink-0">
722
+ <div className="flex items-center gap-2">
723
+ <svg
724
+ width="15"
725
+ height="15"
726
+ viewBox="0 0 24 24"
727
+ fill="none"
728
+ stroke="currentColor"
729
+ strokeWidth="2"
730
+ strokeLinecap="round"
731
+ strokeLinejoin="round"
732
+ className="text-[var(--accent)]"
733
+ >
734
+ <title>{t("configpageview.SecretsVault")}</title>
735
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
736
+ <path d="M7 11V7a5 5 0 0 1 10 0v4" />
737
+ </svg>
738
+ <span className="font-bold text-sm">
739
+ {t("configpageview.SecretsVault1")}
740
+ </span>
741
+ </div>
742
+ <button
743
+ type="button"
744
+ className="text-[var(--muted)] hover:text-[var(--txt)] text-lg leading-none px-1 bg-transparent border-0 cursor-pointer"
745
+ onClick={() => setSecretsOpen(false)}
746
+ >
747
+ {t("bugreportmodal.Times")}
748
+ </button>
749
+ </div>
750
+ <div className="flex-1 min-h-0 overflow-y-auto">
751
+ <SecretsView />
752
+ </div>
753
+ </div>
754
+ </div>
755
+ )}
756
+ </div>
757
+ );
758
+ }