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.
- package/LICENSE +21 -21
- package/README.md +127 -29
- package/package.json +16 -9
- package/src/app/FirstRun.tsx +192 -146
- package/src/app/FirstRunTimeline.tsx +47 -0
- package/src/app/input/AppInputProvider.tsx +1 -1
- package/src/app/keybindings/KeybindingProvider.tsx +1 -1
- package/src/chat/ChatBottomPane.tsx +0 -1
- package/src/chat/ChatInput.tsx +6 -6
- package/src/chat/ChatScreen.tsx +43 -18
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +11 -17
- package/src/chat/ConversationStack.tsx +3 -0
- package/src/chat/CopyPicker.tsx +0 -1
- package/src/chat/MessageList.tsx +62 -45
- package/src/chat/PermissionPrompt.tsx +13 -9
- package/src/chat/PlanApprovalView.tsx +3 -3
- package/src/chat/ResumeView.tsx +1 -4
- package/src/chat/RewindView.tsx +2 -2
- package/src/chat/TranscriptView.tsx +6 -0
- package/src/chat/chatInputState.ts +1 -1
- package/src/chat/chatScreenUtils.ts +22 -11
- package/src/chat/chatSessionState.ts +2 -2
- package/src/chat/chatTurnOrchestrator.ts +16 -81
- package/src/chat/commands.ts +1 -1
- package/src/chat/textCursor.ts +1 -1
- package/src/chat/transcriptViewport.ts +2 -7
- package/src/cli/ResetConfirmView.tsx +1 -1
- package/src/cli/main.tsx +9 -3
- package/src/cli/preview.tsx +0 -5
- package/src/cli/updateNotice.ts +5 -3
- package/src/identity/continuity/editor.ts +7 -107
- package/src/identity/continuity/envelope.ts +1048 -40
- package/src/identity/continuity/history.ts +4 -4
- package/src/identity/continuity/localBackup.ts +249 -0
- package/src/identity/continuity/privateEdit/apply.ts +170 -0
- package/src/identity/continuity/privateEdit/diff.ts +82 -0
- package/src/identity/continuity/privateEdit/files.ts +23 -0
- package/src/identity/continuity/privateEdit/types.ts +28 -0
- package/src/identity/continuity/privateEdit.ts +10 -298
- package/src/identity/continuity/publicSkills.ts +8 -9
- package/src/identity/continuity/snapshots.ts +17 -6
- package/src/identity/continuity/storage/defaults.ts +111 -0
- package/src/identity/continuity/storage/files.ts +72 -0
- package/src/identity/continuity/storage/markdown.ts +81 -0
- package/src/identity/continuity/storage/paths.ts +24 -0
- package/src/identity/continuity/storage/scaffold.ts +124 -0
- package/src/identity/continuity/storage/status.ts +86 -0
- package/src/identity/continuity/storage/types.ts +27 -0
- package/src/identity/continuity/storage.ts +32 -507
- package/src/identity/continuity/zipWriter.ts +95 -0
- package/src/identity/crypto/backupEnvelope.ts +14 -247
- package/src/identity/crypto/eth.ts +7 -7
- package/src/identity/ens/agentRecords.ts +96 -0
- package/src/identity/ens/ensAutomation/contracts.ts +38 -0
- package/src/identity/ens/ensAutomation/delete.ts +80 -0
- package/src/identity/ens/ensAutomation/names.ts +14 -0
- package/src/identity/ens/ensAutomation/operators.ts +29 -0
- package/src/identity/ens/ensAutomation/read.ts +114 -0
- package/src/identity/ens/ensAutomation/root.ts +63 -0
- package/src/identity/ens/ensAutomation/setup.ts +284 -0
- package/src/identity/ens/ensAutomation/transactions.ts +107 -0
- package/src/identity/ens/ensAutomation/types.ts +126 -0
- package/src/identity/ens/ensAutomation.ts +29 -0
- package/src/identity/ens/ensLookup/client.ts +43 -0
- package/src/identity/ens/ensLookup/constants.ts +26 -0
- package/src/identity/ens/ensLookup/discovery.ts +70 -0
- package/src/identity/ens/ensLookup/names.ts +34 -0
- package/src/identity/ens/ensLookup/records.ts +45 -0
- package/src/identity/ens/ensLookup/resolve.ts +75 -0
- package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
- package/src/identity/ens/ensLookup/types.ts +38 -0
- package/src/identity/ens/ensLookup/validation.ts +72 -0
- package/src/identity/ens/ensLookup.ts +19 -0
- package/src/identity/ens/ensRegistration.ts +199 -0
- package/src/identity/ens/resolverDelegation.ts +48 -0
- package/src/identity/hub/IdentityHub.tsx +13 -815
- package/src/identity/hub/OperationalRoutes.tsx +370 -0
- package/src/identity/hub/Routes.tsx +361 -0
- package/src/identity/hub/advancedEnsValidation.ts +45 -0
- package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
- package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
- package/src/identity/hub/components/FlowTimeline.tsx +27 -0
- package/src/identity/hub/components/IdentitySummary.tsx +190 -0
- package/src/identity/hub/components/MenuScreen.tsx +237 -0
- package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
- package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
- package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
- package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
- package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
- package/src/identity/hub/effects/create.ts +310 -0
- package/src/identity/hub/effects/ens/flows.ts +218 -0
- package/src/identity/hub/effects/ens/index.ts +11 -0
- package/src/identity/hub/effects/ens/transactions.ts +239 -0
- package/src/identity/hub/effects/index.ts +74 -0
- package/src/identity/hub/effects/profile/profileState.ts +173 -0
- package/src/identity/hub/effects/publicProfile/index.ts +5 -0
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
- package/src/identity/hub/effects/rebackup/index.ts +7 -0
- package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
- package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
- package/src/identity/hub/effects/receipts.ts +46 -0
- package/src/identity/hub/effects/restore/apply.ts +112 -0
- package/src/identity/hub/effects/restore/auth.ts +159 -0
- package/src/identity/hub/effects/restore/discover.ts +86 -0
- package/src/identity/hub/effects/restore/envelopes.ts +21 -0
- package/src/identity/hub/effects/restore/fetch.ts +25 -0
- package/src/identity/hub/effects/restore/index.ts +22 -0
- package/src/identity/hub/effects/restore/recovery.ts +135 -0
- package/src/identity/hub/effects/restore/resolve.ts +102 -0
- package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
- package/src/identity/hub/effects/restore/shared.ts +91 -0
- package/src/identity/hub/effects/restoreAdmin.ts +93 -0
- package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
- package/src/identity/hub/effects/shared/snapshot.ts +336 -0
- package/src/identity/hub/effects/shared/sync.ts +190 -0
- package/src/identity/hub/effects/token-transfer/index.ts +6 -0
- package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
- package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
- package/src/identity/hub/effects/types.ts +53 -0
- package/src/identity/hub/effects/vault/preflight.ts +50 -0
- package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
- package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
- package/src/identity/hub/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
- package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
- package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
- package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
- package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
- package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
- package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
- package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
- package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
- package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
- package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
- package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
- package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
- package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
- package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
- package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
- package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
- package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
- package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
- package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
- package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
- package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +25 -43
- package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
- package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
- package/src/identity/hub/identityHubReducer.ts +166 -101
- package/src/identity/hub/model/continuity.ts +94 -0
- package/src/identity/hub/model/copy.ts +35 -0
- package/src/identity/hub/model/custody.ts +54 -0
- package/src/identity/hub/model/ens.ts +49 -0
- package/src/identity/hub/model/errors.ts +140 -0
- package/src/identity/hub/model/format.ts +15 -0
- package/src/identity/hub/model/identity.ts +94 -0
- package/src/identity/hub/model/network.ts +32 -0
- package/src/identity/hub/model/transfer.ts +57 -0
- package/src/identity/hub/operatorWallets.ts +131 -0
- package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
- package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
- package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
- package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
- package/src/identity/hub/reconciliation/index.ts +21 -0
- package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
- package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
- package/src/identity/hub/txGuard.ts +51 -0
- package/src/identity/hub/types.ts +17 -0
- package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
- package/src/identity/hub/useIdentityHubController.ts +396 -0
- package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
- package/src/identity/hub/utils.ts +79 -0
- package/src/identity/identityCompat.ts +34 -0
- package/src/identity/profile/agentIcon.ts +61 -0
- package/src/identity/profile/imagePicker.ts +12 -12
- package/src/identity/registry/erc8004/abi.ts +14 -0
- package/src/identity/registry/erc8004/chains.ts +150 -0
- package/src/identity/registry/erc8004/client.ts +11 -0
- package/src/identity/registry/erc8004/discovery.ts +511 -0
- package/src/identity/registry/erc8004/metadata.ts +335 -0
- package/src/identity/registry/erc8004/ownership.ts +121 -0
- package/src/identity/registry/erc8004/preflight.ts +123 -0
- package/src/identity/registry/erc8004/transactions.ts +77 -0
- package/src/identity/registry/erc8004/types.ts +88 -0
- package/src/identity/registry/erc8004/uri.ts +59 -0
- package/src/identity/registry/erc8004/utils.ts +58 -0
- package/src/identity/registry/erc8004.ts +53 -1106
- package/src/identity/registry/fieldParsers.ts +28 -0
- package/src/identity/registry/operatorVault/bytecode.ts +98 -0
- package/src/identity/registry/operatorVault/constants.ts +38 -0
- package/src/identity/registry/operatorVault/read.ts +246 -0
- package/src/identity/registry/operatorVault/transactions.ts +81 -0
- package/src/identity/registry/operatorVault.ts +44 -0
- package/src/identity/storage/ipfs.ts +26 -24
- package/src/identity/wallet/browserWallet/gas.ts +41 -0
- package/src/identity/wallet/browserWallet/html.ts +106 -0
- package/src/identity/wallet/browserWallet/http.ts +28 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
- package/src/identity/wallet/browserWallet/requests.ts +191 -0
- package/src/identity/wallet/browserWallet/session.ts +325 -0
- package/src/identity/wallet/browserWallet/types.ts +192 -0
- package/src/identity/wallet/browserWallet/validation.ts +74 -0
- package/src/identity/wallet/browserWallet.ts +30 -393
- package/src/identity/wallet/page/constants.ts +5 -0
- package/src/identity/wallet/page/controller.ts +251 -0
- package/src/identity/wallet/page/copy.ts +340 -0
- package/src/identity/wallet/page/grainient.ts +278 -0
- package/src/identity/wallet/page/html.ts +28 -0
- package/src/identity/wallet/page/markup.ts +50 -0
- package/src/identity/wallet/page/state.ts +9 -0
- package/src/identity/wallet/page/styles/base.ts +259 -0
- package/src/identity/wallet/page/styles/components.ts +262 -0
- package/src/identity/wallet/page/styles/index.ts +5 -0
- package/src/identity/wallet/page/styles/responsive.ts +247 -0
- package/src/identity/wallet/page/types.ts +47 -0
- package/src/identity/wallet/page/view.ts +535 -0
- package/src/identity/wallet/page/walletProvider.ts +70 -0
- package/src/identity/wallet/page.tsx +38 -0
- package/src/identity/wallet/walletPurposeCompat.ts +27 -0
- package/src/mcp/manager.ts +0 -1
- package/src/models/ModelPicker.tsx +36 -30
- package/src/models/catalog.ts +5 -2
- package/src/models/huggingface.ts +9 -9
- package/src/models/llamacpp.ts +13 -13
- package/src/models/modelDisplay.ts +75 -0
- package/src/models/modelPickerOptions.ts +16 -3
- package/src/models/modelRecommendation.ts +0 -1
- package/src/providers/errors.ts +16 -0
- package/src/providers/gemini.ts +252 -39
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -1
- package/src/runtime/sessionMode.ts +1 -1
- package/src/runtime/systemPrompt.ts +2 -0
- package/src/runtime/toolExecution.ts +18 -22
- package/src/runtime/toolIntent.ts +0 -20
- package/src/runtime/turn.ts +0 -92
- package/src/storage/atomicWrite.ts +4 -1
- package/src/storage/config.ts +181 -5
- package/src/storage/identity.ts +9 -3
- package/src/storage/secrets.ts +2 -2
- package/src/tools/bashSafety.ts +8 -0
- package/src/tools/changeDirectoryTool.ts +1 -1
- package/src/tools/deleteFileTool.ts +4 -4
- package/src/tools/editTool.ts +4 -4
- package/src/tools/editUtils.ts +5 -5
- package/src/tools/privateContinuityEditTool.ts +4 -5
- package/src/tools/privateContinuityReadTool.ts +1 -2
- package/src/tools/registry.ts +30 -0
- package/src/tools/writeFileTool.ts +5 -5
- package/src/ui/BrandSplash.tsx +20 -85
- package/src/ui/ProgressBar.tsx +3 -5
- package/src/ui/Select.tsx +21 -9
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +3 -3
- package/src/ui/TextInput.tsx +191 -29
- package/src/ui/theme.ts +7 -34
- package/src/utils/openExternal.ts +21 -0
- package/src/utils/withRetry.ts +47 -3
- package/src/identity/hub/identityHubEffects.ts +0 -937
- package/src/identity/hub/identityHubModel.ts +0 -291
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
- package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
- package/src/identity/wallet/wallet-page/wallet.html +0 -1202
- /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPublicClient,
|
|
3
|
+
encodeFunctionData,
|
|
4
|
+
fallback,
|
|
5
|
+
getAddress,
|
|
6
|
+
http,
|
|
7
|
+
namehash,
|
|
8
|
+
type Address,
|
|
9
|
+
type Hex,
|
|
10
|
+
type PublicClient,
|
|
11
|
+
} from 'viem'
|
|
12
|
+
import { mainnet } from 'viem/chains'
|
|
13
|
+
import type { EthagentIdentity } from '../../../storage/config.js'
|
|
14
|
+
import type { Erc8004RegistryConfig } from '../../registry/erc8004.js'
|
|
15
|
+
import { ENS_AUTOMATION_RESOLVER_ABI } from '../../ens/ensAutomation.js'
|
|
16
|
+
import { encodeApprove, encodeApprovalRevoke, readDelegation } from '../../ens/resolverDelegation.js'
|
|
17
|
+
import { readResolverAddress } from '../../ens/ensLookup.js'
|
|
18
|
+
import { normalizeApprovedOperatorWallets } from '../operatorWallets.js'
|
|
19
|
+
import { readCustodyMode } from '../model/custody.js'
|
|
20
|
+
import { readOwnerAddressField } from '../../identityCompat.js'
|
|
21
|
+
|
|
22
|
+
export type ApprovalDiff = {
|
|
23
|
+
added: Address[]
|
|
24
|
+
removed: Address[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function computeApprovalDiff(
|
|
28
|
+
beforeApproved: ReadonlyArray<{ address: string }>,
|
|
29
|
+
afterApproved: ReadonlyArray<{ address: string }>,
|
|
30
|
+
): ApprovalDiff {
|
|
31
|
+
const before = new Set(beforeApproved.map(record => record.address.toLowerCase()))
|
|
32
|
+
const after = new Set(afterApproved.map(record => record.address.toLowerCase()))
|
|
33
|
+
const added: Address[] = []
|
|
34
|
+
const removed: Address[] = []
|
|
35
|
+
for (const record of afterApproved) {
|
|
36
|
+
if (!before.has(record.address.toLowerCase())) {
|
|
37
|
+
added.push(getAddress(record.address))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const record of beforeApproved) {
|
|
41
|
+
if (!after.has(record.address.toLowerCase())) {
|
|
42
|
+
removed.push(getAddress(record.address))
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { added, removed }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type ResolverApprovalCalls = {
|
|
49
|
+
resolverAddress: Address
|
|
50
|
+
data: Hex
|
|
51
|
+
calls: Hex[]
|
|
52
|
+
added: Address[]
|
|
53
|
+
removed: Address[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function encodeResolverApprovalChanges(args: {
|
|
57
|
+
ensName: string
|
|
58
|
+
diff: ApprovalDiff
|
|
59
|
+
}): Promise<ResolverApprovalCalls | null> {
|
|
60
|
+
if (args.diff.added.length === 0 && args.diff.removed.length === 0) return null
|
|
61
|
+
const resolverAddress = await readResolverAddress(args.ensName)
|
|
62
|
+
if (!resolverAddress) {
|
|
63
|
+
throw new Error(`no resolver set on ${args.ensName} - finish ENS subdomain setup first`)
|
|
64
|
+
}
|
|
65
|
+
const node = namehash(args.ensName)
|
|
66
|
+
const calls: Hex[] = []
|
|
67
|
+
for (const address of args.diff.added) calls.push(encodeApprove(node, address))
|
|
68
|
+
for (const address of args.diff.removed) calls.push(encodeApprovalRevoke(node, address))
|
|
69
|
+
const data = calls.length === 1
|
|
70
|
+
? calls[0]!
|
|
71
|
+
: encodeFunctionData({
|
|
72
|
+
abi: ENS_AUTOMATION_RESOLVER_ABI,
|
|
73
|
+
functionName: 'multicall',
|
|
74
|
+
args: [calls],
|
|
75
|
+
})
|
|
76
|
+
return {
|
|
77
|
+
resolverAddress,
|
|
78
|
+
data,
|
|
79
|
+
calls,
|
|
80
|
+
added: args.diff.added,
|
|
81
|
+
removed: args.diff.removed,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type RecordsFixPlanItem =
|
|
86
|
+
| { kind: 'missing-approval'; address: Address }
|
|
87
|
+
| { kind: 'stale-approval'; address: Address }
|
|
88
|
+
| { kind: 'no-resolver' }
|
|
89
|
+
| { kind: 'no-ens' }
|
|
90
|
+
|
|
91
|
+
export type RecordsFixPlan = {
|
|
92
|
+
identityAgentId: string
|
|
93
|
+
ensName: string | undefined
|
|
94
|
+
items: RecordsFixPlanItem[]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
type ReconcileArgs = {
|
|
98
|
+
identity: EthagentIdentity
|
|
99
|
+
registry: Erc8004RegistryConfig
|
|
100
|
+
client?: Pick<PublicClient, 'readContract'>
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function reconcileWalletSetup(
|
|
104
|
+
args: ReconcileArgs,
|
|
105
|
+
): Promise<RecordsFixPlan> {
|
|
106
|
+
const baseState = (args.identity.state ?? {}) as Record<string, unknown>
|
|
107
|
+
const ensName = typeof baseState.ensName === 'string' ? baseState.ensName.trim() : ''
|
|
108
|
+
const custodyMode = readCustodyMode(baseState)
|
|
109
|
+
const approved = normalizeApprovedOperatorWallets(baseState.approvedOperatorWallets)
|
|
110
|
+
const items: RecordsFixPlanItem[] = []
|
|
111
|
+
|
|
112
|
+
if (custodyMode !== 'advanced' || approved.length === 0) {
|
|
113
|
+
return { identityAgentId: String(args.identity.agentId ?? ''), ensName: ensName || undefined, items }
|
|
114
|
+
}
|
|
115
|
+
if (!ensName) {
|
|
116
|
+
items.push({ kind: 'no-ens' })
|
|
117
|
+
return { identityAgentId: String(args.identity.agentId ?? ''), ensName: undefined, items }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let resolverAddress: Address | null
|
|
121
|
+
try {
|
|
122
|
+
resolverAddress = await readResolverAddress(ensName)
|
|
123
|
+
} catch {
|
|
124
|
+
resolverAddress = null
|
|
125
|
+
}
|
|
126
|
+
if (!resolverAddress) {
|
|
127
|
+
items.push({ kind: 'no-resolver' })
|
|
128
|
+
return { identityAgentId: String(args.identity.agentId ?? ''), ensName, items }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const ownerAddress = readOwnerAddressField(baseState) ?? args.identity.ownerAddress ?? args.identity.address
|
|
132
|
+
const node = namehash(ensName)
|
|
133
|
+
|
|
134
|
+
for (const record of approved) {
|
|
135
|
+
const isApproved = await readDelegation({
|
|
136
|
+
client: args.client ?? createReadClient(),
|
|
137
|
+
resolverAddress,
|
|
138
|
+
ownerAddress: getAddress(ownerAddress),
|
|
139
|
+
node,
|
|
140
|
+
delegateAddress: getAddress(record.address),
|
|
141
|
+
})
|
|
142
|
+
if (!isApproved) {
|
|
143
|
+
items.push({ kind: 'missing-approval', address: getAddress(record.address) })
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return { identityAgentId: String(args.identity.agentId ?? ''), ensName, items }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function createReadClient(): Pick<PublicClient, 'readContract'> {
|
|
151
|
+
const transports = [
|
|
152
|
+
http('https://ethereum.publicnode.com', { retryCount: 0, timeout: 8_000 }),
|
|
153
|
+
http('https://eth.llamarpc.com', { retryCount: 0, timeout: 8_000 }),
|
|
154
|
+
]
|
|
155
|
+
return createPublicClient({
|
|
156
|
+
chain: mainnet,
|
|
157
|
+
transport: fallback(transports, { retryCount: 0 }),
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
type VerifyResolverApprovalsArgs = {
|
|
162
|
+
ensName: string
|
|
163
|
+
ownerAddress: Address
|
|
164
|
+
resolverAddress: Address
|
|
165
|
+
added: ReadonlyArray<Address>
|
|
166
|
+
removed: ReadonlyArray<Address>
|
|
167
|
+
client?: Pick<PublicClient, 'readContract'>
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function verifyResolverApprovalsLanded(args: VerifyResolverApprovalsArgs): Promise<void> {
|
|
171
|
+
if (args.added.length === 0 && args.removed.length === 0) return
|
|
172
|
+
const client = args.client ?? createReadClient()
|
|
173
|
+
const node = namehash(args.ensName)
|
|
174
|
+
const owner = getAddress(args.ownerAddress)
|
|
175
|
+
for (const address of args.added) {
|
|
176
|
+
const delegate = getAddress(address)
|
|
177
|
+
const isApproved = await readDelegation({
|
|
178
|
+
client,
|
|
179
|
+
resolverAddress: args.resolverAddress,
|
|
180
|
+
ownerAddress: owner,
|
|
181
|
+
node,
|
|
182
|
+
delegateAddress: delegate,
|
|
183
|
+
})
|
|
184
|
+
if (!isApproved) {
|
|
185
|
+
throw new Error(`Resolver delegation didn't land for operator wallet ${delegate}. Your wallet may have rejected the inner operation; retry by selecting Fix Records again.`)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const address of args.removed) {
|
|
189
|
+
const delegate = getAddress(address)
|
|
190
|
+
const isApproved = await readDelegation({
|
|
191
|
+
client,
|
|
192
|
+
resolverAddress: args.resolverAddress,
|
|
193
|
+
ownerAddress: owner,
|
|
194
|
+
node,
|
|
195
|
+
delegateAddress: delegate,
|
|
196
|
+
})
|
|
197
|
+
if (isApproved) {
|
|
198
|
+
throw new Error(`Resolver revocation didn't land for operator wallet ${delegate}. Your wallet may have rejected the inner operation; retry by selecting Fix Records again.`)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function fixPlanRequiresOwnerWallet(plan: RecordsFixPlan): boolean {
|
|
204
|
+
return plan.items.some(item =>
|
|
205
|
+
item.kind === 'missing-approval' || item.kind === 'stale-approval',
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function describeFixPlanItem(item: RecordsFixPlanItem): string {
|
|
210
|
+
switch (item.kind) {
|
|
211
|
+
case 'missing-approval':
|
|
212
|
+
return `operator wallet ${item.address} has no onchain resolver approval, record writes will be rejected`
|
|
213
|
+
case 'stale-approval':
|
|
214
|
+
return `operator wallet ${item.address} has an onchain approval that no longer matches the operator set`
|
|
215
|
+
case 'no-resolver':
|
|
216
|
+
return 'ENS subdomain has no resolver set yet, finish ENS setup before authorizing operator wallets onchain'
|
|
217
|
+
case 'no-ens':
|
|
218
|
+
return 'advanced mode requires an ENS subdomain so operator wallets can sign profile updates onchain'
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type TxGuardKind =
|
|
2
|
+
| 'rebackup'
|
|
3
|
+
| 'public-profile'
|
|
4
|
+
| 'vault-deploy'
|
|
5
|
+
| 'vault-deposit'
|
|
6
|
+
| 'vault-unwrap'
|
|
7
|
+
| 'vault-withdraw'
|
|
8
|
+
|
|
9
|
+
export class TxGuardBusyError extends Error {
|
|
10
|
+
readonly kind: TxGuardKind
|
|
11
|
+
constructor(kind: TxGuardKind, message: string) {
|
|
12
|
+
super(message)
|
|
13
|
+
this.name = 'TxGuardBusyError'
|
|
14
|
+
this.kind = kind
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const inFlight = new Set<TxGuardKind>()
|
|
19
|
+
|
|
20
|
+
export function isTxGuardBusy(kind: TxGuardKind): boolean {
|
|
21
|
+
return inFlight.has(kind)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function acquireTxGuard(kind: TxGuardKind): void {
|
|
25
|
+
inFlight.add(kind)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function releaseTxGuard(kind: TxGuardKind): void {
|
|
29
|
+
inFlight.delete(kind)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function resetTxGuardForTest(): void {
|
|
33
|
+
inFlight.clear()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function txGuardBusyMessage(kind: TxGuardKind): string {
|
|
37
|
+
switch (kind) {
|
|
38
|
+
case 'rebackup':
|
|
39
|
+
return 'A snapshot save is already in flight. Wait for it to complete before retrying.'
|
|
40
|
+
case 'public-profile':
|
|
41
|
+
return 'A public profile update is already in flight. Wait for it to complete before retrying.'
|
|
42
|
+
case 'vault-deploy':
|
|
43
|
+
return 'A vault deploy transaction is already in flight. Wait for it to complete before retrying.'
|
|
44
|
+
case 'vault-deposit':
|
|
45
|
+
return 'A vault deposit transaction is already in flight. Wait for it to complete before retrying.'
|
|
46
|
+
case 'vault-unwrap':
|
|
47
|
+
return 'A vault unwrap transaction is already in flight. Wait for it to complete before retrying.'
|
|
48
|
+
case 'vault-withdraw':
|
|
49
|
+
return 'A token withdraw transaction is already in flight. Wait for it to complete before retrying.'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EthagentConfig, EthagentIdentity } from '../../storage/config.js'
|
|
2
|
+
|
|
3
|
+
export type IdentityHubResult =
|
|
4
|
+
| { kind: 'token'; identity: EthagentIdentity }
|
|
5
|
+
| { kind: 'updated'; config: EthagentConfig; message: string }
|
|
6
|
+
| { kind: 'skip' }
|
|
7
|
+
| { kind: 'cancel' }
|
|
8
|
+
|
|
9
|
+
export type IdentityHubInitialAction = 'create' | 'load' | 'settings' | 'save-snapshot' | 'save-prompt'
|
|
10
|
+
|
|
11
|
+
export type IdentityHubProps = {
|
|
12
|
+
mode: 'first-run' | 'manage'
|
|
13
|
+
config?: EthagentConfig
|
|
14
|
+
initialAction?: IdentityHubInitialAction
|
|
15
|
+
onComplete: (result: IdentityHubResult) => void
|
|
16
|
+
onConfigChange?: (config: EthagentConfig) => void
|
|
17
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import type { EthagentIdentity } from '../../storage/config.js'
|
|
3
|
+
import { catFromIpfs, DEFAULT_IPFS_API_URL } from '../storage/ipfs.js'
|
|
4
|
+
import {
|
|
5
|
+
continuityVaultRef,
|
|
6
|
+
continuityVaultStatus,
|
|
7
|
+
continuityWorkingTreeStatus,
|
|
8
|
+
ensurePublicSkillsFile,
|
|
9
|
+
type ContinuityWorkingTreeStatus,
|
|
10
|
+
} from '../continuity/storage.js'
|
|
11
|
+
import { openFileInEditor } from '../continuity/editor.js'
|
|
12
|
+
import { exportLocalBackup } from '../continuity/localBackup.js'
|
|
13
|
+
import { listPublishedContinuitySnapshots } from '../continuity/snapshots.js'
|
|
14
|
+
import type { Step } from './identityHubReducer.js'
|
|
15
|
+
|
|
16
|
+
type UseIdentityHubContinuityArgs = {
|
|
17
|
+
identity: EthagentIdentity | undefined
|
|
18
|
+
step: Step
|
|
19
|
+
setStep: (step: Step) => void
|
|
20
|
+
handleStepError: (err: unknown, backStep: Step, softCancel?: Step) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useIdentityHubContinuity({
|
|
24
|
+
identity,
|
|
25
|
+
step,
|
|
26
|
+
setStep,
|
|
27
|
+
handleStepError,
|
|
28
|
+
}: UseIdentityHubContinuityArgs): {
|
|
29
|
+
continuityReady: boolean
|
|
30
|
+
setContinuityReady: (ready: boolean) => void
|
|
31
|
+
workingStatus: ContinuityWorkingTreeStatus | null
|
|
32
|
+
openContinuityFile: (kind: 'soul' | 'memory' | 'skills') => Promise<void>
|
|
33
|
+
exportLocalBackupZip: () => Promise<void>
|
|
34
|
+
} {
|
|
35
|
+
const [continuityReady, setContinuityReady] = useState<boolean>(false)
|
|
36
|
+
const [workingStatus, setWorkingStatus] = useState<ContinuityWorkingTreeStatus | null>(null)
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
let cancelled = false
|
|
40
|
+
if (!identity) {
|
|
41
|
+
setContinuityReady(false)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if (!step.kind.startsWith('continuity') && step.kind !== 'details' && step.kind !== 'menu') return
|
|
45
|
+
continuityVaultStatus(identity)
|
|
46
|
+
.then(status => { if (!cancelled) setContinuityReady(status.ready) })
|
|
47
|
+
.catch(() => { if (!cancelled) setContinuityReady(false) })
|
|
48
|
+
return () => { cancelled = true }
|
|
49
|
+
}, [identity, step.kind])
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
let cancelled = false
|
|
53
|
+
if (!identity) return
|
|
54
|
+
if (
|
|
55
|
+
step.kind !== 'menu'
|
|
56
|
+
&& step.kind !== 'continuity-private'
|
|
57
|
+
&& step.kind !== 'continuity-public'
|
|
58
|
+
&& step.kind !== 'save-prompt'
|
|
59
|
+
&& step.kind !== 'rebackup-confirm'
|
|
60
|
+
) return
|
|
61
|
+
|
|
62
|
+
const checkStatus = async () => {
|
|
63
|
+
try {
|
|
64
|
+
const [latest] = await listPublishedContinuitySnapshots(identity, 1)
|
|
65
|
+
const status = await continuityWorkingTreeStatus(identity, latest)
|
|
66
|
+
if (cancelled) return
|
|
67
|
+
setWorkingStatus(status)
|
|
68
|
+
} catch {
|
|
69
|
+
if (cancelled) return
|
|
70
|
+
setWorkingStatus(null)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
void checkStatus()
|
|
75
|
+
|
|
76
|
+
return () => {
|
|
77
|
+
cancelled = true
|
|
78
|
+
}
|
|
79
|
+
}, [identity, step.kind])
|
|
80
|
+
|
|
81
|
+
const openContinuityFile = async (kind: 'soul' | 'memory' | 'skills'): Promise<void> => {
|
|
82
|
+
if (!identity) return
|
|
83
|
+
try {
|
|
84
|
+
if (kind === 'skills') {
|
|
85
|
+
await ensurePublicSkillsFile(identity, {
|
|
86
|
+
fallback: () => readPublishedPublicSkills(identity),
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
const ref = continuityVaultRef(identity)
|
|
90
|
+
const file = kind === 'soul' ? ref.soulPath : kind === 'memory' ? ref.memoryPath : ref.publicSkillsPath
|
|
91
|
+
const result = await openFileInEditor(file)
|
|
92
|
+
const displayName = kind === 'soul' ? 'SOUL.md' : kind === 'memory' ? 'MEMORY.md' : 'skills.json'
|
|
93
|
+
const message = result.ok
|
|
94
|
+
? `opened ${displayName} with ${result.method}.`
|
|
95
|
+
: `open failed: ${result.error}`
|
|
96
|
+
setStep({ kind: 'continuity-private', notice: message, editorOpened: result.ok })
|
|
97
|
+
} catch (err: unknown) {
|
|
98
|
+
handleStepError(err, { kind: 'continuity-private' })
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const exportLocalBackupZip = async (): Promise<void> => {
|
|
103
|
+
if (!identity) return
|
|
104
|
+
try {
|
|
105
|
+
await ensurePublicSkillsFile(identity, {
|
|
106
|
+
fallback: () => readPublishedPublicSkills(identity),
|
|
107
|
+
})
|
|
108
|
+
const result = await exportLocalBackup(identity)
|
|
109
|
+
const message = result.ok
|
|
110
|
+
? `Saved local backup to ${result.path}`
|
|
111
|
+
: result.cancelled
|
|
112
|
+
? 'Backup cancelled'
|
|
113
|
+
: `Backup failed: ${result.error}`
|
|
114
|
+
setStep({ kind: 'continuity-private', notice: message })
|
|
115
|
+
} catch (err: unknown) {
|
|
116
|
+
handleStepError(err, { kind: 'continuity-private' })
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
continuityReady,
|
|
122
|
+
setContinuityReady,
|
|
123
|
+
workingStatus,
|
|
124
|
+
openContinuityFile,
|
|
125
|
+
exportLocalBackupZip,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function readPublishedPublicSkills(identity: EthagentIdentity): Promise<string> {
|
|
130
|
+
const cid = identity.publicSkills?.cid
|
|
131
|
+
if (!cid) throw new Error('No saved public skills CID')
|
|
132
|
+
return new TextDecoder().decode(await catFromIpfs(
|
|
133
|
+
identity.backup?.ipfsApiUrl ?? DEFAULT_IPFS_API_URL,
|
|
134
|
+
cid,
|
|
135
|
+
))
|
|
136
|
+
}
|