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
|
@@ -1,30 +1,10 @@
|
|
|
1
|
-
import { config } from './state.js'
|
|
2
|
-
import type { WalletErrorPayload } from './types.js'
|
|
3
|
-
import { glyphs } from './html.js'
|
|
4
|
-
import {
|
|
5
|
-
accountCopy,
|
|
6
|
-
chainLabel,
|
|
7
|
-
FLOW_COPY,
|
|
8
|
-
isTransactionFlow,
|
|
9
|
-
purposeCopy,
|
|
10
|
-
signCopy,
|
|
11
|
-
STATE_TITLES,
|
|
12
|
-
shortAddr,
|
|
13
|
-
transactionCopy,
|
|
14
|
-
transactionPurposeTitle,
|
|
15
|
-
type FlowCopy,
|
|
16
|
-
} from './copy.js'
|
|
17
|
-
import { walletErrorHtml } from './errorView.js'
|
|
18
|
-
|
|
19
1
|
let card: HTMLElement;
|
|
20
|
-
let promptText: HTMLElement;
|
|
21
2
|
let flowTitle: HTMLElement;
|
|
22
|
-
let
|
|
3
|
+
let flowSubtitle: HTMLElement;
|
|
23
4
|
let flowDetail: HTMLElement;
|
|
24
5
|
let detailsBlock: HTMLElement;
|
|
25
6
|
let detailKey: HTMLElement;
|
|
26
7
|
let detailVal: HTMLElement;
|
|
27
|
-
let netVal: HTMLElement;
|
|
28
8
|
let statusBlock: HTMLElement;
|
|
29
9
|
let statusMarker: HTMLElement;
|
|
30
10
|
export let statusText: HTMLElement;
|
|
@@ -41,14 +21,12 @@ function requiredElement<T extends HTMLElement>(id: string): T {
|
|
|
41
21
|
|
|
42
22
|
export function initializeViewElements(): void {
|
|
43
23
|
card = requiredElement("card");
|
|
44
|
-
promptText = requiredElement("prompt-text");
|
|
45
24
|
flowTitle = requiredElement("flow-title");
|
|
46
|
-
|
|
25
|
+
flowSubtitle = requiredElement("flow-subtitle");
|
|
47
26
|
flowDetail = requiredElement("flow-detail");
|
|
48
27
|
detailsBlock = requiredElement("details-block");
|
|
49
28
|
detailKey = requiredElement("detail-key");
|
|
50
29
|
detailVal = requiredElement("detail-val");
|
|
51
|
-
netVal = requiredElement("net-val");
|
|
52
30
|
statusBlock = requiredElement("status-block");
|
|
53
31
|
statusMarker = requiredElement("status-marker");
|
|
54
32
|
statusText = requiredElement("status-text");
|
|
@@ -58,17 +36,27 @@ export function initializeViewElements(): void {
|
|
|
58
36
|
cancel = requiredElement("cancel");
|
|
59
37
|
}
|
|
60
38
|
|
|
39
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
40
|
+
const SPINNER_INTERVAL_MS = 80;
|
|
61
41
|
let spinning = false;
|
|
42
|
+
let spinnerTimer: ReturnType<typeof setInterval> | null = null;
|
|
43
|
+
let spinnerIndex = 0;
|
|
62
44
|
function startSpinner(): void {
|
|
63
45
|
spinning = true;
|
|
64
|
-
|
|
65
|
-
statusMarker.
|
|
46
|
+
spinnerIndex = 0;
|
|
47
|
+
statusMarker.innerHTML = '<span class="spinner" aria-hidden="true">' + SPINNER_FRAMES[0] + '</span>';
|
|
48
|
+
const glyph = statusMarker.firstElementChild as HTMLElement | null;
|
|
49
|
+
if (spinnerTimer) clearInterval(spinnerTimer);
|
|
50
|
+
spinnerTimer = setInterval(() => {
|
|
51
|
+
spinnerIndex = (spinnerIndex + 1) % SPINNER_FRAMES.length;
|
|
52
|
+
if (glyph) glyph.textContent = SPINNER_FRAMES[spinnerIndex]!;
|
|
53
|
+
}, SPINNER_INTERVAL_MS);
|
|
66
54
|
}
|
|
67
55
|
function stopSpinner(): void {
|
|
56
|
+
if (spinnerTimer) { clearInterval(spinnerTimer); spinnerTimer = null; }
|
|
68
57
|
if (!spinning) return;
|
|
69
58
|
spinning = false;
|
|
70
59
|
statusMarker.innerHTML = "";
|
|
71
|
-
statusMarker.style.background = "";
|
|
72
60
|
}
|
|
73
61
|
function setMarker(text: string): void { stopSpinner(); statusMarker.textContent = text; }
|
|
74
62
|
export function flowCopy(): FlowCopy { return FLOW_COPY[config.kind] || FLOW_COPY.sign!; }
|
|
@@ -79,6 +67,7 @@ export function tabTitleForState(state: string): string {
|
|
|
79
67
|
if (state === "preparing-transaction") return STATE_TITLES.preparingTransaction;
|
|
80
68
|
if (state === "approve-transaction") return STATE_TITLES.approveTransaction;
|
|
81
69
|
if (state === "error") return STATE_TITLES.error;
|
|
70
|
+
if (state === "cancelled") return STATE_TITLES.cancelled;
|
|
82
71
|
if (state === "approve") {
|
|
83
72
|
if (config.kind === "account") return accountCopy().text;
|
|
84
73
|
if (config.kind === "sign") return STATE_TITLES.approveSign;
|
|
@@ -101,7 +90,7 @@ export function setTabTitle(title?: string): void {
|
|
|
101
90
|
const t = title || flowCopy().tabTitle || STATE_TITLES.default;
|
|
102
91
|
document.title = t;
|
|
103
92
|
const chromeTitle = document.getElementById("chrome-title");
|
|
104
|
-
if (chromeTitle) chromeTitle.textContent =
|
|
93
|
+
if (chromeTitle) chromeTitle.textContent = t;
|
|
105
94
|
}
|
|
106
95
|
|
|
107
96
|
function messagePreview(message?: string): string {
|
|
@@ -118,6 +107,7 @@ export function showPreparedMessage(message: string): void {
|
|
|
118
107
|
if (copy.detail !== "message") return;
|
|
119
108
|
const preview = messagePreview(message);
|
|
120
109
|
detailKey.textContent = copy.detail;
|
|
110
|
+
flowDetail.dataset.detail = copy.detail;
|
|
121
111
|
detailVal.textContent = preview;
|
|
122
112
|
flowDetail.hidden = preview.length === 0;
|
|
123
113
|
detailsBlock.hidden = flowDetail.hidden;
|
|
@@ -126,7 +116,6 @@ export function showPreparedMessage(message: string): void {
|
|
|
126
116
|
export function applyFlowChrome(): void {
|
|
127
117
|
const copy = flowCopy();
|
|
128
118
|
card.dataset.flow = copy.accent;
|
|
129
|
-
promptText.textContent = copy.label;
|
|
130
119
|
flowTitle.textContent =
|
|
131
120
|
config.kind === "account" && config.purpose
|
|
132
121
|
? purposeCopy().flowTitle
|
|
@@ -135,290 +124,21 @@ export function applyFlowChrome(): void {
|
|
|
135
124
|
: config.kind === "transaction"
|
|
136
125
|
? transactionPurposeTitle()
|
|
137
126
|
: copy.title;
|
|
127
|
+
const subtitle = (config.purpose && purposeSubtitle()) || copy.subtitle || "";
|
|
128
|
+
flowSubtitle.textContent = subtitle;
|
|
129
|
+
flowSubtitle.hidden = subtitle.length === 0;
|
|
138
130
|
setTabTitle(copy.tabTitle);
|
|
139
131
|
applyTransferTimeline();
|
|
140
132
|
if (!copy.detail) {
|
|
141
|
-
networkRow.hidden = true;
|
|
142
133
|
flowDetail.hidden = true;
|
|
143
134
|
detailsBlock.hidden = true;
|
|
144
135
|
} else {
|
|
145
|
-
networkRow.hidden = false;
|
|
146
136
|
flowDetail.hidden = false;
|
|
147
137
|
detailsBlock.hidden = false;
|
|
148
|
-
netVal.textContent = chainLabel(config.chainIdHex);
|
|
149
138
|
detailKey.textContent = copy.detail;
|
|
139
|
+
flowDetail.dataset.detail = copy.detail;
|
|
150
140
|
detailVal.textContent = detailPreview(copy);
|
|
151
141
|
flowDetail.hidden = detailVal.textContent.length === 0;
|
|
152
142
|
if (flowDetail.hidden) detailsBlock.hidden = true;
|
|
153
143
|
}
|
|
154
144
|
}
|
|
155
|
-
|
|
156
|
-
type LifecycleId =
|
|
157
|
-
| "ens-clear"
|
|
158
|
-
| "ens-link"
|
|
159
|
-
| "ens-update"
|
|
160
|
-
| "ens-register"
|
|
161
|
-
| "custody-switch"
|
|
162
|
-
| "public-profile-vault";
|
|
163
|
-
|
|
164
|
-
const LIFECYCLE_DEFINITIONS: Record<LifecycleId, { steps: string[] }> = {
|
|
165
|
-
"ens-clear": { steps: ["Clear Records on Mainnet", "Save Cleared Snapshot"] },
|
|
166
|
-
"ens-link": { steps: ["Create Subdomain", "Set Records", "Save Snapshot"] },
|
|
167
|
-
"ens-update": { steps: ["Update Records on Mainnet", "Save Updated Snapshot"] },
|
|
168
|
-
"ens-register": { steps: ["Commit ENS Name", "Register ENS Name"] },
|
|
169
|
-
"custody-switch": { steps: ["Deploy Vault", "Deposit Token", "Reconcile Operators"] },
|
|
170
|
-
"public-profile-vault": { steps: ["Sign Profile", "Save Through Vault"] },
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const FLOW_LIFECYCLE: Record<string, LifecycleId> = {
|
|
174
|
-
"ens-clear": "ens-clear",
|
|
175
|
-
"ens-link": "ens-link",
|
|
176
|
-
"ens-update": "ens-update",
|
|
177
|
-
"ens-register": "ens-register",
|
|
178
|
-
"custody-switch": "custody-switch",
|
|
179
|
-
"public-profile-vault": "public-profile-vault",
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const PURPOSE_TIMELINE: Record<string, readonly [string, string]> = {
|
|
183
|
-
"create-agent": ["Sign Recovery Access", "Mint Token"],
|
|
184
|
-
"update-snapshot-owner": ["Sign Snapshot", "Save Onchain"],
|
|
185
|
-
"update-snapshot-operator": ["Sign Snapshot", "Save Onchain"],
|
|
186
|
-
"update-snapshot-connected": ["Sign Snapshot", "Save Onchain"],
|
|
187
|
-
"update-profile-owner": ["Sign Profile", "Save Onchain"],
|
|
188
|
-
"update-profile-operator": ["Sign Profile", "Save Onchain"],
|
|
189
|
-
"update-profile-connected": ["Sign Profile", "Save Onchain"],
|
|
190
|
-
"update-operators": ["Sign Operator List", "Publish List"],
|
|
191
|
-
"create-simple-ens-subdomain": ["Sign Request", "Create Subdomain"],
|
|
192
|
-
"set-simple-ens-records": ["Sign Records", "Write Records"],
|
|
193
|
-
"create-agent-ens-subdomain": ["Sign Request", "Create Subdomain"],
|
|
194
|
-
"set-agent-ens-records": ["Sign Records", "Write Records"],
|
|
195
|
-
"rotate-agent-uri-vault-owner": ["Sign Update", "Save Through Vault"],
|
|
196
|
-
"rotate-agent-uri-vault-operator": ["Sign Update", "Save Through Vault"],
|
|
197
|
-
"update-ens": ["Sign Snapshot", "Save Onchain"],
|
|
198
|
-
"clear-ens": ["Sign Snapshot", "Save Onchain"],
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
function activeLifecycle(): LifecycleId | undefined {
|
|
202
|
-
if (config.flowId && FLOW_LIFECYCLE[config.flowId]) return FLOW_LIFECYCLE[config.flowId];
|
|
203
|
-
return undefined;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
let currentTimelineKey: string | null = null;
|
|
207
|
-
|
|
208
|
-
function lifecycleStepIndex(lifecycle: LifecycleId, state: string | null): number {
|
|
209
|
-
const flowStep = typeof config.flowStep === "number" ? config.flowStep : 1;
|
|
210
|
-
const def = LIFECYCLE_DEFINITIONS[lifecycle];
|
|
211
|
-
const steps = def.steps.length;
|
|
212
|
-
if (state === "done") return Math.min(flowStep, steps);
|
|
213
|
-
return Math.max(0, Math.min(flowStep - 1, steps - 1));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function hasNextLifecyclePrompt(): boolean {
|
|
217
|
-
const lifecycle = activeLifecycle();
|
|
218
|
-
if (!lifecycle) return false;
|
|
219
|
-
const flowStep = typeof config.flowStep === "number" ? config.flowStep : 1;
|
|
220
|
-
return flowStep < LIFECYCLE_DEFINITIONS[lifecycle].steps.length;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function nextLifecycleHint(): string {
|
|
224
|
-
return "Keep this page open. The next wallet step will appear here.";
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function purposeStepIndex(state: string | null): number {
|
|
228
|
-
if (state === "approve-transaction" || state === "submitting") return 1;
|
|
229
|
-
if (state === "done") return 2;
|
|
230
|
-
return 0;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export function applyTransferTimeline(): void {
|
|
234
|
-
const timeline = document.getElementById("timeline") as HTMLElement | null;
|
|
235
|
-
if (!timeline) return;
|
|
236
|
-
|
|
237
|
-
const lifecycle = activeLifecycle();
|
|
238
|
-
let steps: string[] | null = null;
|
|
239
|
-
let activeIndex = 0;
|
|
240
|
-
let key = "";
|
|
241
|
-
|
|
242
|
-
if (lifecycle) {
|
|
243
|
-
steps = LIFECYCLE_DEFINITIONS[lifecycle].steps;
|
|
244
|
-
activeIndex = lifecycleStepIndex(lifecycle, currentState);
|
|
245
|
-
key = "flow:" + lifecycle;
|
|
246
|
-
} else if (config.kind === "sign-transaction") {
|
|
247
|
-
const tuple = PURPOSE_TIMELINE[config.purpose || ""] || (["Sign", "Submit"] as const);
|
|
248
|
-
steps = [tuple[0], tuple[1]];
|
|
249
|
-
activeIndex = purposeStepIndex(currentState);
|
|
250
|
-
key = "purpose:" + (config.purpose || "");
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (!steps) {
|
|
254
|
-
if (!timeline.hidden) timeline.hidden = true;
|
|
255
|
-
currentTimelineKey = null;
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (currentTimelineKey !== key) {
|
|
260
|
-
timeline.innerHTML = "";
|
|
261
|
-
for (const action of steps) {
|
|
262
|
-
const li = document.createElement("li");
|
|
263
|
-
li.className = "timeline-step";
|
|
264
|
-
li.dataset.state = "pending";
|
|
265
|
-
const marker = document.createElement("span");
|
|
266
|
-
marker.className = "timeline-marker";
|
|
267
|
-
marker.setAttribute("aria-hidden", "true");
|
|
268
|
-
const label = document.createElement("span");
|
|
269
|
-
label.className = "timeline-label";
|
|
270
|
-
const actionEl = document.createElement("span");
|
|
271
|
-
actionEl.className = "timeline-action";
|
|
272
|
-
actionEl.textContent = action;
|
|
273
|
-
label.append(actionEl);
|
|
274
|
-
li.append(marker, label);
|
|
275
|
-
timeline.append(li);
|
|
276
|
-
}
|
|
277
|
-
timeline.setAttribute("aria-label", "Wallet popup steps");
|
|
278
|
-
currentTimelineKey = key;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
timeline.hidden = false;
|
|
282
|
-
const stepEls = timeline.querySelectorAll<HTMLElement>(".timeline-step");
|
|
283
|
-
stepEls.forEach((step, i) => {
|
|
284
|
-
const next = i < activeIndex ? "done" : i === activeIndex ? "active" : "pending";
|
|
285
|
-
if (step.dataset.state !== next) step.dataset.state = next;
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function markActiveTimelineStepDone(): void {
|
|
290
|
-
const timeline = document.getElementById("timeline") as HTMLElement | null;
|
|
291
|
-
if (!timeline || timeline.hidden) return;
|
|
292
|
-
timeline.querySelectorAll<HTMLElement>('.timeline-step[data-state="active"]').forEach((step) => {
|
|
293
|
-
step.dataset.state = "done";
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function spinnerText(value: string): string {
|
|
298
|
-
const text = preserveProtocolCaps(value);
|
|
299
|
-
return text.replace(/^(\s*)([a-z])/, (_match, prefix, letter) => `${prefix}${letter.toUpperCase()}`);
|
|
300
|
-
}
|
|
301
|
-
function spinnerHintText(value: string): string { return preserveProtocolCaps(value); }
|
|
302
|
-
function preserveProtocolCaps(value: string): string {
|
|
303
|
-
return String(value || "")
|
|
304
|
-
.replace(/\bapi\b/gi, "API")
|
|
305
|
-
.replace(/\bens\b/gi, "ENS")
|
|
306
|
-
.replace(/\berc-8004\b/gi, "ERC-8004")
|
|
307
|
-
.replace(/\bgguf\b/gi, "GGUF")
|
|
308
|
-
.replace(/\bhugging face\b/gi, "Hugging Face")
|
|
309
|
-
.replace(/\bipfs\b/gi, "IPFS")
|
|
310
|
-
.replace(/\bjson\b/gi, "JSON")
|
|
311
|
-
.replace(/\bjwt\b/gi, "JWT")
|
|
312
|
-
.replace(/\bmemory\.md\b/gi, "MEMORY.md")
|
|
313
|
-
.replace(/\bopenai\b/gi, "OpenAI")
|
|
314
|
-
.replace(/\banthropic\b/gi, "Anthropic")
|
|
315
|
-
.replace(/\bgemini\b/gi, "Gemini")
|
|
316
|
-
.replace(/\bos\b/gi, "OS")
|
|
317
|
-
.replace(/\brpc\b/gi, "RPC")
|
|
318
|
-
.replace(/\bsoul\.md\b/gi, "SOUL.md")
|
|
319
|
-
.replace(/\buri\b/gi, "URI")
|
|
320
|
-
.replace(/\burl\b/gi, "URL");
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function setStatus(marker: string, text: string, hint: string, spin: boolean): void {
|
|
324
|
-
const lineEl = statusText.parentElement as HTMLElement;
|
|
325
|
-
const hintEl = statusHint;
|
|
326
|
-
const displayText = spin ? spinnerText(text) : text;
|
|
327
|
-
const displayHint = spin ? spinnerHintText(hint) : hint;
|
|
328
|
-
const apply = () => {
|
|
329
|
-
if (spin) startSpinner();
|
|
330
|
-
else setMarker(marker);
|
|
331
|
-
statusText.textContent = displayText;
|
|
332
|
-
hintEl.textContent = displayHint;
|
|
333
|
-
requestAnimationFrame(() => {
|
|
334
|
-
lineEl.classList.remove("is-changing");
|
|
335
|
-
hintEl.classList.remove("is-changing");
|
|
336
|
-
});
|
|
337
|
-
};
|
|
338
|
-
if (statusText.textContent === displayText && hintEl.textContent === displayHint) {
|
|
339
|
-
if (spin) startSpinner();
|
|
340
|
-
else setMarker(marker);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
lineEl.classList.add("is-changing");
|
|
344
|
-
hintEl.classList.add("is-changing");
|
|
345
|
-
setTimeout(apply, 220);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
export let currentState: string | null = null;
|
|
349
|
-
|
|
350
|
-
export function setState(state: string, payload?: any): void {
|
|
351
|
-
payload = payload || {};
|
|
352
|
-
currentState = state;
|
|
353
|
-
errorSlot.innerHTML = "";
|
|
354
|
-
statusBlock.style.display = "flex";
|
|
355
|
-
setTabTitle(tabTitleForState(state));
|
|
356
|
-
applyTransferTimeline();
|
|
357
|
-
switch (state) {
|
|
358
|
-
case "connecting":
|
|
359
|
-
setStatus("·", "Connecting To Wallet...", "Open your wallet if needed.", true);
|
|
360
|
-
break;
|
|
361
|
-
case "approve":
|
|
362
|
-
if (config.kind === "account") {
|
|
363
|
-
const copy = accountCopy();
|
|
364
|
-
setStatus("·", copy.text, copy.hint, true);
|
|
365
|
-
} else if (config.kind === "sign") {
|
|
366
|
-
const sigCopy = signCopy();
|
|
367
|
-
setStatus("·", sigCopy.text, sigCopy.hint, true);
|
|
368
|
-
} else {
|
|
369
|
-
const txCopy = transactionCopy();
|
|
370
|
-
setStatus("·", txCopy.text, txCopy.hint, true);
|
|
371
|
-
}
|
|
372
|
-
break;
|
|
373
|
-
case "approve-sign":
|
|
374
|
-
{
|
|
375
|
-
const sigCopy = signCopy();
|
|
376
|
-
setStatus("·", sigCopy.text, sigCopy.hint, true);
|
|
377
|
-
}
|
|
378
|
-
break;
|
|
379
|
-
case "preparing-transaction":
|
|
380
|
-
setStatus("·", purposeCopy().prepare!.text, purposeCopy().prepare!.hint, true);
|
|
381
|
-
break;
|
|
382
|
-
case "approve-transaction":
|
|
383
|
-
{
|
|
384
|
-
const txCopy = transactionCopy();
|
|
385
|
-
setStatus("·", txCopy.text, txCopy.hint, true);
|
|
386
|
-
}
|
|
387
|
-
break;
|
|
388
|
-
case "submitting":
|
|
389
|
-
if (config.kind === "account") setStatus("·", "Connecting Wallet...", "Returning to terminal.", true);
|
|
390
|
-
else if (config.kind === "sign") setStatus("·", "Verifying Signature...", hasNextLifecyclePrompt() ? nextLifecycleHint() : "Returning to terminal.", true);
|
|
391
|
-
else setStatus("·", "Submitted · Waiting For Confirmation...", "Your wallet accepted the transaction.", true);
|
|
392
|
-
break;
|
|
393
|
-
case "done":
|
|
394
|
-
stopSpinner();
|
|
395
|
-
statusMarker.innerHTML =
|
|
396
|
-
'<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor"' +
|
|
397
|
-
' stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">' +
|
|
398
|
-
'<polyline points="20 6 9 17 4 12"></polyline></svg>';
|
|
399
|
-
statusText.textContent =
|
|
400
|
-
config.kind === "account" ? "Connected · Returning"
|
|
401
|
-
: config.kind === "sign" ? (hasNextLifecyclePrompt() ? "Signed · Waiting" : "Signed · Returning")
|
|
402
|
-
: "Submitted · Returning";
|
|
403
|
-
statusHint.textContent = payload.txHash
|
|
404
|
-
? (isTransactionFlow() ? "Transaction submitted. Returning." : "This tab will close shortly.")
|
|
405
|
-
: hasNextLifecyclePrompt() ? nextLifecycleHint() : "This tab will close shortly.";
|
|
406
|
-
markActiveTimelineStepDone();
|
|
407
|
-
break;
|
|
408
|
-
case "error":
|
|
409
|
-
stopSpinner();
|
|
410
|
-
statusBlock.style.display = "none";
|
|
411
|
-
renderError(payload);
|
|
412
|
-
break;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
let lastWalletError: WalletErrorPayload | null = null;
|
|
417
|
-
|
|
418
|
-
function renderError(payload: WalletErrorPayload): void {
|
|
419
|
-
errorSlot.innerHTML = walletErrorHtml(payload);
|
|
420
|
-
lastWalletError = payload;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
export function getLastWalletError(): WalletErrorPayload | null { return lastWalletError }
|
|
424
|
-
export function clearLastWalletError(): void { lastWalletError = null }
|
package/src/storage/config.ts
CHANGED
|
@@ -4,10 +4,6 @@ import os from 'node:os'
|
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
import { atomicWriteText } from './atomicWrite.js'
|
|
6
6
|
|
|
7
|
-
export const PROVIDERS = ['llamacpp', 'openai', 'anthropic', 'gemini'] as const
|
|
8
|
-
export type ProviderId = (typeof PROVIDERS)[number]
|
|
9
|
-
const LEGACY_PROVIDERS = ['ollama', ...PROVIDERS] as const
|
|
10
|
-
|
|
11
7
|
export const SELECTABLE_NETWORKS = ['mainnet', 'base'] as const
|
|
12
8
|
export type SelectableNetwork = (typeof SELECTABLE_NETWORKS)[number]
|
|
13
9
|
|
|
@@ -75,12 +71,8 @@ const IdentitySchema = z.object({
|
|
|
75
71
|
})
|
|
76
72
|
|
|
77
73
|
const ConfigSchema = z.object({
|
|
78
|
-
version: z.literal(
|
|
79
|
-
|
|
80
|
-
model: z.string().min(1),
|
|
81
|
-
baseUrl: z.string().url().optional(),
|
|
82
|
-
localMmprojPath: z.string().min(1).optional(),
|
|
83
|
-
firstRunAt: z.string(),
|
|
74
|
+
version: z.literal(2),
|
|
75
|
+
firstSeenAt: z.string(),
|
|
84
76
|
identity: IdentitySchema.optional(),
|
|
85
77
|
erc8004: z.object({
|
|
86
78
|
chainId: z.number().int().positive(),
|
|
@@ -96,16 +88,17 @@ const ConfigSchema = z.object({
|
|
|
96
88
|
configVersion: z.number().int().nonnegative().optional(),
|
|
97
89
|
})
|
|
98
90
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
91
|
+
const LegacyV1Schema = z.object({
|
|
92
|
+
version: z.literal(1),
|
|
93
|
+
firstRunAt: z.string().optional(),
|
|
94
|
+
identity: IdentitySchema.optional(),
|
|
95
|
+
erc8004: ConfigSchema.shape.erc8004,
|
|
96
|
+
selectedNetwork: z.enum(SELECTABLE_NETWORKS).optional(),
|
|
97
|
+
configVersion: z.number().int().nonnegative().optional(),
|
|
98
|
+
}).passthrough()
|
|
105
99
|
|
|
106
100
|
export type EthagentIdentity = z.infer<typeof IdentitySchema>
|
|
107
101
|
export type TransferSnapshotMetadata = NonNullable<NonNullable<EthagentIdentity['backup']>['transferSnapshot']>
|
|
108
|
-
|
|
109
102
|
export type EthagentConfig = z.infer<typeof ConfigSchema>
|
|
110
103
|
|
|
111
104
|
export function getConfigDir(): string {
|
|
@@ -133,14 +126,23 @@ export async function loadConfig(): Promise<EthagentConfig | null> {
|
|
|
133
126
|
const parsed = JSON.parse(raw)
|
|
134
127
|
const active = ConfigSchema.safeParse(parsed)
|
|
135
128
|
if (active.success) return normalizeConfig(active.data)
|
|
136
|
-
const legacy =
|
|
137
|
-
if (legacy.success) return
|
|
129
|
+
const legacy = LegacyV1Schema.safeParse(parsed)
|
|
130
|
+
if (legacy.success) return normalizeConfig(migrateLegacyV1(legacy.data))
|
|
131
|
+
await preserveUnreadableConfig(raw)
|
|
138
132
|
return null
|
|
139
133
|
} catch {
|
|
134
|
+
await preserveUnreadableConfig(raw)
|
|
140
135
|
return null
|
|
141
136
|
}
|
|
142
137
|
}
|
|
143
138
|
|
|
139
|
+
async function preserveUnreadableConfig(raw: string): Promise<void> {
|
|
140
|
+
try {
|
|
141
|
+
await fs.writeFile(`${getConfigPath()}.corrupt`, raw, { encoding: 'utf8', mode: 0o600 })
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
144
146
|
export async function saveConfig(config: EthagentConfig): Promise<void> {
|
|
145
147
|
await ensureConfigDir()
|
|
146
148
|
const bumped: EthagentConfig = {
|
|
@@ -210,10 +212,8 @@ export function buildSeedConfigForIdentity(args: {
|
|
|
210
212
|
identityRegistryAddress: string
|
|
211
213
|
}): EthagentConfig {
|
|
212
214
|
return {
|
|
213
|
-
version:
|
|
214
|
-
|
|
215
|
-
model: defaultModelFor('llamacpp'),
|
|
216
|
-
firstRunAt: new Date().toISOString(),
|
|
215
|
+
version: 2,
|
|
216
|
+
firstSeenAt: new Date().toISOString(),
|
|
217
217
|
identity: { ...args.identity, source: 'erc8004' },
|
|
218
218
|
erc8004: {
|
|
219
219
|
chainId: args.chainId,
|
|
@@ -288,34 +288,8 @@ export async function deleteConfig(): Promise<void> {
|
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
export function defaultModelFor(provider: ProviderId): string {
|
|
292
|
-
switch (provider) {
|
|
293
|
-
case 'openai': return 'gpt-5.2'
|
|
294
|
-
case 'anthropic': return 'claude-sonnet-4-5'
|
|
295
|
-
case 'gemini': return 'gemini-2.0-flash'
|
|
296
|
-
case 'llamacpp': return 'huggingface-link'
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export function defaultBaseUrlFor(provider: ProviderId): string | undefined {
|
|
301
|
-
if (provider === 'llamacpp') return 'http://localhost:8080/v1'
|
|
302
|
-
return undefined
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export type LocalProviderId = Extract<ProviderId, 'llamacpp'>
|
|
306
|
-
|
|
307
|
-
export function localProviderBaseUrlFor(provider: LocalProviderId, baseUrl?: string): string {
|
|
308
|
-
const fallback = defaultBaseUrlFor(provider) ?? ''
|
|
309
|
-
if (!baseUrl) return fallback
|
|
310
|
-
return isDefaultBaseUrlFor(baseUrl, LEGACY_OLLAMA_BASE_URL) ? fallback : baseUrl
|
|
311
|
-
}
|
|
312
|
-
|
|
313
291
|
export function normalizeConfig(config: EthagentConfig): EthagentConfig {
|
|
314
292
|
let next = config
|
|
315
|
-
if (next.provider === 'llamacpp') {
|
|
316
|
-
const baseUrl = localProviderBaseUrlFor(next.provider, next.baseUrl)
|
|
317
|
-
if (next.baseUrl !== baseUrl) next = { ...next, baseUrl }
|
|
318
|
-
}
|
|
319
293
|
if (!next.erc8004 && next.identity?.chainId && next.identity.identityRegistryAddress && next.identity.rpcUrl) {
|
|
320
294
|
next = {
|
|
321
295
|
...next,
|
|
@@ -329,37 +303,13 @@ export function normalizeConfig(config: EthagentConfig): EthagentConfig {
|
|
|
329
303
|
return next
|
|
330
304
|
}
|
|
331
305
|
|
|
332
|
-
function
|
|
333
|
-
if (config.provider !== 'ollama') return normalizeConfig(ConfigSchema.parse(config))
|
|
306
|
+
function migrateLegacyV1(input: z.infer<typeof LegacyV1Schema>): EthagentConfig {
|
|
334
307
|
return {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function isDefaultBaseUrlFor(value: string, fallback: string | undefined): boolean {
|
|
343
|
-
if (!fallback) return false
|
|
344
|
-
try {
|
|
345
|
-
const url = new URL(value)
|
|
346
|
-
const defaultUrl = new URL(fallback)
|
|
347
|
-
if (url.protocol !== defaultUrl.protocol) return false
|
|
348
|
-
if (url.hostname.toLowerCase() !== defaultUrl.hostname.toLowerCase()) return false
|
|
349
|
-
if (effectivePort(url) !== effectivePort(defaultUrl)) return false
|
|
350
|
-
const path = stripTrailingSlash(url.pathname) || '/'
|
|
351
|
-
const defaultPath = stripTrailingSlash(defaultUrl.pathname) || '/'
|
|
352
|
-
return path === '/' || path === defaultPath
|
|
353
|
-
} catch {
|
|
354
|
-
return stripTrailingSlash(value) === stripTrailingSlash(fallback)
|
|
308
|
+
version: 2,
|
|
309
|
+
firstSeenAt: input.firstRunAt ?? new Date().toISOString(),
|
|
310
|
+
...(input.identity ? { identity: input.identity } : {}),
|
|
311
|
+
...(input.erc8004 ? { erc8004: input.erc8004 } : {}),
|
|
312
|
+
...(input.selectedNetwork ? { selectedNetwork: input.selectedNetwork } : {}),
|
|
313
|
+
...(input.configVersion !== undefined ? { configVersion: input.configVersion } : {}),
|
|
355
314
|
}
|
|
356
315
|
}
|
|
357
|
-
|
|
358
|
-
function effectivePort(url: URL): string {
|
|
359
|
-
if (url.port) return url.port
|
|
360
|
-
return url.protocol === 'https:' ? '443' : '80'
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function stripTrailingSlash(value: string): string {
|
|
364
|
-
return value.replace(/\/+$/, '')
|
|
365
|
-
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import { getConfigDir } from './config.js'
|
|
3
|
+
import { rmSecret } from './secrets.js'
|
|
4
|
+
|
|
5
|
+
const RESET_SECRET_ACCOUNTS = ['ethereum:default', 'pinata:jwt'] as const
|
|
6
|
+
|
|
7
|
+
export type ResetPlan = {
|
|
8
|
+
configDir: string
|
|
9
|
+
secretAccounts: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ResetDeps = {
|
|
13
|
+
rm?: (dir: string) => Promise<void>
|
|
14
|
+
removeSecret?: (account: string) => Promise<void>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resetPlan(): ResetPlan {
|
|
18
|
+
return {
|
|
19
|
+
configDir: getConfigDir(),
|
|
20
|
+
secretAccounts: [...RESET_SECRET_ACCOUNTS],
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function runReset(deps: ResetDeps = {}): Promise<ResetPlan> {
|
|
25
|
+
const plan = resetPlan()
|
|
26
|
+
const rm = deps.rm ?? (dir => fs.rm(dir, { recursive: true, force: true }))
|
|
27
|
+
const removeSecret = deps.removeSecret ?? rmSecret
|
|
28
|
+
await rm(plan.configDir)
|
|
29
|
+
for (const account of plan.secretAccounts) await removeSecret(account)
|
|
30
|
+
return plan
|
|
31
|
+
}
|
package/src/storage/secrets.ts
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import os from 'node:os'
|
|
4
4
|
import crypto from 'node:crypto'
|
|
5
|
-
import { getConfigDir, ensureConfigDir
|
|
5
|
+
import { getConfigDir, ensureConfigDir } from './config.js'
|
|
6
6
|
import { atomicWriteText } from './atomicWrite.js'
|
|
7
7
|
|
|
8
8
|
const KEYTAR_SERVICE = 'ethagent'
|
|
@@ -167,18 +167,3 @@ export async function whichBackend(): Promise<KeyBackend> {
|
|
|
167
167
|
return (await loadKeytar()) ? 'keyring' : 'encrypted-file'
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
export function getKey(provider: ProviderId): Promise<string | null> {
|
|
171
|
-
return getSecret(provider)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export function setKey(provider: ProviderId, value: string): Promise<KeyBackend> {
|
|
175
|
-
return setSecret(provider, value)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function rmKey(provider: ProviderId): Promise<void> {
|
|
179
|
-
return rmSecret(provider)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function hasKey(provider: ProviderId): Promise<boolean> {
|
|
183
|
-
return hasSecret(provider)
|
|
184
|
-
}
|