ethagent 3.3.4 → 4.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/.claude-plugin/marketplace.json +11 -0
- package/.claude-plugin/plugin.json +35 -0
- package/LICENSE +1 -1
- package/README.md +64 -104
- package/commands/ethagent.md +40 -0
- package/package.json +16 -16
- package/src/app/keybindings/KeybindingProvider.tsx +1 -6
- package/src/app/keybindings/types.ts +1 -6
- package/src/cli/ResetConfirmView.tsx +54 -53
- package/src/cli/demo.ts +86 -0
- package/src/cli/hookIo.ts +45 -0
- package/src/cli/main.tsx +94 -123
- package/src/cli/memoryGuard.ts +49 -0
- package/src/cli/reset.ts +28 -70
- package/src/cli/sessionStart.ts +33 -0
- package/src/cli/status.ts +46 -0
- package/src/cli/sync.ts +167 -0
- package/src/cli/syncAdapters/claude-code.ts +86 -0
- package/src/cli/syncAdapters/codex.ts +66 -0
- package/src/cli/syncAdapters/index.ts +45 -0
- package/src/cli/syncAdapters/managedBlock.ts +175 -0
- package/src/cli/syncAdapters/shared.ts +63 -0
- package/src/identity/continuity/envelopeParse.ts +20 -1
- package/src/identity/continuity/publicSkills.ts +3 -1
- package/src/identity/continuity/skills/publicSkillsSync.ts +2 -1
- package/src/identity/continuity/skills/scaffold.ts +5 -2
- package/src/identity/continuity/snapshots.ts +12 -5
- package/src/identity/continuity/storage/defaults.ts +20 -19
- package/src/identity/continuity/storage/status.ts +1 -1
- package/src/identity/ens/ensLookup/constants.ts +1 -1
- package/src/identity/manager/IdentityManager.tsx +33 -0
- package/src/identity/{hub → manager}/OperationalRoutes.tsx +37 -18
- package/src/identity/{hub → manager}/Routes.tsx +48 -34
- package/src/identity/{hub → manager}/continuity/ContinuityDashboardScreen.tsx +9 -19
- package/src/identity/{hub → manager}/continuity/RebackupStorageScreen.tsx +3 -3
- package/src/identity/manager/continuity/RecoveryConfirmScreen.tsx +102 -0
- package/src/identity/{hub → manager}/continuity/SavePromptScreen.tsx +2 -3
- package/src/identity/{hub → manager}/continuity/completion.ts +1 -1
- package/src/identity/{hub → manager}/continuity/effects.ts +1 -1
- package/src/identity/{hub → manager}/continuity/skills/DeleteSkillConfirmScreen.tsx +2 -2
- package/src/identity/{hub → manager}/continuity/skills/NewSkillScreen.tsx +0 -5
- package/src/identity/{hub → manager}/continuity/skills/NewSkillVisibilityScreen.tsx +4 -4
- package/src/identity/{hub → manager}/continuity/skills/SkillActionsScreen.tsx +6 -22
- package/src/identity/{hub → manager}/continuity/skills/SkillsTreeScreen.tsx +5 -17
- package/src/identity/{hub → manager}/continuity/snapshot.ts +1 -1
- package/src/identity/{hub → manager}/continuity/vault.ts +1 -1
- package/src/identity/{hub → manager}/create/CreateFlow.tsx +59 -32
- package/src/identity/{hub → manager}/create/effects.ts +19 -10
- package/src/identity/manager/create/importScan.ts +122 -0
- package/src/identity/{hub → manager}/custody/CustodyEditFlow.tsx +17 -61
- package/src/identity/{hub → manager}/custody/actions.ts +1 -15
- package/src/identity/{hub → manager}/custody/routes.tsx +20 -40
- package/src/identity/{hub → manager}/custody/transactions.ts +1 -0
- package/src/identity/{hub → manager}/custody/types.ts +1 -2
- package/src/identity/{hub → manager}/custody/useCustodyEffects.ts +1 -1
- package/src/identity/{hub → manager}/ens/EnsEditAdvancedScreens.tsx +2 -2
- package/src/identity/{hub → manager}/ens/EnsEditMaintenanceScreens.tsx +12 -23
- package/src/identity/{hub → manager}/ens/EnsEditReviewScreens.tsx +18 -42
- package/src/identity/{hub → manager}/ens/EnsEditRunners.tsx +1 -1
- package/src/identity/{hub → manager}/ens/EnsEditShared.tsx +0 -2
- package/src/identity/{hub → manager}/ens/EnsEditSimpleScreens.tsx +10 -19
- package/src/identity/{hub → manager}/ens/EnsFlow.tsx +133 -41
- package/src/identity/{hub → manager}/ens/EnsOperatorWalletsScreen.tsx +14 -19
- package/src/identity/{hub → manager}/ens/editCopy.ts +1 -14
- package/src/identity/{hub → manager}/profile/EditProfileFlow.tsx +99 -66
- package/src/identity/{hub → manager}/profile/effects.ts +1 -3
- package/src/identity/{hub → manager}/profile/operatorSave.ts +1 -1
- package/src/identity/{hub → manager}/profile/state.ts +1 -1
- package/src/identity/{hub/identityHubReducer.ts → manager/reducer.ts} +25 -26
- package/src/identity/{hub → manager}/restore/RestoreFlow.tsx +16 -24
- package/src/identity/{hub → manager}/restore/apply.ts +1 -1
- package/src/identity/{hub → manager}/restore/auth.ts +1 -1
- package/src/identity/{hub → manager}/restore/discover.ts +1 -1
- package/src/identity/{hub → manager}/restore/fetch.ts +1 -1
- package/src/identity/{hub → manager}/restore/restoreAdmin.ts +1 -1
- package/src/identity/{hub → manager}/restore/useRestoreEffects.ts +2 -9
- package/src/identity/{hub → manager}/settings/StorageCredentialScreen.tsx +10 -25
- package/src/identity/{hub → manager}/shared/components/DetailsScreen.tsx +5 -7
- package/src/identity/{hub → manager}/shared/components/ErrorScreen.tsx +6 -10
- package/src/identity/{hub → manager}/shared/components/FlowTimeline.tsx +4 -3
- package/src/identity/{hub → manager}/shared/components/IdentitySummary.tsx +19 -59
- package/src/identity/manager/shared/components/LazyMenu.tsx +147 -0
- package/src/identity/manager/shared/components/MenuScreen.tsx +220 -0
- package/src/identity/manager/shared/components/OperationCompleteScreen.tsx +28 -0
- package/src/identity/{hub → manager}/shared/components/UnlinkedIdentityScreen.tsx +9 -10
- package/src/identity/{hub → manager}/shared/components/WalletApprovalScreen.tsx +1 -2
- package/src/identity/manager/shared/components/Wordmark.tsx +54 -0
- package/src/identity/{hub → manager}/shared/components/menuFlagsFromReconciliation.ts +39 -15
- package/src/identity/{hub → manager}/shared/effects/profilePrep.ts +1 -1
- package/src/identity/manager/shared/effects/types.ts +30 -0
- package/src/identity/{hub → manager}/shared/model/copy.ts +0 -4
- package/src/identity/{hub → manager}/shared/model/errors.ts +32 -3
- package/src/identity/{hub → manager}/shared/model/network.ts +2 -2
- package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/hook.ts +5 -0
- package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/run.ts +1 -1
- package/src/identity/{hub/shared/reconciliation/useAgentReconciliation.ts → manager/shared/reconciliation/index.ts} +6 -0
- package/src/identity/{hub → manager}/shared/utils.ts +6 -10
- package/src/identity/{hub → manager}/transfer/TokenTransferFlow.tsx +3 -3
- package/src/identity/{hub → manager}/transfer/TokenTransferScreens.tsx +4 -10
- package/src/identity/{hub → manager}/transfer/effects.ts +1 -1
- package/src/identity/{hub → manager}/types.ts +5 -6
- package/src/identity/{hub/useIdentityHubContinuity.ts → manager/useContinuity.ts} +59 -27
- package/src/identity/{hub/useIdentityHubController.ts → manager/useController.ts} +38 -35
- package/src/identity/{hub/useIdentityHubSideEffects.ts → manager/useSideEffects.ts} +40 -4
- package/src/identity/registry/erc8004/discovery.ts +3 -17
- package/src/identity/registry/erc8004/utils.ts +1 -1
- package/src/identity/storage/ipfs.ts +21 -1
- package/src/identity/wallet/browserWallet/html.ts +10 -2
- package/src/identity/wallet/browserWallet/http.ts +18 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +5 -1
- package/src/identity/wallet/browserWallet/requests.ts +10 -28
- package/src/identity/wallet/browserWallet/session.ts +26 -33
- package/src/identity/wallet/browserWallet/validation.ts +14 -0
- package/src/identity/wallet/browserWallet/walletPageSource.ts +22 -40
- package/src/identity/wallet/page/boot.ts +43 -0
- package/src/identity/wallet/page/config.ts +59 -0
- package/src/identity/wallet/page/constants.ts +12 -0
- package/src/identity/wallet/page/copy.ts +47 -68
- package/src/identity/wallet/page/css.ts +638 -0
- package/src/identity/wallet/page/{errorView.ts → errors.ts} +5 -14
- package/src/identity/wallet/page/{controller.ts → flow.ts} +4 -71
- package/src/identity/wallet/page/markup.ts +44 -34
- package/src/identity/wallet/page/{walletProvider.ts → provider.ts} +0 -3
- package/src/identity/wallet/page/resize.ts +95 -0
- package/src/identity/wallet/page/state.ts +135 -8
- package/src/identity/wallet/page/timeline.ts +161 -0
- package/src/identity/wallet/page/view.ts +22 -302
- package/src/storage/config.ts +30 -80
- package/src/storage/reset.ts +31 -0
- package/src/storage/secrets.ts +1 -16
- package/src/ui/Select.tsx +27 -5
- package/src/ui/Spinner.tsx +16 -15
- package/src/ui/Surface.tsx +21 -17
- package/src/ui/TextArea.tsx +173 -0
- package/src/ui/TextInput.tsx +31 -133
- package/src/ui/theme.ts +22 -13
- package/src/utils/clipboard.ts +0 -140
- package/src/app/FirstRun.tsx +0 -577
- package/src/app/FirstRunTimeline.tsx +0 -51
- package/src/app/firstRunConfig.ts +0 -26
- package/src/app/hooks/useCancelRequest.ts +0 -22
- package/src/app/hooks/useDoublePress.ts +0 -46
- package/src/app/hooks/useExitOnCtrlC.ts +0 -36
- package/src/auth/openaiOAuth/credentials.ts +0 -47
- package/src/auth/openaiOAuth/crypto.ts +0 -23
- package/src/auth/openaiOAuth/index.ts +0 -238
- package/src/auth/openaiOAuth/landingPage.ts +0 -116
- package/src/auth/openaiOAuth/listener.ts +0 -151
- package/src/auth/openaiOAuth/refresh.ts +0 -70
- package/src/auth/openaiOAuth/shared.ts +0 -115
- package/src/chat/ChatBottomPane.tsx +0 -296
- package/src/chat/ChatScreen.tsx +0 -1685
- package/src/chat/ConversationStack.tsx +0 -56
- package/src/chat/MessageList.tsx +0 -638
- package/src/chat/SessionStatus.tsx +0 -53
- package/src/chat/chatEnvironment.ts +0 -16
- package/src/chat/chatScreenUtils.ts +0 -194
- package/src/chat/chatSessionState.ts +0 -146
- package/src/chat/chatTurnContext.ts +0 -50
- package/src/chat/chatTurnOrchestrator.ts +0 -603
- package/src/chat/chatTurnRows.ts +0 -64
- package/src/chat/commands.ts +0 -494
- package/src/chat/continuityEditReview.ts +0 -42
- package/src/chat/display/DiffView.tsx +0 -193
- package/src/chat/display/SyntaxText.tsx +0 -192
- package/src/chat/display/toolCallDisplay.ts +0 -103
- package/src/chat/display/toolResultDisplay.ts +0 -19
- package/src/chat/input/ChatInput.tsx +0 -625
- package/src/chat/input/chatInputHelpers.ts +0 -62
- package/src/chat/input/chatInputState.ts +0 -247
- package/src/chat/input/chatPaste.ts +0 -49
- package/src/chat/input/imageRefs.ts +0 -30
- package/src/chat/input/inputRendering.tsx +0 -93
- package/src/chat/input/textCursor.ts +0 -212
- package/src/chat/messageMarkdown.ts +0 -220
- package/src/chat/messageRows.ts +0 -43
- package/src/chat/planImplementation.ts +0 -62
- package/src/chat/slashCommandHandlers.ts +0 -122
- package/src/chat/slashCommandViews.ts +0 -120
- package/src/chat/transcript/TranscriptView.tsx +0 -184
- package/src/chat/transcript/transcriptViewport.ts +0 -295
- package/src/chat/views/ContextLimitView.tsx +0 -95
- package/src/chat/views/ContinuityEditReviewView.tsx +0 -50
- package/src/chat/views/CopyPicker.tsx +0 -50
- package/src/chat/views/PermissionPrompt.tsx +0 -156
- package/src/chat/views/PermissionsView.tsx +0 -165
- package/src/chat/views/PlanApprovalView.tsx +0 -91
- package/src/chat/views/ResumeView.tsx +0 -273
- package/src/chat/views/RewindView.tsx +0 -412
- package/src/cli/preview.tsx +0 -14
- package/src/cli/updateNotice.ts +0 -54
- package/src/identity/continuity/privateEdit/apply.ts +0 -170
- package/src/identity/continuity/privateEdit/diff.ts +0 -6
- package/src/identity/continuity/privateEdit/files.ts +0 -23
- package/src/identity/continuity/privateEdit/types.ts +0 -28
- package/src/identity/continuity/privateEdit.ts +0 -46
- package/src/identity/hub/IdentityHub.tsx +0 -14
- package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +0 -104
- package/src/identity/hub/ens/effects.ts +0 -218
- package/src/identity/hub/shared/components/MenuScreen.tsx +0 -241
- package/src/identity/hub/shared/effects/types.ts +0 -53
- package/src/identity/hub/shared/reconciliation/index.ts +0 -14
- package/src/identity/wallet/page/grainient.ts +0 -278
- package/src/identity/wallet/page/html.ts +0 -28
- package/src/identity/wallet/page/styles/base.ts +0 -260
- package/src/identity/wallet/page/styles/components.ts +0 -262
- package/src/identity/wallet/page/styles/index.ts +0 -5
- package/src/identity/wallet/page/styles/responsive.ts +0 -247
- package/src/identity/wallet/page.tsx +0 -38
- package/src/mcp/approvals.ts +0 -113
- package/src/mcp/config.ts +0 -235
- package/src/mcp/manager.ts +0 -482
- package/src/mcp/managerHelpers.ts +0 -70
- package/src/mcp/names.ts +0 -19
- package/src/mcp/output.ts +0 -96
- package/src/models/ModelPicker.tsx +0 -1009
- package/src/models/catalog.ts +0 -327
- package/src/models/huggingface.ts +0 -712
- package/src/models/huggingfaceStorage.ts +0 -136
- package/src/models/llamacpp.ts +0 -848
- package/src/models/llamacppCommands.ts +0 -44
- package/src/models/llamacppConfig.ts +0 -34
- package/src/models/llamacppDiscovery.ts +0 -176
- package/src/models/llamacppOutput.ts +0 -65
- package/src/models/llamacppPreflight.ts +0 -158
- package/src/models/modelDisplay.ts +0 -180
- package/src/models/modelPickerCatalogFlow.ts +0 -56
- package/src/models/modelPickerCredentials.ts +0 -166
- package/src/models/modelPickerData.ts +0 -41
- package/src/models/modelPickerDisplay.tsx +0 -132
- package/src/models/modelPickerHfFlow.ts +0 -192
- package/src/models/modelPickerLocalRunnerFlow.ts +0 -115
- package/src/models/modelPickerOptions.ts +0 -457
- package/src/models/modelPickerTypes.ts +0 -69
- package/src/models/modelPickerUninstallFlow.ts +0 -48
- package/src/models/modelPickerViewHelpers.ts +0 -174
- package/src/models/modelRecommendation.ts +0 -139
- package/src/models/providerDisplay.ts +0 -16
- package/src/models/runtimeDetection.ts +0 -81
- package/src/models/uncensoredCatalog.ts +0 -86
- package/src/providers/anthropic.ts +0 -290
- package/src/providers/contracts.ts +0 -71
- package/src/providers/errors.ts +0 -80
- package/src/providers/gemini.ts +0 -391
- package/src/providers/openai-chat.ts +0 -474
- package/src/providers/openai-responses-format.ts +0 -177
- package/src/providers/openai-responses.ts +0 -306
- package/src/providers/openaiChatWire.ts +0 -124
- package/src/providers/registry.ts +0 -120
- package/src/providers/retry.ts +0 -58
- package/src/providers/sse.ts +0 -93
- package/src/runtime/compaction.ts +0 -395
- package/src/runtime/cwd.ts +0 -43
- package/src/runtime/providerTurn.ts +0 -38
- package/src/runtime/sessionMode.ts +0 -55
- package/src/runtime/systemPrompt.ts +0 -213
- package/src/runtime/textToolParser.ts +0 -161
- package/src/runtime/toolClaimGuards.ts +0 -143
- package/src/runtime/toolExecution.ts +0 -304
- package/src/runtime/toolIntent.ts +0 -143
- package/src/runtime/turn.ts +0 -369
- package/src/runtime/turnNudges.ts +0 -223
- package/src/runtime/turnTypes.ts +0 -86
- package/src/storage/factoryReset.ts +0 -127
- package/src/storage/history.ts +0 -58
- package/src/storage/permissions.ts +0 -76
- package/src/storage/rewind.ts +0 -266
- package/src/storage/sessionExport.ts +0 -49
- package/src/storage/sessions.ts +0 -495
- package/src/tools/bashSafety.ts +0 -186
- package/src/tools/bashTool.ts +0 -140
- package/src/tools/changeDirectoryTool.ts +0 -213
- package/src/tools/contracts.ts +0 -192
- package/src/tools/deleteFileTool.ts +0 -116
- package/src/tools/editTool.ts +0 -165
- package/src/tools/editUtils.ts +0 -170
- package/src/tools/fileDiff.ts +0 -261
- package/src/tools/listDirectoryTool.ts +0 -55
- package/src/tools/listSkillFilesTool.ts +0 -77
- package/src/tools/listSkillsTool.ts +0 -68
- package/src/tools/mcpResourceTools.ts +0 -95
- package/src/tools/permissionRules.ts +0 -85
- package/src/tools/privateContinuityEditTool.ts +0 -187
- package/src/tools/privateContinuityReadTool.ts +0 -106
- package/src/tools/readSkillTool.ts +0 -107
- package/src/tools/readTool.ts +0 -85
- package/src/tools/registry.ts +0 -103
- package/src/tools/writeFileTool.ts +0 -167
- package/src/ui/BrandSplash.tsx +0 -133
- package/src/ui/terminalTitle.ts +0 -30
- package/src/utils/images.ts +0 -140
- package/src/utils/markdownSegments.ts +0 -51
- package/src/utils/messages.ts +0 -37
- package/src/utils/withRetry.ts +0 -324
- /package/src/identity/{hub → manager}/continuity/state.ts +0 -0
- /package/src/identity/{hub → manager}/custody/helpers.ts +0 -0
- /package/src/identity/{hub → manager}/custody/preflight.ts +0 -0
- /package/src/identity/{hub → manager}/custody/state.ts +0 -0
- /package/src/identity/{hub → manager}/custody/useCustodyFlow.tsx +0 -0
- /package/src/identity/{hub → manager}/ens/EnsEditFlow.tsx +0 -0
- /package/src/identity/{hub → manager}/ens/advancedEnsValidation.ts +0 -0
- /package/src/identity/{hub → manager}/ens/state.ts +0 -0
- /package/src/identity/{hub → manager}/ens/transactions.ts +0 -0
- /package/src/identity/{hub → manager}/ens/types.ts +0 -0
- /package/src/identity/{hub → manager}/profile/identity.ts +0 -0
- /package/src/identity/{hub → manager}/restore/envelopes.ts +0 -0
- /package/src/identity/{hub → manager}/restore/helpers.ts +0 -0
- /package/src/identity/{hub → manager}/restore/recovery.ts +0 -0
- /package/src/identity/{hub → manager}/restore/resolve.ts +0 -0
- /package/src/identity/{hub → manager}/shared/components/BusyScreen.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/components/NetworkScreen.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/components/PinataJwtInput.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/effects/receipts.ts +0 -0
- /package/src/identity/{hub → manager}/shared/effects/sync.ts +0 -0
- /package/src/identity/{hub → manager}/shared/model/format.ts +0 -0
- /package/src/identity/{hub → manager}/shared/operatorWallets.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/ownership.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/types.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/walletSetup.ts +0 -0
- /package/src/identity/{hub → manager}/shared/txGuard.ts +0 -0
- /package/src/identity/{hub → manager}/transfer/progress.ts +0 -0
- /package/src/identity/{hub → manager}/transfer/state.ts +0 -0
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
import { networkLabel } from '../shared/model/network.js'
|
|
15
15
|
import { shortAddress } from '../shared/model/format.js'
|
|
16
16
|
import { registryConfigFromConfig } from '../../registry/registryConfig.js'
|
|
17
|
-
import type { Step } from '../
|
|
17
|
+
import type { Step } from '../reducer.js'
|
|
18
18
|
import { WalletApprovalScreen } from '../shared/components/WalletApprovalScreen.js'
|
|
19
19
|
import { BusyScreen } from '../shared/components/BusyScreen.js'
|
|
20
20
|
import type { BrowserWalletReady } from '../../wallet/browserWallet.js'
|
|
@@ -92,7 +92,7 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
92
92
|
<BusyScreen
|
|
93
93
|
title={isSwitch ? 'Finding Agents' : 'Finding Agents'}
|
|
94
94
|
subtitle={step.ownerHandle}
|
|
95
|
-
label="checking tokens
|
|
95
|
+
label="checking owned tokens..."
|
|
96
96
|
onCancel={onBack}
|
|
97
97
|
/>
|
|
98
98
|
)
|
|
@@ -102,17 +102,14 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
102
102
|
return (
|
|
103
103
|
<Surface
|
|
104
104
|
title={isSwitch ? 'Switch Agent' : 'Restore Agent'}
|
|
105
|
-
subtitle="
|
|
105
|
+
subtitle="This wallet doesn't directly own an agent token."
|
|
106
106
|
footer={footerHint('enter select · esc back')}
|
|
107
107
|
>
|
|
108
|
-
<Text color={theme.dim}>If this wallet is an approved operator wallet, choose how to find the agent token.</Text>
|
|
109
108
|
<Select<'ens' | 'token-id' | 'back'>
|
|
110
109
|
options={[
|
|
111
|
-
{ value: 'ens',
|
|
112
|
-
{ value: '
|
|
113
|
-
{ value: '
|
|
114
|
-
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
115
|
-
{ value: 'back', label: 'Back', hint: 'Pick a different network', role: 'utility' },
|
|
110
|
+
{ value: 'ens', label: 'Enter ENS Name', hint: 'Via ENS subdomain' },
|
|
111
|
+
{ value: 'token-id', label: 'Enter Token ID', hint: 'By token ID' },
|
|
112
|
+
{ value: 'back', label: 'Back', role: 'utility' },
|
|
116
113
|
]}
|
|
117
114
|
hintLayout="inline"
|
|
118
115
|
onSubmit={value => value === 'back' ? onBack() : onPickRecoveryMethod(value)}
|
|
@@ -131,7 +128,7 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
131
128
|
footer={footerHint('esc cancels')}
|
|
132
129
|
>
|
|
133
130
|
<Box marginTop={1}>
|
|
134
|
-
<Spinner label="looking up ENS
|
|
131
|
+
<Spinner label="looking up ENS..." />
|
|
135
132
|
</Box>
|
|
136
133
|
</Surface>
|
|
137
134
|
)
|
|
@@ -139,10 +136,9 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
139
136
|
return (
|
|
140
137
|
<Surface
|
|
141
138
|
title={isSwitch ? 'Switch Agent' : 'Restore Agent'}
|
|
142
|
-
subtitle="Enter the agent
|
|
139
|
+
subtitle="Enter the agent ENS name."
|
|
143
140
|
footer={footerHint('enter continue · esc back')}
|
|
144
141
|
>
|
|
145
|
-
<Text color={theme.dim}>The full agent subdomain, e.g. agent.example.eth.</Text>
|
|
146
142
|
<TextInput
|
|
147
143
|
placeholder="agent.example.eth"
|
|
148
144
|
onSubmit={value => onEnsSubmit(value.trim())}
|
|
@@ -167,7 +163,7 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
167
163
|
footer={footerHint('esc cancels')}
|
|
168
164
|
>
|
|
169
165
|
<Box marginTop={1}>
|
|
170
|
-
<Spinner label="looking up token
|
|
166
|
+
<Spinner label="looking up token..." />
|
|
171
167
|
</Box>
|
|
172
168
|
</Surface>
|
|
173
169
|
)
|
|
@@ -175,10 +171,9 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
175
171
|
return (
|
|
176
172
|
<Surface
|
|
177
173
|
title={isSwitch ? 'Switch Agent' : 'Restore Agent'}
|
|
178
|
-
subtitle={`
|
|
174
|
+
subtitle={`Token ID on ${networkLabelForRegistry(step.registry)}.`}
|
|
179
175
|
footer={footerHint('enter continue · esc back')}
|
|
180
176
|
>
|
|
181
|
-
<Text color={theme.dim}>The integer token ID assigned at mint.</Text>
|
|
182
177
|
<TextInput
|
|
183
178
|
placeholder="45744"
|
|
184
179
|
onSubmit={value => onTokenIdSubmit(value.trim())}
|
|
@@ -205,9 +200,8 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
205
200
|
<Text color={theme.dim}>{view.detail}</Text>
|
|
206
201
|
<Select<'retry' | 'network'>
|
|
207
202
|
options={[
|
|
208
|
-
{ value: 'retry',
|
|
209
|
-
{ value: '
|
|
210
|
-
{ value: 'network', label: 'Choose Network', hint: 'Search a different ERC-8004 network' },
|
|
203
|
+
{ value: 'retry', label: 'Retry Search' },
|
|
204
|
+
{ value: 'network', label: 'Choose Network' },
|
|
211
205
|
]}
|
|
212
206
|
hintLayout="inline"
|
|
213
207
|
onSubmit={choice => {
|
|
@@ -238,11 +232,9 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
238
232
|
hint: tokenCandidateHint(candidate),
|
|
239
233
|
}
|
|
240
234
|
}),
|
|
241
|
-
{ value: '__ens__',
|
|
242
|
-
{ value: '
|
|
243
|
-
{ value: '
|
|
244
|
-
{ value: '__back__', role: 'section', label: 'Navigation' },
|
|
245
|
-
{ value: '__back__', label: 'Back', hint: 'Return to the previous step', role: 'utility' },
|
|
235
|
+
{ value: '__ens__', label: 'Enter ENS Name', hint: 'Via ENS subdomain' },
|
|
236
|
+
{ value: '__token-id__', label: 'Enter Token ID', hint: 'By token ID' },
|
|
237
|
+
{ value: '__back__', label: 'Back', role: 'utility' },
|
|
246
238
|
]}
|
|
247
239
|
hintLayout="inline"
|
|
248
240
|
onSubmit={value => {
|
|
@@ -262,7 +254,7 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
262
254
|
<BusyScreen
|
|
263
255
|
title={isSwitch ? 'Switching Agent' : 'Restoring Your Agent'}
|
|
264
256
|
subtitle="IPFS"
|
|
265
|
-
label="opening
|
|
257
|
+
label="opening from IPFS..."
|
|
266
258
|
onCancel={onBack}
|
|
267
259
|
/>
|
|
268
260
|
)
|
|
@@ -14,7 +14,7 @@ import { syncAgentCardManifest } from '../../continuity/skills/publicSkillsSync.
|
|
|
14
14
|
import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
|
|
15
15
|
import { requestBrowserWalletSignature } from '../../wallet/browserWallet.js'
|
|
16
16
|
import { setVaultAddressField } from '../../identityCompat.js'
|
|
17
|
-
import type { Step } from '../
|
|
17
|
+
import type { Step } from '../reducer.js'
|
|
18
18
|
import type { EffectCallbacks } from '../shared/effects/types.js'
|
|
19
19
|
import { isContinuitySnapshotEnvelope } from './envelopes.js'
|
|
20
20
|
import { restoreSignatureRequestForStep } from './auth.js'
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { assertAgentStateBackupOwner } from '../../crypto/backupEnvelope.js'
|
|
11
11
|
import type { Erc8004AgentCandidate } from '../../registry/erc8004.js'
|
|
12
12
|
import { requestBrowserWalletAccount, type WalletPurpose } from '../../wallet/browserWallet.js'
|
|
13
|
-
import type { Step } from '../
|
|
13
|
+
import type { Step } from '../reducer.js'
|
|
14
14
|
import type { EffectCallbacks } from '../shared/effects/types.js'
|
|
15
15
|
import { isContinuitySnapshotEnvelope, parseRestorableEnvelope } from './envelopes.js'
|
|
16
16
|
import {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type Erc8004AgentCandidate,
|
|
8
8
|
type Erc8004RegistryConfig,
|
|
9
9
|
} from '../../registry/erc8004.js'
|
|
10
|
-
import type { RestorePurpose, Step } from '../
|
|
10
|
+
import type { RestorePurpose, Step } from '../reducer.js'
|
|
11
11
|
import type { EffectCallbacks } from '../shared/effects/types.js'
|
|
12
12
|
import { isAbortError, isAuthorizedOperatorAddress, requesterAddressFromHandle } from './helpers.js'
|
|
13
13
|
import type { Address } from 'viem'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { catFromIpfs } from '../../storage/ipfs.js'
|
|
2
|
-
import type { Step } from '../
|
|
2
|
+
import type { Step } from '../reducer.js'
|
|
3
3
|
import type { EffectCallbacks } from '../shared/effects/types.js'
|
|
4
4
|
import { parseRestorableEnvelope } from './envelopes.js'
|
|
5
5
|
import { assertCandidateCanReadEnvelope, restoreSignatureRequestForStep } from './auth.js'
|
|
@@ -2,7 +2,7 @@ import type { EthagentConfig } from '../../../storage/config.js'
|
|
|
2
2
|
import { saveConfig } from '../../../storage/config.js'
|
|
3
3
|
import { normalizeErc8004RegistryConfig } from '../../registry/erc8004.js'
|
|
4
4
|
import { registryConfigFromConfig } from '../../registry/registryConfig.js'
|
|
5
|
-
import type { Step } from '../
|
|
5
|
+
import type { Step } from '../reducer.js'
|
|
6
6
|
import type { EffectCallbacks } from '../shared/effects/types.js'
|
|
7
7
|
|
|
8
8
|
export async function runRestoreRegistrySubmit(
|
|
@@ -5,15 +5,8 @@ import { runRestoreConnectWallet } from './auth.js'
|
|
|
5
5
|
import { runRestoreDiscover } from './discover.js'
|
|
6
6
|
import { runRestoreFetch } from './fetch.js'
|
|
7
7
|
import type { EffectCallbacks } from '../shared/effects/types.js'
|
|
8
|
-
import type { Step } from '../
|
|
9
|
-
|
|
10
|
-
const MIN_BUSY_ERROR_MS = 2000
|
|
11
|
-
|
|
12
|
-
function waitForMinimumBusyTime(startedAt: number): Promise<void> {
|
|
13
|
-
const elapsed = Date.now() - startedAt
|
|
14
|
-
if (elapsed >= MIN_BUSY_ERROR_MS) return Promise.resolve()
|
|
15
|
-
return new Promise(resolve => setTimeout(resolve, MIN_BUSY_ERROR_MS - elapsed))
|
|
16
|
-
}
|
|
8
|
+
import type { Step } from '../reducer.js'
|
|
9
|
+
import { waitForMinimumBusyTime } from '../shared/utils.js'
|
|
17
10
|
|
|
18
11
|
type RestoreFlowEffectsArgs = {
|
|
19
12
|
step: Step
|
|
@@ -4,17 +4,10 @@ import { Surface } from '../../../ui/Surface.js'
|
|
|
4
4
|
import { Select } from '../../../ui/Select.js'
|
|
5
5
|
import { theme } from '../../../ui/theme.js'
|
|
6
6
|
import { PinataJwtInput } from '../shared/components/PinataJwtInput.js'
|
|
7
|
-
import type { Step } from '../
|
|
7
|
+
import type { Step } from '../reducer.js'
|
|
8
8
|
|
|
9
9
|
type StorageCredentialAction = 'edit' | 'forget' | 'back'
|
|
10
10
|
|
|
11
|
-
export const STORAGE_CREDENTIAL_FORGET_COPY = [
|
|
12
|
-
'removes the saved IPFS storage token from this machine.',
|
|
13
|
-
'existing pinned IPFS backups are not deleted.',
|
|
14
|
-
'new encrypted snapshots cannot be pinned with that account until you save a token again.',
|
|
15
|
-
'agent identity and sessions stay on this machine.',
|
|
16
|
-
] as const
|
|
17
|
-
|
|
18
11
|
type StorageCredentialScreenProps = {
|
|
19
12
|
step: Extract<Step, { kind: 'storage-credential' | 'storage-credential-input' | 'storage-credential-forget-confirm' }>
|
|
20
13
|
hasCredential: boolean
|
|
@@ -53,24 +46,18 @@ export const StorageCredentialScreen: React.FC<StorageCredentialScreenProps> = (
|
|
|
53
46
|
return (
|
|
54
47
|
<Surface
|
|
55
48
|
title="Forget IPFS Storage?"
|
|
56
|
-
subtitle="
|
|
49
|
+
subtitle="Local token only. Pinned files are not deleted."
|
|
57
50
|
footer={footer}
|
|
58
51
|
>
|
|
59
52
|
<Box flexDirection="column">
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
))}
|
|
63
|
-
</Box>
|
|
64
|
-
<Box marginTop={1}>
|
|
65
|
-
<Text color={theme.accentPeriwinkle}>Remove the token from this machine?</Text>
|
|
53
|
+
<Text color={theme.dim}>Removes the saved pinning token from this machine.</Text>
|
|
54
|
+
<Text color={theme.dim}>Existing IPFS backups and agent data are not affected.</Text>
|
|
66
55
|
</Box>
|
|
67
56
|
<Box marginTop={1}>
|
|
68
57
|
<Select<StorageCredentialAction>
|
|
69
58
|
options={[
|
|
70
|
-
{ value: 'forget',
|
|
71
|
-
{ value: '
|
|
72
|
-
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
73
|
-
{ value: 'back', label: 'Keep Credential', hint: 'Return without changing storage access', role: 'utility' },
|
|
59
|
+
{ value: 'forget', label: 'Forget Credential' },
|
|
60
|
+
{ value: 'back', label: 'Keep Credential', role: 'utility' },
|
|
74
61
|
]}
|
|
75
62
|
hintLayout="inline"
|
|
76
63
|
onSubmit={choice => choice === 'forget' ? onConfirmForget() : onCancel()}
|
|
@@ -84,17 +71,15 @@ export const StorageCredentialScreen: React.FC<StorageCredentialScreenProps> = (
|
|
|
84
71
|
return (
|
|
85
72
|
<Surface
|
|
86
73
|
title="IPFS Storage"
|
|
87
|
-
subtitle="
|
|
74
|
+
subtitle="Pin encrypted snapshots from this machine."
|
|
88
75
|
footer={footer}
|
|
89
76
|
>
|
|
90
77
|
<Box marginTop={1}>
|
|
91
78
|
<Select<StorageCredentialAction>
|
|
92
79
|
options={[
|
|
93
|
-
{ value: 'edit',
|
|
94
|
-
{ value: '
|
|
95
|
-
{ value: '
|
|
96
|
-
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
97
|
-
{ value: 'back', label: 'Back', hint: 'Return to Identity Hub menu', role: 'utility' },
|
|
80
|
+
{ value: 'edit', label: hasCredential ? 'Replace Credential' : 'Save Credential' },
|
|
81
|
+
{ value: 'forget', label: 'Forget Credential', disabled: !hasCredential },
|
|
82
|
+
{ value: 'back', label: 'Back', role: 'utility' },
|
|
98
83
|
]}
|
|
99
84
|
hintLayout="inline"
|
|
100
85
|
onSubmit={choice => {
|
|
@@ -4,7 +4,7 @@ import { theme } from '../../../../ui/theme.js'
|
|
|
4
4
|
import { Surface } from '../../../../ui/Surface.js'
|
|
5
5
|
import { Select, type SelectOption } from '../../../../ui/Select.js'
|
|
6
6
|
import type { EthagentConfig, EthagentIdentity } from '../../../../storage/config.js'
|
|
7
|
-
import { copyableIdentityFields
|
|
7
|
+
import { copyableIdentityFields } from '../model/copy.js'
|
|
8
8
|
import { IdentitySummary } from './IdentitySummary.js'
|
|
9
9
|
import type { ContinuityWorkingTreeStatus } from '../../../continuity/storage.js'
|
|
10
10
|
|
|
@@ -35,19 +35,17 @@ export const DetailsScreen: React.FC<DetailsScreenProps> = ({
|
|
|
35
35
|
}) => {
|
|
36
36
|
const copyable = copyableIdentityFields(identity, config)
|
|
37
37
|
const options: Array<SelectOption<CopyAction>> = [
|
|
38
|
-
...(copyable.length > 0 ? [{ value: 'back' as const, role: 'section' as const, label: 'Values' }] : []),
|
|
39
38
|
...copyable.map(field => ({
|
|
40
39
|
value: `copy:${field.label}` as const,
|
|
41
40
|
label: field.label,
|
|
42
41
|
hint: shortPreview(field.value),
|
|
43
42
|
})),
|
|
44
43
|
...(copyable.length === 0 ? [{ value: 'back' as const, role: 'notice' as const, label: 'No Values Available Yet' }] : []),
|
|
45
|
-
{ value: 'back',
|
|
46
|
-
{ value: 'back', label: 'Back', hint: 'Return to Identity Hub menu', role: 'utility' },
|
|
44
|
+
{ value: 'back', label: 'Back', role: 'utility' },
|
|
47
45
|
]
|
|
48
46
|
|
|
49
47
|
return (
|
|
50
|
-
<Surface title="Token Values" subtitle={unlinked ? 'Token
|
|
48
|
+
<Surface title="Token Values" subtitle={unlinked ? 'Token unlinked; values kept for reference.' : undefined} footer={footer}>
|
|
51
49
|
<IdentitySummary identity={identity} config={config} workingStatus={workingStatus} onchainOwner={onchainOwner} />
|
|
52
50
|
{copyNotice ? <Text color={theme.accentPeriwinkle} bold>{copyNotice}</Text> : null}
|
|
53
51
|
<Box marginTop={1}>
|
|
@@ -68,6 +66,6 @@ export const DetailsScreen: React.FC<DetailsScreenProps> = ({
|
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
function shortPreview(value: string): string {
|
|
71
|
-
if (value.length <=
|
|
72
|
-
return `${value.slice(0,
|
|
69
|
+
if (value.length <= 22) return value
|
|
70
|
+
return `${value.slice(0, 11)}...${value.slice(-8)}`
|
|
73
71
|
}
|
|
@@ -3,15 +3,14 @@ import { Text } from 'ink'
|
|
|
3
3
|
import { Surface } from '../../../../ui/Surface.js'
|
|
4
4
|
import { Select } from '../../../../ui/Select.js'
|
|
5
5
|
import { theme } from '../../../../ui/theme.js'
|
|
6
|
-
import type {
|
|
7
|
-
import type { Step } from '../../
|
|
6
|
+
import type { IdentityManagerErrorView } from '../model/errors.js'
|
|
7
|
+
import type { Step } from '../../reducer.js'
|
|
8
8
|
|
|
9
9
|
type ErrorScreenProps = {
|
|
10
|
-
error:
|
|
10
|
+
error: IdentityManagerErrorView
|
|
11
11
|
back: Step
|
|
12
12
|
footer: React.ReactNode
|
|
13
13
|
closeLabel?: string
|
|
14
|
-
closeHint?: string
|
|
15
14
|
onBack: (back: Step) => void
|
|
16
15
|
onClose: () => void
|
|
17
16
|
}
|
|
@@ -20,8 +19,7 @@ export const ErrorScreen: React.FC<ErrorScreenProps> = ({
|
|
|
20
19
|
error,
|
|
21
20
|
back,
|
|
22
21
|
footer,
|
|
23
|
-
closeLabel = 'Close
|
|
24
|
-
closeHint = 'Return to chat without retrying',
|
|
22
|
+
closeLabel = 'Close',
|
|
25
23
|
onBack,
|
|
26
24
|
onClose,
|
|
27
25
|
}) => (
|
|
@@ -29,10 +27,8 @@ export const ErrorScreen: React.FC<ErrorScreenProps> = ({
|
|
|
29
27
|
{error.hint ? <Text color={theme.dim}>{error.hint}</Text> : null}
|
|
30
28
|
<Select<'back' | 'close'>
|
|
31
29
|
options={[
|
|
32
|
-
{ value: 'back',
|
|
33
|
-
{ value: '
|
|
34
|
-
{ value: 'close', role: 'section', label: 'Exit' },
|
|
35
|
-
{ value: 'close', label: closeLabel, hint: closeHint, role: 'utility' },
|
|
30
|
+
{ value: 'back', label: 'Back' },
|
|
31
|
+
{ value: 'close', label: closeLabel, role: 'utility' },
|
|
36
32
|
]}
|
|
37
33
|
hintLayout="inline"
|
|
38
34
|
onSubmit={choice => {
|
|
@@ -13,15 +13,16 @@ export const FlowTimeline: React.FC<FlowTimelineProps> = ({ steps, current }) =>
|
|
|
13
13
|
const n = index + 1
|
|
14
14
|
const active = n === current
|
|
15
15
|
const done = n < current
|
|
16
|
-
const glyph =
|
|
16
|
+
const glyph = active ? '●' : done ? '●' : '○'
|
|
17
17
|
return (
|
|
18
18
|
<React.Fragment key={`${index}:${step}`}>
|
|
19
|
-
{index > 0 ? <Text>
|
|
19
|
+
{index > 0 ? <Text> </Text> : null}
|
|
20
20
|
<Text color={active ? theme.accentPeriwinkle : done ? theme.dim : theme.textSubtle} bold={active}>
|
|
21
|
-
{glyph}
|
|
21
|
+
{glyph}
|
|
22
22
|
</Text>
|
|
23
23
|
</React.Fragment>
|
|
24
24
|
)
|
|
25
25
|
})}
|
|
26
|
+
<Text color={theme.dim}>{` ${current}/${steps.length}`}</Text>
|
|
26
27
|
</Text>
|
|
27
28
|
)
|
|
@@ -8,11 +8,7 @@ import {
|
|
|
8
8
|
readCustodyMode,
|
|
9
9
|
readIdentityStateString,
|
|
10
10
|
} from '../../custody/state.js'
|
|
11
|
-
import {
|
|
12
|
-
hasPendingPublish,
|
|
13
|
-
localChangeStatusView,
|
|
14
|
-
type LocalChangeStatusView,
|
|
15
|
-
} from '../../continuity/state.js'
|
|
11
|
+
import { hasPendingPublish } from '../../continuity/state.js'
|
|
16
12
|
import { ensValidationReasonText, selectEnsStatus } from '../../ens/state.js'
|
|
17
13
|
import { shortAddress } from '../model/format.js'
|
|
18
14
|
import { identitySummaryRows, lastBackupLabel } from '../../profile/identity.js'
|
|
@@ -24,14 +20,13 @@ interface IdentitySummaryProps {
|
|
|
24
20
|
identity?: EthagentIdentity
|
|
25
21
|
config?: EthagentConfig
|
|
26
22
|
workingStatus?: ContinuityWorkingTreeStatus | null
|
|
27
|
-
hideLocalChanges?: boolean
|
|
28
23
|
hideHeader?: boolean
|
|
29
24
|
tokenLinked?: boolean
|
|
30
25
|
onchainOwner?: string
|
|
31
26
|
compact?: boolean
|
|
32
27
|
}
|
|
33
28
|
|
|
34
|
-
export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config,
|
|
29
|
+
export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config, hideHeader = false, tokenLinked = true, onchainOwner, compact = false }) => {
|
|
35
30
|
if (!identity) {
|
|
36
31
|
return (
|
|
37
32
|
<Text color={theme.dim}>No agent yet. Create or load one.</Text>
|
|
@@ -43,7 +38,6 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
43
38
|
const stateName = readIdentityStateString(identity.state, 'name')
|
|
44
39
|
|
|
45
40
|
const row = (label: string) => rows.find(item => item.label === label)
|
|
46
|
-
const localChangeStatus = localChangeStatusView(workingStatus)
|
|
47
41
|
|
|
48
42
|
const ensStatus = selectEnsStatus(identity)
|
|
49
43
|
const custodyMode = readCustodyMode(identity.state)
|
|
@@ -61,9 +55,10 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
61
55
|
: displayValue(tokenValue)
|
|
62
56
|
|
|
63
57
|
if (compact) {
|
|
64
|
-
const
|
|
58
|
+
const rawName = stateName || 'Active Agent'
|
|
59
|
+
const name = rawName.length > 16 ? `${rawName.slice(0, 15)}…` : rawName
|
|
65
60
|
const tokenSegment = identity.agentId ? `#${identity.agentId}` : null
|
|
66
|
-
const networkSegment = identity.agentId ?
|
|
61
|
+
const networkSegment = identity.agentId ? networkValue : null
|
|
67
62
|
const ensSegment = ensStatus.kind === 'linked'
|
|
68
63
|
? ensStatus.name
|
|
69
64
|
: ensStatus.kind === 'issue'
|
|
@@ -71,9 +66,9 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
71
66
|
: null
|
|
72
67
|
return (
|
|
73
68
|
<Text>
|
|
74
|
-
<Text color={theme.
|
|
75
|
-
{tokenSegment ? <><Text color={theme.dim}> · </Text><Text color={theme.
|
|
76
|
-
{networkSegment ? <><Text color={theme.dim}> · </Text><Text color={theme.
|
|
69
|
+
<Text color={theme.textSubtle}>{name}</Text>
|
|
70
|
+
{tokenSegment ? <><Text color={theme.dim}> · </Text><Text color={theme.dim}>{tokenSegment}</Text></> : null}
|
|
71
|
+
{networkSegment ? <><Text color={theme.dim}> · </Text><Text color={theme.dim}>{networkSegment}</Text></> : null}
|
|
77
72
|
{ensSegment ? <><Text color={theme.dim}> · </Text><Text color={ensStatus.kind === 'issue' ? theme.accentError : theme.accentPeriwinkle}>{ensSegment}</Text></> : null}
|
|
78
73
|
</Text>
|
|
79
74
|
)
|
|
@@ -153,11 +148,6 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
153
148
|
<TransferSnapshotStatus status={transferSnapshot} />
|
|
154
149
|
</Box>
|
|
155
150
|
) : null}
|
|
156
|
-
{!hideLocalChanges && (
|
|
157
|
-
<Box marginTop={1}>
|
|
158
|
-
<LocalChangeStatusLine status={localChangeStatus} />
|
|
159
|
-
</Box>
|
|
160
|
-
)}
|
|
161
151
|
</Box>
|
|
162
152
|
)
|
|
163
153
|
}
|
|
@@ -165,32 +155,22 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
165
155
|
type SummaryCell = { label: string; value: React.ReactNode }
|
|
166
156
|
|
|
167
157
|
const LEFT_LABEL_WIDTH = 12
|
|
168
|
-
|
|
169
|
-
const
|
|
158
|
+
|
|
159
|
+
const SummaryCellLine: React.FC<{ cell: SummaryCell }> = ({ cell }) => (
|
|
160
|
+
<Text>
|
|
161
|
+
<Text color={theme.dim}>{cell.label.padEnd(LEFT_LABEL_WIDTH)}</Text>
|
|
162
|
+
{cell.value}
|
|
163
|
+
</Text>
|
|
164
|
+
)
|
|
170
165
|
|
|
171
166
|
const SummaryRow: React.FC<{ left: SummaryCell; right?: SummaryCell }> = ({ left, right }) => {
|
|
172
167
|
if (!right) {
|
|
173
|
-
return
|
|
174
|
-
<Text>
|
|
175
|
-
<Text color={theme.dim}>{left.label.padEnd(LEFT_LABEL_WIDTH)}</Text>
|
|
176
|
-
{left.value}
|
|
177
|
-
</Text>
|
|
178
|
-
)
|
|
168
|
+
return <SummaryCellLine cell={left} />
|
|
179
169
|
}
|
|
180
170
|
return (
|
|
181
|
-
<Box flexDirection="
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
<Text color={theme.dim}>{left.label.padEnd(LEFT_LABEL_WIDTH)}</Text>
|
|
185
|
-
{left.value}
|
|
186
|
-
</Text>
|
|
187
|
-
</Box>
|
|
188
|
-
<Box width={RIGHT_CELL_WIDTH}>
|
|
189
|
-
<Text>
|
|
190
|
-
<Text color={theme.dim}>{right.label.padEnd(LEFT_LABEL_WIDTH)}</Text>
|
|
191
|
-
{right.value}
|
|
192
|
-
</Text>
|
|
193
|
-
</Box>
|
|
171
|
+
<Box flexDirection="column">
|
|
172
|
+
<SummaryCellLine cell={left} />
|
|
173
|
+
<SummaryCellLine cell={right} />
|
|
194
174
|
</Box>
|
|
195
175
|
)
|
|
196
176
|
}
|
|
@@ -221,26 +201,6 @@ const TransferSnapshotStatus: React.FC<{ status: NonNullable<TransferSnapshotVie
|
|
|
221
201
|
)
|
|
222
202
|
}
|
|
223
203
|
|
|
224
|
-
const LocalChangeStatusLine: React.FC<{ status: LocalChangeStatusView }> = ({ status }) => {
|
|
225
|
-
if (status.hasLocalChanges) {
|
|
226
|
-
return (
|
|
227
|
-
<Box flexDirection="column">
|
|
228
|
-
<Text color={theme.accentError} bold>
|
|
229
|
-
Local changes detected
|
|
230
|
-
{status.files.length > 0 ? `: ${status.files.join(', ')}` : ''}
|
|
231
|
-
</Text>
|
|
232
|
-
<Text color={theme.dim}>Save Snapshot Now to publish.</Text>
|
|
233
|
-
</Box>
|
|
234
|
-
)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (!status.detail) return null
|
|
238
|
-
|
|
239
|
-
const color = status.tone === 'ok' || status.tone === 'warn' ? theme.accentPeriwinkle : theme.dim
|
|
240
|
-
const label = status.detail === 'None detected' ? 'No local changes detected' : status.detail
|
|
241
|
-
return <Text color={color}>{label}</Text>
|
|
242
|
-
}
|
|
243
|
-
|
|
244
204
|
function displayValue(value: string): string {
|
|
245
205
|
const mapped = DISPLAY_VALUES[value]
|
|
246
206
|
return mapped ?? value
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { theme, gradientColor, PANEL_WIDTH } from '../../../../ui/theme.js'
|
|
4
|
+
import { useAppInput } from '../../../../app/input/AppInputProvider.js'
|
|
5
|
+
|
|
6
|
+
export type LazyMenuItem<T> = {
|
|
7
|
+
kind?: 'item'
|
|
8
|
+
value: T
|
|
9
|
+
label: string
|
|
10
|
+
shortcut?: string
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
hint?: string
|
|
13
|
+
note?: string
|
|
14
|
+
noteColor?: string
|
|
15
|
+
inlineNote?: string
|
|
16
|
+
inlineNoteColor?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type LazyMenuSection = {
|
|
20
|
+
kind: 'section'
|
|
21
|
+
label: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type LazyMenuRow<T> = LazyMenuItem<T> | LazyMenuSection
|
|
25
|
+
|
|
26
|
+
function isItem<T>(row: LazyMenuRow<T>): row is LazyMenuItem<T> {
|
|
27
|
+
return row.kind !== 'section'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function rainbowColor(index: number, total: number): string {
|
|
31
|
+
return gradientColor(total <= 1 ? 0 : index / (total - 1))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type Props<T> = {
|
|
35
|
+
rows: Array<LazyMenuRow<T>>
|
|
36
|
+
width?: number
|
|
37
|
+
onSubmit: (value: T) => void
|
|
38
|
+
onCancel?: () => void
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function LazyMenu<T>({ rows, width = PANEL_WIDTH, onSubmit, onCancel }: Props<T>) {
|
|
42
|
+
const firstSelectable = Math.max(0, rows.findIndex(r => isItem(r) && !r.disabled))
|
|
43
|
+
const [index, setIndex] = useState(firstSelectable)
|
|
44
|
+
|
|
45
|
+
const sig = useMemo(
|
|
46
|
+
() => rows.map(r => isItem(r) ? `i:${r.shortcut ?? ''}${r.disabled ? '!' : ''}` : `s:${r.label}`).join('|'),
|
|
47
|
+
[rows],
|
|
48
|
+
)
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setIndex(prev => {
|
|
51
|
+
const at = rows[prev]
|
|
52
|
+
if (at && isItem(at) && !at.disabled) return prev
|
|
53
|
+
const next = rows.findIndex(r => isItem(r) && !r.disabled)
|
|
54
|
+
return next === -1 ? 0 : next
|
|
55
|
+
})
|
|
56
|
+
}, [sig, rows])
|
|
57
|
+
|
|
58
|
+
const moveBy = (delta: number) => {
|
|
59
|
+
if (rows.length === 0) return
|
|
60
|
+
let next = index
|
|
61
|
+
for (let i = 0; i < rows.length; i += 1) {
|
|
62
|
+
next = (next + delta + rows.length) % rows.length
|
|
63
|
+
const candidate = rows[next]
|
|
64
|
+
if (candidate && isItem(candidate) && !candidate.disabled) { setIndex(next); return }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
useAppInput((input, key) => {
|
|
69
|
+
if (key.upArrow || input === 'k') moveBy(-1)
|
|
70
|
+
else if (key.downArrow || input === 'j') moveBy(1)
|
|
71
|
+
else if (key.return) {
|
|
72
|
+
const r = rows[index]
|
|
73
|
+
if (r && isItem(r) && !r.disabled) onSubmit(r.value)
|
|
74
|
+
} else if (key.escape || (key.ctrl && input === 'c')) {
|
|
75
|
+
onCancel?.()
|
|
76
|
+
} else if (input && !key.ctrl && !key.meta) {
|
|
77
|
+
const lower = input.toLowerCase()
|
|
78
|
+
const hit = rows.findIndex(r => isItem(r) && !r.disabled && r.shortcut?.toLowerCase() === lower)
|
|
79
|
+
if (hit >= 0) {
|
|
80
|
+
const candidate = rows[hit]!
|
|
81
|
+
if (isItem(candidate)) { setIndex(hit); onSubmit(candidate.value) }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Box flexDirection="column">
|
|
88
|
+
{rows.map((row, i) => {
|
|
89
|
+
if (!isItem(row)) {
|
|
90
|
+
return (
|
|
91
|
+
<React.Fragment key={i}>
|
|
92
|
+
<Box flexDirection="row" width={width}>
|
|
93
|
+
<Text color={theme.menuStatus} bold>{row.label}</Text>
|
|
94
|
+
</Box>
|
|
95
|
+
</React.Fragment>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
const active = i === index
|
|
99
|
+
const disabled = !!row.disabled
|
|
100
|
+
const cursorColor = disabled ? theme.border : active ? theme.accentPeriwinkle : theme.dim
|
|
101
|
+
const shortcutColor = disabled ? theme.border : theme.menuShortcut
|
|
102
|
+
const chars = row.label.split('')
|
|
103
|
+
const shortcut = row.shortcut
|
|
104
|
+
const inline = row.inlineNote
|
|
105
|
+
const inlineWidth = inline ? inline.length + 2 : 0
|
|
106
|
+
const pad = shortcut ? Math.max(2, width - (2 + row.label.length + inlineWidth + shortcut.length)) : 0
|
|
107
|
+
return (
|
|
108
|
+
<React.Fragment key={i}>
|
|
109
|
+
<Box flexDirection="row" {...(shortcut ? { width } : {})}>
|
|
110
|
+
<Text color={cursorColor}>{active ? '> ' : ' '}</Text>
|
|
111
|
+
<Text>
|
|
112
|
+
{active && !disabled
|
|
113
|
+
? chars.map((ch, ci) => (
|
|
114
|
+
<Text key={ci} color={rainbowColor(ci, chars.length)}>{ch}</Text>
|
|
115
|
+
))
|
|
116
|
+
: <Text color={disabled ? theme.border : theme.text}>{row.label}</Text>
|
|
117
|
+
}
|
|
118
|
+
</Text>
|
|
119
|
+
{inline ? (
|
|
120
|
+
<>
|
|
121
|
+
<Text>{' '}</Text>
|
|
122
|
+
<Text color={row.inlineNoteColor ?? theme.dim}>{inline}</Text>
|
|
123
|
+
</>
|
|
124
|
+
) : null}
|
|
125
|
+
{shortcut ? (
|
|
126
|
+
<>
|
|
127
|
+
<Text>{' '.repeat(pad)}</Text>
|
|
128
|
+
<Text color={shortcutColor}>{shortcut}</Text>
|
|
129
|
+
</>
|
|
130
|
+
) : null}
|
|
131
|
+
</Box>
|
|
132
|
+
{row.hint && disabled ? (
|
|
133
|
+
<Box paddingLeft={2}>
|
|
134
|
+
<Text color={theme.dim}>{row.hint}</Text>
|
|
135
|
+
</Box>
|
|
136
|
+
) : null}
|
|
137
|
+
{row.note ? (
|
|
138
|
+
<Box paddingLeft={2}>
|
|
139
|
+
<Text color={row.noteColor ?? theme.dim}>{row.note}</Text>
|
|
140
|
+
</Box>
|
|
141
|
+
) : null}
|
|
142
|
+
</React.Fragment>
|
|
143
|
+
)
|
|
144
|
+
})}
|
|
145
|
+
</Box>
|
|
146
|
+
)
|
|
147
|
+
}
|