ethagent 1.1.2 → 2.0.1
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 +124 -32
- package/package.json +8 -3
- package/src/app/FirstRun.tsx +190 -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 +35 -15
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +10 -22
- 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/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 +4 -2
- 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 -817
- 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/{screens → flows/continuity}/RecoveryConfirmScreen.tsx +28 -19
- 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 +23 -44
- 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 +164 -99
- 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 +20 -8
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +2 -2
- package/src/ui/TextInput.tsx +63 -20
- 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 -371
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -156
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -146
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -106
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- 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,190 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { theme } from '../../../ui/theme.js'
|
|
4
|
+
import type { EthagentConfig, EthagentIdentity } from '../../../storage/config.js'
|
|
5
|
+
import {
|
|
6
|
+
displayCustodyMode,
|
|
7
|
+
identityOwnerAddress,
|
|
8
|
+
readCustodyMode,
|
|
9
|
+
readIdentityStateString,
|
|
10
|
+
} from '../model/custody.js'
|
|
11
|
+
import {
|
|
12
|
+
hasPendingPublish,
|
|
13
|
+
localChangeStatusView,
|
|
14
|
+
type LocalChangeStatusView,
|
|
15
|
+
} from '../model/continuity.js'
|
|
16
|
+
import { ensValidationReasonText, selectEnsStatus } from '../model/ens.js'
|
|
17
|
+
import { shortAddress } from '../model/format.js'
|
|
18
|
+
import { identitySummaryRows, lastBackupLabel } from '../model/identity.js'
|
|
19
|
+
import { transferSnapshotView, type TransferSnapshotView } from '../model/transfer.js'
|
|
20
|
+
|
|
21
|
+
import type { ContinuityWorkingTreeStatus } from '../../continuity/storage.js'
|
|
22
|
+
|
|
23
|
+
interface IdentitySummaryProps {
|
|
24
|
+
identity?: EthagentIdentity
|
|
25
|
+
config?: EthagentConfig
|
|
26
|
+
workingStatus?: ContinuityWorkingTreeStatus | null
|
|
27
|
+
hideLocalChanges?: boolean
|
|
28
|
+
tokenLinked?: boolean
|
|
29
|
+
onchainOwner?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config, workingStatus, hideLocalChanges = false, tokenLinked = true, onchainOwner }) => {
|
|
33
|
+
if (!identity) {
|
|
34
|
+
return (
|
|
35
|
+
<Text color={theme.dim}>No agent yet. Create or load one.</Text>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const rows = identitySummaryRows(identity, config)
|
|
40
|
+
const lastBackup = lastBackupLabel(identity)
|
|
41
|
+
const stateName = readIdentityStateString(identity.state, 'name')
|
|
42
|
+
|
|
43
|
+
const row = (label: string) => rows.find(item => item.label === label)
|
|
44
|
+
const localChangeStatus = localChangeStatusView(workingStatus)
|
|
45
|
+
|
|
46
|
+
const ensStatus = selectEnsStatus(identity)
|
|
47
|
+
const custodyMode = readCustodyMode(identity.state)
|
|
48
|
+
const activeOperator = readIdentityStateString(identity.state, 'activeOperatorAddress')
|
|
49
|
+
const approvedOperatorCount = Array.isArray((identity.state as Record<string, unknown> | undefined)?.approvedOperatorWallets)
|
|
50
|
+
? ((identity.state as Record<string, unknown>).approvedOperatorWallets as unknown[]).length
|
|
51
|
+
: 0
|
|
52
|
+
const ownerAddress = identityOwnerAddress(identity, onchainOwner)
|
|
53
|
+
const transferSnapshot = transferSnapshotView(identity)
|
|
54
|
+
|
|
55
|
+
const tokenValue = row('token')?.value ?? 'Not Created'
|
|
56
|
+
const networkValue = row('network')?.value ?? 'Unknown'
|
|
57
|
+
const tokenLine = identity.agentId
|
|
58
|
+
? `${tokenValue} · ${displayValue(networkValue)}`
|
|
59
|
+
: displayValue(tokenValue)
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Box flexDirection="column">
|
|
63
|
+
<Text color={theme.accentPeriwinkle} bold>{stateName || 'Active Agent'}</Text>
|
|
64
|
+
<Text color={identity.agentId ? theme.text : theme.dim} bold={Boolean(identity.agentId)}>{tokenLine}</Text>
|
|
65
|
+
<Text>
|
|
66
|
+
<Text color={theme.dim}>{'ENS'.padEnd(12)}</Text>
|
|
67
|
+
{ensStatus.kind === 'linked'
|
|
68
|
+
? <Text color={theme.accentPeriwinkle}>{ensStatus.name}</Text>
|
|
69
|
+
: ensStatus.kind === 'issue'
|
|
70
|
+
? <Text color={theme.accentError}>{ensStatus.name} ({ensValidationReasonText(ensStatus.reason)})</Text>
|
|
71
|
+
: <Text color={theme.dim}>Not Linked</Text>}
|
|
72
|
+
</Text>
|
|
73
|
+
{tokenLinked ? (
|
|
74
|
+
<Text>
|
|
75
|
+
<Text color={theme.dim}>{'Custody'.padEnd(12)}</Text>
|
|
76
|
+
<Text color={custodyMode ? theme.text : theme.dim}>{displayCustodyMode(custodyMode)}</Text>
|
|
77
|
+
</Text>
|
|
78
|
+
) : null}
|
|
79
|
+
{ownerAddress ? (
|
|
80
|
+
<Text>
|
|
81
|
+
<Text color={theme.dim}>{'Owner'.padEnd(12)}</Text>
|
|
82
|
+
<Text color={theme.text}>{shortAddress(ownerAddress)}</Text>
|
|
83
|
+
</Text>
|
|
84
|
+
) : null}
|
|
85
|
+
{(() => {
|
|
86
|
+
if (custodyMode !== 'advanced') return null
|
|
87
|
+
const vaultAddress = readIdentityStateString(identity.state, 'operatorVaultAddress')
|
|
88
|
+
if (!vaultAddress) return null
|
|
89
|
+
return (
|
|
90
|
+
<Text>
|
|
91
|
+
<Text color={theme.dim}>{'Agent Vault'.padEnd(12)}</Text>
|
|
92
|
+
<Text color={theme.text}>{shortAddress(vaultAddress)}</Text>
|
|
93
|
+
</Text>
|
|
94
|
+
)
|
|
95
|
+
})()}
|
|
96
|
+
{tokenLinked && custodyMode === 'advanced' ? (
|
|
97
|
+
<Text>
|
|
98
|
+
<Text color={theme.dim}>{'Operators'.padEnd(12)}</Text>
|
|
99
|
+
{approvedOperatorCount > 1 ? (
|
|
100
|
+
<Text color={theme.text}>{`${approvedOperatorCount} authorized${activeOperator ? ` (active ${shortAddress(activeOperator)})` : ''}`}</Text>
|
|
101
|
+
) : activeOperator ? (
|
|
102
|
+
<Text color={theme.text}>{shortAddress(activeOperator)}</Text>
|
|
103
|
+
) : (
|
|
104
|
+
<Text color={theme.dim}>None Authorized</Text>
|
|
105
|
+
)}
|
|
106
|
+
</Text>
|
|
107
|
+
) : null}
|
|
108
|
+
<Text>
|
|
109
|
+
<Text color={theme.dim}>{'Last Saved'.padEnd(12)}</Text>
|
|
110
|
+
<Text color={lastBackup === 'never' ? theme.dim : theme.text}>{displayValue(lastBackup)}</Text>
|
|
111
|
+
</Text>
|
|
112
|
+
{hasPendingPublish(identity) ? (
|
|
113
|
+
<Text>
|
|
114
|
+
<Text color={theme.dim}>{'Pending'.padEnd(12)}</Text>
|
|
115
|
+
<Text color={theme.dim}>local snapshot ahead of chain, owner wallet rotates the pointer</Text>
|
|
116
|
+
</Text>
|
|
117
|
+
) : null}
|
|
118
|
+
{transferSnapshot ? (
|
|
119
|
+
<Box marginTop={1}>
|
|
120
|
+
<TransferSnapshotStatus status={transferSnapshot} />
|
|
121
|
+
</Box>
|
|
122
|
+
) : null}
|
|
123
|
+
{!hideLocalChanges && (
|
|
124
|
+
<Box marginTop={1}>
|
|
125
|
+
<LocalChangeStatusLine status={localChangeStatus} />
|
|
126
|
+
</Box>
|
|
127
|
+
)}
|
|
128
|
+
</Box>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const TransferSnapshotStatus: React.FC<{ status: NonNullable<TransferSnapshotView> }> = ({ status }) => {
|
|
133
|
+
const receiverLabel = status.receiverHandle && status.receiverHandle !== status.receiver
|
|
134
|
+
? `${shortAddress(status.receiver)} (${status.receiverHandle})`
|
|
135
|
+
: shortAddress(status.receiver)
|
|
136
|
+
const title = status.kind === 'ready-to-transfer'
|
|
137
|
+
? 'Transfer snapshot ready'
|
|
138
|
+
: 'Transfer snapshot received'
|
|
139
|
+
const detail = status.kind === 'ready-to-transfer'
|
|
140
|
+
? 'sender can transfer externally'
|
|
141
|
+
: 'receiver can restore from this snapshot'
|
|
142
|
+
return (
|
|
143
|
+
<Box flexDirection="column">
|
|
144
|
+
<Text color={theme.accentPeriwinkle} bold>{title}</Text>
|
|
145
|
+
<Text>
|
|
146
|
+
<Text color={theme.dim}>{'Sender'.padEnd(12)}</Text>
|
|
147
|
+
<Text color={theme.text}>{shortAddress(status.sender)}</Text>
|
|
148
|
+
</Text>
|
|
149
|
+
<Text>
|
|
150
|
+
<Text color={theme.dim}>{'Receiver'.padEnd(12)}</Text>
|
|
151
|
+
<Text color={theme.text}>{receiverLabel}</Text>
|
|
152
|
+
</Text>
|
|
153
|
+
<Text color={theme.textSubtle}>{status.slotCount} decrypt slots · {detail}</Text>
|
|
154
|
+
</Box>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const LocalChangeStatusLine: React.FC<{ status: LocalChangeStatusView }> = ({ status }) => {
|
|
159
|
+
if (status.hasLocalChanges) {
|
|
160
|
+
return (
|
|
161
|
+
<Text color={theme.accentError} bold>
|
|
162
|
+
Local changes detected
|
|
163
|
+
{status.files.length > 0 ? `: ${status.files.join(', ')}` : ''}
|
|
164
|
+
</Text>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!status.detail) return null
|
|
169
|
+
|
|
170
|
+
const color = status.tone === 'ok' || status.tone === 'warn' ? theme.accentPeriwinkle : theme.dim
|
|
171
|
+
const label = status.detail === 'None detected' ? 'No local changes detected' : status.detail
|
|
172
|
+
return <Text color={color}>{label}</Text>
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function displayValue(value: string): string {
|
|
176
|
+
const mapped = DISPLAY_VALUES[value]
|
|
177
|
+
return mapped ?? value
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const DISPLAY_VALUES: Record<string, string> = {
|
|
181
|
+
'not attached': 'Not Attached',
|
|
182
|
+
'not connected': 'Not Connected',
|
|
183
|
+
'not created': 'Not Created',
|
|
184
|
+
'not saved': 'Not Saved',
|
|
185
|
+
'not saved yet': 'Not Saved Yet',
|
|
186
|
+
'never': 'Never',
|
|
187
|
+
'unknown': 'Unknown',
|
|
188
|
+
'ethereum mainnet': 'Ethereum Mainnet',
|
|
189
|
+
'base': 'Base',
|
|
190
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { Surface } from '../../../ui/Surface.js'
|
|
4
|
+
import { Select, type SelectOption } from '../../../ui/Select.js'
|
|
5
|
+
import { theme } from '../../../ui/theme.js'
|
|
6
|
+
import type { EthagentConfig, EthagentIdentity } from '../../../storage/config.js'
|
|
7
|
+
import type { ContinuityWorkingTreeStatus } from '../../continuity/storage.js'
|
|
8
|
+
import { identityPerspective, readCustodyMode } from '../model/custody.js'
|
|
9
|
+
import { identityValuesCopyHint } from '../model/copy.js'
|
|
10
|
+
import { transferSnapshotView } from '../model/transfer.js'
|
|
11
|
+
import { IdentitySummary } from './IdentitySummary.js'
|
|
12
|
+
import type { AgentReconciliation } from '../reconciliation/index.js'
|
|
13
|
+
import { menuFlagsFromReconciliation } from './menuFlagsFromReconciliation.js'
|
|
14
|
+
|
|
15
|
+
type MenuScreenProps = {
|
|
16
|
+
mode: 'first-run' | 'manage'
|
|
17
|
+
config?: EthagentConfig
|
|
18
|
+
identity?: EthagentIdentity
|
|
19
|
+
workingStatus?: ContinuityWorkingTreeStatus | null
|
|
20
|
+
canRebackup: boolean
|
|
21
|
+
reconciliation?: AgentReconciliation
|
|
22
|
+
footer: React.ReactNode
|
|
23
|
+
onCreate: () => void
|
|
24
|
+
onLoad: () => void
|
|
25
|
+
onBackupNow: () => void
|
|
26
|
+
onRefetchLatest: () => void
|
|
27
|
+
onPublicProfile: () => void
|
|
28
|
+
onEnsName: () => void
|
|
29
|
+
onWalletSetup: () => void
|
|
30
|
+
onContinuity: () => void
|
|
31
|
+
onIdentityValues: () => void
|
|
32
|
+
onPrepareTransfer: () => void
|
|
33
|
+
onStorage: () => void
|
|
34
|
+
onSkip: () => void
|
|
35
|
+
onCancel: () => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type Action =
|
|
39
|
+
| 'public-profile'
|
|
40
|
+
| 'ens-name'
|
|
41
|
+
| 'wallet-setup'
|
|
42
|
+
| 'continuity'
|
|
43
|
+
| 'backup'
|
|
44
|
+
| 'refetch'
|
|
45
|
+
| 'identity-values'
|
|
46
|
+
| 'prepare-transfer'
|
|
47
|
+
| 'storage'
|
|
48
|
+
| 'create'
|
|
49
|
+
| 'load'
|
|
50
|
+
| 'skip'
|
|
51
|
+
| 'cancel'
|
|
52
|
+
|
|
53
|
+
export const MenuScreen: React.FC<MenuScreenProps> = ({
|
|
54
|
+
mode,
|
|
55
|
+
config,
|
|
56
|
+
identity,
|
|
57
|
+
workingStatus,
|
|
58
|
+
canRebackup,
|
|
59
|
+
reconciliation,
|
|
60
|
+
footer,
|
|
61
|
+
onCreate,
|
|
62
|
+
onLoad,
|
|
63
|
+
onBackupNow,
|
|
64
|
+
onRefetchLatest,
|
|
65
|
+
onPublicProfile,
|
|
66
|
+
onEnsName,
|
|
67
|
+
onWalletSetup,
|
|
68
|
+
onContinuity,
|
|
69
|
+
onIdentityValues,
|
|
70
|
+
onPrepareTransfer,
|
|
71
|
+
onStorage,
|
|
72
|
+
onSkip,
|
|
73
|
+
onCancel,
|
|
74
|
+
}) => {
|
|
75
|
+
const title = mode === 'first-run' ? 'Set Up Agent Identity' : 'Identity Hub'
|
|
76
|
+
const subtitle = mode === 'first-run'
|
|
77
|
+
? 'Create a portable agent or load one you already own.'
|
|
78
|
+
: 'Manage agent identity, custody, encrypted continuity, and recovery.'
|
|
79
|
+
|
|
80
|
+
const canRefetch = Boolean(canRebackup && identity?.backup?.cid)
|
|
81
|
+
const custodyMode = identity ? readCustodyMode(identity.state) : undefined
|
|
82
|
+
|
|
83
|
+
const perspective = identityPerspective(identity)
|
|
84
|
+
const flags = reconciliation
|
|
85
|
+
? menuFlagsFromReconciliation(reconciliation, perspective)
|
|
86
|
+
: (perspective === 'operator'
|
|
87
|
+
? menuFlagsFromReconciliation({
|
|
88
|
+
token: 'unknown', custody: 'unknown', agentUri: 'unknown', ensRecords: 'unknown',
|
|
89
|
+
vault: 'unknown', workingTree: 'unknown', rpc: 'reachable', driftCount: 0, lastCheckedAt: '',
|
|
90
|
+
}, perspective)
|
|
91
|
+
: null)
|
|
92
|
+
|
|
93
|
+
const walletSetupBaseHint = custodyMode === 'advanced'
|
|
94
|
+
? 'Advanced. Owner wallet, agent vault, authorized operator wallets'
|
|
95
|
+
: 'Simple. Switch to Advanced to delegate URI rotation through a dedicated OperatorVault'
|
|
96
|
+
|
|
97
|
+
const walletSetupLabel = flags?.custodyAsterisk ? 'Custody Mode *' : 'Custody Mode'
|
|
98
|
+
const walletSetupHint = flags?.custodyModeReason ?? flags?.custodyHint ?? walletSetupBaseHint
|
|
99
|
+
|
|
100
|
+
const saveSnapshotLabel = flags?.saveSnapshotAsterisk ? 'Save Snapshot Now *' : 'Save Snapshot Now'
|
|
101
|
+
const saveSnapshotHint = flags?.saveSnapshotHint ?? 'Encrypt and publish latest snapshot'
|
|
102
|
+
|
|
103
|
+
const ensNameHint = flags?.ensNameReason ?? 'Public name or subdomain for this agent'
|
|
104
|
+
|
|
105
|
+
const prepareTransferHint = flags?.prepareTransferReason ?? 'Create transfer snapshot and handoff slots'
|
|
106
|
+
|
|
107
|
+
const tokenValuesHint = flags?.tokenValuesUnlinkedNote ?? identityValuesCopyHint(identity)
|
|
108
|
+
|
|
109
|
+
const options: Array<SelectOption<Action>> = identity
|
|
110
|
+
? [
|
|
111
|
+
{ value: 'public-profile', role: 'section', label: 'Public Identity' },
|
|
112
|
+
{ value: 'public-profile', label: 'Public Profile', hint: 'Name, description, icon, and Agent Card' },
|
|
113
|
+
{ value: 'ens-name', label: 'ENS Name', hint: ensNameHint, disabled: flags?.ensNameDisabled ?? false },
|
|
114
|
+
{ value: 'continuity', role: 'section', label: 'Continuity' },
|
|
115
|
+
{ value: 'continuity', label: 'Soul, Memory, Skills', hint: 'SOUL.md, MEMORY.md, skills.json on this device' },
|
|
116
|
+
{ value: 'backup', label: saveSnapshotLabel, hint: saveSnapshotHint, disabled: !canRebackup || (flags?.saveSnapshotDisabled ?? false) },
|
|
117
|
+
{ value: 'refetch', label: 'Refetch Latest', hint: 'Restore local files from latest saved snapshot', disabled: !canRefetch || (flags?.refetchLatestDisabled ?? false) },
|
|
118
|
+
{ value: 'wallet-setup', role: 'section', label: 'Custody' },
|
|
119
|
+
{ value: 'wallet-setup', label: walletSetupLabel, hint: walletSetupHint, disabled: !identity.agentId || (flags?.custodyModeDisabled ?? false) },
|
|
120
|
+
{ value: 'prepare-transfer', label: 'Prepare Transfer', hint: prepareTransferHint, disabled: flags?.prepareTransferDisabled ?? false },
|
|
121
|
+
{ value: 'identity-values', role: 'section', label: 'Token' },
|
|
122
|
+
{ value: 'identity-values', label: 'Token Values', hint: tokenValuesHint },
|
|
123
|
+
{ value: 'load', label: 'Load Agent', hint: 'Refresh this agent from chain, or load a different one' },
|
|
124
|
+
{ value: 'create', label: 'New Agent', hint: 'Mint another token and make it active' },
|
|
125
|
+
{ value: 'storage', role: 'section', label: 'Storage' },
|
|
126
|
+
{ value: 'storage', label: 'IPFS Storage', hint: 'Publishing credentials for encrypted snapshots' },
|
|
127
|
+
{ value: 'cancel', role: 'section', label: 'Exit' },
|
|
128
|
+
{ value: 'cancel', label: 'Close Identity Hub', hint: 'Return to chat without changing identity', role: 'utility' },
|
|
129
|
+
]
|
|
130
|
+
: [
|
|
131
|
+
{ value: 'create', role: 'section', label: 'Setup' },
|
|
132
|
+
{ value: 'create', label: 'Create New Agent', hint: 'Mint a wallet-owned token for this machine' },
|
|
133
|
+
{ value: 'load', label: 'Load Existing Agent', hint: 'Find a token owned by this wallet or linked to it' },
|
|
134
|
+
{ value: mode === 'first-run' ? 'skip' : 'cancel', role: 'section', label: 'Exit' },
|
|
135
|
+
...(mode === 'first-run'
|
|
136
|
+
? [
|
|
137
|
+
{ value: 'skip' as Action, label: 'Skip For Now', hint: 'Continue now, use /identity later', role: 'utility' as const },
|
|
138
|
+
]
|
|
139
|
+
: [
|
|
140
|
+
{ value: 'cancel' as Action, label: 'Close Identity Hub', hint: 'Return to chat without changing identity', role: 'utility' as const },
|
|
141
|
+
]),
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
const reconciliationBanner = identity && reconciliation
|
|
145
|
+
? renderReconciliationBanner(reconciliation, identity)
|
|
146
|
+
: null
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<Surface title={title} subtitle={subtitle} footer={footer}>
|
|
150
|
+
<IdentitySummary
|
|
151
|
+
identity={identity}
|
|
152
|
+
config={config}
|
|
153
|
+
workingStatus={workingStatus}
|
|
154
|
+
tokenLinked={reconciliation ? reconciliation.token === 'linked' : true}
|
|
155
|
+
{...(reconciliation?.onChainOwner ? { onchainOwner: reconciliation.onChainOwner } : {})}
|
|
156
|
+
/>
|
|
157
|
+
{reconciliationBanner ? (
|
|
158
|
+
<Box marginTop={1} flexDirection="column">
|
|
159
|
+
{reconciliationBanner}
|
|
160
|
+
</Box>
|
|
161
|
+
) : null}
|
|
162
|
+
<Box marginTop={1}>
|
|
163
|
+
<Select<Action>
|
|
164
|
+
options={options}
|
|
165
|
+
hintLayout="inline"
|
|
166
|
+
onSubmit={choice => {
|
|
167
|
+
if (choice === 'skip') return onSkip()
|
|
168
|
+
if (choice === 'cancel') return onCancel()
|
|
169
|
+
if (choice === 'public-profile') return onPublicProfile()
|
|
170
|
+
if (choice === 'ens-name') return onEnsName()
|
|
171
|
+
if (choice === 'wallet-setup') return onWalletSetup()
|
|
172
|
+
if (choice === 'continuity') return onContinuity()
|
|
173
|
+
if (choice === 'backup') return onBackupNow()
|
|
174
|
+
if (choice === 'refetch') return onRefetchLatest()
|
|
175
|
+
if (choice === 'identity-values') return onIdentityValues()
|
|
176
|
+
if (choice === 'prepare-transfer') return onPrepareTransfer()
|
|
177
|
+
if (choice === 'storage') return onStorage()
|
|
178
|
+
if (choice === 'load') return onLoad()
|
|
179
|
+
if (choice === 'create') return onCreate()
|
|
180
|
+
}}
|
|
181
|
+
onCancel={mode === 'first-run' ? undefined : onCancel}
|
|
182
|
+
/>
|
|
183
|
+
</Box>
|
|
184
|
+
</Surface>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentIdentity): React.ReactNode {
|
|
189
|
+
if (r.token === 'no-agent') return null
|
|
190
|
+
if (r.token === 'unlinked') {
|
|
191
|
+
const tokenLabel = r.tokenAgentId ? `Token #${r.tokenAgentId}` : 'Token'
|
|
192
|
+
const transferSnapshot = transferSnapshotView(identity)
|
|
193
|
+
if (transferSnapshot) {
|
|
194
|
+
return (
|
|
195
|
+
<>
|
|
196
|
+
<Text color={theme.accentError} bold>Agent Unlinked</Text>
|
|
197
|
+
<Text color={theme.textSubtle}>{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, skills.json remain. Back them up before this directory is reused.</Text>
|
|
198
|
+
<Text color={theme.textSubtle}>Use Load Agent or New Agent to re-enable disabled actions.</Text>
|
|
199
|
+
</>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
return (
|
|
203
|
+
<>
|
|
204
|
+
<Text color={theme.accentError} bold>Agent Unlinked</Text>
|
|
205
|
+
<Text color={theme.textSubtle}>{tokenLabel} left without Prepare Transfer. Back up local SOUL.md, MEMORY.md, skills.json before loading another agent.</Text>
|
|
206
|
+
<Text color={theme.textSubtle}>For continuity handoff: ask the new holder to return the token, then run Prepare Transfer before re-sending.</Text>
|
|
207
|
+
<Text color={theme.textSubtle}>Use Load Agent or New Agent to re-enable disabled actions.</Text>
|
|
208
|
+
</>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
if (r.token === 'unknown') {
|
|
212
|
+
return (
|
|
213
|
+
<>
|
|
214
|
+
<Text color={theme.dim}>Ownership Check Failed (RPC?)</Text>
|
|
215
|
+
{r.tokenDetail ? <Text color={theme.dim}>{r.tokenDetail}</Text> : null}
|
|
216
|
+
</>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
if (r.driftCount === 0) {
|
|
220
|
+
return null
|
|
221
|
+
}
|
|
222
|
+
const lines: string[] = []
|
|
223
|
+
if (r.custody === 'mid-flow-uri-pending') lines.push('Advanced setup pending. Open Custody Mode to finish.')
|
|
224
|
+
if (r.agentUri === 'local-newer') lines.push('Local state newer than chain. Save Snapshot Now to publish.')
|
|
225
|
+
if (r.agentUri === 'chain-newer') lines.push('Onchain agentURI is newer than local. Refetch Latest.')
|
|
226
|
+
if (r.ensRecords === 'drift') lines.push('ENS records out of sync. Open Custody Mode to Fix Records.')
|
|
227
|
+
if (r.vault === 'missing') lines.push('Recorded vault address has no contract at it. Open Custody Mode to redeploy.')
|
|
228
|
+
if (r.workingTree === 'dirty') lines.push('Local edits pending. Save Snapshot Now to publish.')
|
|
229
|
+
return (
|
|
230
|
+
<>
|
|
231
|
+
<Text color={theme.accentPeriwinkle} bold>Agent Linked. {lines.length} item{lines.length === 1 ? '' : 's'} need attention</Text>
|
|
232
|
+
{lines.map((line, i) => (
|
|
233
|
+
<Text key={i} color={theme.textSubtle}>· {line}</Text>
|
|
234
|
+
))}
|
|
235
|
+
</>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
@@ -3,7 +3,7 @@ import { Surface } from '../../../ui/Surface.js'
|
|
|
3
3
|
import { Select, type SelectOption } from '../../../ui/Select.js'
|
|
4
4
|
import type { SelectableNetwork } from '../../../storage/config.js'
|
|
5
5
|
import { SELECTABLE_NETWORKS } from '../../../storage/config.js'
|
|
6
|
-
import { networkLabel, networkSubtitle } from '../
|
|
6
|
+
import { networkLabel, networkSubtitle } from '../model/network.js'
|
|
7
7
|
|
|
8
8
|
type NetworkScreenProps = {
|
|
9
9
|
subtitle: string
|
|
@@ -14,9 +14,9 @@ type NetworkScreenProps = {
|
|
|
14
14
|
|
|
15
15
|
export const NetworkScreen: React.FC<NetworkScreenProps> = ({ subtitle, footer, onSelect, onCancel }) => {
|
|
16
16
|
const options: Array<SelectOption<SelectableNetwork>> = [
|
|
17
|
-
{ value: 'mainnet', role: 'section',
|
|
17
|
+
{ value: 'mainnet', role: 'section', label: 'High Security' },
|
|
18
18
|
networkOption('mainnet'),
|
|
19
|
-
{ value: '
|
|
19
|
+
{ value: 'base', role: 'section', label: 'Lower Cost' },
|
|
20
20
|
...SELECTABLE_NETWORKS.filter(network => network !== 'mainnet').map(networkOption),
|
|
21
21
|
]
|
|
22
22
|
|
package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx}
RENAMED
|
@@ -4,34 +4,38 @@ import { Surface } from '../../../ui/Surface.js'
|
|
|
4
4
|
import { TextInput } from '../../../ui/TextInput.js'
|
|
5
5
|
import { theme } from '../../../ui/theme.js'
|
|
6
6
|
import { extractPinataJwt } from '../../storage/ipfs.js'
|
|
7
|
-
import type { Step } from '../identityHubReducer.js'
|
|
8
7
|
|
|
9
8
|
const PINATA_API_KEYS_URL = 'https://app.pinata.cloud/developers/api-keys'
|
|
10
9
|
|
|
11
|
-
type
|
|
12
|
-
|
|
10
|
+
type PinataJwtInputProps = {
|
|
11
|
+
inputKey: string
|
|
12
|
+
title?: string
|
|
13
|
+
subtitle?: string
|
|
13
14
|
footer: React.ReactNode
|
|
14
15
|
onSubmit: (input: string) => void
|
|
15
16
|
onCancel: () => void
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
export const PinataJwtInput: React.FC<PinataJwtInputProps> = ({
|
|
20
|
+
inputKey,
|
|
21
|
+
title,
|
|
22
|
+
subtitle,
|
|
23
|
+
footer,
|
|
24
|
+
onSubmit,
|
|
25
|
+
onCancel,
|
|
26
|
+
}) => (
|
|
27
|
+
<Surface
|
|
28
|
+
title={title ?? 'Connect IPFS Storage'}
|
|
29
|
+
subtitle={subtitle ?? 'Save a Pinata JWT so ethagent can pin encrypted state to IPFS.'}
|
|
30
|
+
footer={footer}
|
|
31
|
+
>
|
|
28
32
|
<Text>
|
|
29
33
|
<Text color={theme.dim}>Paste your Pinata JWT. Get one at </Text>
|
|
30
|
-
<Text color={theme.
|
|
34
|
+
<Text color={theme.accentPeriwinkle} underline>{PINATA_API_KEYS_URL}</Text>
|
|
31
35
|
</Text>
|
|
32
36
|
<Text color={theme.dim}>Saved encrypted on this device · used only for IPFS pinning</Text>
|
|
33
37
|
<TextInput
|
|
34
|
-
key=
|
|
38
|
+
key={inputKey}
|
|
35
39
|
isSecret
|
|
36
40
|
placeholder="Pinata JWT"
|
|
37
41
|
validate={v => {
|
|
@@ -45,6 +49,5 @@ export const RebackupStorageScreen: React.FC<RebackupStorageScreenProps> = ({ st
|
|
|
45
49
|
onSubmit={onSubmit}
|
|
46
50
|
onCancel={onCancel}
|
|
47
51
|
/>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
52
|
+
</Surface>
|
|
53
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { Surface } from '../../../ui/Surface.js'
|
|
4
|
+
import { Select } from '../../../ui/Select.js'
|
|
5
|
+
import { theme } from '../../../ui/theme.js'
|
|
6
|
+
import { transferSnapshotView } from '../model/transfer.js'
|
|
7
|
+
import type { EthagentIdentity } from '../../../storage/config.js'
|
|
8
|
+
|
|
9
|
+
type UnlinkedIdentityScreenProps = {
|
|
10
|
+
identity?: EthagentIdentity
|
|
11
|
+
agentId?: string
|
|
12
|
+
onLoadAgent: () => void
|
|
13
|
+
onOpenMenu: () => void
|
|
14
|
+
onRetry?: () => void
|
|
15
|
+
onCancel: () => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Action = 'load-agent' | 'open-menu' | 'retry'
|
|
19
|
+
|
|
20
|
+
export const UnlinkedIdentityScreen: React.FC<UnlinkedIdentityScreenProps> = ({
|
|
21
|
+
identity,
|
|
22
|
+
agentId,
|
|
23
|
+
onLoadAgent,
|
|
24
|
+
onOpenMenu,
|
|
25
|
+
onRetry,
|
|
26
|
+
onCancel,
|
|
27
|
+
}) => {
|
|
28
|
+
const options: Array<{ value: Action; label: string; hint?: string; role?: 'section' | 'utility' }> = [
|
|
29
|
+
{ value: 'load-agent', role: 'section', label: 'Load Agent' },
|
|
30
|
+
{ value: 'load-agent', label: 'Load Agent', hint: 'Reconnect this token by signing with the current owner wallet, or load a different one' },
|
|
31
|
+
{ value: 'open-menu', role: 'section', label: 'Identity Hub' },
|
|
32
|
+
{ value: 'open-menu', label: 'Open Identity Hub', hint: 'Browse local identity, continuity files, and settings without reconnecting' },
|
|
33
|
+
]
|
|
34
|
+
if (onRetry) {
|
|
35
|
+
options.push({ value: 'retry', role: 'section', label: 'Recheck' })
|
|
36
|
+
options.push({ value: 'retry', label: 'Retry Ownership Check', hint: 'Re-query the chain to confirm the current owner', role: 'utility' })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const tokenLabel = agentId ? `Token #${agentId}` : 'Token'
|
|
40
|
+
const transferSnapshot = transferSnapshotView(identity)
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Surface
|
|
44
|
+
title="No Linked Agent"
|
|
45
|
+
subtitle="The agent token recorded locally is not currently owned by your wallet."
|
|
46
|
+
footer={<Text color={theme.dim}>enter selects, esc back</Text>}
|
|
47
|
+
>
|
|
48
|
+
<Box flexDirection="column">
|
|
49
|
+
{transferSnapshot ? (
|
|
50
|
+
<Text color={theme.textSubtle}>
|
|
51
|
+
{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, skills.json remain. Back them up before this directory is reused.
|
|
52
|
+
</Text>
|
|
53
|
+
) : (
|
|
54
|
+
<>
|
|
55
|
+
<Text color={theme.accentPeriwinkle}>{tokenLabel} left this wallet without Prepare Transfer, so the new holder has no continuity handoff.</Text>
|
|
56
|
+
<Text color={theme.textSubtle}>
|
|
57
|
+
Local SOUL.md, MEMORY.md, skills.json remain. Back them up before this directory is reused.
|
|
58
|
+
</Text>
|
|
59
|
+
</>
|
|
60
|
+
)}
|
|
61
|
+
</Box>
|
|
62
|
+
<Box marginTop={1}>
|
|
63
|
+
<Select<Action>
|
|
64
|
+
options={options}
|
|
65
|
+
hintLayout="inline"
|
|
66
|
+
onSubmit={choice => {
|
|
67
|
+
if (choice === 'load-agent') return onLoadAgent()
|
|
68
|
+
if (choice === 'open-menu') return onOpenMenu()
|
|
69
|
+
if (choice === 'retry') return onRetry?.()
|
|
70
|
+
}}
|
|
71
|
+
onCancel={onCancel}
|
|
72
|
+
/>
|
|
73
|
+
</Box>
|
|
74
|
+
</Surface>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { Box, Text } from 'ink'
|
|
3
|
-
import { exec } from 'node:child_process'
|
|
4
3
|
import { Surface } from '../../../ui/Surface.js'
|
|
5
4
|
import { Spinner } from '../../../ui/Spinner.js'
|
|
6
5
|
import { theme } from '../../../ui/theme.js'
|
|
7
6
|
import { useAppInput } from '../../../app/input/AppInputProvider.js'
|
|
7
|
+
import { openExternalUrl } from '../../../utils/openExternal.js'
|
|
8
8
|
import type { BrowserWalletReady } from '../../wallet/browserWallet.js'
|
|
9
9
|
|
|
10
10
|
type WalletApprovalScreenProps = {
|
|
11
11
|
title: string
|
|
12
|
-
subtitle:
|
|
12
|
+
subtitle: React.ReactNode
|
|
13
13
|
walletSession: BrowserWalletReady | null
|
|
14
14
|
label: string
|
|
15
15
|
onCancel?: () => void
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
export const OPEN_BROWSER_HINT = 'Press enter to open in browser...'
|
|
19
|
+
const PREPARING_WALLET_REQUEST_LABEL = 'preparing wallet request...'
|
|
20
|
+
|
|
18
21
|
export const WalletApprovalScreen: React.FC<WalletApprovalScreenProps> = ({ title, subtitle, walletSession, label, onCancel }) => {
|
|
19
22
|
useAppInput((_input, key) => {
|
|
20
23
|
if (key.escape && onCancel) onCancel()
|
|
21
24
|
if (key.return && walletSession?.url) {
|
|
22
|
-
|
|
23
|
-
exec(`${command} "${walletSession.url}"`).on('error', () => {})
|
|
25
|
+
openExternalUrl(walletSession.url)
|
|
24
26
|
}
|
|
25
27
|
}, { isActive: Boolean(onCancel) || Boolean(walletSession) })
|
|
26
28
|
const footer = onCancel ? <Text color={theme.dim}>esc cancels</Text> : undefined
|
|
@@ -28,15 +30,14 @@ export const WalletApprovalScreen: React.FC<WalletApprovalScreenProps> = ({ titl
|
|
|
28
30
|
<Surface title={title} subtitle={subtitle} footer={footer}>
|
|
29
31
|
{walletSession ? (
|
|
30
32
|
<Box flexDirection="column">
|
|
31
|
-
<Text color={theme.
|
|
32
|
-
<Text color={theme.
|
|
33
|
-
<Text color={theme.dim}>press enter to open in browser...</Text>
|
|
33
|
+
<Text color={theme.accentBlue} underline>{walletSession.url}</Text>
|
|
34
|
+
<Text color={theme.dim}>{OPEN_BROWSER_HINT}</Text>
|
|
34
35
|
<Box marginTop={1}>
|
|
35
36
|
<Spinner label={label} />
|
|
36
37
|
</Box>
|
|
37
38
|
</Box>
|
|
38
39
|
) : (
|
|
39
|
-
<Spinner label=
|
|
40
|
+
<Spinner label={PREPARING_WALLET_REQUEST_LABEL} />
|
|
40
41
|
)}
|
|
41
42
|
</Surface>
|
|
42
43
|
)
|