ethagent 1.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +127 -29
- package/package.json +16 -9
- package/src/app/FirstRun.tsx +192 -146
- package/src/app/FirstRunTimeline.tsx +47 -0
- package/src/app/input/AppInputProvider.tsx +1 -1
- package/src/app/keybindings/KeybindingProvider.tsx +1 -1
- package/src/chat/ChatBottomPane.tsx +0 -1
- package/src/chat/ChatInput.tsx +6 -6
- package/src/chat/ChatScreen.tsx +43 -18
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +11 -17
- package/src/chat/ConversationStack.tsx +3 -0
- package/src/chat/CopyPicker.tsx +0 -1
- package/src/chat/MessageList.tsx +62 -45
- package/src/chat/PermissionPrompt.tsx +13 -9
- package/src/chat/PlanApprovalView.tsx +3 -3
- package/src/chat/ResumeView.tsx +1 -4
- package/src/chat/RewindView.tsx +2 -2
- package/src/chat/TranscriptView.tsx +6 -0
- package/src/chat/chatInputState.ts +1 -1
- package/src/chat/chatScreenUtils.ts +22 -11
- package/src/chat/chatSessionState.ts +2 -2
- package/src/chat/chatTurnOrchestrator.ts +16 -81
- package/src/chat/commands.ts +1 -1
- package/src/chat/textCursor.ts +1 -1
- package/src/chat/transcriptViewport.ts +2 -7
- package/src/cli/ResetConfirmView.tsx +1 -1
- package/src/cli/main.tsx +9 -3
- package/src/cli/preview.tsx +0 -5
- package/src/cli/updateNotice.ts +5 -3
- package/src/identity/continuity/editor.ts +7 -107
- package/src/identity/continuity/envelope.ts +1048 -40
- package/src/identity/continuity/history.ts +4 -4
- package/src/identity/continuity/localBackup.ts +249 -0
- package/src/identity/continuity/privateEdit/apply.ts +170 -0
- package/src/identity/continuity/privateEdit/diff.ts +82 -0
- package/src/identity/continuity/privateEdit/files.ts +23 -0
- package/src/identity/continuity/privateEdit/types.ts +28 -0
- package/src/identity/continuity/privateEdit.ts +10 -298
- package/src/identity/continuity/publicSkills.ts +8 -9
- package/src/identity/continuity/snapshots.ts +17 -6
- package/src/identity/continuity/storage/defaults.ts +111 -0
- package/src/identity/continuity/storage/files.ts +72 -0
- package/src/identity/continuity/storage/markdown.ts +81 -0
- package/src/identity/continuity/storage/paths.ts +24 -0
- package/src/identity/continuity/storage/scaffold.ts +124 -0
- package/src/identity/continuity/storage/status.ts +86 -0
- package/src/identity/continuity/storage/types.ts +27 -0
- package/src/identity/continuity/storage.ts +32 -507
- package/src/identity/continuity/zipWriter.ts +95 -0
- package/src/identity/crypto/backupEnvelope.ts +14 -247
- package/src/identity/crypto/eth.ts +7 -7
- package/src/identity/ens/agentRecords.ts +96 -0
- package/src/identity/ens/ensAutomation/contracts.ts +38 -0
- package/src/identity/ens/ensAutomation/delete.ts +80 -0
- package/src/identity/ens/ensAutomation/names.ts +14 -0
- package/src/identity/ens/ensAutomation/operators.ts +29 -0
- package/src/identity/ens/ensAutomation/read.ts +114 -0
- package/src/identity/ens/ensAutomation/root.ts +63 -0
- package/src/identity/ens/ensAutomation/setup.ts +284 -0
- package/src/identity/ens/ensAutomation/transactions.ts +107 -0
- package/src/identity/ens/ensAutomation/types.ts +126 -0
- package/src/identity/ens/ensAutomation.ts +29 -0
- package/src/identity/ens/ensLookup/client.ts +43 -0
- package/src/identity/ens/ensLookup/constants.ts +26 -0
- package/src/identity/ens/ensLookup/discovery.ts +70 -0
- package/src/identity/ens/ensLookup/names.ts +34 -0
- package/src/identity/ens/ensLookup/records.ts +45 -0
- package/src/identity/ens/ensLookup/resolve.ts +75 -0
- package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
- package/src/identity/ens/ensLookup/types.ts +38 -0
- package/src/identity/ens/ensLookup/validation.ts +72 -0
- package/src/identity/ens/ensLookup.ts +19 -0
- package/src/identity/ens/ensRegistration.ts +199 -0
- package/src/identity/ens/resolverDelegation.ts +48 -0
- package/src/identity/hub/IdentityHub.tsx +13 -815
- package/src/identity/hub/OperationalRoutes.tsx +370 -0
- package/src/identity/hub/Routes.tsx +361 -0
- package/src/identity/hub/advancedEnsValidation.ts +45 -0
- package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
- package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
- package/src/identity/hub/components/FlowTimeline.tsx +27 -0
- package/src/identity/hub/components/IdentitySummary.tsx +190 -0
- package/src/identity/hub/components/MenuScreen.tsx +237 -0
- package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
- package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
- package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
- package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
- package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
- package/src/identity/hub/effects/create.ts +310 -0
- package/src/identity/hub/effects/ens/flows.ts +218 -0
- package/src/identity/hub/effects/ens/index.ts +11 -0
- package/src/identity/hub/effects/ens/transactions.ts +239 -0
- package/src/identity/hub/effects/index.ts +74 -0
- package/src/identity/hub/effects/profile/profileState.ts +173 -0
- package/src/identity/hub/effects/publicProfile/index.ts +5 -0
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
- package/src/identity/hub/effects/rebackup/index.ts +7 -0
- package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
- package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
- package/src/identity/hub/effects/receipts.ts +46 -0
- package/src/identity/hub/effects/restore/apply.ts +112 -0
- package/src/identity/hub/effects/restore/auth.ts +159 -0
- package/src/identity/hub/effects/restore/discover.ts +86 -0
- package/src/identity/hub/effects/restore/envelopes.ts +21 -0
- package/src/identity/hub/effects/restore/fetch.ts +25 -0
- package/src/identity/hub/effects/restore/index.ts +22 -0
- package/src/identity/hub/effects/restore/recovery.ts +135 -0
- package/src/identity/hub/effects/restore/resolve.ts +102 -0
- package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
- package/src/identity/hub/effects/restore/shared.ts +91 -0
- package/src/identity/hub/effects/restoreAdmin.ts +93 -0
- package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
- package/src/identity/hub/effects/shared/snapshot.ts +336 -0
- package/src/identity/hub/effects/shared/sync.ts +190 -0
- package/src/identity/hub/effects/token-transfer/index.ts +6 -0
- package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
- package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
- package/src/identity/hub/effects/types.ts +53 -0
- package/src/identity/hub/effects/vault/preflight.ts +50 -0
- package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
- package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
- package/src/identity/hub/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
- package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
- package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
- package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
- package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
- package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
- package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
- package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
- package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
- package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
- package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
- package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
- package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
- package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
- package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
- package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
- package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
- package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
- package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
- package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
- package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
- package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +25 -43
- package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
- package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
- package/src/identity/hub/identityHubReducer.ts +166 -101
- package/src/identity/hub/model/continuity.ts +94 -0
- package/src/identity/hub/model/copy.ts +35 -0
- package/src/identity/hub/model/custody.ts +54 -0
- package/src/identity/hub/model/ens.ts +49 -0
- package/src/identity/hub/model/errors.ts +140 -0
- package/src/identity/hub/model/format.ts +15 -0
- package/src/identity/hub/model/identity.ts +94 -0
- package/src/identity/hub/model/network.ts +32 -0
- package/src/identity/hub/model/transfer.ts +57 -0
- package/src/identity/hub/operatorWallets.ts +131 -0
- package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
- package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
- package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
- package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
- package/src/identity/hub/reconciliation/index.ts +21 -0
- package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
- package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
- package/src/identity/hub/txGuard.ts +51 -0
- package/src/identity/hub/types.ts +17 -0
- package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
- package/src/identity/hub/useIdentityHubController.ts +396 -0
- package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
- package/src/identity/hub/utils.ts +79 -0
- package/src/identity/identityCompat.ts +34 -0
- package/src/identity/profile/agentIcon.ts +61 -0
- package/src/identity/profile/imagePicker.ts +12 -12
- package/src/identity/registry/erc8004/abi.ts +14 -0
- package/src/identity/registry/erc8004/chains.ts +150 -0
- package/src/identity/registry/erc8004/client.ts +11 -0
- package/src/identity/registry/erc8004/discovery.ts +511 -0
- package/src/identity/registry/erc8004/metadata.ts +335 -0
- package/src/identity/registry/erc8004/ownership.ts +121 -0
- package/src/identity/registry/erc8004/preflight.ts +123 -0
- package/src/identity/registry/erc8004/transactions.ts +77 -0
- package/src/identity/registry/erc8004/types.ts +88 -0
- package/src/identity/registry/erc8004/uri.ts +59 -0
- package/src/identity/registry/erc8004/utils.ts +58 -0
- package/src/identity/registry/erc8004.ts +53 -1106
- package/src/identity/registry/fieldParsers.ts +28 -0
- package/src/identity/registry/operatorVault/bytecode.ts +98 -0
- package/src/identity/registry/operatorVault/constants.ts +38 -0
- package/src/identity/registry/operatorVault/read.ts +246 -0
- package/src/identity/registry/operatorVault/transactions.ts +81 -0
- package/src/identity/registry/operatorVault.ts +44 -0
- package/src/identity/storage/ipfs.ts +26 -24
- package/src/identity/wallet/browserWallet/gas.ts +41 -0
- package/src/identity/wallet/browserWallet/html.ts +106 -0
- package/src/identity/wallet/browserWallet/http.ts +28 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
- package/src/identity/wallet/browserWallet/requests.ts +191 -0
- package/src/identity/wallet/browserWallet/session.ts +325 -0
- package/src/identity/wallet/browserWallet/types.ts +192 -0
- package/src/identity/wallet/browserWallet/validation.ts +74 -0
- package/src/identity/wallet/browserWallet.ts +30 -393
- package/src/identity/wallet/page/constants.ts +5 -0
- package/src/identity/wallet/page/controller.ts +251 -0
- package/src/identity/wallet/page/copy.ts +340 -0
- package/src/identity/wallet/page/grainient.ts +278 -0
- package/src/identity/wallet/page/html.ts +28 -0
- package/src/identity/wallet/page/markup.ts +50 -0
- package/src/identity/wallet/page/state.ts +9 -0
- package/src/identity/wallet/page/styles/base.ts +259 -0
- package/src/identity/wallet/page/styles/components.ts +262 -0
- package/src/identity/wallet/page/styles/index.ts +5 -0
- package/src/identity/wallet/page/styles/responsive.ts +247 -0
- package/src/identity/wallet/page/types.ts +47 -0
- package/src/identity/wallet/page/view.ts +535 -0
- package/src/identity/wallet/page/walletProvider.ts +70 -0
- package/src/identity/wallet/page.tsx +38 -0
- package/src/identity/wallet/walletPurposeCompat.ts +27 -0
- package/src/mcp/manager.ts +0 -1
- package/src/models/ModelPicker.tsx +36 -30
- package/src/models/catalog.ts +5 -2
- package/src/models/huggingface.ts +9 -9
- package/src/models/llamacpp.ts +13 -13
- package/src/models/modelDisplay.ts +75 -0
- package/src/models/modelPickerOptions.ts +16 -3
- package/src/models/modelRecommendation.ts +0 -1
- package/src/providers/errors.ts +16 -0
- package/src/providers/gemini.ts +252 -39
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -1
- package/src/runtime/sessionMode.ts +1 -1
- package/src/runtime/systemPrompt.ts +2 -0
- package/src/runtime/toolExecution.ts +18 -22
- package/src/runtime/toolIntent.ts +0 -20
- package/src/runtime/turn.ts +0 -92
- package/src/storage/atomicWrite.ts +4 -1
- package/src/storage/config.ts +181 -5
- package/src/storage/identity.ts +9 -3
- package/src/storage/secrets.ts +2 -2
- package/src/tools/bashSafety.ts +8 -0
- package/src/tools/changeDirectoryTool.ts +1 -1
- package/src/tools/deleteFileTool.ts +4 -4
- package/src/tools/editTool.ts +4 -4
- package/src/tools/editUtils.ts +5 -5
- package/src/tools/privateContinuityEditTool.ts +4 -5
- package/src/tools/privateContinuityReadTool.ts +1 -2
- package/src/tools/registry.ts +30 -0
- package/src/tools/writeFileTool.ts +5 -5
- package/src/ui/BrandSplash.tsx +20 -85
- package/src/ui/ProgressBar.tsx +3 -5
- package/src/ui/Select.tsx +21 -9
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +3 -3
- package/src/ui/TextInput.tsx +191 -29
- package/src/ui/theme.ts +7 -34
- package/src/utils/openExternal.ts +21 -0
- package/src/utils/withRetry.ts +47 -3
- package/src/identity/hub/identityHubEffects.ts +0 -937
- package/src/identity/hub/identityHubModel.ts +0 -291
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
- package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
- package/src/identity/wallet/wallet-page/wallet.html +0 -1202
- /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import type { EthagentConfig, EthagentIdentity, SelectableNetwork } from '../../storage/config.js'
|
|
2
|
-
import {
|
|
3
|
-
RegisterAgentPreflightError,
|
|
4
|
-
supportedErc8004ChainForId,
|
|
5
|
-
type Erc8004AgentCandidate,
|
|
6
|
-
} from '../registry/erc8004.js'
|
|
7
|
-
import { AgentStateOwnerMismatchError } from '../crypto/backupEnvelope.js'
|
|
8
|
-
import { ContinuitySnapshotOwnerMismatchError } from '../continuity/envelope.js'
|
|
9
|
-
import { resolveSelectedNetwork } from '../registry/registryConfig.js'
|
|
10
|
-
|
|
11
|
-
export const PREFLIGHT_AGENT_URI = 'ipfs://bafybeigdyrztma2dbfczw7q6ooozbxlqzyw5r7w4f3qw2axvvxqg3w6y7q'
|
|
12
|
-
|
|
13
|
-
export type IdentityHubErrorView = {
|
|
14
|
-
title: string
|
|
15
|
-
detail?: string
|
|
16
|
-
hint?: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function initialAgentState(name: string, description: string, ownerAddress: string): Record<string, unknown> {
|
|
20
|
-
return {
|
|
21
|
-
version: 1,
|
|
22
|
-
name,
|
|
23
|
-
description,
|
|
24
|
-
ownerAddress,
|
|
25
|
-
createdAt: new Date().toISOString(),
|
|
26
|
-
preferences: {},
|
|
27
|
-
memory: {},
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function identityHubErrorView(err: unknown): IdentityHubErrorView {
|
|
32
|
-
if (err instanceof RegisterAgentPreflightError) {
|
|
33
|
-
return {
|
|
34
|
-
title: err.title,
|
|
35
|
-
detail: err.detail,
|
|
36
|
-
hint: err.hint,
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (err instanceof AgentStateOwnerMismatchError) {
|
|
40
|
-
return {
|
|
41
|
-
title: 'backup locked to another wallet',
|
|
42
|
-
detail: `wallet ${shortAddress(err.currentOwner)} cannot read state encrypted for ${shortAddress(err.backupOwner)}.`,
|
|
43
|
-
hint: 'Use the wallet that created this backup.',
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (err instanceof ContinuitySnapshotOwnerMismatchError) {
|
|
47
|
-
return {
|
|
48
|
-
title: 'continuity locked to another wallet',
|
|
49
|
-
detail: `wallet ${shortAddress(err.currentOwner)} cannot read SOUL.md or MEMORY.md encrypted for ${shortAddress(err.snapshotOwner)}.`,
|
|
50
|
-
hint: 'Use the wallet that created this continuity snapshot.',
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
const message = err instanceof Error ? err.message : String(err)
|
|
54
|
-
if (message === 'fetch failed') {
|
|
55
|
-
return {
|
|
56
|
-
title: 'storage unavailable',
|
|
57
|
-
detail: 'could not reach storage.',
|
|
58
|
-
hint: 'check the connection, then try again.',
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
title: 'identity error',
|
|
63
|
-
detail: message,
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function pinataErrorText(err: unknown): string {
|
|
68
|
-
const view = identityHubErrorView(err)
|
|
69
|
-
return view.detail ?? view.title
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function isRegistrationPreflightError(err: unknown): boolean {
|
|
73
|
-
return err instanceof RegisterAgentPreflightError
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function shortCid(cid: string): string {
|
|
77
|
-
if (cid.length <= 18) return cid
|
|
78
|
-
return `${cid.slice(0, 10)}...${cid.slice(-6)}`
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function shortAddress(address: string): string {
|
|
82
|
-
if (address.length <= 14) return address
|
|
83
|
-
return `${address.slice(0, 6)}...${address.slice(-4)}`
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function shortHash(value: string): string {
|
|
87
|
-
if (value.length <= 18) return value
|
|
88
|
-
return `${value.slice(0, 8)}...${value.slice(-6)}`
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function tokenLabel(candidate: Erc8004AgentCandidate): string {
|
|
92
|
-
return tokenCandidateLabel(candidate)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function tokenCandidateLabel(candidate: Erc8004AgentCandidate): string {
|
|
96
|
-
return candidate.name?.trim() || `Agent Token #${candidate.agentId.toString()}`
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function tokenCandidateSelectLabel(
|
|
100
|
-
candidate: Erc8004AgentCandidate,
|
|
101
|
-
current = false,
|
|
102
|
-
): string {
|
|
103
|
-
return `${tokenCandidateLabel(candidate)}${current ? ' *' : ''}`
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function tokenCandidateHint(candidate: Erc8004AgentCandidate): string {
|
|
107
|
-
const chain = supportedErc8004ChainForId(candidate.chainId)
|
|
108
|
-
const network = chain?.network ? networkLabel(chain.network) : chain?.name ?? `chain ${candidate.chainId}`
|
|
109
|
-
const parts = [
|
|
110
|
-
candidate.name?.trim() ? `token #${candidate.agentId.toString()}` : null,
|
|
111
|
-
network,
|
|
112
|
-
candidate.backup?.createdAt ? `backup ${formatDate(candidate.backup.createdAt)}` : null,
|
|
113
|
-
].filter((part): part is string => Boolean(part))
|
|
114
|
-
return parts.join(' · ')
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function isCurrentAgentCandidate(
|
|
118
|
-
identity: EthagentIdentity | undefined,
|
|
119
|
-
candidate: Erc8004AgentCandidate,
|
|
120
|
-
): boolean {
|
|
121
|
-
if (!identity?.agentId) return false
|
|
122
|
-
if (identity.agentId !== candidate.agentId.toString()) return false
|
|
123
|
-
|
|
124
|
-
const owner = identity.ownerAddress ?? identity.address
|
|
125
|
-
if (owner && owner.toLowerCase() !== candidate.ownerAddress.toLowerCase()) return false
|
|
126
|
-
if (identity.chainId !== undefined && identity.chainId !== candidate.chainId) return false
|
|
127
|
-
if (
|
|
128
|
-
identity.identityRegistryAddress
|
|
129
|
-
&& identity.identityRegistryAddress.toLowerCase() !== candidate.identityRegistryAddress.toLowerCase()
|
|
130
|
-
) {
|
|
131
|
-
return false
|
|
132
|
-
}
|
|
133
|
-
return true
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function storageLabel(apiUrl: string): string {
|
|
137
|
-
void apiUrl
|
|
138
|
-
return 'IPFS'
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const NETWORK_LABELS: Record<SelectableNetwork, string> = {
|
|
142
|
-
mainnet: 'ethereum mainnet',
|
|
143
|
-
arbitrum: 'arbitrum one',
|
|
144
|
-
base: 'base',
|
|
145
|
-
optimism: 'optimism',
|
|
146
|
-
polygon: 'polygon',
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function networkLabel(network: SelectableNetwork): string {
|
|
150
|
-
return NETWORK_LABELS[network]
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const NETWORK_SUBTITLES: Record<SelectableNetwork, string> = {
|
|
154
|
-
mainnet: 'agent tokens on ethereum mainnet.',
|
|
155
|
-
arbitrum: 'agent tokens on arbitrum one.',
|
|
156
|
-
base: 'agent tokens on base.',
|
|
157
|
-
optimism: 'agent tokens on optimism.',
|
|
158
|
-
polygon: 'agent tokens on polygon.',
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function networkSubtitle(network: SelectableNetwork): string {
|
|
162
|
-
return NETWORK_SUBTITLES[network]
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function networkMenuTagline(): string {
|
|
166
|
-
return 'choose where your agent token is created or found.'
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function currentNetworkLine(config?: EthagentConfig): string {
|
|
170
|
-
return networkLabel(resolveSelectedNetwork(config))
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function chainSummaryRow(config?: EthagentConfig, identity?: EthagentIdentity): {
|
|
174
|
-
label: string
|
|
175
|
-
value: string
|
|
176
|
-
tone: 'ok' | 'dim'
|
|
177
|
-
} {
|
|
178
|
-
const network = resolveSelectedNetwork(config)
|
|
179
|
-
const fromIdentity = identity?.chainId ? supportedErc8004ChainForId(identity.chainId)?.name.toLowerCase() : undefined
|
|
180
|
-
const value = fromIdentity ?? networkLabel(network)
|
|
181
|
-
return { label: 'chain', value, tone: identity?.chainId ? 'ok' : 'dim' }
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function lastBackupLabel(identity?: EthagentIdentity): string {
|
|
185
|
-
const created = identity?.backup?.createdAt
|
|
186
|
-
return created ? formatDate(created) : 'never'
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function identitySummaryRows(
|
|
190
|
-
identity: EthagentIdentity | undefined,
|
|
191
|
-
config?: EthagentConfig,
|
|
192
|
-
): Array<{
|
|
193
|
-
label: string
|
|
194
|
-
value: string
|
|
195
|
-
tone: 'ok' | 'dim'
|
|
196
|
-
}> {
|
|
197
|
-
const backup = identity?.backup
|
|
198
|
-
const owner = identity?.ownerAddress ?? identity?.address
|
|
199
|
-
const ownerValue = owner ? shortAddress(owner) : 'not connected'
|
|
200
|
-
const tokenValue = identity?.agentId ? `#${identity.agentId}` : 'not created'
|
|
201
|
-
const chain = chainSummaryRow(config, identity)
|
|
202
|
-
const stateValue = backup?.cid ? shortCid(backup.cid) : 'not saved yet'
|
|
203
|
-
const skillsValue = identity?.publicSkills?.cid ? shortCid(identity.publicSkills.cid) : 'not saved'
|
|
204
|
-
const cardValue = identity?.publicSkills?.agentCardCid ? shortCid(identity.publicSkills.agentCardCid) : 'not saved'
|
|
205
|
-
const imageValue = typeof identity?.state?.imageUrl === 'string' && identity.state.imageUrl.trim() ? 'attached' : 'not attached'
|
|
206
|
-
return [
|
|
207
|
-
{ label: 'owner', value: ownerValue, tone: identity ? 'ok' : 'dim' },
|
|
208
|
-
{ label: 'token', value: tokenValue, tone: identity?.agentId ? 'ok' : 'dim' },
|
|
209
|
-
{ label: 'network', value: chain.value, tone: chain.tone },
|
|
210
|
-
{ label: 'state', value: stateValue, tone: backup ? 'ok' : 'dim' },
|
|
211
|
-
{ label: 'skills', value: skillsValue, tone: identity?.publicSkills?.cid ? 'ok' : 'dim' },
|
|
212
|
-
{ label: 'card', value: cardValue, tone: identity?.publicSkills?.agentCardCid ? 'ok' : 'dim' },
|
|
213
|
-
{ label: 'image', value: imageValue, tone: imageValue === 'attached' ? 'ok' : 'dim' },
|
|
214
|
-
]
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export type IdentityDetailSection = {
|
|
218
|
-
title: string
|
|
219
|
-
rows: Array<{
|
|
220
|
-
label: string
|
|
221
|
-
value: string
|
|
222
|
-
tone: 'ok' | 'dim'
|
|
223
|
-
}>
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export function identityDetailSections(
|
|
227
|
-
identity: EthagentIdentity | undefined,
|
|
228
|
-
config?: EthagentConfig,
|
|
229
|
-
): IdentityDetailSection[] {
|
|
230
|
-
const backup = identity?.backup
|
|
231
|
-
const owner = identity?.ownerAddress ?? identity?.address
|
|
232
|
-
const chain = chainSummaryRow(config, identity)
|
|
233
|
-
const stateCid = backup?.cid ?? 'not saved yet'
|
|
234
|
-
const registrationCid = identity?.metadataCid ?? 'not saved yet'
|
|
235
|
-
const publicSkillsCid = identity?.publicSkills?.cid ?? 'not saved'
|
|
236
|
-
const agentCardCid = identity?.publicSkills?.agentCardCid ?? 'not saved'
|
|
237
|
-
|
|
238
|
-
return [
|
|
239
|
-
{
|
|
240
|
-
title: 'Agent',
|
|
241
|
-
rows: [
|
|
242
|
-
{ label: 'token', value: identity?.agentId ? `#${identity.agentId}` : 'not created', tone: identity?.agentId ? 'ok' : 'dim' },
|
|
243
|
-
{ label: 'network', value: chain.value, tone: chain.tone },
|
|
244
|
-
{ label: 'registration', value: registrationCid, tone: identity?.metadataCid ? 'ok' : 'dim' },
|
|
245
|
-
],
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
title: 'Owner',
|
|
249
|
-
rows: [
|
|
250
|
-
{ label: 'wallet', value: owner ?? 'not connected', tone: owner ? 'ok' : 'dim' },
|
|
251
|
-
],
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
title: 'Recovery',
|
|
255
|
-
rows: [
|
|
256
|
-
{ label: 'state CID', value: stateCid, tone: backup?.cid ? 'ok' : 'dim' },
|
|
257
|
-
{ label: 'skills CID', value: publicSkillsCid, tone: identity?.publicSkills?.cid ? 'ok' : 'dim' },
|
|
258
|
-
{ label: 'agent card CID', value: agentCardCid, tone: identity?.publicSkills?.agentCardCid ? 'ok' : 'dim' },
|
|
259
|
-
{ label: 'storage', value: backup?.ipfsApiUrl ? storageLabel(backup.ipfsApiUrl) : 'not saved yet', tone: backup?.ipfsApiUrl ? 'ok' : 'dim' },
|
|
260
|
-
{ label: 'created', value: identity?.createdAt ? formatDate(identity.createdAt) : 'not created', tone: identity?.createdAt ? 'ok' : 'dim' },
|
|
261
|
-
{ label: 'last backup', value: backup?.createdAt ? formatDate(backup.createdAt) : 'never', tone: backup?.createdAt ? 'ok' : 'dim' },
|
|
262
|
-
{ label: 'status', value: backup?.status ?? 'unknown', tone: backup?.status ? 'ok' : 'dim' },
|
|
263
|
-
],
|
|
264
|
-
},
|
|
265
|
-
]
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export type CopyableField = {
|
|
269
|
-
label: string
|
|
270
|
-
value: string
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
export function copyableIdentityFields(identity?: EthagentIdentity): CopyableField[] {
|
|
274
|
-
if (!identity) return []
|
|
275
|
-
const fields: CopyableField[] = []
|
|
276
|
-
if (identity.backup?.cid) fields.push({ label: 'State CID', value: identity.backup.cid })
|
|
277
|
-
if (identity.publicSkills?.cid) fields.push({ label: 'Skills CID', value: identity.publicSkills.cid })
|
|
278
|
-
if (identity.publicSkills?.agentCardCid) fields.push({ label: 'Agent Card CID', value: identity.publicSkills.agentCardCid })
|
|
279
|
-
if (identity.metadataCid) fields.push({ label: 'Registration CID', value: identity.metadataCid })
|
|
280
|
-
if (identity.agentUri) fields.push({ label: 'Agent URI', value: identity.agentUri })
|
|
281
|
-
const owner = identity.ownerAddress ?? identity.address
|
|
282
|
-
if (owner) fields.push({ label: 'Owner Address', value: owner })
|
|
283
|
-
if (identity.agentId) fields.push({ label: 'Token ID', value: identity.agentId })
|
|
284
|
-
return fields
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function formatDate(input: string): string {
|
|
288
|
-
const date = new Date(input)
|
|
289
|
-
if (Number.isNaN(date.getTime())) return input
|
|
290
|
-
return date.toISOString().slice(0, 10)
|
|
291
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
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 type { EthagentConfig, EthagentIdentity } from '../../../storage/config.js'
|
|
7
|
-
import type { ContinuityWorkingTreeStatus } from '../../continuity/storage.js'
|
|
8
|
-
import { IdentitySummary } from './IdentitySummary.js'
|
|
9
|
-
import { shortCid } from '../identityHubModel.js'
|
|
10
|
-
|
|
11
|
-
type PrivateAction = 'soul' | 'memory' | 'back'
|
|
12
|
-
type PublicAction = 'edit' | 'skills' | 'back'
|
|
13
|
-
|
|
14
|
-
type CommonProps = {
|
|
15
|
-
identity?: EthagentIdentity
|
|
16
|
-
config?: EthagentConfig
|
|
17
|
-
workingStatus?: ContinuityWorkingTreeStatus | null
|
|
18
|
-
ready: boolean
|
|
19
|
-
notice?: string
|
|
20
|
-
footer: React.ReactNode
|
|
21
|
-
onBack: () => void
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const SaveFromHubHint: React.FC<{ workingStatus?: ContinuityWorkingTreeStatus | null }> = ({ workingStatus }) => {
|
|
25
|
-
const needsBackup = workingStatus?.publishState === 'local-changes'
|
|
26
|
-
|| workingStatus?.publishState === 'not-published'
|
|
27
|
-
|| workingStatus?.publishState === 'verify-needed'
|
|
28
|
-
if (!needsBackup) return null
|
|
29
|
-
return (
|
|
30
|
-
<Box marginTop={1}>
|
|
31
|
-
<Text color={theme.accentPeach}>Save path: Identity Hub > Save Snapshot Now > Yes, Save Snapshot Now.</Text>
|
|
32
|
-
</Box>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const PrivateContinuityScreen: React.FC<CommonProps & {
|
|
37
|
-
onOpenSoul: () => void
|
|
38
|
-
onOpenMemory: () => void
|
|
39
|
-
}> = ({
|
|
40
|
-
identity,
|
|
41
|
-
config,
|
|
42
|
-
workingStatus,
|
|
43
|
-
ready,
|
|
44
|
-
notice,
|
|
45
|
-
footer,
|
|
46
|
-
onOpenSoul,
|
|
47
|
-
onOpenMemory,
|
|
48
|
-
onBack,
|
|
49
|
-
}) => (
|
|
50
|
-
<Surface title="Private Memory Files" subtitle={notice ?? privateSubtitle(ready)} footer={footer}>
|
|
51
|
-
<IdentitySummary identity={identity} config={config} workingStatus={workingStatus} compact />
|
|
52
|
-
<PrivateRows identity={identity} ready={ready} />
|
|
53
|
-
<SaveFromHubHint workingStatus={workingStatus} />
|
|
54
|
-
<Box marginTop={1}>
|
|
55
|
-
<Select<PrivateAction>
|
|
56
|
-
options={[
|
|
57
|
-
{ value: 'soul', role: 'section', prefix: '--', label: 'Open Local Files' },
|
|
58
|
-
{ value: 'soul', label: 'Open SOUL.md', hint: 'Edit persona and operating preferences', disabled: !ready },
|
|
59
|
-
{ value: 'memory', label: 'Open MEMORY.md', hint: 'Edit private working memory for this agent', disabled: !ready },
|
|
60
|
-
{ value: 'back', role: 'section', prefix: '--', label: 'Navigation' },
|
|
61
|
-
{ value: 'back', label: 'Back To Identity Hub', hint: 'Return without changing private files', role: 'utility' },
|
|
62
|
-
]}
|
|
63
|
-
hintLayout="inline"
|
|
64
|
-
onSubmit={choice => {
|
|
65
|
-
if (choice === 'soul') return onOpenSoul()
|
|
66
|
-
if (choice === 'memory') return onOpenMemory()
|
|
67
|
-
return onBack()
|
|
68
|
-
}}
|
|
69
|
-
onCancel={onBack}
|
|
70
|
-
/>
|
|
71
|
-
</Box>
|
|
72
|
-
</Surface>
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
export const PublicSkillsScreen: React.FC<CommonProps & {
|
|
76
|
-
onEditProfile: () => void
|
|
77
|
-
onOpenSkills: () => void
|
|
78
|
-
}> = ({ identity, config, workingStatus, notice, footer, onEditProfile, onOpenSkills, onBack }) => (
|
|
79
|
-
<Surface title="Public Profile" subtitle={notice ?? 'Manage public metadata, skills.json, and the agent card.'} footer={footer}>
|
|
80
|
-
<IdentitySummary identity={identity} config={config} workingStatus={workingStatus} compact />
|
|
81
|
-
<PublicProfileRows identity={identity} />
|
|
82
|
-
<SaveFromHubHint workingStatus={workingStatus} />
|
|
83
|
-
<Box marginTop={1}>
|
|
84
|
-
<Select<PublicAction>
|
|
85
|
-
options={[
|
|
86
|
-
{ value: 'edit', role: 'section', prefix: '--', label: 'Profile' },
|
|
87
|
-
{ value: 'edit', label: 'Edit Name, Description, Image', hint: 'Upload a local image to IPFS automatically' },
|
|
88
|
-
{ value: 'skills', role: 'section', prefix: '--', label: 'Capabilities' },
|
|
89
|
-
{ value: 'skills', label: 'Open skills.json', hint: 'Edit public capabilities and notes' },
|
|
90
|
-
{ value: 'back', role: 'section', prefix: '--', label: 'Navigation' },
|
|
91
|
-
{ value: 'back', label: 'Back To Identity Hub', hint: 'Return without changing public metadata', role: 'utility' },
|
|
92
|
-
]}
|
|
93
|
-
hintLayout="inline"
|
|
94
|
-
onSubmit={choice => {
|
|
95
|
-
if (choice === 'edit') return onEditProfile()
|
|
96
|
-
if (choice === 'skills') return onOpenSkills()
|
|
97
|
-
return onBack()
|
|
98
|
-
}}
|
|
99
|
-
onCancel={onBack}
|
|
100
|
-
/>
|
|
101
|
-
</Box>
|
|
102
|
-
</Surface>
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
const PrivateRows: React.FC<{ identity?: EthagentIdentity; ready: boolean }> = ({ identity, ready }) => (
|
|
106
|
-
<Box flexDirection="column" marginTop={1}>
|
|
107
|
-
<Text>
|
|
108
|
-
<Text color={theme.dim}>{'Local Files'.padEnd(13)}</Text>
|
|
109
|
-
<Text color={ready ? theme.text : theme.dim}>{ready ? 'SOUL.md and MEMORY.md Ready' : 'Missing Local Working Files'}</Text>
|
|
110
|
-
</Text>
|
|
111
|
-
<Text>
|
|
112
|
-
<Text color={theme.dim}>{'Snapshot'.padEnd(13)}</Text>
|
|
113
|
-
<Text color={identity?.backup?.cid ? theme.text : theme.dim}>{identity?.backup?.cid ? shortCid(identity.backup.cid) : 'Not Saved Yet'}</Text>
|
|
114
|
-
</Text>
|
|
115
|
-
</Box>
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
const PublicProfileRows: React.FC<{ identity?: EthagentIdentity }> = ({ identity }) => (
|
|
119
|
-
<Box flexDirection="column" marginTop={1}>
|
|
120
|
-
<Text>
|
|
121
|
-
<Text color={theme.dim}>{'skills.json'.padEnd(13)}</Text>
|
|
122
|
-
<Text color={identity?.publicSkills?.cid ? theme.text : theme.dim}>{identity?.publicSkills?.cid ? shortCid(identity.publicSkills.cid) : 'Not Saved'}</Text>
|
|
123
|
-
</Text>
|
|
124
|
-
<Text>
|
|
125
|
-
<Text color={theme.dim}>{'Agent Card'.padEnd(13)}</Text>
|
|
126
|
-
<Text color={identity?.publicSkills?.agentCardCid ? theme.text : theme.dim}>{identity?.publicSkills?.agentCardCid ? shortCid(identity.publicSkills.agentCardCid) : 'Not Saved'}</Text>
|
|
127
|
-
</Text>
|
|
128
|
-
<Text>
|
|
129
|
-
<Text color={theme.dim}>{'Image'.padEnd(13)}</Text>
|
|
130
|
-
<Text color={readStateString(identity?.state, 'imageUrl') ? theme.text : theme.dim}>{readStateString(identity?.state, 'imageUrl') ? 'Attached' : 'Not Attached'}</Text>
|
|
131
|
-
</Text>
|
|
132
|
-
</Box>
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
function privateSubtitle(ready: boolean): string {
|
|
136
|
-
return ready
|
|
137
|
-
? 'SOUL.md and MEMORY.md are private local files on this machine.'
|
|
138
|
-
: 'Use "Refetch Latest Snapshot" from the Identity Hub menu to recover files.'
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function readStateString(state: Record<string, unknown> | undefined, key: string): string {
|
|
142
|
-
const value = state?.[key]
|
|
143
|
-
return typeof value === 'string' ? value.trim() : ''
|
|
144
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
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 { TextInput } from '../../../ui/TextInput.js'
|
|
6
|
-
import { theme } from '../../../ui/theme.js'
|
|
7
|
-
import type { Step } from '../identityHubReducer.js'
|
|
8
|
-
|
|
9
|
-
type EditProfileFlowProps = {
|
|
10
|
-
step: Extract<Step, { kind: 'edit-profile-name' | 'edit-profile-description' | 'edit-profile-image' }>
|
|
11
|
-
onNameSubmit: (name: string) => void
|
|
12
|
-
onDescriptionSubmit: (description: string) => void
|
|
13
|
-
onImageSubmit: (imagePath: string) => void
|
|
14
|
-
onImagePick: () => void
|
|
15
|
-
onBack: () => void
|
|
16
|
-
onMenu: () => void
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const footerHint = (hint: string) => <Text color={theme.dim}>{hint}</Text>
|
|
20
|
-
|
|
21
|
-
export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
|
|
22
|
-
step,
|
|
23
|
-
onNameSubmit,
|
|
24
|
-
onDescriptionSubmit,
|
|
25
|
-
onImageSubmit,
|
|
26
|
-
onImagePick,
|
|
27
|
-
onBack,
|
|
28
|
-
onMenu,
|
|
29
|
-
}) => {
|
|
30
|
-
const [manualImagePath, setManualImagePath] = React.useState(false)
|
|
31
|
-
|
|
32
|
-
if (step.kind === 'edit-profile-name') {
|
|
33
|
-
const currentName = readStateString(step.identity.state, 'name')
|
|
34
|
-
return (
|
|
35
|
-
<Surface
|
|
36
|
-
title="Rename Agent Identity"
|
|
37
|
-
subtitle="This updates public token metadata and the Agent Card."
|
|
38
|
-
footer={footerHint('enter continues · esc back')}
|
|
39
|
-
>
|
|
40
|
-
<Text color={theme.dim}>Currently: {currentName || '(unnamed)'}</Text>
|
|
41
|
-
<TextInput
|
|
42
|
-
key="edit-profile-name"
|
|
43
|
-
initialValue={currentName}
|
|
44
|
-
placeholder="agent name"
|
|
45
|
-
validate={value => value.trim().length >= 2 ? null : 'name must be at least 2 characters'}
|
|
46
|
-
onSubmit={value => onNameSubmit(value.trim())}
|
|
47
|
-
onCancel={onMenu}
|
|
48
|
-
/>
|
|
49
|
-
</Surface>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (step.kind === 'edit-profile-image') {
|
|
54
|
-
const currentImage = readStateString(step.identity.state, 'imageUrl')
|
|
55
|
-
if (!manualImagePath) {
|
|
56
|
-
return (
|
|
57
|
-
<Surface
|
|
58
|
-
title="Upload Agent Image"
|
|
59
|
-
subtitle="Choose a local image. ethagent uploads it to IPFS and attaches it to token metadata."
|
|
60
|
-
footer={footerHint('enter select · esc back')}
|
|
61
|
-
>
|
|
62
|
-
<Box flexDirection="column">
|
|
63
|
-
<Text color={theme.dim}>Current: {currentImage || '(no image)'}</Text>
|
|
64
|
-
{step.error ? <Text color="#e87070">{step.error}</Text> : null}
|
|
65
|
-
</Box>
|
|
66
|
-
<Box marginTop={1}>
|
|
67
|
-
<Select<'choose' | 'manual' | 'skip' | 'delete' | 'back'>
|
|
68
|
-
options={[
|
|
69
|
-
{ value: 'choose', role: 'section', prefix: '--', label: 'Image' },
|
|
70
|
-
{ value: 'choose', label: 'Choose Image File', hint: 'Open the operating system file picker' },
|
|
71
|
-
{ value: 'manual', label: 'Enter Path Manually', hint: 'Fallback if a file picker is unavailable' },
|
|
72
|
-
...(currentImage ? [{ value: 'delete' as const, label: 'Delete Current Image', hint: 'Remove the attached image from public profile' }] : []),
|
|
73
|
-
{ value: 'skip', label: currentImage ? 'Keep Current Image' : 'No Image', hint: 'Save without changing the image' },
|
|
74
|
-
{ value: 'back', role: 'section', prefix: '--', label: 'Navigation' },
|
|
75
|
-
{ value: 'back', label: 'Back', hint: 'Return to description', role: 'utility' },
|
|
76
|
-
]}
|
|
77
|
-
hintLayout="inline"
|
|
78
|
-
onSubmit={choice => {
|
|
79
|
-
if (choice === 'choose') return onImagePick()
|
|
80
|
-
if (choice === 'manual') return setManualImagePath(true)
|
|
81
|
-
if (choice === 'delete') return onImageSubmit('delete')
|
|
82
|
-
if (choice === 'skip') return onImageSubmit('')
|
|
83
|
-
return onBack()
|
|
84
|
-
}}
|
|
85
|
-
onCancel={onBack}
|
|
86
|
-
/>
|
|
87
|
-
</Box>
|
|
88
|
-
</Surface>
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
return (
|
|
92
|
-
<Surface
|
|
93
|
-
title="Enter Image Path"
|
|
94
|
-
subtitle="Fallback path entry. URLs are rejected because ethagent uploads the local file itself."
|
|
95
|
-
footer={footerHint('enter saves · esc back')}
|
|
96
|
-
>
|
|
97
|
-
<Text color={theme.dim}>Current: {currentImage || '(no image)'}</Text>
|
|
98
|
-
<TextInput
|
|
99
|
-
key="edit-profile-image"
|
|
100
|
-
placeholder="local image path"
|
|
101
|
-
allowEmpty
|
|
102
|
-
validate={value => validateImagePath(value)}
|
|
103
|
-
onSubmit={value => onImageSubmit(value.trim())}
|
|
104
|
-
onCancel={() => setManualImagePath(false)}
|
|
105
|
-
/>
|
|
106
|
-
</Surface>
|
|
107
|
-
)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const currentDescription = readStateString(step.identity.state, 'description')
|
|
111
|
-
return (
|
|
112
|
-
<Surface
|
|
113
|
-
title="Describe Agent Identity"
|
|
114
|
-
subtitle="This updates public token metadata and the Agent Card."
|
|
115
|
-
footer={footerHint('enter continues · esc back')}
|
|
116
|
-
>
|
|
117
|
-
<Text color={theme.dim}>Currently: {currentDescription || '(no description)'}</Text>
|
|
118
|
-
<TextInput
|
|
119
|
-
key="edit-profile-description"
|
|
120
|
-
initialValue={currentDescription}
|
|
121
|
-
placeholder="description"
|
|
122
|
-
allowEmpty
|
|
123
|
-
onSubmit={value => onDescriptionSubmit(value.trim())}
|
|
124
|
-
onCancel={onBack}
|
|
125
|
-
/>
|
|
126
|
-
</Surface>
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function validateImagePath(value: string): string | null {
|
|
131
|
-
const trimmed = value.trim()
|
|
132
|
-
if (!trimmed) return null
|
|
133
|
-
if (/^https?:\/\//i.test(trimmed) || /^ipfs:\/\//i.test(trimmed)) {
|
|
134
|
-
return 'enter a local image file path; ethagent will upload it to IPFS'
|
|
135
|
-
}
|
|
136
|
-
if (!/\.(png|jpe?g|gif|webp|svg)$/i.test(trimmed)) {
|
|
137
|
-
return 'image must be png, jpg, gif, webp, or svg'
|
|
138
|
-
}
|
|
139
|
-
return null
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function readStateString(state: Record<string, unknown> | undefined, key: string): string {
|
|
143
|
-
const value = state?.[key]
|
|
144
|
-
return typeof value === 'string' ? value : ''
|
|
145
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
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 { identitySummaryRows, lastBackupLabel } from '../identityHubModel.js'
|
|
6
|
-
|
|
7
|
-
import type { ContinuityWorkingTreeStatus } from '../../continuity/storage.js'
|
|
8
|
-
|
|
9
|
-
export const IdentitySummary: React.FC<{
|
|
10
|
-
identity?: EthagentIdentity
|
|
11
|
-
config?: EthagentConfig
|
|
12
|
-
workingStatus?: ContinuityWorkingTreeStatus | null
|
|
13
|
-
compact?: boolean
|
|
14
|
-
}> = ({ identity, config, workingStatus, compact = false }) => {
|
|
15
|
-
if (!identity) {
|
|
16
|
-
return (
|
|
17
|
-
<Text color={theme.dim}>No agent yet. Create or load one.</Text>
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const rows = identitySummaryRows(identity, config)
|
|
22
|
-
const lastBackup = lastBackupLabel(identity)
|
|
23
|
-
const stateName = typeof (identity.state as Record<string, unknown> | undefined)?.name === 'string'
|
|
24
|
-
? ((identity.state as Record<string, unknown>).name as string).trim()
|
|
25
|
-
: ''
|
|
26
|
-
|
|
27
|
-
const row = (label: string) => rows.find(item => item.label === label)
|
|
28
|
-
|
|
29
|
-
const needsBackup = workingStatus?.publishState === 'local-changes' || workingStatus?.publishState === 'not-published' || workingStatus?.publishState === 'verify-needed'
|
|
30
|
-
let changedFiles: string[] = []
|
|
31
|
-
if (needsBackup) {
|
|
32
|
-
if (workingStatus?.localContentHashes && workingStatus?.publishedContentHashes) {
|
|
33
|
-
if (workingStatus.localContentHashes['SOUL.md'] !== workingStatus.publishedContentHashes['SOUL.md']) changedFiles.push('SOUL.md')
|
|
34
|
-
if (workingStatus.localContentHashes['MEMORY.md'] !== workingStatus.publishedContentHashes['MEMORY.md']) changedFiles.push('MEMORY.md')
|
|
35
|
-
if (workingStatus.localContentHashes['skills.json'] !== workingStatus.publishedContentHashes['skills.json']) changedFiles.push('skills.json')
|
|
36
|
-
} else {
|
|
37
|
-
changedFiles = ['SOUL.md', 'MEMORY.md', 'skills.json']
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const lastSavedRow = needsBackup
|
|
42
|
-
? { label: 'Unsaved', value: changedFiles.length > 0 ? changedFiles.join(', ') : 'Markdown files', tone: 'warn' as const, highlight: true }
|
|
43
|
-
: { label: 'Last Saved', value: lastBackup, tone: lastBackup === 'never' ? 'dim' as const : 'ok' as const }
|
|
44
|
-
|
|
45
|
-
const summaryRows = [
|
|
46
|
-
{ label: 'Token', value: row('token')?.value ?? 'Not Created', tone: row('token')?.tone ?? 'dim', highlight: true },
|
|
47
|
-
{ label: 'Network', value: row('network')?.value ?? 'Unknown', tone: row('network')?.tone ?? 'dim' },
|
|
48
|
-
{ label: 'Owner', value: row('owner')?.value ?? 'Not Connected', tone: row('owner')?.tone ?? 'dim' },
|
|
49
|
-
{ label: 'Snapshot', value: row('state')?.value ?? 'Not Saved Yet', tone: row('state')?.tone ?? 'dim', highlight: true },
|
|
50
|
-
lastSavedRow,
|
|
51
|
-
{ label: 'Skills', value: row('skills')?.value ?? 'Not Saved', tone: row('skills')?.tone ?? 'dim' },
|
|
52
|
-
{ label: 'Agent Card', value: row('card')?.value ?? 'Not Saved', tone: row('card')?.tone ?? 'dim' },
|
|
53
|
-
{ label: 'Image', value: row('image')?.value ?? 'Not Attached', tone: row('image')?.tone ?? 'dim' },
|
|
54
|
-
]
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<Box flexDirection="column">
|
|
58
|
-
<Text color={theme.accentPrimary} bold>{stateName || 'Active Agent'}</Text>
|
|
59
|
-
{summaryRows.map(row => {
|
|
60
|
-
const valueColor = row.tone === 'warn' ? 'red' : (row.tone === 'ok' ? theme.text : theme.dim)
|
|
61
|
-
return (
|
|
62
|
-
<Text key={row.label}>
|
|
63
|
-
<Text color={theme.dim}>{row.label.padEnd(12)}</Text>
|
|
64
|
-
<Text color={valueColor} bold={row.highlight}>{displayValue(row.value)}</Text>
|
|
65
|
-
</Text>
|
|
66
|
-
)
|
|
67
|
-
})}
|
|
68
|
-
</Box>
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function displayValue(value: string): string {
|
|
73
|
-
const mapped = DISPLAY_VALUES[value]
|
|
74
|
-
return mapped ?? value
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const DISPLAY_VALUES: Record<string, string> = {
|
|
78
|
-
'not attached': 'Not Attached',
|
|
79
|
-
'not connected': 'Not Connected',
|
|
80
|
-
'not created': 'Not Created',
|
|
81
|
-
'not saved': 'Not Saved',
|
|
82
|
-
'not saved yet': 'Not Saved Yet',
|
|
83
|
-
'never': 'Never',
|
|
84
|
-
'unknown': 'Unknown',
|
|
85
|
-
'ethereum mainnet': 'Ethereum Mainnet',
|
|
86
|
-
'arbitrum one': 'Arbitrum One',
|
|
87
|
-
'base': 'Base',
|
|
88
|
-
'optimism': 'Optimism',
|
|
89
|
-
'polygon': 'Polygon',
|
|
90
|
-
}
|