ethagent 1.1.1 → 2.0.0

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 (271) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +127 -29
  3. package/package.json +16 -9
  4. package/src/app/FirstRun.tsx +192 -146
  5. package/src/app/FirstRunTimeline.tsx +47 -0
  6. package/src/app/input/AppInputProvider.tsx +1 -1
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -1
  8. package/src/chat/ChatBottomPane.tsx +0 -1
  9. package/src/chat/ChatInput.tsx +6 -6
  10. package/src/chat/ChatScreen.tsx +43 -18
  11. package/src/chat/ContextLimitView.tsx +4 -4
  12. package/src/chat/ContinuityEditReviewView.tsx +11 -17
  13. package/src/chat/ConversationStack.tsx +3 -0
  14. package/src/chat/CopyPicker.tsx +0 -1
  15. package/src/chat/MessageList.tsx +62 -45
  16. package/src/chat/PermissionPrompt.tsx +13 -9
  17. package/src/chat/PlanApprovalView.tsx +3 -3
  18. package/src/chat/ResumeView.tsx +1 -4
  19. package/src/chat/RewindView.tsx +2 -2
  20. package/src/chat/TranscriptView.tsx +6 -0
  21. package/src/chat/chatInputState.ts +1 -1
  22. package/src/chat/chatScreenUtils.ts +22 -11
  23. package/src/chat/chatSessionState.ts +2 -2
  24. package/src/chat/chatTurnOrchestrator.ts +16 -81
  25. package/src/chat/commands.ts +1 -1
  26. package/src/chat/textCursor.ts +1 -1
  27. package/src/chat/transcriptViewport.ts +2 -7
  28. package/src/cli/ResetConfirmView.tsx +1 -1
  29. package/src/cli/main.tsx +9 -3
  30. package/src/cli/preview.tsx +0 -5
  31. package/src/cli/updateNotice.ts +5 -3
  32. package/src/identity/continuity/editor.ts +7 -107
  33. package/src/identity/continuity/envelope.ts +1048 -40
  34. package/src/identity/continuity/history.ts +4 -4
  35. package/src/identity/continuity/localBackup.ts +249 -0
  36. package/src/identity/continuity/privateEdit/apply.ts +170 -0
  37. package/src/identity/continuity/privateEdit/diff.ts +82 -0
  38. package/src/identity/continuity/privateEdit/files.ts +23 -0
  39. package/src/identity/continuity/privateEdit/types.ts +28 -0
  40. package/src/identity/continuity/privateEdit.ts +10 -298
  41. package/src/identity/continuity/publicSkills.ts +8 -9
  42. package/src/identity/continuity/snapshots.ts +17 -6
  43. package/src/identity/continuity/storage/defaults.ts +111 -0
  44. package/src/identity/continuity/storage/files.ts +72 -0
  45. package/src/identity/continuity/storage/markdown.ts +81 -0
  46. package/src/identity/continuity/storage/paths.ts +24 -0
  47. package/src/identity/continuity/storage/scaffold.ts +124 -0
  48. package/src/identity/continuity/storage/status.ts +86 -0
  49. package/src/identity/continuity/storage/types.ts +27 -0
  50. package/src/identity/continuity/storage.ts +32 -507
  51. package/src/identity/continuity/zipWriter.ts +95 -0
  52. package/src/identity/crypto/backupEnvelope.ts +14 -247
  53. package/src/identity/crypto/eth.ts +7 -7
  54. package/src/identity/ens/agentRecords.ts +96 -0
  55. package/src/identity/ens/ensAutomation/contracts.ts +38 -0
  56. package/src/identity/ens/ensAutomation/delete.ts +80 -0
  57. package/src/identity/ens/ensAutomation/names.ts +14 -0
  58. package/src/identity/ens/ensAutomation/operators.ts +29 -0
  59. package/src/identity/ens/ensAutomation/read.ts +114 -0
  60. package/src/identity/ens/ensAutomation/root.ts +63 -0
  61. package/src/identity/ens/ensAutomation/setup.ts +284 -0
  62. package/src/identity/ens/ensAutomation/transactions.ts +107 -0
  63. package/src/identity/ens/ensAutomation/types.ts +126 -0
  64. package/src/identity/ens/ensAutomation.ts +29 -0
  65. package/src/identity/ens/ensLookup/client.ts +43 -0
  66. package/src/identity/ens/ensLookup/constants.ts +26 -0
  67. package/src/identity/ens/ensLookup/discovery.ts +70 -0
  68. package/src/identity/ens/ensLookup/names.ts +34 -0
  69. package/src/identity/ens/ensLookup/records.ts +45 -0
  70. package/src/identity/ens/ensLookup/resolve.ts +75 -0
  71. package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
  72. package/src/identity/ens/ensLookup/types.ts +38 -0
  73. package/src/identity/ens/ensLookup/validation.ts +72 -0
  74. package/src/identity/ens/ensLookup.ts +19 -0
  75. package/src/identity/ens/ensRegistration.ts +199 -0
  76. package/src/identity/ens/resolverDelegation.ts +48 -0
  77. package/src/identity/hub/IdentityHub.tsx +13 -815
  78. package/src/identity/hub/OperationalRoutes.tsx +370 -0
  79. package/src/identity/hub/Routes.tsx +361 -0
  80. package/src/identity/hub/advancedEnsValidation.ts +45 -0
  81. package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
  82. package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
  83. package/src/identity/hub/components/FlowTimeline.tsx +27 -0
  84. package/src/identity/hub/components/IdentitySummary.tsx +190 -0
  85. package/src/identity/hub/components/MenuScreen.tsx +237 -0
  86. package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
  87. package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
  88. package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
  89. package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
  90. package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
  91. package/src/identity/hub/effects/create.ts +310 -0
  92. package/src/identity/hub/effects/ens/flows.ts +218 -0
  93. package/src/identity/hub/effects/ens/index.ts +11 -0
  94. package/src/identity/hub/effects/ens/transactions.ts +239 -0
  95. package/src/identity/hub/effects/index.ts +74 -0
  96. package/src/identity/hub/effects/profile/profileState.ts +173 -0
  97. package/src/identity/hub/effects/publicProfile/index.ts +5 -0
  98. package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
  99. package/src/identity/hub/effects/rebackup/index.ts +7 -0
  100. package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
  101. package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
  102. package/src/identity/hub/effects/receipts.ts +46 -0
  103. package/src/identity/hub/effects/restore/apply.ts +112 -0
  104. package/src/identity/hub/effects/restore/auth.ts +159 -0
  105. package/src/identity/hub/effects/restore/discover.ts +86 -0
  106. package/src/identity/hub/effects/restore/envelopes.ts +21 -0
  107. package/src/identity/hub/effects/restore/fetch.ts +25 -0
  108. package/src/identity/hub/effects/restore/index.ts +22 -0
  109. package/src/identity/hub/effects/restore/recovery.ts +135 -0
  110. package/src/identity/hub/effects/restore/resolve.ts +102 -0
  111. package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
  112. package/src/identity/hub/effects/restore/shared.ts +91 -0
  113. package/src/identity/hub/effects/restoreAdmin.ts +93 -0
  114. package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
  115. package/src/identity/hub/effects/shared/snapshot.ts +336 -0
  116. package/src/identity/hub/effects/shared/sync.ts +190 -0
  117. package/src/identity/hub/effects/token-transfer/index.ts +6 -0
  118. package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
  119. package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
  120. package/src/identity/hub/effects/types.ts +53 -0
  121. package/src/identity/hub/effects/vault/preflight.ts +50 -0
  122. package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
  123. package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
  124. package/src/identity/hub/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
  125. package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
  126. package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
  127. package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
  128. package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
  129. package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
  130. package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
  131. package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
  132. package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
  133. package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
  134. package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
  135. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
  136. package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
  137. package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
  138. package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
  139. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
  140. package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
  141. package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
  142. package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
  143. package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
  144. package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
  145. package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
  146. package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
  147. package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
  148. package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
  149. package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +25 -43
  150. package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
  151. package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
  152. package/src/identity/hub/identityHubReducer.ts +166 -101
  153. package/src/identity/hub/model/continuity.ts +94 -0
  154. package/src/identity/hub/model/copy.ts +35 -0
  155. package/src/identity/hub/model/custody.ts +54 -0
  156. package/src/identity/hub/model/ens.ts +49 -0
  157. package/src/identity/hub/model/errors.ts +140 -0
  158. package/src/identity/hub/model/format.ts +15 -0
  159. package/src/identity/hub/model/identity.ts +94 -0
  160. package/src/identity/hub/model/network.ts +32 -0
  161. package/src/identity/hub/model/transfer.ts +57 -0
  162. package/src/identity/hub/operatorWallets.ts +131 -0
  163. package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
  164. package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
  165. package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
  166. package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
  167. package/src/identity/hub/reconciliation/index.ts +21 -0
  168. package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
  169. package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
  170. package/src/identity/hub/txGuard.ts +51 -0
  171. package/src/identity/hub/types.ts +17 -0
  172. package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
  173. package/src/identity/hub/useIdentityHubController.ts +396 -0
  174. package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
  175. package/src/identity/hub/utils.ts +79 -0
  176. package/src/identity/identityCompat.ts +34 -0
  177. package/src/identity/profile/agentIcon.ts +61 -0
  178. package/src/identity/profile/imagePicker.ts +12 -12
  179. package/src/identity/registry/erc8004/abi.ts +14 -0
  180. package/src/identity/registry/erc8004/chains.ts +150 -0
  181. package/src/identity/registry/erc8004/client.ts +11 -0
  182. package/src/identity/registry/erc8004/discovery.ts +511 -0
  183. package/src/identity/registry/erc8004/metadata.ts +335 -0
  184. package/src/identity/registry/erc8004/ownership.ts +121 -0
  185. package/src/identity/registry/erc8004/preflight.ts +123 -0
  186. package/src/identity/registry/erc8004/transactions.ts +77 -0
  187. package/src/identity/registry/erc8004/types.ts +88 -0
  188. package/src/identity/registry/erc8004/uri.ts +59 -0
  189. package/src/identity/registry/erc8004/utils.ts +58 -0
  190. package/src/identity/registry/erc8004.ts +53 -1106
  191. package/src/identity/registry/fieldParsers.ts +28 -0
  192. package/src/identity/registry/operatorVault/bytecode.ts +98 -0
  193. package/src/identity/registry/operatorVault/constants.ts +38 -0
  194. package/src/identity/registry/operatorVault/read.ts +246 -0
  195. package/src/identity/registry/operatorVault/transactions.ts +81 -0
  196. package/src/identity/registry/operatorVault.ts +44 -0
  197. package/src/identity/storage/ipfs.ts +26 -24
  198. package/src/identity/wallet/browserWallet/gas.ts +41 -0
  199. package/src/identity/wallet/browserWallet/html.ts +106 -0
  200. package/src/identity/wallet/browserWallet/http.ts +28 -0
  201. package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
  202. package/src/identity/wallet/browserWallet/requests.ts +191 -0
  203. package/src/identity/wallet/browserWallet/session.ts +325 -0
  204. package/src/identity/wallet/browserWallet/types.ts +192 -0
  205. package/src/identity/wallet/browserWallet/validation.ts +74 -0
  206. package/src/identity/wallet/browserWallet.ts +30 -393
  207. package/src/identity/wallet/page/constants.ts +5 -0
  208. package/src/identity/wallet/page/controller.ts +251 -0
  209. package/src/identity/wallet/page/copy.ts +340 -0
  210. package/src/identity/wallet/page/grainient.ts +278 -0
  211. package/src/identity/wallet/page/html.ts +28 -0
  212. package/src/identity/wallet/page/markup.ts +50 -0
  213. package/src/identity/wallet/page/state.ts +9 -0
  214. package/src/identity/wallet/page/styles/base.ts +259 -0
  215. package/src/identity/wallet/page/styles/components.ts +262 -0
  216. package/src/identity/wallet/page/styles/index.ts +5 -0
  217. package/src/identity/wallet/page/styles/responsive.ts +247 -0
  218. package/src/identity/wallet/page/types.ts +47 -0
  219. package/src/identity/wallet/page/view.ts +535 -0
  220. package/src/identity/wallet/page/walletProvider.ts +70 -0
  221. package/src/identity/wallet/page.tsx +38 -0
  222. package/src/identity/wallet/walletPurposeCompat.ts +27 -0
  223. package/src/mcp/manager.ts +0 -1
  224. package/src/models/ModelPicker.tsx +36 -30
  225. package/src/models/catalog.ts +5 -2
  226. package/src/models/huggingface.ts +9 -9
  227. package/src/models/llamacpp.ts +13 -13
  228. package/src/models/modelDisplay.ts +75 -0
  229. package/src/models/modelPickerOptions.ts +16 -3
  230. package/src/models/modelRecommendation.ts +0 -1
  231. package/src/providers/errors.ts +16 -0
  232. package/src/providers/gemini.ts +252 -39
  233. package/src/providers/registry.ts +2 -2
  234. package/src/providers/retry.ts +1 -1
  235. package/src/runtime/sessionMode.ts +1 -1
  236. package/src/runtime/systemPrompt.ts +2 -0
  237. package/src/runtime/toolExecution.ts +18 -22
  238. package/src/runtime/toolIntent.ts +0 -20
  239. package/src/runtime/turn.ts +0 -92
  240. package/src/storage/atomicWrite.ts +4 -1
  241. package/src/storage/config.ts +181 -5
  242. package/src/storage/identity.ts +9 -3
  243. package/src/storage/secrets.ts +2 -2
  244. package/src/tools/bashSafety.ts +8 -0
  245. package/src/tools/changeDirectoryTool.ts +1 -1
  246. package/src/tools/deleteFileTool.ts +4 -4
  247. package/src/tools/editTool.ts +4 -4
  248. package/src/tools/editUtils.ts +5 -5
  249. package/src/tools/privateContinuityEditTool.ts +4 -5
  250. package/src/tools/privateContinuityReadTool.ts +1 -2
  251. package/src/tools/registry.ts +30 -0
  252. package/src/tools/writeFileTool.ts +5 -5
  253. package/src/ui/BrandSplash.tsx +20 -85
  254. package/src/ui/ProgressBar.tsx +3 -5
  255. package/src/ui/Select.tsx +21 -9
  256. package/src/ui/Spinner.tsx +38 -3
  257. package/src/ui/Surface.tsx +3 -3
  258. package/src/ui/TextInput.tsx +191 -29
  259. package/src/ui/theme.ts +7 -34
  260. package/src/utils/openExternal.ts +21 -0
  261. package/src/utils/withRetry.ts +47 -3
  262. package/src/identity/hub/identityHubEffects.ts +0 -937
  263. package/src/identity/hub/identityHubModel.ts +0 -291
  264. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
  265. package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
  266. package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
  267. package/src/identity/hub/screens/MenuScreen.tsx +0 -117
  268. package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
  269. package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
  270. package/src/identity/wallet/wallet-page/wallet.html +0 -1202
  271. /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
@@ -1,816 +1,14 @@
1
- import React, { useEffect, useReducer, useState } from 'react'
2
- import { Text } from 'ink'
3
- import { theme } from '../../ui/theme.js'
4
- import { type EthagentConfig, type EthagentIdentity, type SelectableNetwork } from '../../storage/config.js'
5
- import { clearIdentity, setTokenIdentity } from '../../storage/identity.js'
6
- import { copyToClipboard } from '../../utils/clipboard.js'
7
- import { catFromIpfs, DEFAULT_IPFS_API_URL } from '../storage/ipfs.js'
8
- import { openImageFilePicker } from '../profile/imagePicker.js'
9
- import { hasPinataJwt, clearPinataJwt, savePinataJwt } from '../storage/pinataJwt.js'
10
- import { registryConfigFromConfig } from '../registry/registryConfig.js'
11
- import { identityHubErrorView, isRegistrationPreflightError, pinataErrorText } from './identityHubModel.js'
12
- import { identityHubReducer, type ProfileUpdates, type Step } from './identityHubReducer.js'
13
- import {
14
- runCreatePreflight,
15
- runCreateSigning,
16
- runRestoreConnectWallet,
17
- runRestoreDiscover,
18
- runRestoreTokenIdSubmit,
19
- runRestoreFetch,
20
- runRestoreAuthorize,
21
- runRegistrySubmit,
22
- runRestoreRegistrySubmit,
23
- runStorageSubmit,
24
- runRebackupPreflight,
25
- runRebackupSigning,
26
- runRebackupStorageSubmit,
27
- runRecoveryRefetch,
28
- isAgentTokenIdRequiredError,
29
- type EffectCallbacks,
30
- type RestoreProgress,
31
- } from './identityHubEffects.js'
32
- import {
33
- continuityVaultRef,
34
- continuityVaultStatus,
35
- continuityWorkingTreeStatus,
36
- ensurePublicSkillsFile,
37
- type ContinuityWorkingTreeStatus,
38
- } from '../continuity/storage.js'
39
- import { openFileInEditor } from '../continuity/editor.js'
40
- import { listPublishedContinuitySnapshots } from '../continuity/snapshots.js'
41
- import type { BrowserWalletReady } from '../wallet/browserWallet.js'
42
- import { MenuScreen } from './screens/MenuScreen.js'
43
- import { CreateFlow } from './screens/CreateFlow.js'
44
- import { RestoreFlow } from './screens/RestoreFlow.js'
45
- import { NetworkScreen } from './screens/NetworkScreen.js'
46
- import { DetailsScreen } from './screens/DetailsScreen.js'
47
- import { ErrorScreen } from './screens/ErrorScreen.js'
48
- import { WalletApprovalScreen } from './screens/WalletApprovalScreen.js'
49
- import { RebackupStorageScreen } from './screens/RebackupStorageScreen.js'
50
- import { BusyScreen } from './screens/BusyScreen.js'
51
- import { EditProfileFlow } from './screens/EditProfileFlow.js'
52
- import { StorageCredentialScreen } from './screens/StorageCredentialScreen.js'
53
- import {
54
- PrivateContinuityScreen,
55
- PublicSkillsScreen,
56
- } from './screens/ContinuityDashboardScreen.js'
57
- import { RecoveryConfirmScreen } from './screens/RecoveryConfirmScreen.js'
58
- import { chainIdForNetwork, erc8004ConfigForSupportedChain, type Erc8004RegistryConfig } from '../registry/erc8004.js'
59
-
60
- const MIN_BUSY_ERROR_MS = 2000
61
-
62
- function isWalletCancelled(err: unknown): boolean {
63
- if (!err) return false
64
- const message = err instanceof Error ? err.message : String(err)
65
- return /browser wallet request was cancelled/i.test(message)
66
- || /user rejected/i.test(message)
67
- }
68
-
69
- function isStorageError(err: unknown): boolean {
70
- const message = err instanceof Error ? err.message : String(err)
71
- return /pinata|ipfs|pin|storage/i.test(message)
72
- }
73
-
74
- function waitForMinimumBusyTime(startedAt: number): Promise<void> {
75
- const remaining = MIN_BUSY_ERROR_MS - (Date.now() - startedAt)
76
- return remaining > 0
77
- ? new Promise(resolve => setTimeout(resolve, remaining))
78
- : Promise.resolve()
79
- }
80
-
81
- export type IdentityHubResult =
82
- | { kind: 'token'; identity: EthagentIdentity }
83
- | { kind: 'updated'; config: EthagentConfig; message: string }
84
- | { kind: 'skip' }
85
- | { kind: 'cancel' }
86
-
87
- type IdentityHubProps = {
88
- mode: 'first-run' | 'manage'
89
- config?: EthagentConfig
90
- cwd?: string
91
- initialAction?: IdentityHubInitialAction
92
- onComplete: (result: IdentityHubResult) => void
93
- onConfigChange?: (config: EthagentConfig) => void
94
- }
95
-
96
- export type IdentityHubInitialAction = 'create' | 'load' | 'settings' | 'save-snapshot'
97
-
98
- export const IdentityHub: React.FC<IdentityHubProps> = ({ mode, config, initialAction, onComplete, onConfigChange }) => {
99
- const identity = config?.identity
100
- const [step, dispatch] = useReducer(identityHubReducer, initialStepForAction(initialAction, config))
101
- const [walletSession, setWalletSession] = useState<BrowserWalletReady | null>(null)
102
- const [restoreProgress, setRestoreProgress] = useState<RestoreProgress | null>(null)
103
- const [jwtSaved, setJwtSaved] = useState<boolean>(false)
104
- const [copyNotice, setCopyNotice] = useState<string | null>(null)
105
- const [continuityReady, setContinuityReady] = useState<boolean>(false)
106
- const [workingStatus, setWorkingStatus] = useState<ContinuityWorkingTreeStatus | null>(null)
107
- const canRebackup = Boolean(identity?.agentId && (identity?.identityRegistryAddress || config?.erc8004?.identityRegistryAddress))
108
-
109
- const setStep = (s: Step) => dispatch({ type: 'preflightResolved', step: s })
110
- const back = () => dispatch({ type: 'back', from: step })
111
-
112
- useEffect(() => { setWalletSession(null) }, [step.kind])
113
- useEffect(() => {
114
- if (step.kind !== 'restore-authorizing') setRestoreProgress(null)
115
- }, [step.kind])
116
-
117
- useEffect(() => {
118
- let cancelled = false
119
- hasPinataJwt().then(v => { if (!cancelled) setJwtSaved(v) }).catch(() => {})
120
- return () => { cancelled = true }
121
- }, [step.kind])
122
-
123
- useEffect(() => { setCopyNotice(null) }, [step.kind])
124
-
125
- useEffect(() => {
126
- let cancelled = false
127
- if (!identity) {
128
- setContinuityReady(false)
129
- return
130
- }
131
- if (!step.kind.startsWith('continuity') && step.kind !== 'details' && step.kind !== 'menu') return
132
- continuityVaultStatus(identity)
133
- .then(status => { if (!cancelled) setContinuityReady(status.ready) })
134
- .catch(() => { if (!cancelled) setContinuityReady(false) })
135
- return () => { cancelled = true }
136
- }, [identity, step.kind])
137
-
138
- const completeTokenIdentity = async (nextIdentity: EthagentIdentity, message: string): Promise<void> => {
139
- if (mode === 'first-run' || !config) {
140
- onComplete({ kind: 'token', identity: nextIdentity })
141
- return
142
- }
143
- const nextConfig = await setTokenIdentity(config, nextIdentity)
144
- onComplete({ kind: 'updated', config: nextConfig, message })
145
- }
146
-
147
- const callbacks: EffectCallbacks = {
148
- onStep: setStep,
149
- onWalletReady: setWalletSession,
150
- onIdentityComplete: completeTokenIdentity,
151
- onRestoreProgress: setRestoreProgress,
152
- }
153
-
154
- const errorStep = (err: unknown, backStep: Step): void => {
155
- setStep({ kind: 'error', error: identityHubErrorView(err), back: backStep })
156
- }
157
-
158
- const handleStepError = (err: unknown, backStep: Step, softCancel: Step = backStep): void => {
159
- if (isWalletCancelled(err)) {
160
- setStep(softCancel)
161
- return
162
- }
163
- errorStep(err, backStep)
164
- }
165
-
166
- const resolveRegistryForIdentity = (target: EthagentIdentity): Erc8004RegistryConfig | null => {
167
- const resolution = registryConfigFromConfig(config)
168
- if (target.chainId && target.identityRegistryAddress) {
169
- return {
170
- chainId: target.chainId,
171
- rpcUrl: target.rpcUrl ?? resolution.defaultRpcUrl,
172
- identityRegistryAddress: target.identityRegistryAddress as `0x${string}`,
173
- }
174
- }
175
- if (resolution.config) return resolution.config
176
- return null
177
- }
178
-
179
- const triggerRebackup = (backStep: Step, profileUpdates?: ProfileUpdates): void => {
180
- if (!identity) return
181
- const registry = resolveRegistryForIdentity(identity)
182
- if (!registry) {
183
- errorStep(new Error('no agent registry configured for this identity'), backStep)
184
- return
185
- }
186
- runRebackupPreflight(identity, registry, callbacks, profileUpdates, backStep)
187
- .catch((err: unknown) => errorStep(err, backStep))
188
- }
189
-
190
-
191
-
192
- useEffect(() => {
193
- if (step.kind !== 'rebackup-start') return
194
- triggerRebackup(step.back)
195
- }, [step])
196
-
197
- useEffect(() => {
198
- let cancelled = false
199
- if (!identity) return
200
- if (step.kind !== 'menu' && step.kind !== 'continuity-private' && step.kind !== 'continuity-public') return
201
-
202
- const checkStatus = async () => {
203
- try {
204
- const [latest] = await listPublishedContinuitySnapshots(identity, 1)
205
- const status = await continuityWorkingTreeStatus(identity, latest)
206
- if (cancelled) return
207
- setWorkingStatus(status)
208
- } catch {
209
- if (cancelled) return
210
- setWorkingStatus(null)
211
- }
212
- }
213
-
214
- void checkStatus()
215
-
216
- return () => {
217
- cancelled = true
218
- }
219
- }, [identity, step.kind])
220
-
221
- useEffect(() => {
222
- if (step.kind !== 'create-preflight') return
223
- let cancelled = false
224
- const startedAt = Date.now()
225
- runCreatePreflight(step, config, callbacks)
226
- .catch(async (err: unknown) => {
227
- await waitForMinimumBusyTime(startedAt)
228
- if (!cancelled) errorStep(err, { kind: 'create-network', name: step.name, description: step.description })
229
- })
230
- return () => { cancelled = true }
231
- }, [step])
232
-
233
- useEffect(() => {
234
- if (step.kind !== 'create-signing') return
235
- let cancelled = false
236
- const backStep: Step = { kind: 'create-network', name: step.name, description: step.description }
237
- runCreateSigning(step, callbacks)
238
- .catch((err: unknown) => {
239
- if (cancelled) return
240
- if (isRegistrationPreflightError(err)) {
241
- errorStep(err, backStep)
242
- return
243
- }
244
- if (isStorageError(err)) {
245
- setStep({
246
- kind: 'create-storage',
247
- name: step.name,
248
- description: step.description,
249
- registry: step.registry,
250
- error: pinataErrorText(err),
251
- pinataJwt: step.pinataJwt,
252
- })
253
- return
254
- }
255
- handleStepError(err, backStep)
256
- })
257
- return () => { cancelled = true }
258
- }, [step])
259
-
260
- useEffect(() => {
261
- if (step.kind !== 'restore-discovering') return
262
- let cancelled = false
263
- const startedAt = Date.now()
264
- runRestoreDiscover(step, config, callbacks)
265
- .catch(async (err: unknown) => {
266
- await waitForMinimumBusyTime(startedAt)
267
- if (cancelled) return
268
- if (isAgentTokenIdRequiredError(err)) {
269
- setStep({ kind: 'restore-token-id', ownerHandle: err.ownerAddress, registry: err.registry, error: err.message, purpose: step.purpose })
270
- return
271
- }
272
- errorStep(err, { kind: 'restore-network', ownerHandle: step.ownerHandle, purpose: step.purpose })
273
- })
274
- return () => { cancelled = true }
275
- }, [step])
276
-
277
- useEffect(() => {
278
- if (step.kind !== 'restore-wallet') return
279
- let cancelled = false
280
- runRestoreConnectWallet(step, callbacks)
281
- .catch((err: unknown) => { if (!cancelled) handleStepError(err, { kind: 'restore-owner', purpose: step.purpose }) })
282
- return () => { cancelled = true }
283
- }, [step])
284
-
285
- useEffect(() => {
286
- if (step.kind !== 'restore-fetching') return
287
- let cancelled = false
288
- const startedAt = Date.now()
289
- runRestoreFetch(step, callbacks)
290
- .catch(async (err: unknown) => {
291
- await waitForMinimumBusyTime(startedAt)
292
- if (!cancelled) errorStep(err, { kind: 'restore-network', ownerHandle: step.candidate.ownerAddress, purpose: step.purpose })
293
- })
294
- return () => { cancelled = true }
295
- }, [step])
296
-
297
- useEffect(() => {
298
- if (step.kind !== 'restore-authorizing') return
299
- let cancelled = false
300
- runRestoreAuthorize(step, callbacks)
301
- .catch((err: unknown) => {
302
- if (!cancelled) handleStepError(err, { kind: 'restore-network', ownerHandle: step.candidate.ownerAddress, purpose: step.purpose })
303
- })
304
- return () => { cancelled = true }
305
- }, [step])
306
-
307
- useEffect(() => {
308
- if (step.kind !== 'rebackup-signing') return
309
- let cancelled = false
310
- runRebackupSigning(step, callbacks)
311
- .catch((err: unknown) => {
312
- if (cancelled) return
313
- if (isStorageError(err)) {
314
- setStep({
315
- kind: 'rebackup-storage',
316
- identity: step.identity,
317
- registry: step.registry,
318
- error: pinataErrorText(err),
319
- pinataJwt: step.pinataJwt,
320
- profileUpdates: step.profileUpdates,
321
- returnTo: step.returnTo,
322
- })
323
- return
324
- }
325
- handleStepError(err, step.returnTo ?? { kind: 'menu' })
326
- })
327
- return () => { cancelled = true }
328
- }, [step])
329
-
330
- useEffect(() => {
331
- if (step.kind !== 'public-profile-signing') return
332
- let cancelled = false
333
- runPublicProfileSigning(step, callbacks)
334
- .catch((err: unknown) => {
335
- if (cancelled) return
336
- if (isStorageError(err)) {
337
- setStep({
338
- kind: 'public-profile-storage',
339
- identity: step.identity,
340
- registry: step.registry,
341
- error: pinataErrorText(err),
342
- pinataJwt: step.pinataJwt,
343
- profileUpdates: step.profileUpdates,
344
- returnTo: step.returnTo,
345
- })
346
- return
347
- }
348
- handleStepError(err, step.returnTo ?? { kind: 'continuity-public' })
349
- })
350
- return () => { cancelled = true }
351
- }, [step])
352
-
353
-
354
-
355
- useEffect(() => {
356
- if (step.kind !== 'recovery-refetching') return
357
- let cancelled = false
358
- runRecoveryRefetch(step.identity, step.registry, callbacks)
359
- .then(() => {
360
- if (!cancelled) setContinuityReady(true)
361
- })
362
- .catch((err: unknown) => {
363
- if (!cancelled) handleStepError(err, { kind: 'menu' })
364
- })
365
- return () => { cancelled = true }
366
- }, [step])
367
-
368
-
369
- const openContinuityFile = async (kind: 'soul' | 'memory' | 'skills'): Promise<void> => {
370
- if (!identity) return
371
- try {
372
- if (kind === 'skills') {
373
- await ensurePublicSkillsFile(identity, {
374
- fallback: () => readPublishedPublicSkills(identity),
375
- })
376
- }
377
- const ref = continuityVaultRef(identity)
378
- const file = kind === 'soul' ? ref.soulPath : kind === 'memory' ? ref.memoryPath : ref.publicSkillsPath
379
- const result = await openFileInEditor(file)
380
- const message = result.ok
381
- ? `opened ${kind === 'soul' ? 'SOUL.md' : kind === 'memory' ? 'MEMORY.md' : 'skills.json'} with ${result.method}.`
382
- : `open failed: ${result.error}`
383
- setStep(kind === 'skills'
384
- ? { kind: 'continuity-public', notice: message }
385
- : { kind: 'continuity-private', notice: message })
386
- } catch (err: unknown) {
387
- errorStep(err, kind === 'skills' ? { kind: 'continuity-public' } : { kind: 'continuity-private' })
388
- }
389
- }
390
-
391
- const footer = <Text color={theme.dim}>enter select · esc back</Text>
392
-
393
- if (step.kind === 'menu') {
394
- return (
395
- <MenuScreen
396
- mode={mode}
397
- config={config}
398
- identity={identity}
399
- workingStatus={workingStatus}
400
- canRebackup={canRebackup}
401
- footer={footer}
402
- onCreate={() => {
403
- if (identity) setStep({ kind: 'replace-confirm', next: 'create' })
404
- else setStep({ kind: 'create-name' })
405
- }}
406
- onLoad={() => {
407
- setCopyNotice(null)
408
- setStep({ kind: 'restore-wallet', purpose: identity ? 'switch' : 'restore' })
409
- }}
410
- onBackupNow={() => setStep({ kind: 'rebackup-confirm', back: { kind: 'menu' } })}
411
- onRefetchLatest={() => setStep({ kind: 'recovery-refetch-confirm', back: { kind: 'menu' } })}
412
- onPublicProfile={() => setStep({ kind: 'continuity-public' })}
413
- onPrivateMemory={() => setStep({ kind: 'continuity-private' })}
414
- onCopyValues={() => setStep({ kind: 'details' })}
415
- onStorageCredential={() => setStep({ kind: 'storage-credential' })}
416
- onSkip={() => onComplete({ kind: 'skip' })}
417
- onCancel={() => onComplete({ kind: 'cancel' })}
418
- />
419
- )
420
- }
421
-
422
- if (isCreateStep(step)) {
423
- return (
424
- <CreateFlow
425
- step={step}
426
- walletSession={walletSession}
427
- onSetStep={setStep}
428
- onNameSubmit={name => setStep({ kind: 'create-description', name })}
429
- onDescriptionSubmit={(name, description) => setStep({ kind: 'create-network', name, description })}
430
- onRegistrySubmit={async value => {
431
- if (step.kind !== 'create-registry') return
432
- try {
433
- await runRegistrySubmit(value, step, config, onConfigChange, callbacks)
434
- } catch (err: unknown) {
435
- setStep({ kind: 'create-registry', name: step.name, description: step.description, resolution: step.resolution, error: (err as Error).message })
436
- }
437
- }}
438
- onStorageSubmit={async input => {
439
- if (step.kind !== 'create-storage') return
440
- try {
441
- await runStorageSubmit(input, step, callbacks)
442
- } catch (err: unknown) {
443
- setStep({
444
- kind: 'create-storage',
445
- name: step.name,
446
- description: step.description,
447
- registry: step.registry,
448
- error: (err as Error).message,
449
- pinataJwt: step.pinataJwt,
450
- })
451
- }
452
- }}
453
- onStorageError={error => {
454
- if (step.kind !== 'create-storage') return
455
- setStep({ ...step, error })
456
- }}
457
- onBack={back}
458
- onMenu={() => setStep({ kind: 'menu' })}
459
- />
460
- )
461
- }
462
-
463
- if (step.kind === 'create-network') {
464
- return (
465
- <NetworkScreen
466
- subtitle="Choose where to create this agent."
467
- footer={footer}
468
- onSelect={(network: SelectableNetwork) => {
469
- setStep({ kind: 'create-preflight', name: step.name, description: step.description, network })
470
- }}
471
- onCancel={back}
472
- />
473
- )
474
- }
475
-
476
- if (step.kind === 'restore-network') {
477
- return (
478
- <NetworkScreen
479
- subtitle="Choose a network to search for your agents."
480
- footer={footer}
481
- onSelect={(network: SelectableNetwork) => {
482
- try {
483
- const registry = erc8004ConfigForSupportedChain(chainIdForNetwork(network))
484
- setStep({ kind: 'restore-discovering', ownerHandle: step.ownerHandle, registry, purpose: step.purpose })
485
- } catch (err: unknown) {
486
- errorStep(err, { kind: 'restore-network', ownerHandle: step.ownerHandle, purpose: step.purpose })
487
- }
488
- }}
489
- onCancel={back}
490
- />
491
- )
492
- }
493
-
494
- if (isRestoreStep(step)) {
495
- return (
496
- <RestoreFlow
497
- step={step}
498
- config={config}
499
- walletSession={walletSession}
500
- restoreProgress={restoreProgress}
501
- onConnectWallet={() => {
502
- const purpose = step.kind === 'restore-owner' ? step.purpose : undefined
503
- setStep({ kind: 'restore-wallet', purpose })
504
- }}
505
- onRestoreRegistrySubmit={async value => {
506
- if (step.kind !== 'restore-registry') return
507
- try {
508
- await runRestoreRegistrySubmit(value, step, config, onConfigChange, callbacks)
509
- } catch (err: unknown) {
510
- setStep({ kind: 'restore-registry', ownerHandle: step.ownerHandle, error: (err as Error).message, purpose: step.purpose })
511
- }
512
- }}
513
- onTokenIdSubmit={async value => {
514
- if (step.kind !== 'restore-token-id') return
515
- try {
516
- await runRestoreTokenIdSubmit(value, step, callbacks)
517
- } catch (err: unknown) {
518
- setStep({ ...step, error: (err as Error).message })
519
- }
520
- }}
521
- onTokenSelect={value => {
522
- if (step.kind !== 'restore-select-token') return
523
- const candidate = step.candidates.find(item => item.agentId.toString() === value)
524
- if (!candidate?.backup?.cid) return
525
- setStep({ kind: 'restore-fetching', cid: candidate.backup.cid, apiUrl: DEFAULT_IPFS_API_URL, candidate, purpose: step.purpose })
526
- }}
527
- onBack={back}
528
- />
529
- )
530
- }
531
-
532
- if (step.kind === 'details') {
533
- return (
534
- <DetailsScreen
535
- identity={identity}
536
- config={config}
537
- copyNotice={copyNotice}
538
- footer={footer}
539
- onCopy={async (label, value) => {
540
- const result = await copyToClipboard(value)
541
- setCopyNotice(result.ok ? `copied ${label} via ${result.method}.` : `copy failed: ${result.error}`)
542
- setStep({ kind: 'details' })
543
- }}
544
- onBack={back}
545
- />
546
- )
547
- }
548
-
549
- const openPublicProfileEdit = (backStep: Step): void => {
550
- if (!identity) return
551
- const registry = resolveRegistryForIdentity(identity)
552
- if (!registry) {
553
- errorStep(new Error('no agent registry configured for this identity'), backStep)
554
- return
555
- }
556
- setStep({ kind: 'edit-profile-name', identity, registry, returnTo: backStep })
557
- }
558
-
559
- if (step.kind === 'rebackup-confirm') {
560
- return (
561
- <RecoveryConfirmScreen
562
- mode="publish"
563
- workingStatus={workingStatus}
564
- footer={footer}
565
- onConfirm={() => triggerRebackup(step.back)}
566
- onBack={back}
567
- />
568
- )
569
- }
570
-
571
- if (step.kind === 'recovery-refetch-confirm') {
572
- return (
573
- <RecoveryConfirmScreen
574
- mode="refetch"
575
- workingStatus={workingStatus}
576
- footer={footer}
577
- onConfirm={() => {
578
- if (!identity) return
579
- const registry = resolveRegistryForIdentity(identity)
580
- if (!registry) {
581
- errorStep(new Error('no agent registry configured for this identity'), step.back)
582
- return
583
- }
584
- setStep({ kind: 'recovery-refetching', identity, registry, back: step.back })
585
- }}
586
- onBack={back}
587
- />
588
- )
589
- }
590
-
591
- if (step.kind === 'recovery-refetching') {
592
- return (
593
- <WalletApprovalScreen
594
- title="Refetch Latest Snapshot"
595
- subtitle="Wallet approval decrypts the latest saved snapshot and overwrites local SOUL.md, MEMORY.md, and skills.json."
596
- walletSession={walletSession}
597
- label={restoreProgress?.label ?? 'fetching latest snapshot from chain...'}
598
- onCancel={() => setStep(step.back)}
599
- />
600
- )
601
- }
602
-
603
- if (step.kind === 'continuity-private') {
604
- return (
605
- <PrivateContinuityScreen
606
- identity={identity}
607
- config={config}
608
- workingStatus={workingStatus}
609
- ready={continuityReady}
610
- notice={step.notice}
611
- footer={footer}
612
- onOpenSoul={() => { void openContinuityFile('soul') }}
613
- onOpenMemory={() => { void openContinuityFile('memory') }}
614
- onBack={back}
615
- />
616
- )
617
- }
618
-
619
- if (step.kind === 'continuity-public') {
620
- return (
621
- <PublicSkillsScreen
622
- identity={identity}
623
- config={config}
624
- workingStatus={workingStatus}
625
- ready={continuityReady}
626
- notice={step.notice}
627
- footer={footer}
628
- onEditProfile={() => openPublicProfileEdit({ kind: 'continuity-public' })}
629
- onOpenSkills={() => { void openContinuityFile('skills') }}
630
- onBack={back}
631
- />
632
- )
633
- }
634
-
635
- if (step.kind === 'storage-credential' || step.kind === 'storage-credential-input' || step.kind === 'storage-credential-forget-confirm') {
636
- return (
637
- <StorageCredentialScreen
638
- step={step}
639
- hasCredential={jwtSaved}
640
- footer={footer}
641
- onEdit={() => setStep({ kind: 'storage-credential-input' })}
642
- onForget={() => setStep({ kind: 'storage-credential-forget-confirm' })}
643
- onConfirmForget={async () => {
644
- await clearPinataJwt().catch(() => {})
645
- setJwtSaved(false)
646
- setCopyNotice('IPFS storage credential removed.')
647
- setStep({ kind: 'menu' })
648
- }}
649
- onSubmit={async input => {
650
- try {
651
- await savePinataJwt(input)
652
- setJwtSaved(true)
653
- setCopyNotice('IPFS storage credential saved.')
654
- setStep({ kind: 'menu' })
655
- } catch (err: unknown) {
656
- setStep({ kind: 'storage-credential-input', error: (err as Error).message })
657
- }
658
- }}
659
- onCancel={back}
660
- />
661
- )
662
- }
663
-
664
- if (step.kind === 'edit-profile-name' || step.kind === 'edit-profile-description' || step.kind === 'edit-profile-image') {
665
- return (
666
- <EditProfileFlow
667
- step={step}
668
- onNameSubmit={name => {
669
- if (step.kind !== 'edit-profile-name') return
670
- setStep({ kind: 'edit-profile-description', identity: step.identity, registry: step.registry, name, returnTo: step.returnTo })
671
- }}
672
- onDescriptionSubmit={description => {
673
- if (step.kind !== 'edit-profile-description') return
674
- setStep({ kind: 'edit-profile-image', identity: step.identity, registry: step.registry, name: step.name, description, returnTo: step.returnTo })
675
- }}
676
- onImageSubmit={imagePath => {
677
- if (step.kind !== 'edit-profile-image') return
678
- const updates: ProfileUpdates = { name: step.name, description: step.description, ...(imagePath ? { imagePath } : {}) }
679
- triggerRebackup(step.returnTo ?? { kind: 'continuity-public' }, updates)
680
- }}
681
- onImagePick={() => {
682
- if (step.kind !== 'edit-profile-image') return
683
- const imageStep = step
684
- void openImageFilePicker()
685
- .then(result => {
686
- if (!result.ok) {
687
- setStep({ ...imageStep, error: result.cancelled ? 'image selection cancelled.' : `${result.error}. enter a path manually if needed.` })
688
- return
689
- }
690
- const updates: ProfileUpdates = { name: imageStep.name, description: imageStep.description, imagePath: result.file }
691
- triggerRebackup(imageStep.returnTo ?? { kind: 'continuity-public' }, updates)
692
- })
693
- .catch((err: unknown) => {
694
- setStep({ ...imageStep, error: `${(err as Error).message}. enter a path manually if needed.` })
695
- })
696
- }}
697
- onBack={back}
698
- onMenu={() => setStep(step.returnTo ?? { kind: 'continuity-public' })}
699
- />
700
- )
701
- }
702
-
703
- if (step.kind === 'rebackup-signing') {
704
- return (
705
- <WalletApprovalScreen
706
- title="Approve Encrypted Snapshot"
707
- subtitle="Signs, encrypts private SOUL.md and MEMORY.md, pins them, and refreshes recovery metadata."
708
- walletSession={walletSession}
709
- label="waiting for wallet approval..."
710
- onCancel={() => setStep(step.returnTo ?? { kind: 'menu' })}
711
- />
712
- )
713
- }
714
-
715
-
716
-
717
- if (step.kind === 'rebackup-start') {
718
- return (
719
- <BusyScreen
720
- title="Identity Hub"
721
- label="preparing encrypted snapshot..."
722
- onCancel={back}
723
- />
724
- )
725
- }
726
-
727
-
728
-
729
-
730
- if (step.kind === 'rebackup-storage') {
731
- return (
732
- <RebackupStorageScreen
733
- step={step}
734
- footer={footer}
735
- onSubmit={async input => {
736
- try {
737
- await runRebackupStorageSubmit(input, step, callbacks)
738
- } catch (err: unknown) {
739
- setStep({ ...step, error: (err as Error).message })
740
- }
741
- }}
742
- onCancel={back}
743
- />
744
- )
745
- }
746
-
747
- if (step.kind === 'restore-wallet') {
748
- return (
749
- <WalletApprovalScreen
750
- title="Connect Wallet"
751
- subtitle="Select the wallet that owns the agent you want to load."
752
- walletSession={walletSession}
753
- label="waiting for wallet..."
754
- onCancel={back}
755
- />
756
- )
757
- }
758
-
759
- if (step.kind === 'busy') {
760
- return (
761
- <BusyScreen
762
- title="Identity Hub"
763
- label={step.label}
764
- onCancel={back}
765
- />
766
- )
767
- }
768
-
769
- if (step.kind === 'error') {
770
- return (
771
- <ErrorScreen
772
- error={step.error}
773
- back={step.back}
774
- footer={footer}
775
- onBack={backStep => setStep(backStep)}
776
- onClose={() => onComplete({ kind: 'cancel' })}
777
- />
778
- )
779
- }
780
-
781
- return null
782
- }
783
-
784
- async function readPublishedPublicSkills(identity: EthagentIdentity): Promise<string> {
785
- const cid = identity.publicSkills?.cid
786
- if (!cid) throw new Error('no saved public skills CID')
787
- return new TextDecoder().decode(await catFromIpfs(
788
- identity.backup?.ipfsApiUrl ?? DEFAULT_IPFS_API_URL,
789
- cid,
790
- ))
791
- }
792
-
793
- function isCreateStep(step: Step): step is Extract<Step, { kind: 'replace-confirm' | 'create-name' | 'create-description' | 'create-preflight' | 'create-registry' | 'create-signing' | 'create-storage' }> {
794
- return step.kind === 'replace-confirm'
795
- || step.kind === 'create-name'
796
- || step.kind === 'create-description'
797
- || step.kind === 'create-preflight'
798
- || step.kind === 'create-registry'
799
- || step.kind === 'create-signing'
800
- || step.kind === 'create-storage'
801
- }
802
-
803
- function isRestoreStep(step: Step): step is Exclude<Extract<Step, { kind: `restore-${string}` }>, { kind: 'restore-wallet' | 'restore-network' }> {
804
- return step.kind.startsWith('restore-') && step.kind !== 'restore-wallet' && step.kind !== 'restore-network'
805
- }
806
-
807
- function initialStepForAction(
808
- action: IdentityHubInitialAction | undefined,
809
- config: EthagentConfig | undefined,
810
- ): Step {
811
- if (action === 'create') return config?.identity ? { kind: 'replace-confirm', next: 'create' } : { kind: 'create-name' }
812
- if (action === 'load') return { kind: 'restore-wallet', purpose: config?.identity ? 'switch' : 'restore' }
813
- if (action === 'save-snapshot') return config?.identity ? { kind: 'rebackup-start', back: { kind: 'menu' } } : { kind: 'menu' }
814
- if (action === 'settings') return { kind: 'menu' }
815
- return { kind: 'menu' }
1
+ import React from 'react'
2
+ import { IdentityHubRoutes } from './Routes.js'
3
+ import type { IdentityHubProps } from './types.js'
4
+ import { useIdentityHubController } from './useIdentityHubController.js'
5
+
6
+ export type {
7
+ IdentityHubInitialAction,
8
+ IdentityHubResult,
9
+ } from './types.js'
10
+
11
+ export const IdentityHub: React.FC<IdentityHubProps> = props => {
12
+ const controller = useIdentityHubController(props)
13
+ return <IdentityHubRoutes controller={controller} />
816
14
  }