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,25 +1,25 @@
1
1
  import React from 'react'
2
2
  import { Box, Text } from 'ink'
3
3
  import type { Address } from 'viem'
4
- import { Surface } from '../../../../ui/Surface.js'
5
- import { Select } from '../../../../ui/Select.js'
6
- import { Spinner } from '../../../../ui/Spinner.js'
7
- import { theme } from '../../../../ui/theme.js'
8
- import type { BrowserWalletReady } from '../../../wallet/browserWallet.js'
4
+ import { Surface } from '../../../ui/Surface.js'
5
+ import { Select } from '../../../ui/Select.js'
6
+ import { Spinner } from '../../../ui/Spinner.js'
7
+ import { theme } from '../../../ui/theme.js'
8
+ import type { BrowserWalletReady } from '../../wallet/browserWallet.js'
9
9
  import {
10
10
  type CustodyMode,
11
- } from '../../model/custody.js'
12
- import { shortAddress } from '../../model/format.js'
11
+ } from '../custody/state.js'
12
+ import { shortAddress } from '../shared/model/format.js'
13
13
  import {
14
14
  emptyAgentEnsRecords,
15
15
  recordsHaveCurrentValues,
16
16
  unlinkEnsLinkOptions,
17
- } from './ensEditCopy.js'
17
+ } from './editCopy.js'
18
18
  import {
19
- AssignEnsCurrentSetup,
20
19
  EnsSetupRow,
21
20
  footerHint,
22
21
  } from './EnsEditShared.js'
22
+ import { IdentitySummary } from '../shared/components/IdentitySummary.js'
23
23
  import { UnlinkEnsReviewScreen } from './EnsEditReviewScreens.js'
24
24
  import {
25
25
  DeleteSubdomainTxRunner,
@@ -28,10 +28,11 @@ import {
28
28
  import type {
29
29
  EnsEditProps,
30
30
  EnsPhase,
31
- } from './ensEditTypes.js'
31
+ } from './types.js'
32
32
 
33
33
  type MaintenanceScreenProps = {
34
34
  phase: EnsPhase
35
+ identity: EnsEditProps['identity']
35
36
  currentEnsName: string
36
37
  currentEnsCanDelete: boolean
37
38
  savedCustodyMode: CustodyMode | undefined
@@ -49,11 +50,11 @@ type MaintenanceScreenProps = {
49
50
  onBack: () => void
50
51
  onEnsUnlink: EnsEditProps['onEnsUnlink']
51
52
  onEnsRecordsUpdate: EnsEditProps['onEnsRecordsUpdate']
52
- onManageOperatorWalletAccess: EnsEditProps['onManageOperatorWalletAccess']
53
53
  }
54
54
 
55
55
  export function renderEnsMaintenancePhase({
56
56
  phase,
57
+ identity,
57
58
  currentEnsName,
58
59
  currentEnsCanDelete,
59
60
  savedCustodyMode,
@@ -71,10 +72,9 @@ export function renderEnsMaintenancePhase({
71
72
  onBack,
72
73
  onEnsUnlink,
73
74
  onEnsRecordsUpdate,
74
- onManageOperatorWalletAccess,
75
75
  }: MaintenanceScreenProps): React.ReactNode | null {
76
76
  if (phase.kind === 'mode-select') {
77
- type EnsAction = 'link' | 'unlink' | 'delete-subdomain' | 'manage-operator-wallets' | 'back'
77
+ type EnsAction = 'link' | 'unlink' | 'back'
78
78
  const isAdvanced = savedCustodyMode === 'advanced'
79
79
  const multiNeedsCustodySetup = isAdvanced && !savedOwnerAddress
80
80
  const subtitle = currentEnsName
@@ -83,19 +83,11 @@ export function renderEnsMaintenancePhase({
83
83
  const linkHint = multiNeedsCustodySetup
84
84
  ? 'Set Advanced custody first via Custody Mode'
85
85
  : isAdvanced
86
- ? 'Walks you through Root, Name, Operator Wallet, Review, and Apply'
86
+ ? 'Walks you through Root, Name, Review, and Apply'
87
87
  : 'Walks you through Root, Name, Review, and Apply'
88
88
  const options: Array<{ value: EnsAction; role?: 'section' | 'utility'; label: string; hint?: string; disabled?: boolean }> = []
89
- options.push({ value: 'link', role: 'section', label: 'Name' })
90
89
  if (currentEnsName) {
91
90
  options.push({ value: 'unlink', label: 'Unlink Name', hint: 'Removes this name from the token. Set up a different name afterward by linking again.' })
92
- if (currentEnsCanDelete) {
93
- options.push({
94
- value: 'delete-subdomain',
95
- label: 'Delete Subdomain',
96
- hint: 'Clear the onchain subdomain entry at the parent name. The label is freed for reuse, and this token unlinks from it.',
97
- })
98
- }
99
91
  } else {
100
92
  options.push({
101
93
  value: 'link',
@@ -104,36 +96,16 @@ export function renderEnsMaintenancePhase({
104
96
  disabled: multiNeedsCustodySetup,
105
97
  })
106
98
  }
107
- if (isAdvanced && savedOwnerAddress) {
108
- options.push({ value: 'manage-operator-wallets', role: 'section', label: 'Operator Wallets' })
109
- options.push({
110
- value: 'manage-operator-wallets',
111
- label: 'Manage Operator Wallets',
112
- hint: "Authorize or revoke wallets that can update this name's token and profile text records on your behalf",
113
- })
114
- }
115
99
  options.push({ value: 'back', role: 'section', label: 'Navigation' })
116
100
  options.push({ value: 'back', label: 'Back', hint: 'Return to Identity Hub', role: 'utility' })
117
101
  return (
118
102
  <Surface
119
103
  title="ENS Name"
120
104
  subtitle={subtitle}
121
- footer={footerHint('picking an action starts a stepped flow · enter select · esc back')}
105
+ footer={footerHint('enter select · esc back')}
122
106
  >
123
- <AssignEnsCurrentSetup
124
- currentEnsName={currentEnsName}
125
- currentMode={savedCustodyMode}
126
- ownerAddress={savedOwnerAddress}
127
- operatorAddress={savedOperator}
128
- tokenNetworkLabel={registryNetworkLabel}
129
- />
130
- {!currentEnsName ? (
131
- <Box marginTop={1}>
132
- <Text color={theme.dim}>No subdomain linked yet. Discovery falls back to the token ID + network pair until one is set.</Text>
133
- </Box>
134
- ) : null}
135
- {validationError ? <Text color={theme.accentError}>{validationError}</Text> : null}
136
- <Box marginTop={1}>
107
+ {validationError ? <Box marginBottom={1}><Text color={theme.accentError}>{validationError}</Text></Box> : null}
108
+ <Box>
137
109
  <Select<EnsAction>
138
110
  options={options}
139
111
  hintLayout="inline"
@@ -143,14 +115,6 @@ export function renderEnsMaintenancePhase({
143
115
  runUnlinkEnsLoading(currentEnsName)
144
116
  return
145
117
  }
146
- if (choice === 'delete-subdomain' && currentEnsName && currentEnsCanDelete) {
147
- runDeleteSubdomainPreflight(currentEnsName)
148
- return
149
- }
150
- if (choice === 'manage-operator-wallets') {
151
- onManageOperatorWalletAccess()
152
- return
153
- }
154
118
  if (choice === 'link') {
155
119
  if (multiNeedsCustodySetup) return
156
120
  if (isAdvanced && savedOwnerAddress) {
@@ -175,9 +139,7 @@ export function renderEnsMaintenancePhase({
175
139
  subtitle={`Reading ethagent records from ${phase.fullName}`}
176
140
  footer={footerHint('esc back')}
177
141
  >
178
- <Box marginTop={1}>
179
- <Spinner label="reading current ENS record values..." />
180
- </Box>
142
+ <Spinner label="reading current ENS record values..." />
181
143
  <EscCancel onCancel={() => setPhase({ kind: 'mode-select' })} />
182
144
  </Surface>
183
145
  )
@@ -210,9 +172,7 @@ export function renderEnsMaintenancePhase({
210
172
  subtitle={`Verifying the parent of ${phase.fullName} on Ethereum mainnet.`}
211
173
  footer={footerHint('esc back')}
212
174
  >
213
- <Box marginTop={1}>
214
- <Spinner label="reading parent owner from ENS..." />
215
- </Box>
175
+ <Spinner label="reading parent owner from ENS..." />
216
176
  <EscCancel onCancel={() => setPhase({ kind: 'mode-select' })} />
217
177
  </Surface>
218
178
  )
@@ -225,10 +185,10 @@ export function renderEnsMaintenancePhase({
225
185
  subtitle={`Onchain check for ${phase.fullName} did not pass.`}
226
186
  footer={footerHint('enter select · esc back')}
227
187
  >
228
- <Box flexDirection="column" marginTop={1}>
188
+ <Box flexDirection="column" marginBottom={1}>
229
189
  <Text color={theme.accentError}>{phase.reason}</Text>
230
190
  </Box>
231
- <Box marginTop={1}>
191
+ <Box>
232
192
  <Select<'back'>
233
193
  options={[
234
194
  { value: 'back', role: 'section', label: 'Navigation' },
@@ -251,7 +211,7 @@ export function renderEnsMaintenancePhase({
251
211
  subtitle={`Clear the onchain entry for ${plan.fullName} at ${plan.parentName}.`}
252
212
  footer={footerHint('enter select · esc back')}
253
213
  >
254
- <Box flexDirection="column" marginTop={1}>
214
+ <Box flexDirection="column" marginBottom={1}>
255
215
  <EnsSetupRow label="Subdomain" value={plan.fullName} />
256
216
  <EnsSetupRow label="Parent" value={plan.parentName} />
257
217
  <EnsSetupRow label="Owner wallet" value={shortAddress(plan.parentOwnerAddress)} />
@@ -264,7 +224,7 @@ export function renderEnsMaintenancePhase({
264
224
  value="Onchain: subdomain owner and resolver set to 0. Locally: this token unlinks from the name."
265
225
  />
266
226
  </Box>
267
- <Box marginTop={1}>
227
+ <Box>
268
228
  <Select<'delete' | 'back'>
269
229
  options={[
270
230
  { value: 'delete', role: 'section', label: 'Action' },
@@ -310,10 +270,10 @@ export function renderEnsMaintenancePhase({
310
270
  subtitle={`${phase.fullName} is cleared onchain and unlinked from this token.`}
311
271
  footer={footerHint('enter select · esc back')}
312
272
  >
313
- <Box flexDirection="column" marginTop={1}>
273
+ <Box flexDirection="column" marginBottom={1}>
314
274
  <Text color={theme.text}>The label is freed for reuse on the parent name.</Text>
315
275
  </Box>
316
- <Box marginTop={1}>
276
+ <Box>
317
277
  <Select<'back'>
318
278
  options={[
319
279
  { value: 'back', role: 'section', label: 'Navigation' },
@@ -1,38 +1,38 @@
1
1
  import React from 'react'
2
2
  import { Box, Text } from 'ink'
3
3
  import { getAddress, type Address } from 'viem'
4
- import { Surface } from '../../../../ui/Surface.js'
5
- import { Select, type SelectOption } from '../../../../ui/Select.js'
6
- import { theme } from '../../../../ui/theme.js'
4
+ import { Surface } from '../../../ui/Surface.js'
5
+ import { Select, type SelectOption } from '../../../ui/Select.js'
6
+ import { theme } from '../../../ui/theme.js'
7
7
  import {
8
8
  formatRecordValue,
9
9
  recordLabel,
10
10
  type AgentEnsRecords,
11
11
  type AgentRecordDiff,
12
- } from '../../../ens/agentRecords.js'
13
- import type { EnsValidation } from '../../../ens/ensLookup.js'
12
+ } from '../../ens/agentRecords.js'
13
+ import type { EnsValidation } from '../../ens/ensLookup.js'
14
14
  import type {
15
15
  EnsSetupBlockedPlan,
16
16
  EnsSetupPlan,
17
- } from '../../../ens/ensAutomation.js'
18
- import { createErc8004PublicClient, type Erc8004RegistryConfig } from '../../../registry/erc8004.js'
17
+ } from '../../ens/ensAutomation.js'
18
+ import { createErc8004PublicClient, type Erc8004RegistryConfig } from '../../registry/erc8004.js'
19
19
  import {
20
20
  displayCustodyMode,
21
21
  type CustodyMode,
22
- } from '../../model/custody.js'
23
- import { ensValidationReasonText } from '../../model/ens.js'
24
- import { shortAddress } from '../../model/format.js'
22
+ } from '../custody/state.js'
23
+ import { ensValidationReasonText } from './state.js'
24
+ import { shortAddress } from '../shared/model/format.js'
25
25
  import {
26
26
  manualReasonTitle,
27
27
  modeSwitchHeading,
28
28
  setupSwitchNotice,
29
- } from './ensEditCopy.js'
29
+ } from './editCopy.js'
30
30
  import {
31
31
  EnsSetupRow,
32
32
  footerHint,
33
33
  renderRecordValue,
34
34
  } from './EnsEditShared.js'
35
- import type { EnsIssueValidation } from './ensEditTypes.js'
35
+ import type { EnsIssueValidation } from './types.js'
36
36
 
37
37
  type SimpleEnsIssueScreenProps = {
38
38
  fullName: string
@@ -113,19 +113,6 @@ export const EnsSetupReviewScreen: React.FC<EnsSetupReviewScreenProps> = ({
113
113
  }) => {
114
114
  type Action = 'begin' | 'back'
115
115
  const isSimple = setup.mode === 'simple'
116
- const [ownerIsSmartAccount, setOwnerIsSmartAccount] = React.useState(false)
117
- React.useEffect(() => {
118
- if (isSimple) return
119
- let cancelled = false
120
- const client = createErc8004PublicClient(registry)
121
- client.getBytecode({ address: getAddress(setup.ownerAddress) })
122
- .then(code => {
123
- if (cancelled) return
124
- if (code && code !== '0x') setOwnerIsSmartAccount(true)
125
- })
126
- .catch(() => {})
127
- return () => { cancelled = true }
128
- }, [isSimple, registry, setup.ownerAddress])
129
116
  const signerLabel = isSimple ? 'Connected wallet' : 'Owner wallet'
130
117
  const switchNotice = setupSwitchNotice(currentEnsName, currentMode, setup.fullName, setup.mode)
131
118
  const createLabel = setup.registryAction === 'create-subdomain'
@@ -160,11 +147,6 @@ export const EnsSetupReviewScreen: React.FC<EnsSetupReviewScreenProps> = ({
160
147
  <EnsSetupRow label="ENS network" value="Ethereum Mainnet" />
161
148
  <EnsSetupRow label="Signer wallet" value={`${shortAddress(setup.ownerAddress)} (${signerLabel.toLowerCase()})`} />
162
149
  <EnsSetupRow label="Registry action" value={createLabel} />
163
- {ownerIsSmartAccount ? (
164
- <Box marginTop={1}>
165
- <Text color={theme.accentError}>Owner wallet appears to be a smart account. Resolver delegation may require special handling depending on your wallet provider; if the operator wallet later cannot write ENS records, run "Fix Records" to retry.</Text>
166
- </Box>
167
- ) : null}
168
150
  </Box>
169
151
  <Box marginTop={1}>
170
152
  <Select<Action>
@@ -0,0 +1,62 @@
1
+ import React from 'react'
2
+ import type { Address } from 'viem'
3
+ import { mainnet } from 'viem/chains'
4
+ import { useAppInput } from '../../../app/input/AppInputProvider.js'
5
+ import {
6
+ createMainnetClient,
7
+ } from '../../ens/ensLookup.js'
8
+ import type { EnsSubdomainDeletePlan } from '../../ens/ensAutomation.js'
9
+ import {
10
+ sendBrowserWalletTransaction,
11
+ type BrowserWalletReady,
12
+ } from '../../wallet/browserWallet.js'
13
+ import { WalletApprovalScreen } from '../shared/components/WalletApprovalScreen.js'
14
+
15
+ export const EscCancel: React.FC<{ onCancel: () => void }> = ({ onCancel }) => {
16
+ useAppInput((_input, key) => {
17
+ if (key.escape) onCancel()
18
+ })
19
+ return null
20
+ }
21
+
22
+ export const DeleteSubdomainTxRunner: React.FC<{
23
+ plan: EnsSubdomainDeletePlan
24
+ ownerAddress: Address
25
+ walletSession: BrowserWalletReady | null
26
+ onWalletReady: (session: BrowserWalletReady | null) => void
27
+ onDeleted: () => void
28
+ onError: (msg: string) => void
29
+ }> = ({ plan, ownerAddress, walletSession, onWalletReady, onDeleted, onError }) => {
30
+ const startedRef = React.useRef(false)
31
+ React.useEffect(() => {
32
+ if (startedRef.current) return
33
+ startedRef.current = true
34
+ sendBrowserWalletTransaction({
35
+ chainId: mainnet.id,
36
+ expectedAccount: ownerAddress,
37
+ to: plan.transaction.to,
38
+ data: plan.transaction.data,
39
+ purpose: 'delete-ens-subdomain',
40
+ onReady: ready => onWalletReady(ready),
41
+ })
42
+ .then(async result => {
43
+ onWalletReady(null)
44
+ const client = createMainnetClient()
45
+ await client.waitForTransactionReceipt({ hash: result.txHash })
46
+ onDeleted()
47
+ })
48
+ .catch((err: unknown) => {
49
+ onWalletReady(null)
50
+ onError(err instanceof Error ? err.message : String(err))
51
+ })
52
+ }, [])
53
+ return (
54
+ <WalletApprovalScreen
55
+ title="Delete ENS Subdomain"
56
+ subtitle={`Clearing the subnode for ${plan.fullName} at ${plan.parentName} on Ethereum mainnet.`}
57
+ walletSession={walletSession}
58
+ label="waiting for owner wallet transaction..."
59
+ onCancel={() => onError('Subdomain deletion cancelled.')}
60
+ />
61
+ )
62
+ }
@@ -1,26 +1,26 @@
1
1
  import React from 'react'
2
2
  import { Box, Text } from 'ink'
3
3
  import type { Address } from 'viem'
4
- import { Surface } from '../../../../ui/Surface.js'
5
- import { TextInput } from '../../../../ui/TextInput.js'
6
- import { theme } from '../../../../ui/theme.js'
4
+ import { Surface } from '../../../ui/Surface.js'
5
+ import { TextInput } from '../../../ui/TextInput.js'
6
+ import { theme } from '../../../ui/theme.js'
7
7
  import {
8
8
  formatRecordValue,
9
9
  type AgentEnsRecordState,
10
- } from '../../../ens/agentRecords.js'
10
+ } from '../../ens/agentRecords.js'
11
11
  import {
12
12
  isEthDomain,
13
13
  sanitizeSubdomainPrefix,
14
- } from '../../../ens/ensLookup.js'
14
+ } from '../../ens/ensLookup.js'
15
15
  import {
16
16
  displayCustodyMode,
17
17
  readIdentityStateString,
18
18
  type CustodyMode,
19
- } from '../../model/custody.js'
20
- import { ensValidationReasonText } from '../../model/ens.js'
21
- import { shortAddress } from '../../model/format.js'
22
- import { readValidationFromState } from './ensEditCopy.js'
23
- import type { EnsEditProps } from './ensEditTypes.js'
19
+ } from '../custody/state.js'
20
+ import { ensValidationReasonText } from './state.js'
21
+ import { shortAddress } from '../shared/model/format.js'
22
+ import { readValidationFromState } from './editCopy.js'
23
+ import type { EnsEditProps } from './types.js'
24
24
 
25
25
  export const footerHint = (hint: string) => <Text color={theme.dim}>{hint}</Text>
26
26
 
@@ -123,13 +123,14 @@ export const AssignEnsCurrentSetup: React.FC<AssignEnsCurrentSetupProps> = ({
123
123
  type SubdomainEntryProps = {
124
124
  parent: string
125
125
  ownerAddress: Address
126
- suggestion: string
126
+ initialValue?: string
127
+ placeholder?: string
127
128
  error?: string
128
129
  onConfirm: (fullName: string) => void
129
130
  onBack: () => void
130
131
  }
131
132
 
132
- export const SubdomainEntry: React.FC<SubdomainEntryProps> = ({ parent, ownerAddress, suggestion, error, onConfirm, onBack }) => (
133
+ export const SubdomainEntry: React.FC<SubdomainEntryProps> = ({ parent, ownerAddress, initialValue, placeholder, error, onConfirm, onBack }) => (
133
134
  <Surface
134
135
  title={`Subdomain of ${parent}`}
135
136
  footer={footerHint('enter continues · esc back')}
@@ -139,8 +140,8 @@ export const SubdomainEntry: React.FC<SubdomainEntryProps> = ({ parent, ownerAdd
139
140
  {error ? <Text color={theme.accentError}>{error}</Text> : null}
140
141
  <TextInput
141
142
  key={`edit-ens-subdomain-${parent}`}
142
- initialValue={suggestion}
143
- placeholder="subdomain name"
143
+ initialValue={initialValue ?? ''}
144
+ placeholder={placeholder || 'subdomain name'}
144
145
  validate={value => {
145
146
  const v = sanitizeSubdomainPrefix(value)
146
147
  if (!v) return 'Subdomain name cannot be empty'