ethagent 2.1.1 → 2.3.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 (177) hide show
  1. package/package.json +2 -1
  2. package/src/app/FirstRun.tsx +1 -7
  3. package/src/app/FirstRunTimeline.tsx +1 -1
  4. package/src/auth/openaiOAuth/credentials.ts +47 -0
  5. package/src/auth/openaiOAuth/crypto.ts +23 -0
  6. package/src/auth/openaiOAuth/index.ts +238 -0
  7. package/src/auth/openaiOAuth/landingPage.ts +125 -0
  8. package/src/auth/openaiOAuth/listener.ts +151 -0
  9. package/src/auth/openaiOAuth/refresh.ts +70 -0
  10. package/src/auth/openaiOAuth/shared.ts +115 -0
  11. package/src/chat/ChatBottomPane.tsx +20 -11
  12. package/src/chat/ChatScreen.tsx +160 -35
  13. package/src/chat/ConversationStack.tsx +1 -1
  14. package/src/chat/MessageList.tsx +185 -72
  15. package/src/chat/SessionStatus.tsx +3 -1
  16. package/src/chat/chatScreenUtils.ts +11 -15
  17. package/src/chat/chatSessionState.ts +3 -2
  18. package/src/chat/chatTurnOrchestrator.ts +1 -7
  19. package/src/chat/commands.ts +28 -27
  20. package/src/chat/display/DiffView.tsx +193 -0
  21. package/src/chat/display/SyntaxText.tsx +192 -0
  22. package/src/chat/display/toolCallDisplay.ts +103 -0
  23. package/src/chat/display/toolResultDisplay.ts +19 -0
  24. package/src/chat/{ChatInput.tsx → input/ChatInput.tsx} +36 -23
  25. package/src/chat/{TranscriptView.tsx → transcript/TranscriptView.tsx} +24 -50
  26. package/src/chat/{transcriptViewport.ts → transcript/transcriptViewport.ts} +12 -30
  27. package/src/chat/{ContextLimitView.tsx → views/ContextLimitView.tsx} +3 -3
  28. package/src/chat/{ContinuityEditReviewView.tsx → views/ContinuityEditReviewView.tsx} +11 -3
  29. package/src/chat/{CopyPicker.tsx → views/CopyPicker.tsx} +4 -5
  30. package/src/chat/{PermissionPrompt.tsx → views/PermissionPrompt.tsx} +16 -17
  31. package/src/chat/{PermissionsView.tsx → views/PermissionsView.tsx} +6 -6
  32. package/src/chat/{PlanApprovalView.tsx → views/PlanApprovalView.tsx} +4 -4
  33. package/src/chat/{ResumeView.tsx → views/ResumeView.tsx} +35 -35
  34. package/src/chat/views/RewindView.tsx +410 -0
  35. package/src/identity/continuity/privateEdit/diff.ts +2 -78
  36. package/src/identity/ens/agentRecords.ts +5 -19
  37. package/src/identity/ens/ensAutomation/setup.ts +0 -1
  38. package/src/identity/ens/ensAutomation/types.ts +0 -1
  39. package/src/identity/hub/OperationalRoutes.tsx +23 -32
  40. package/src/identity/hub/Routes.tsx +13 -13
  41. package/src/identity/hub/{flows/continuity → continuity}/ContinuityDashboardScreen.tsx +9 -9
  42. package/src/identity/hub/{flows/continuity → continuity}/RebackupStorageScreen.tsx +2 -2
  43. package/src/identity/hub/{flows/continuity → continuity}/RecoveryConfirmScreen.tsx +5 -5
  44. package/src/identity/hub/{flows/continuity → continuity}/SavePromptScreen.tsx +5 -5
  45. package/src/identity/hub/{effects/rebackup/runRebackup.ts → continuity/effects.ts} +19 -19
  46. package/src/identity/hub/{effects/rebackup → continuity}/index.ts +1 -1
  47. package/src/identity/hub/{effects/shared → continuity}/snapshot.ts +8 -8
  48. package/src/identity/hub/{effects/rebackup → continuity}/vault.ts +15 -15
  49. package/src/identity/hub/{flows/create → create}/CreateFlow.tsx +13 -13
  50. package/src/identity/hub/{effects/create.ts → create/effects.ts} +4 -4
  51. package/src/identity/hub/{flows/custody → custody}/CustodyEditFlow.tsx +10 -48
  52. package/src/identity/hub/{flows/custody/custodyFlowActions.ts → custody/actions.ts} +11 -9
  53. package/src/identity/hub/{flows/custody/custodyFlowHelpers.ts → custody/helpers.ts} +4 -4
  54. package/src/identity/hub/{effects/vault → custody}/preflight.ts +5 -5
  55. package/src/identity/hub/{flows/custody/custodyFlowRoutes.tsx → custody/routes.tsx} +8 -8
  56. package/src/identity/hub/{flows/custody/custodyEffects.ts → custody/transactions.ts} +9 -9
  57. package/src/identity/hub/{flows/custody/custodyFlowTypes.ts → custody/types.ts} +6 -6
  58. package/src/identity/hub/{flows/custody/custodyFlowEffects.ts → custody/useCustodyEffects.ts} +7 -7
  59. package/src/identity/hub/{flows/custody → custody}/useCustodyFlow.tsx +5 -5
  60. package/src/identity/hub/ens/EnsEditAdvancedScreens.tsx +241 -0
  61. package/src/identity/hub/{flows/ens → ens}/EnsEditFlow.tsx +27 -82
  62. package/src/identity/hub/{flows/ens → ens}/EnsEditMaintenanceScreens.tsx +25 -65
  63. package/src/identity/hub/{flows/ens → ens}/EnsEditReviewScreens.tsx +12 -30
  64. package/src/identity/hub/ens/EnsEditRunners.tsx +62 -0
  65. package/src/identity/hub/{flows/ens → ens}/EnsEditShared.tsx +15 -14
  66. package/src/identity/hub/{flows/ens → ens}/EnsEditSimpleScreens.tsx +68 -217
  67. package/src/identity/hub/{flows/ens/IdentityHubEnsFlow.tsx → ens/EnsFlow.tsx} +18 -11
  68. package/src/identity/hub/{flows/ens/OperatorWalletsScreen.tsx → ens/EnsOperatorWalletsScreen.tsx} +17 -48
  69. package/src/identity/hub/{advancedEnsValidation.ts → ens/advancedEnsValidation.ts} +2 -2
  70. package/src/identity/hub/{flows/ens/ensEditCopy.ts → ens/editCopy.ts} +4 -4
  71. package/src/identity/hub/{effects/ens/flows.ts → ens/effects.ts} +7 -7
  72. package/src/identity/hub/{effects/ens → ens}/index.ts +1 -1
  73. package/src/identity/hub/{model/ens.ts → ens/state.ts} +1 -1
  74. package/src/identity/hub/{effects/ens → ens}/transactions.ts +232 -232
  75. package/src/identity/hub/{flows/ens/ensEditTypes.ts → ens/types.ts} +12 -26
  76. package/src/identity/hub/identityHubReducer.ts +3 -3
  77. package/src/identity/hub/{flows/profile → profile}/EditProfileFlow.tsx +17 -10
  78. package/src/identity/hub/{effects/publicProfile/runPublicProfileSave.ts → profile/effects.ts} +55 -177
  79. package/src/identity/hub/{model → profile}/identity.ts +3 -3
  80. package/src/identity/hub/{effects/profile/profileState.ts → profile/state.ts} +181 -173
  81. package/src/identity/hub/{flows/restore → restore}/RestoreFlow.tsx +21 -21
  82. package/src/identity/hub/{effects/restore → restore}/apply.ts +10 -10
  83. package/src/identity/hub/{effects/restore → restore}/auth.ts +7 -7
  84. package/src/identity/hub/{effects/restore → restore}/discover.ts +6 -6
  85. package/src/identity/hub/{effects/restore → restore}/envelopes.ts +2 -2
  86. package/src/identity/hub/{effects/restore → restore}/fetch.ts +3 -3
  87. package/src/identity/hub/{effects/restore/shared.ts → restore/helpers.ts} +6 -6
  88. package/src/identity/hub/{effects/restore → restore}/recovery.ts +10 -10
  89. package/src/identity/hub/{effects/restore → restore}/resolve.ts +4 -4
  90. package/src/identity/hub/restore/restoreAdmin.ts +34 -0
  91. package/src/identity/hub/{flows/restore/useRestoreFlowEffects.ts → restore/useRestoreEffects.ts} +5 -5
  92. package/src/identity/hub/{flows/settings → settings}/StorageCredentialScreen.tsx +5 -5
  93. package/src/identity/hub/{components → shared/components}/BusyScreen.tsx +4 -4
  94. package/src/identity/hub/{components → shared/components}/DetailsScreen.tsx +4 -4
  95. package/src/identity/hub/{components → shared/components}/ErrorScreen.tsx +4 -4
  96. package/src/identity/hub/{components → shared/components}/FlowTimeline.tsx +1 -1
  97. package/src/identity/hub/{components → shared/components}/IdentitySummary.tsx +16 -11
  98. package/src/identity/hub/{components → shared/components}/MenuScreen.tsx +8 -9
  99. package/src/identity/hub/{components → shared/components}/NetworkScreen.tsx +4 -4
  100. package/src/identity/hub/{components → shared/components}/PinataJwtInput.tsx +4 -4
  101. package/src/identity/hub/{components → shared/components}/UnlinkedIdentityScreen.tsx +5 -5
  102. package/src/identity/hub/{components → shared/components}/WalletApprovalScreen.tsx +6 -6
  103. package/src/identity/hub/{components → shared/components}/menuFlagsFromReconciliation.ts +2 -4
  104. package/src/identity/hub/{effects/shared → shared/effects}/profilePrep.ts +1 -1
  105. package/src/identity/hub/{effects → shared/effects}/receipts.ts +2 -2
  106. package/src/identity/hub/{effects/shared → shared/effects}/sync.ts +6 -47
  107. package/src/identity/hub/{effects → shared/effects}/types.ts +3 -3
  108. package/src/identity/hub/{model → shared/model}/copy.ts +2 -2
  109. package/src/identity/hub/{model → shared/model}/errors.ts +5 -5
  110. package/src/identity/hub/{model → shared/model}/network.ts +3 -3
  111. package/src/identity/hub/{operatorWallets.ts → shared/operatorWallets.ts} +1 -1
  112. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/hook.ts +1 -2
  113. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/ownership.ts +2 -2
  114. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/run.ts +7 -40
  115. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/types.ts +0 -4
  116. package/src/identity/hub/{reconciliation → shared/reconciliation}/index.ts +0 -7
  117. package/src/identity/hub/shared/reconciliation/walletSetup.ts +27 -0
  118. package/src/identity/hub/{utils.ts → shared/utils.ts} +5 -5
  119. package/src/identity/hub/{flows/token-transfer/IdentityHubTokenTransferFlow.tsx → transfer/TokenTransferFlow.tsx} +8 -8
  120. package/src/identity/hub/{flows/token-transfer → transfer}/TokenTransferScreens.tsx +14 -14
  121. package/src/identity/hub/{effects/token-transfer/runTokenTransfer.ts → transfer/effects.ts} +16 -16
  122. package/src/identity/hub/{effects/token-transfer → transfer}/progress.ts +1 -1
  123. package/src/identity/hub/useIdentityHubController.ts +11 -11
  124. package/src/identity/hub/useIdentityHubSideEffects.ts +11 -11
  125. package/src/identity/wallet/browserWallet/types.ts +0 -5
  126. package/src/identity/wallet/page/copy.ts +1 -31
  127. package/src/identity/wallet/walletPurposeCompat.ts +0 -2
  128. package/src/models/ModelPicker.tsx +248 -8
  129. package/src/models/catalog.ts +29 -1
  130. package/src/models/modelPickerOptions.ts +12 -10
  131. package/src/models/providerDisplay.ts +16 -0
  132. package/src/providers/errors.ts +6 -4
  133. package/src/providers/openai-chat.ts +2 -1
  134. package/src/providers/openai-responses-format.ts +156 -0
  135. package/src/providers/openai-responses.ts +276 -0
  136. package/src/providers/registry.ts +85 -8
  137. package/src/runtime/sessionMode.ts +1 -1
  138. package/src/runtime/systemPrompt.ts +4 -2
  139. package/src/runtime/toolExecution.ts +9 -6
  140. package/src/runtime/turn.ts +29 -1
  141. package/src/storage/rewind.ts +20 -0
  142. package/src/storage/secrets.ts +4 -1
  143. package/src/storage/sessions.ts +2 -1
  144. package/src/tools/bashSafety.ts +7 -3
  145. package/src/tools/bashTool.ts +1 -1
  146. package/src/tools/contracts.ts +3 -0
  147. package/src/tools/deleteFileTool.ts +8 -3
  148. package/src/tools/editTool.ts +10 -5
  149. package/src/tools/fileDiff.ts +261 -0
  150. package/src/tools/privateContinuityEditTool.ts +11 -1
  151. package/src/tools/writeFileTool.ts +8 -3
  152. package/src/ui/Spinner.tsx +25 -3
  153. package/src/ui/TextInput.tsx +2 -2
  154. package/src/ui/theme.ts +17 -0
  155. package/src/utils/clipboard.ts +10 -7
  156. package/src/utils/openExternal.ts +20 -10
  157. package/src/chat/RewindView.tsx +0 -386
  158. package/src/chat/toolResultDisplay.ts +0 -8
  159. package/src/identity/ens/ensRegistration.ts +0 -199
  160. package/src/identity/hub/effects/index.ts +0 -74
  161. package/src/identity/hub/effects/publicProfile/index.ts +0 -5
  162. package/src/identity/hub/effects/restore/restoreEffects.ts +0 -22
  163. package/src/identity/hub/effects/restoreAdmin.ts +0 -93
  164. package/src/identity/hub/effects/token-transfer/index.ts +0 -6
  165. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +0 -336
  166. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +0 -198
  167. package/src/identity/hub/reconciliation/walletSetup.ts +0 -220
  168. /package/src/chat/{chatInputState.ts → input/chatInputState.ts} +0 -0
  169. /package/src/chat/{chatPaste.ts → input/chatPaste.ts} +0 -0
  170. /package/src/chat/{textCursor.ts → input/textCursor.ts} +0 -0
  171. /package/src/identity/hub/{model/continuity.ts → continuity/state.ts} +0 -0
  172. /package/src/identity/hub/{model/custody.ts → custody/state.ts} +0 -0
  173. /package/src/identity/hub/{effects/restore → restore}/index.ts +0 -0
  174. /package/src/identity/hub/{model → shared/model}/format.ts +0 -0
  175. /package/src/identity/hub/{reconciliation → shared/reconciliation}/useAgentReconciliation.ts +0 -0
  176. /package/src/identity/hub/{txGuard.ts → shared/txGuard.ts} +0 -0
  177. /package/src/identity/hub/{model/transfer.ts → transfer/state.ts} +0 -0
@@ -1,336 +0,0 @@
1
- import React from 'react'
2
- import { Box, Text } from 'ink'
3
- import { getAddress, type Address } from 'viem'
4
- import { Surface } from '../../../../ui/Surface.js'
5
- import { Select } from '../../../../ui/Select.js'
6
- import { TextInput } from '../../../../ui/TextInput.js'
7
- import { Spinner } from '../../../../ui/Spinner.js'
8
- import { theme } from '../../../../ui/theme.js'
9
- import {
10
- normalizeEthDomain,
11
- sanitizeSubdomainPrefix,
12
- } from '../../../ens/ensLookup.js'
13
- import { isRootEthName } from '../../../ens/ensAutomation.js'
14
- import type { Erc8004RegistryConfig } from '../../../registry/erc8004.js'
15
- import type { BrowserWalletReady } from '../../../wallet/browserWallet.js'
16
- import {
17
- type CustodyMode,
18
- } from '../../model/custody.js'
19
- import { shortAddress } from '../../model/format.js'
20
- import { WalletApprovalScreen } from '../../components/WalletApprovalScreen.js'
21
- import { advancedSubdomainStatusText } from './ensEditCopy.js'
22
- import {
23
- EnsSetupRow,
24
- footerHint,
25
- } from './EnsEditShared.js'
26
- import {
27
- EnsSetupBlockedScreen,
28
- EnsSetupReviewScreen,
29
- } from './EnsEditReviewScreens.js'
30
- import { EscCancel } from './EnsEditRunners.js'
31
- import type {
32
- EnsEditProps,
33
- EnsPhase,
34
- } from './ensEditTypes.js'
35
-
36
- type AdvancedScreenProps = {
37
- phase: EnsPhase
38
- ownerAddress: Address
39
- agentId: EnsEditProps['identity']['agentId']
40
- savedOwnerAddress: string
41
- savedOperator: string
42
- savedRootName: string
43
- savedSubdomainLabel: string
44
- agentNameSuggestion: string
45
- currentEnsName: string
46
- savedCustodyMode: CustodyMode | undefined
47
- registry: Erc8004RegistryConfig
48
- operatorWalletSession: BrowserWalletReady | null
49
- setPhase: (phase: EnsPhase) => void
50
- connectOperatorWallet: (rootName: string, label: string) => void
51
- runAdvancedRootCheck: (rootName: string) => void
52
- runAdvancedSubdomainCheck: (rootName: string, label: string) => void
53
- runAdvancedPreflight: (rootName: string, label: string, operatorWallet: Address) => void
54
- onEnsSetup: EnsEditProps['onEnsSetup']
55
- onEnsLink: EnsEditProps['onEnsLink']
56
- }
57
-
58
- export function renderAdvancedEnsPhase({
59
- phase,
60
- ownerAddress,
61
- agentId,
62
- savedOwnerAddress,
63
- savedOperator,
64
- savedRootName,
65
- savedSubdomainLabel,
66
- agentNameSuggestion,
67
- currentEnsName,
68
- savedCustodyMode,
69
- registry,
70
- operatorWalletSession,
71
- setPhase,
72
- connectOperatorWallet,
73
- runAdvancedRootCheck,
74
- runAdvancedSubdomainCheck,
75
- runAdvancedPreflight,
76
- onEnsSetup,
77
- onEnsLink,
78
- }: AdvancedScreenProps): React.ReactNode | null {
79
- if (phase.kind === 'advanced-transfer-check') {
80
- type TransferCheckAction = 'skip' | 'back'
81
- return (
82
- <Surface
83
- title="Token Custody Check"
84
- subtitle="ENS setup continues only after the owner wallet holds this token."
85
- footer={footerHint('enter select · esc back')}
86
- >
87
- <Box flexDirection="column">
88
- <Text color={theme.dim}>Current token owner: <Text color={theme.text}>{shortAddress(ownerAddress)}</Text></Text>
89
- <Box marginTop={1} flexDirection="column">
90
- <EnsSetupRow label="Owner wallet" value={`Holds ERC-8004 token #${agentId ?? 'unknown'} and signs ENS records.`} />
91
- <EnsSetupRow label="Operator wallet" value="Restores snapshots; never controls the token." />
92
- <EnsSetupRow label="Token moves" value="If the token is in the Vault, withdraw it first from Custody Mode." />
93
- </Box>
94
- </Box>
95
- <Box marginTop={1}>
96
- <Select<TransferCheckAction>
97
- options={[
98
- { value: 'skip', role: 'section', label: 'Setup' },
99
- { value: 'skip', label: 'Continue ENS Setup', hint: 'The connected wallet is already the owner wallet' },
100
- { value: 'back', role: 'section', label: 'Navigation' },
101
- { value: 'back', label: 'Back', hint: 'Return to setup type', role: 'utility' },
102
- ]}
103
- hintLayout="inline"
104
- onSubmit={choice => {
105
- if (choice === 'skip') return setPhase({ kind: 'advanced-root', rootName: savedRootName })
106
- return setPhase({ kind: 'mode-select' })
107
- }}
108
- onCancel={() => setPhase({ kind: 'mode-select' })}
109
- />
110
- </Box>
111
- </Surface>
112
- )
113
- }
114
-
115
- if (phase.kind === 'advanced-root') {
116
- return (
117
- <Surface
118
- title="Root ENS"
119
- footer={footerHint('enter next · esc back')}
120
- >
121
- <Box flexDirection="column">
122
- <Text color={theme.dim}>Enter the parent .eth name. The owner wallet must manage it and own this ERC-8004 token.</Text>
123
- {savedOwnerAddress ? <Text color={theme.dim}>Saved owner wallet: <Text color={theme.text}>{shortAddress(savedOwnerAddress)}</Text></Text> : null}
124
- {phase.error ? <Text color={theme.accentError}>{phase.error}</Text> : null}
125
- </Box>
126
- <Box marginTop={1}>
127
- <TextInput
128
- key="advanced-root"
129
- initialValue={phase.rootName || savedRootName}
130
- placeholder="name.eth"
131
- validate={value => {
132
- const root = normalizeEthDomain(value)
133
- if (!root) return 'Enter a parent .eth name'
134
- if (!isRootEthName(root)) return 'Enter the parent .eth name, e.g. name.eth'
135
- return null
136
- }}
137
- onSubmit={value => runAdvancedRootCheck(normalizeEthDomain(value))}
138
- onCancel={() => setPhase({ kind: 'mode-select' })}
139
- />
140
- </Box>
141
- </Surface>
142
- )
143
- }
144
-
145
- if (phase.kind === 'advanced-root-check') {
146
- return (
147
- <Surface
148
- title="Checking ENS and Token Ownership"
149
- subtitle={`Verifying the connected wallet manages ${phase.rootName} and owns the ERC-8004 token.`}
150
- footer={footerHint('esc back')}
151
- >
152
- <Box marginTop={1}>
153
- <Text color={theme.textSubtle}>Reading from Ethereum mainnet...</Text>
154
- </Box>
155
- </Surface>
156
- )
157
- }
158
-
159
- if (phase.kind === 'advanced-subdomain') {
160
- const rootName = phase.rootName
161
- return (
162
- <Surface
163
- title="Agent Subdomain"
164
- footer={footerHint('enter next · esc back')}
165
- >
166
- <Box flexDirection="column">
167
- <Text color={theme.dim}>Create one subdomain for this agent only. Root .eth names stay parent names.</Text>
168
- <Text color={theme.dim}>Parent: <Text color={theme.text}>{rootName}</Text></Text>
169
- {phase.error ? <Text color={theme.accentError}>{phase.error}</Text> : null}
170
- </Box>
171
- <Box marginTop={1}>
172
- <TextInput
173
- key={`advanced-subdomain-${rootName}`}
174
- initialValue={phase.label || savedSubdomainLabel || agentNameSuggestion}
175
- placeholder="agent-name"
176
- validate={value => {
177
- const trimmed = value.trim()
178
- const label = sanitizeSubdomainPrefix(trimmed)
179
- if (!label) return 'Enter a subdomain label'
180
- if (trimmed.includes('.')) return 'Enter only the subdomain label'
181
- if (label !== trimmed.toLowerCase()) return 'Use lowercase letters, numbers, and hyphens only'
182
- return null
183
- }}
184
- onSubmit={value => runAdvancedSubdomainCheck(rootName, sanitizeSubdomainPrefix(value))}
185
- onCancel={() => setPhase({ kind: 'advanced-root', rootName })}
186
- />
187
- </Box>
188
- </Surface>
189
- )
190
- }
191
-
192
- if (phase.kind === 'advanced-subdomain-check') {
193
- return (
194
- <Surface
195
- title="Check Agent Subdomain"
196
- footer={footerHint('esc back')}
197
- >
198
- <Box flexDirection="column">
199
- <Text color={theme.dim}>Agent ENS: <Text color={theme.text}>{phase.label}.{phase.rootName}</Text></Text>
200
- <Text color={theme.dim}>Checking whether the subdomain is ready or needs the owner wallet to create it.</Text>
201
- </Box>
202
- <Box marginTop={1}>
203
- <Spinner label="checking agent subdomain..." />
204
- </Box>
205
- <EscCancel onCancel={() => setPhase({ kind: 'advanced-subdomain', rootName: phase.rootName, label: phase.label })} />
206
- </Surface>
207
- )
208
- }
209
-
210
- if (phase.kind === 'advanced-operator-wallet') {
211
- const { rootName, label } = phase
212
- return (
213
- <Surface
214
- title="Operator Wallet"
215
- footer={footerHint('enter select · esc back')}
216
- >
217
- <Box flexDirection="column">
218
- <Text color={theme.dim}>Agent ENS: <Text color={theme.text}>{label}.{rootName}</Text></Text>
219
- {phase.registryAction ? <Text color={theme.dim}>{advancedSubdomainStatusText(phase.registryAction)}</Text> : null}
220
- <Text color={theme.dim}>Choose the operator wallet for snapshot restore access and onchain ERC-8004 URI rotation via the Vault.</Text>
221
- <Text color={theme.dim}>The operator wallet has no authority over this ENS subdomain or any token transfer; the owner wallet is the sole signer for both.</Text>
222
- <Text color={theme.dim}>We only read the operator's address here so it can be added to the snapshot envelope and vault operator list later.</Text>
223
- {savedOperator ? <Text color={theme.dim}>Saved operator wallet: <Text color={theme.text}>{shortAddress(savedOperator)}</Text></Text> : null}
224
- {phase.error ? <Text color={theme.accentError}>{phase.error}</Text> : null}
225
- </Box>
226
- <Box marginTop={1}>
227
- <Select<'connect' | 'enter' | 'back'>
228
- options={[
229
- { value: 'connect', role: 'section', label: 'Operator Wallet' },
230
- { value: 'connect', label: 'Connect Wallet', hint: 'Connect the wallet that will be the operator' },
231
- { value: 'enter', label: 'Enter Wallet Address', hint: 'Paste the operator wallet address' },
232
- { value: 'back', role: 'section', label: 'Navigation' },
233
- { value: 'back', label: 'Back', hint: 'Return to subdomain', role: 'utility' },
234
- ]}
235
- hintLayout="inline"
236
- onSubmit={choice => {
237
- if (choice === 'connect') return connectOperatorWallet(rootName, label)
238
- if (choice === 'enter') return setPhase({ kind: 'advanced-operator-wallet-manual', rootName, label })
239
- return setPhase({ kind: 'advanced-subdomain', rootName, label })
240
- }}
241
- onCancel={() => setPhase({ kind: 'advanced-subdomain', rootName, label })}
242
- />
243
- </Box>
244
- </Surface>
245
- )
246
- }
247
-
248
- if (phase.kind === 'advanced-operator-wallet-manual') {
249
- const { rootName, label } = phase
250
- return (
251
- <Surface
252
- title="Operator Wallet"
253
- footer={footerHint('enter next · esc back')}
254
- >
255
- <Box flexDirection="column">
256
- <Text color={theme.dim}>The operator wallet is saved in ERC-8004 metadata for lookup and restore access.</Text>
257
- <Text color={theme.dim}>It gets no token approval or transfer right.</Text>
258
- <Text color={theme.dim}>Owner wallet signs the ENS and ERC-8004 transactions after this address is checked.</Text>
259
- <Text color={theme.dim}>Any future token move still starts with Prepare Token Transfer.</Text>
260
- {phase.error ? <Text color={theme.accentError}>{phase.error}</Text> : null}
261
- </Box>
262
- <Box marginTop={1}>
263
- <TextInput
264
- key="advanced-operator-wallet-manual"
265
- initialValue={savedOperator}
266
- placeholder="0x..."
267
- validate={value => /^0x[0-9a-fA-F]{40}$/.test(value.trim()) ? null : 'enter a valid 0x address'}
268
- onSubmit={value => runAdvancedPreflight(rootName, label, getAddress(value.trim()))}
269
- onCancel={() => setPhase({ kind: 'advanced-operator-wallet', rootName, label })}
270
- />
271
- </Box>
272
- </Surface>
273
- )
274
- }
275
-
276
- if (phase.kind === 'advanced-operator-wallet-connecting') {
277
- return (
278
- <WalletApprovalScreen
279
- title="Connect Wallet"
280
- subtitle="Connect the operator wallet only to read its address for ERC-8004 metadata. It does not sign or submit a transaction."
281
- walletSession={operatorWalletSession}
282
- label="waiting for wallet connection..."
283
- onCancel={() => setPhase({ kind: 'advanced-operator-wallet', rootName: phase.rootName, label: phase.label })}
284
- />
285
- )
286
- }
287
-
288
- if (phase.kind === 'advanced-preflight') {
289
- return (
290
- <Surface
291
- title="Check ENS Setup"
292
- footer={footerHint('esc back')}
293
- >
294
- <Box marginTop={1}>
295
- <Spinner label="checking ens setup..." />
296
- </Box>
297
- <EscCancel onCancel={() => setPhase({ kind: 'advanced-operator-wallet', rootName: phase.rootName, label: phase.label })} />
298
- </Surface>
299
- )
300
- }
301
-
302
- if (phase.kind === 'advanced-review') {
303
- return (
304
- <EnsSetupReviewScreen
305
- setup={phase.setup}
306
- currentEnsName={currentEnsName}
307
- currentMode={savedCustodyMode}
308
- registry={registry}
309
- onBegin={() => {
310
- if (phase.setup.txCount > 0) {
311
- onEnsSetup(phase.setup)
312
- return
313
- }
314
- onEnsLink(phase.setup.fullName, {
315
- mode: 'advanced',
316
- ownerAddress: phase.setup.ownerAddress,
317
- operatorWallet: phase.setup.operatorAddress,
318
- })
319
- }}
320
- onBack={() => setPhase({ kind: 'advanced-operator-wallet', rootName: phase.setup.rootName, label: phase.setup.label })}
321
- />
322
- )
323
- }
324
-
325
- if (phase.kind === 'advanced-manual') {
326
- return (
327
- <EnsSetupBlockedScreen
328
- fallback={phase.fallback}
329
- onCheckAgain={() => runAdvancedPreflight(phase.fallback.rootName, phase.fallback.label, phase.fallback.operatorAddress)}
330
- onBack={() => setPhase({ kind: 'advanced-operator-wallet', rootName: phase.fallback.rootName, label: phase.fallback.label })}
331
- />
332
- )
333
- }
334
-
335
- return null
336
- }
@@ -1,198 +0,0 @@
1
- import React from 'react'
2
- import { Box, Text } from 'ink'
3
- import type { Address } from 'viem'
4
- import { mainnet } from 'viem/chains'
5
- import { Surface } from '../../../../ui/Surface.js'
6
- import { useAppInput } from '../../../../app/input/AppInputProvider.js'
7
- import {
8
- createMainnetClient,
9
- } from '../../../ens/ensLookup.js'
10
- import {
11
- buildCommitment,
12
- encodeCommitTransaction,
13
- encodeRegisterTransaction,
14
- MIN_COMMIT_AGE_SECONDS,
15
- ONE_YEAR_SECONDS,
16
- } from '../../../ens/ensRegistration.js'
17
- import type { EnsSubdomainDeletePlan } from '../../../ens/ensAutomation.js'
18
- import {
19
- sendBrowserWalletTransaction,
20
- type BrowserWalletReady,
21
- } from '../../../wallet/browserWallet.js'
22
- import { WalletApprovalScreen } from '../../components/WalletApprovalScreen.js'
23
- import { footerHint } from './EnsEditShared.js'
24
- import type { RegisterCommitPhase } from './ensEditTypes.js'
25
- import { theme } from '../../../../ui/theme.js'
26
-
27
- export const EscCancel: React.FC<{ onCancel: () => void }> = ({ onCancel }) => {
28
- useAppInput((_input, key) => {
29
- if (key.escape) onCancel()
30
- })
31
- return null
32
- }
33
-
34
- export const RegisterRootCommitRunner: React.FC<{
35
- phase: RegisterCommitPhase
36
- ownerAddress: Address
37
- walletSession: BrowserWalletReady | null
38
- onWalletReady: (session: BrowserWalletReady | null) => void
39
- onCommitted: () => void
40
- onError: (msg: string) => void
41
- }> = ({ phase, ownerAddress, walletSession, onWalletReady, onCommitted, onError }) => {
42
- const startedRef = React.useRef(false)
43
- React.useEffect(() => {
44
- if (startedRef.current) return
45
- startedRef.current = true
46
- const built = buildCommitment({ label: phase.label, owner: ownerAddress, durationSeconds: BigInt(ONE_YEAR_SECONDS), secret: phase.secret })
47
- const tx = encodeCommitTransaction(built.commitment)
48
- sendBrowserWalletTransaction({
49
- chainId: mainnet.id,
50
- expectedAccount: ownerAddress,
51
- to: tx.to,
52
- data: tx.data,
53
- purpose: 'register-root-commit',
54
- onReady: ready => onWalletReady(ready),
55
- })
56
- .then(async result => {
57
- onWalletReady(null)
58
- const client = createMainnetClient()
59
- await client.waitForTransactionReceipt({ hash: result.txHash })
60
- onCommitted()
61
- })
62
- .catch((err: unknown) => {
63
- onWalletReady(null)
64
- onError(err instanceof Error ? err.message : String(err))
65
- })
66
- }, [])
67
- return (
68
- <WalletApprovalScreen
69
- title="Commit ENS Name"
70
- subtitle={`Submitting the first of two transactions for ${phase.label}.eth on Ethereum mainnet. Wait for confirmation.`}
71
- walletSession={walletSession}
72
- label="waiting for connected wallet transaction..."
73
- onCancel={() => onError('Commit cancelled. Restart from the name input.')}
74
- />
75
- )
76
- }
77
-
78
- export const RegisterRootWaitScreen: React.FC<{
79
- phase: RegisterCommitPhase & { commitMinedAt: number }
80
- onReady: () => void
81
- onCancel: () => void
82
- }> = ({ phase, onReady, onCancel }) => {
83
- const [, setTick] = React.useState(0)
84
- React.useEffect(() => {
85
- const interval = setInterval(() => setTick(t => t + 1), 500)
86
- return () => clearInterval(interval)
87
- }, [])
88
- const elapsedSec = Math.floor((Date.now() - phase.commitMinedAt) / 1000)
89
- const remaining = Math.max(0, MIN_COMMIT_AGE_SECONDS + 1 - elapsedSec)
90
- React.useEffect(() => {
91
- if (remaining === 0) onReady()
92
- }, [remaining])
93
- return (
94
- <Surface
95
- title="Commit Confirmed, Waiting"
96
- subtitle={`Anti-frontrun delay before registering ${phase.label}.eth. ENS requires a 60-second wait between commit and register.`}
97
- footer={footerHint('esc cancel registration')}
98
- >
99
- <Box marginTop={1} flexDirection="column">
100
- <Text color={theme.text}>{remaining > 0 ? `${remaining} seconds remaining...` : 'Ready, advancing to the registration transaction...'}</Text>
101
- <Text color={theme.dim}>Keep this window open. The commit expires after 24 hours if you walk away.</Text>
102
- </Box>
103
- <EscCancel onCancel={onCancel} />
104
- </Surface>
105
- )
106
- }
107
-
108
- export const DeleteSubdomainTxRunner: React.FC<{
109
- plan: EnsSubdomainDeletePlan
110
- ownerAddress: Address
111
- walletSession: BrowserWalletReady | null
112
- onWalletReady: (session: BrowserWalletReady | null) => void
113
- onDeleted: () => void
114
- onError: (msg: string) => void
115
- }> = ({ plan, ownerAddress, walletSession, onWalletReady, onDeleted, onError }) => {
116
- const startedRef = React.useRef(false)
117
- React.useEffect(() => {
118
- if (startedRef.current) return
119
- startedRef.current = true
120
- sendBrowserWalletTransaction({
121
- chainId: mainnet.id,
122
- expectedAccount: ownerAddress,
123
- to: plan.transaction.to,
124
- data: plan.transaction.data,
125
- purpose: 'delete-ens-subdomain',
126
- onReady: ready => onWalletReady(ready),
127
- })
128
- .then(async result => {
129
- onWalletReady(null)
130
- const client = createMainnetClient()
131
- await client.waitForTransactionReceipt({ hash: result.txHash })
132
- onDeleted()
133
- })
134
- .catch((err: unknown) => {
135
- onWalletReady(null)
136
- onError(err instanceof Error ? err.message : String(err))
137
- })
138
- }, [])
139
- return (
140
- <WalletApprovalScreen
141
- title="Delete ENS Subdomain"
142
- subtitle={`Clearing the subnode for ${plan.fullName} at ${plan.parentName} on Ethereum mainnet.`}
143
- walletSession={walletSession}
144
- label="waiting for owner wallet transaction..."
145
- onCancel={() => onError('Subdomain deletion cancelled.')}
146
- />
147
- )
148
- }
149
-
150
- export const RegisterRootTxRunner: React.FC<{
151
- phase: RegisterCommitPhase
152
- ownerAddress: Address
153
- walletSession: BrowserWalletReady | null
154
- onWalletReady: (session: BrowserWalletReady | null) => void
155
- onRegistered: () => void
156
- onError: (msg: string) => void
157
- }> = ({ phase, ownerAddress, walletSession, onWalletReady, onRegistered, onError }) => {
158
- const startedRef = React.useRef(false)
159
- React.useEffect(() => {
160
- if (startedRef.current) return
161
- startedRef.current = true
162
- const tx = encodeRegisterTransaction({
163
- label: phase.label,
164
- owner: ownerAddress,
165
- durationSeconds: BigInt(ONE_YEAR_SECONDS),
166
- secret: phase.secret,
167
- rentPrice: phase.price,
168
- })
169
- sendBrowserWalletTransaction({
170
- chainId: mainnet.id,
171
- expectedAccount: ownerAddress,
172
- to: tx.to,
173
- data: tx.data,
174
- value: tx.value,
175
- purpose: 'register-root-tx',
176
- onReady: ready => onWalletReady(ready),
177
- })
178
- .then(async result => {
179
- onWalletReady(null)
180
- const client = createMainnetClient()
181
- await client.waitForTransactionReceipt({ hash: result.txHash })
182
- onRegistered()
183
- })
184
- .catch((err: unknown) => {
185
- onWalletReady(null)
186
- onError(err instanceof Error ? err.message : String(err))
187
- })
188
- }, [])
189
- return (
190
- <WalletApprovalScreen
191
- title="Register ENS Name"
192
- subtitle={`Paying 1 year of rent and registering ${phase.label}.eth on Ethereum mainnet.`}
193
- walletSession={walletSession}
194
- label="waiting for connected wallet transaction..."
195
- onCancel={() => onError('Registration cancelled. The commit will expire in 24 hours; restart from the name input to retry.')}
196
- />
197
- )
198
- }