ethagent 1.1.2 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +124 -32
  3. package/package.json +8 -3
  4. package/src/app/FirstRun.tsx +190 -146
  5. package/src/app/FirstRunTimeline.tsx +47 -0
  6. package/src/app/input/AppInputProvider.tsx +1 -1
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -1
  8. package/src/chat/ChatBottomPane.tsx +0 -1
  9. package/src/chat/ChatInput.tsx +6 -6
  10. package/src/chat/ChatScreen.tsx +35 -15
  11. package/src/chat/ContextLimitView.tsx +4 -4
  12. package/src/chat/ContinuityEditReviewView.tsx +10 -22
  13. package/src/chat/CopyPicker.tsx +0 -1
  14. package/src/chat/MessageList.tsx +62 -45
  15. package/src/chat/PermissionPrompt.tsx +13 -9
  16. package/src/chat/PlanApprovalView.tsx +3 -3
  17. package/src/chat/ResumeView.tsx +1 -4
  18. package/src/chat/RewindView.tsx +2 -2
  19. package/src/chat/chatInputState.ts +1 -1
  20. package/src/chat/chatScreenUtils.ts +22 -11
  21. package/src/chat/chatSessionState.ts +2 -2
  22. package/src/chat/chatTurnOrchestrator.ts +16 -81
  23. package/src/chat/commands.ts +1 -1
  24. package/src/chat/textCursor.ts +1 -1
  25. package/src/chat/transcriptViewport.ts +2 -7
  26. package/src/cli/ResetConfirmView.tsx +1 -1
  27. package/src/cli/main.tsx +9 -3
  28. package/src/cli/preview.tsx +0 -5
  29. package/src/cli/updateNotice.ts +4 -2
  30. package/src/identity/continuity/editor.ts +7 -107
  31. package/src/identity/continuity/envelope.ts +1048 -40
  32. package/src/identity/continuity/history.ts +4 -4
  33. package/src/identity/continuity/localBackup.ts +249 -0
  34. package/src/identity/continuity/privateEdit/apply.ts +170 -0
  35. package/src/identity/continuity/privateEdit/diff.ts +82 -0
  36. package/src/identity/continuity/privateEdit/files.ts +23 -0
  37. package/src/identity/continuity/privateEdit/types.ts +28 -0
  38. package/src/identity/continuity/privateEdit.ts +10 -298
  39. package/src/identity/continuity/publicSkills.ts +8 -9
  40. package/src/identity/continuity/snapshots.ts +17 -6
  41. package/src/identity/continuity/storage/defaults.ts +111 -0
  42. package/src/identity/continuity/storage/files.ts +72 -0
  43. package/src/identity/continuity/storage/markdown.ts +81 -0
  44. package/src/identity/continuity/storage/paths.ts +24 -0
  45. package/src/identity/continuity/storage/scaffold.ts +124 -0
  46. package/src/identity/continuity/storage/status.ts +86 -0
  47. package/src/identity/continuity/storage/types.ts +27 -0
  48. package/src/identity/continuity/storage.ts +32 -507
  49. package/src/identity/continuity/zipWriter.ts +95 -0
  50. package/src/identity/crypto/backupEnvelope.ts +14 -247
  51. package/src/identity/crypto/eth.ts +7 -7
  52. package/src/identity/ens/agentRecords.ts +96 -0
  53. package/src/identity/ens/ensAutomation/contracts.ts +38 -0
  54. package/src/identity/ens/ensAutomation/delete.ts +80 -0
  55. package/src/identity/ens/ensAutomation/names.ts +14 -0
  56. package/src/identity/ens/ensAutomation/operators.ts +29 -0
  57. package/src/identity/ens/ensAutomation/read.ts +114 -0
  58. package/src/identity/ens/ensAutomation/root.ts +63 -0
  59. package/src/identity/ens/ensAutomation/setup.ts +284 -0
  60. package/src/identity/ens/ensAutomation/transactions.ts +107 -0
  61. package/src/identity/ens/ensAutomation/types.ts +126 -0
  62. package/src/identity/ens/ensAutomation.ts +29 -0
  63. package/src/identity/ens/ensLookup/client.ts +43 -0
  64. package/src/identity/ens/ensLookup/constants.ts +26 -0
  65. package/src/identity/ens/ensLookup/discovery.ts +70 -0
  66. package/src/identity/ens/ensLookup/names.ts +34 -0
  67. package/src/identity/ens/ensLookup/records.ts +45 -0
  68. package/src/identity/ens/ensLookup/resolve.ts +75 -0
  69. package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
  70. package/src/identity/ens/ensLookup/types.ts +38 -0
  71. package/src/identity/ens/ensLookup/validation.ts +72 -0
  72. package/src/identity/ens/ensLookup.ts +19 -0
  73. package/src/identity/ens/ensRegistration.ts +199 -0
  74. package/src/identity/ens/resolverDelegation.ts +48 -0
  75. package/src/identity/hub/IdentityHub.tsx +13 -817
  76. package/src/identity/hub/OperationalRoutes.tsx +370 -0
  77. package/src/identity/hub/Routes.tsx +361 -0
  78. package/src/identity/hub/advancedEnsValidation.ts +45 -0
  79. package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
  80. package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
  81. package/src/identity/hub/components/FlowTimeline.tsx +27 -0
  82. package/src/identity/hub/components/IdentitySummary.tsx +190 -0
  83. package/src/identity/hub/components/MenuScreen.tsx +237 -0
  84. package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
  85. package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
  86. package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
  87. package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
  88. package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
  89. package/src/identity/hub/effects/create.ts +310 -0
  90. package/src/identity/hub/effects/ens/flows.ts +218 -0
  91. package/src/identity/hub/effects/ens/index.ts +11 -0
  92. package/src/identity/hub/effects/ens/transactions.ts +239 -0
  93. package/src/identity/hub/effects/index.ts +74 -0
  94. package/src/identity/hub/effects/profile/profileState.ts +173 -0
  95. package/src/identity/hub/effects/publicProfile/index.ts +5 -0
  96. package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
  97. package/src/identity/hub/effects/rebackup/index.ts +7 -0
  98. package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
  99. package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
  100. package/src/identity/hub/effects/receipts.ts +46 -0
  101. package/src/identity/hub/effects/restore/apply.ts +112 -0
  102. package/src/identity/hub/effects/restore/auth.ts +159 -0
  103. package/src/identity/hub/effects/restore/discover.ts +86 -0
  104. package/src/identity/hub/effects/restore/envelopes.ts +21 -0
  105. package/src/identity/hub/effects/restore/fetch.ts +25 -0
  106. package/src/identity/hub/effects/restore/index.ts +22 -0
  107. package/src/identity/hub/effects/restore/recovery.ts +135 -0
  108. package/src/identity/hub/effects/restore/resolve.ts +102 -0
  109. package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
  110. package/src/identity/hub/effects/restore/shared.ts +91 -0
  111. package/src/identity/hub/effects/restoreAdmin.ts +93 -0
  112. package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
  113. package/src/identity/hub/effects/shared/snapshot.ts +336 -0
  114. package/src/identity/hub/effects/shared/sync.ts +190 -0
  115. package/src/identity/hub/effects/token-transfer/index.ts +6 -0
  116. package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
  117. package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
  118. package/src/identity/hub/effects/types.ts +53 -0
  119. package/src/identity/hub/effects/vault/preflight.ts +50 -0
  120. package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
  121. package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
  122. package/src/identity/hub/{screens → flows/continuity}/RecoveryConfirmScreen.tsx +28 -19
  123. package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
  124. package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
  125. package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
  126. package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
  127. package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
  128. package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
  129. package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
  130. package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
  131. package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
  132. package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
  133. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
  134. package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
  135. package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
  136. package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
  137. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
  138. package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
  139. package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
  140. package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
  141. package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
  142. package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
  143. package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
  144. package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
  145. package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
  146. package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
  147. package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +23 -44
  148. package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
  149. package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
  150. package/src/identity/hub/identityHubReducer.ts +164 -99
  151. package/src/identity/hub/model/continuity.ts +94 -0
  152. package/src/identity/hub/model/copy.ts +35 -0
  153. package/src/identity/hub/model/custody.ts +54 -0
  154. package/src/identity/hub/model/ens.ts +49 -0
  155. package/src/identity/hub/model/errors.ts +140 -0
  156. package/src/identity/hub/model/format.ts +15 -0
  157. package/src/identity/hub/model/identity.ts +94 -0
  158. package/src/identity/hub/model/network.ts +32 -0
  159. package/src/identity/hub/model/transfer.ts +57 -0
  160. package/src/identity/hub/operatorWallets.ts +131 -0
  161. package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
  162. package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
  163. package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
  164. package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
  165. package/src/identity/hub/reconciliation/index.ts +21 -0
  166. package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
  167. package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
  168. package/src/identity/hub/txGuard.ts +51 -0
  169. package/src/identity/hub/types.ts +17 -0
  170. package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
  171. package/src/identity/hub/useIdentityHubController.ts +396 -0
  172. package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
  173. package/src/identity/hub/utils.ts +79 -0
  174. package/src/identity/identityCompat.ts +34 -0
  175. package/src/identity/profile/agentIcon.ts +61 -0
  176. package/src/identity/profile/imagePicker.ts +12 -12
  177. package/src/identity/registry/erc8004/abi.ts +14 -0
  178. package/src/identity/registry/erc8004/chains.ts +150 -0
  179. package/src/identity/registry/erc8004/client.ts +11 -0
  180. package/src/identity/registry/erc8004/discovery.ts +511 -0
  181. package/src/identity/registry/erc8004/metadata.ts +335 -0
  182. package/src/identity/registry/erc8004/ownership.ts +121 -0
  183. package/src/identity/registry/erc8004/preflight.ts +123 -0
  184. package/src/identity/registry/erc8004/transactions.ts +77 -0
  185. package/src/identity/registry/erc8004/types.ts +88 -0
  186. package/src/identity/registry/erc8004/uri.ts +59 -0
  187. package/src/identity/registry/erc8004/utils.ts +58 -0
  188. package/src/identity/registry/erc8004.ts +53 -1106
  189. package/src/identity/registry/fieldParsers.ts +28 -0
  190. package/src/identity/registry/operatorVault/bytecode.ts +98 -0
  191. package/src/identity/registry/operatorVault/constants.ts +38 -0
  192. package/src/identity/registry/operatorVault/read.ts +246 -0
  193. package/src/identity/registry/operatorVault/transactions.ts +81 -0
  194. package/src/identity/registry/operatorVault.ts +44 -0
  195. package/src/identity/storage/ipfs.ts +26 -24
  196. package/src/identity/wallet/browserWallet/gas.ts +41 -0
  197. package/src/identity/wallet/browserWallet/html.ts +106 -0
  198. package/src/identity/wallet/browserWallet/http.ts +28 -0
  199. package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
  200. package/src/identity/wallet/browserWallet/requests.ts +191 -0
  201. package/src/identity/wallet/browserWallet/session.ts +325 -0
  202. package/src/identity/wallet/browserWallet/types.ts +192 -0
  203. package/src/identity/wallet/browserWallet/validation.ts +74 -0
  204. package/src/identity/wallet/browserWallet.ts +30 -393
  205. package/src/identity/wallet/page/constants.ts +5 -0
  206. package/src/identity/wallet/page/controller.ts +251 -0
  207. package/src/identity/wallet/page/copy.ts +340 -0
  208. package/src/identity/wallet/page/grainient.ts +278 -0
  209. package/src/identity/wallet/page/html.ts +28 -0
  210. package/src/identity/wallet/page/markup.ts +50 -0
  211. package/src/identity/wallet/page/state.ts +9 -0
  212. package/src/identity/wallet/page/styles/base.ts +259 -0
  213. package/src/identity/wallet/page/styles/components.ts +262 -0
  214. package/src/identity/wallet/page/styles/index.ts +5 -0
  215. package/src/identity/wallet/page/styles/responsive.ts +247 -0
  216. package/src/identity/wallet/page/types.ts +47 -0
  217. package/src/identity/wallet/page/view.ts +535 -0
  218. package/src/identity/wallet/page/walletProvider.ts +70 -0
  219. package/src/identity/wallet/page.tsx +38 -0
  220. package/src/identity/wallet/walletPurposeCompat.ts +27 -0
  221. package/src/mcp/manager.ts +0 -1
  222. package/src/models/ModelPicker.tsx +36 -30
  223. package/src/models/catalog.ts +5 -2
  224. package/src/models/huggingface.ts +9 -9
  225. package/src/models/llamacpp.ts +13 -13
  226. package/src/models/modelDisplay.ts +75 -0
  227. package/src/models/modelPickerOptions.ts +16 -3
  228. package/src/models/modelRecommendation.ts +0 -1
  229. package/src/providers/errors.ts +16 -0
  230. package/src/providers/gemini.ts +252 -39
  231. package/src/providers/registry.ts +2 -2
  232. package/src/providers/retry.ts +1 -1
  233. package/src/runtime/sessionMode.ts +1 -1
  234. package/src/runtime/systemPrompt.ts +2 -0
  235. package/src/runtime/toolExecution.ts +18 -22
  236. package/src/runtime/toolIntent.ts +0 -20
  237. package/src/runtime/turn.ts +0 -92
  238. package/src/storage/atomicWrite.ts +4 -1
  239. package/src/storage/config.ts +181 -5
  240. package/src/storage/identity.ts +9 -3
  241. package/src/storage/secrets.ts +2 -2
  242. package/src/tools/bashSafety.ts +8 -0
  243. package/src/tools/changeDirectoryTool.ts +1 -1
  244. package/src/tools/deleteFileTool.ts +4 -4
  245. package/src/tools/editTool.ts +4 -4
  246. package/src/tools/editUtils.ts +5 -5
  247. package/src/tools/privateContinuityEditTool.ts +4 -5
  248. package/src/tools/privateContinuityReadTool.ts +1 -2
  249. package/src/tools/registry.ts +30 -0
  250. package/src/tools/writeFileTool.ts +5 -5
  251. package/src/ui/BrandSplash.tsx +20 -85
  252. package/src/ui/ProgressBar.tsx +3 -5
  253. package/src/ui/Select.tsx +20 -8
  254. package/src/ui/Spinner.tsx +38 -3
  255. package/src/ui/Surface.tsx +2 -2
  256. package/src/ui/TextInput.tsx +63 -20
  257. package/src/ui/theme.ts +7 -34
  258. package/src/utils/openExternal.ts +21 -0
  259. package/src/utils/withRetry.ts +47 -3
  260. package/src/identity/hub/identityHubEffects.ts +0 -937
  261. package/src/identity/hub/identityHubModel.ts +0 -371
  262. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -156
  263. package/src/identity/hub/screens/EditProfileFlow.tsx +0 -146
  264. package/src/identity/hub/screens/IdentitySummary.tsx +0 -106
  265. package/src/identity/hub/screens/MenuScreen.tsx +0 -117
  266. package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
  267. package/src/identity/wallet/wallet-page/wallet.html +0 -1202
  268. /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
@@ -0,0 +1,535 @@
1
+ import { config } from './state.js'
2
+ import type { WalletErrorPayload } from './types.js'
3
+ import { escapeHtml, glyphs } from './html.js'
4
+ import {
5
+ accountCopy,
6
+ chainLabel,
7
+ FLOW_COPY,
8
+ isTransactionFlow,
9
+ PURPOSE_COPY,
10
+ purposeCopy,
11
+ signCopy,
12
+ STATE_TITLES,
13
+ shortAddr,
14
+ transactionCopy,
15
+ transactionPurposeTitle,
16
+ type FlowCopy,
17
+ type PurposeCopyEntry,
18
+ } from './copy.js'
19
+
20
+ let card: HTMLElement;
21
+ let promptText: HTMLElement;
22
+ let flowTitle: HTMLElement;
23
+ let networkRow: HTMLElement;
24
+ let flowDetail: HTMLElement;
25
+ let detailsBlock: HTMLElement;
26
+ let detailKey: HTMLElement;
27
+ let detailVal: HTMLElement;
28
+ let netVal: HTMLElement;
29
+ let statusBlock: HTMLElement;
30
+ let statusMarker: HTMLElement;
31
+ export let statusText: HTMLElement;
32
+ export let statusHint: HTMLElement;
33
+ export let errorSlot: HTMLElement;
34
+ export let approve: HTMLButtonElement;
35
+ export let cancel: HTMLButtonElement;
36
+
37
+ function requiredElement<T extends HTMLElement>(id: string): T {
38
+ const element = document.getElementById(id);
39
+ if (!element) throw new Error("Wallet page missing #" + id);
40
+ return element as T;
41
+ }
42
+
43
+ export function initializeViewElements(): void {
44
+ card = requiredElement("card");
45
+ promptText = requiredElement("prompt-text");
46
+ flowTitle = requiredElement("flow-title");
47
+ networkRow = requiredElement("network-row");
48
+ flowDetail = requiredElement("flow-detail");
49
+ detailsBlock = requiredElement("details-block");
50
+ detailKey = requiredElement("detail-key");
51
+ detailVal = requiredElement("detail-val");
52
+ netVal = requiredElement("net-val");
53
+ statusBlock = requiredElement("status-block");
54
+ statusMarker = requiredElement("status-marker");
55
+ statusText = requiredElement("status-text");
56
+ statusHint = requiredElement("status-hint");
57
+ errorSlot = requiredElement("error-block-slot");
58
+ approve = requiredElement("approve");
59
+ cancel = requiredElement("cancel");
60
+ }
61
+
62
+ let spinning = false;
63
+ function startSpinner(): void {
64
+ spinning = true;
65
+ statusMarker.innerHTML = '<span class="spinner" aria-hidden="true"></span>';
66
+ statusMarker.style.background = "transparent";
67
+ }
68
+ function stopSpinner(): void {
69
+ if (!spinning) return;
70
+ spinning = false;
71
+ statusMarker.innerHTML = "";
72
+ statusMarker.style.background = "";
73
+ }
74
+ function setMarker(text: string): void { stopSpinner(); statusMarker.textContent = text; }
75
+ export function flowCopy(): FlowCopy { return FLOW_COPY[config.kind] || FLOW_COPY.sign!; }
76
+
77
+ export function tabTitleForState(state: string): string {
78
+ if (state === "connecting") return STATE_TITLES.connecting;
79
+ if (state === "approve-sign") return STATE_TITLES.approveSign;
80
+ if (state === "preparing-transaction") return STATE_TITLES.preparingTransaction;
81
+ if (state === "approve-transaction") return STATE_TITLES.approveTransaction;
82
+ if (state === "error") return STATE_TITLES.error;
83
+ if (state === "approve") {
84
+ if (config.kind === "account") return accountCopy().text;
85
+ if (config.kind === "sign") return STATE_TITLES.approveSign;
86
+ return STATE_TITLES.approveTransaction;
87
+ }
88
+ if (state === "submitting") {
89
+ if (config.kind === "account") return STATE_TITLES.connecting;
90
+ if (config.kind === "sign") return "Verifying signature";
91
+ return "Confirming transaction";
92
+ }
93
+ if (state === "done") {
94
+ if (config.kind === "account") return "Wallet connected";
95
+ if (config.kind === "sign") return "Message signed";
96
+ return "Transaction submitted";
97
+ }
98
+ return flowCopy().tabTitle || STATE_TITLES.default;
99
+ }
100
+
101
+ export function setTabTitle(title?: string): void {
102
+ const t = title || flowCopy().tabTitle || STATE_TITLES.default;
103
+ document.title = t;
104
+ const chromeTitle = document.getElementById("chrome-title");
105
+ if (chromeTitle) chromeTitle.textContent = "ethagent · " + t;
106
+ }
107
+
108
+ function messagePreview(message?: string): string {
109
+ const preview = String(message || "").split("\n")[0] ?? "";
110
+ return preview.length > 64 ? preview.slice(0, 64) + glyphs.ellipsis : preview;
111
+ }
112
+ function detailPreview(copy: FlowCopy): string {
113
+ if (copy.detail === "message") return messagePreview(config.message);
114
+ if (copy.detail === "registry" && config.tx) return shortAddr(config.tx.to);
115
+ return "";
116
+ }
117
+ export function showPreparedMessage(message: string): void {
118
+ const copy = flowCopy();
119
+ if (copy.detail !== "message") return;
120
+ const preview = messagePreview(message);
121
+ detailKey.textContent = copy.detail;
122
+ detailVal.textContent = preview;
123
+ flowDetail.hidden = preview.length === 0;
124
+ detailsBlock.hidden = flowDetail.hidden;
125
+ }
126
+
127
+ export function applyFlowChrome(): void {
128
+ const copy = flowCopy();
129
+ card.dataset.flow = copy.accent;
130
+ promptText.textContent = copy.label;
131
+ flowTitle.textContent =
132
+ config.kind === "account" && config.purpose
133
+ ? purposeCopy().flowTitle
134
+ : (config.kind === "sign" || config.kind === "sign-transaction") && config.purpose
135
+ ? purposeCopy().flowTitle
136
+ : config.kind === "transaction"
137
+ ? transactionPurposeTitle()
138
+ : copy.title;
139
+ setTabTitle(copy.tabTitle);
140
+ applyTransferTimeline();
141
+ if (!copy.detail) {
142
+ networkRow.hidden = true;
143
+ flowDetail.hidden = true;
144
+ detailsBlock.hidden = true;
145
+ } else {
146
+ networkRow.hidden = false;
147
+ flowDetail.hidden = false;
148
+ detailsBlock.hidden = false;
149
+ netVal.textContent = chainLabel(config.chainIdHex);
150
+ detailKey.textContent = copy.detail;
151
+ detailVal.textContent = detailPreview(copy);
152
+ flowDetail.hidden = detailVal.textContent.length === 0;
153
+ if (flowDetail.hidden) detailsBlock.hidden = true;
154
+ }
155
+ }
156
+
157
+ type LifecycleId =
158
+ | "ens-clear"
159
+ | "ens-link"
160
+ | "ens-update"
161
+ | "ens-register"
162
+ | "custody-switch"
163
+ | "public-profile-vault";
164
+
165
+ const LIFECYCLE_DEFINITIONS: Record<LifecycleId, { steps: string[] }> = {
166
+ "ens-clear": { steps: ["Clear Records on Mainnet", "Save Cleared Snapshot"] },
167
+ "ens-link": { steps: ["Create Subdomain", "Set Records", "Save Snapshot"] },
168
+ "ens-update": { steps: ["Update Records on Mainnet", "Save Updated Snapshot"] },
169
+ "ens-register": { steps: ["Commit ENS Name", "Register ENS Name"] },
170
+ "custody-switch": { steps: ["Deploy Vault", "Deposit Token", "Reconcile Operators"] },
171
+ "public-profile-vault": { steps: ["Sign Profile", "Save Through Vault"] },
172
+ };
173
+
174
+ const FLOW_LIFECYCLE: Record<string, LifecycleId> = {
175
+ "ens-clear": "ens-clear",
176
+ "ens-link": "ens-link",
177
+ "ens-update": "ens-update",
178
+ "ens-register": "ens-register",
179
+ "custody-switch": "custody-switch",
180
+ "public-profile-vault": "public-profile-vault",
181
+ };
182
+
183
+ const PURPOSE_TIMELINE: Record<string, readonly [string, string]> = {
184
+ "create-agent": ["Sign Recovery Access", "Mint Token"],
185
+ "update-snapshot-owner": ["Sign Snapshot", "Save Onchain"],
186
+ "update-snapshot-operator": ["Sign Snapshot", "Save Onchain"],
187
+ "update-snapshot-connected": ["Sign Snapshot", "Save Onchain"],
188
+ "update-profile-owner": ["Sign Profile", "Save Onchain"],
189
+ "update-profile-operator": ["Sign Profile", "Save Onchain"],
190
+ "update-profile-connected": ["Sign Profile", "Save Onchain"],
191
+ "update-operators": ["Sign Operator List", "Publish List"],
192
+ "create-simple-ens-subdomain": ["Sign Request", "Create Subdomain"],
193
+ "set-simple-ens-records": ["Sign Records", "Write Records"],
194
+ "create-agent-ens-subdomain": ["Sign Request", "Create Subdomain"],
195
+ "set-agent-ens-records": ["Sign Records", "Write Records"],
196
+ "rotate-agent-uri-vault-owner": ["Sign Update", "Save Through Vault"],
197
+ "rotate-agent-uri-vault-operator": ["Sign Update", "Save Through Vault"],
198
+ "update-ens": ["Sign Snapshot", "Save Onchain"],
199
+ "clear-ens": ["Sign Snapshot", "Save Onchain"],
200
+ };
201
+
202
+ function activeLifecycle(): LifecycleId | undefined {
203
+ if (config.flowId && FLOW_LIFECYCLE[config.flowId]) return FLOW_LIFECYCLE[config.flowId];
204
+ return undefined;
205
+ }
206
+
207
+ let currentTimelineKey: string | null = null;
208
+
209
+ function lifecycleStepIndex(lifecycle: LifecycleId, state: string | null): number {
210
+ const flowStep = typeof config.flowStep === "number" ? config.flowStep : 1;
211
+ const def = LIFECYCLE_DEFINITIONS[lifecycle];
212
+ const steps = def.steps.length;
213
+ if (state === "done") return Math.min(flowStep, steps);
214
+ return Math.max(0, Math.min(flowStep - 1, steps - 1));
215
+ }
216
+
217
+ function hasNextLifecyclePrompt(): boolean {
218
+ const lifecycle = activeLifecycle();
219
+ if (!lifecycle) return false;
220
+ const flowStep = typeof config.flowStep === "number" ? config.flowStep : 1;
221
+ return flowStep < LIFECYCLE_DEFINITIONS[lifecycle].steps.length;
222
+ }
223
+
224
+ function nextLifecycleHint(): string {
225
+ return "Keep this page open. The next wallet step will appear here.";
226
+ }
227
+
228
+ function purposeStepIndex(state: string | null): number {
229
+ if (state === "approve-transaction" || state === "submitting") return 1;
230
+ if (state === "done") return 2;
231
+ return 0;
232
+ }
233
+
234
+ export function applyTransferTimeline(): void {
235
+ const timeline = document.getElementById("timeline") as HTMLElement | null;
236
+ if (!timeline) return;
237
+
238
+ const lifecycle = activeLifecycle();
239
+ let steps: string[] | null = null;
240
+ let activeIndex = 0;
241
+ let key = "";
242
+
243
+ if (lifecycle) {
244
+ steps = LIFECYCLE_DEFINITIONS[lifecycle].steps;
245
+ activeIndex = lifecycleStepIndex(lifecycle, currentState);
246
+ key = "flow:" + lifecycle;
247
+ } else if (config.kind === "sign-transaction") {
248
+ const tuple = PURPOSE_TIMELINE[config.purpose || ""] || (["Sign", "Submit"] as const);
249
+ steps = [tuple[0], tuple[1]];
250
+ activeIndex = purposeStepIndex(currentState);
251
+ key = "purpose:" + (config.purpose || "");
252
+ }
253
+
254
+ if (!steps) {
255
+ if (!timeline.hidden) timeline.hidden = true;
256
+ currentTimelineKey = null;
257
+ return;
258
+ }
259
+
260
+ if (currentTimelineKey !== key) {
261
+ timeline.innerHTML = "";
262
+ for (const action of steps) {
263
+ const li = document.createElement("li");
264
+ li.className = "timeline-step";
265
+ li.dataset.state = "pending";
266
+ const marker = document.createElement("span");
267
+ marker.className = "timeline-marker";
268
+ marker.setAttribute("aria-hidden", "true");
269
+ const label = document.createElement("span");
270
+ label.className = "timeline-label";
271
+ const actionEl = document.createElement("span");
272
+ actionEl.className = "timeline-action";
273
+ actionEl.textContent = action;
274
+ label.append(actionEl);
275
+ li.append(marker, label);
276
+ timeline.append(li);
277
+ }
278
+ timeline.setAttribute("aria-label", "Wallet popup steps");
279
+ currentTimelineKey = key;
280
+ }
281
+
282
+ timeline.hidden = false;
283
+ const stepEls = timeline.querySelectorAll<HTMLElement>(".timeline-step");
284
+ stepEls.forEach((step, i) => {
285
+ const next = i < activeIndex ? "done" : i === activeIndex ? "active" : "pending";
286
+ if (step.dataset.state !== next) step.dataset.state = next;
287
+ });
288
+ }
289
+
290
+ function markActiveTimelineStepDone(): void {
291
+ const timeline = document.getElementById("timeline") as HTMLElement | null;
292
+ if (!timeline || timeline.hidden) return;
293
+ timeline.querySelectorAll<HTMLElement>('.timeline-step[data-state="active"]').forEach((step) => {
294
+ step.dataset.state = "done";
295
+ });
296
+ }
297
+
298
+ function spinnerText(value: string): string {
299
+ const text = preserveProtocolCaps(value);
300
+ return text.replace(/^(\s*)([a-z])/, (_match, prefix, letter) => `${prefix}${letter.toUpperCase()}`);
301
+ }
302
+ function spinnerHintText(value: string): string { return preserveProtocolCaps(value); }
303
+ function preserveProtocolCaps(value: string): string {
304
+ return String(value || "")
305
+ .replace(/\bapi\b/gi, "API")
306
+ .replace(/\bens\b/gi, "ENS")
307
+ .replace(/\berc-8004\b/gi, "ERC-8004")
308
+ .replace(/\bgguf\b/gi, "GGUF")
309
+ .replace(/\bhugging face\b/gi, "Hugging Face")
310
+ .replace(/\bipfs\b/gi, "IPFS")
311
+ .replace(/\bjson\b/gi, "JSON")
312
+ .replace(/\bjwt\b/gi, "JWT")
313
+ .replace(/\bmemory\.md\b/gi, "MEMORY.md")
314
+ .replace(/\bopenai\b/gi, "OpenAI")
315
+ .replace(/\banthropic\b/gi, "Anthropic")
316
+ .replace(/\bgemini\b/gi, "Gemini")
317
+ .replace(/\bos\b/gi, "OS")
318
+ .replace(/\brpc\b/gi, "RPC")
319
+ .replace(/\bsoul\.md\b/gi, "SOUL.md")
320
+ .replace(/\buri\b/gi, "URI")
321
+ .replace(/\burl\b/gi, "URL");
322
+ }
323
+
324
+ function setStatus(marker: string, text: string, hint: string, spin: boolean): void {
325
+ const lineEl = statusText.parentElement as HTMLElement;
326
+ const hintEl = statusHint;
327
+ const displayText = spin ? spinnerText(text) : text;
328
+ const displayHint = spin ? spinnerHintText(hint) : hint;
329
+ const apply = () => {
330
+ if (spin) startSpinner();
331
+ else setMarker(marker);
332
+ statusText.textContent = displayText;
333
+ hintEl.textContent = displayHint;
334
+ requestAnimationFrame(() => {
335
+ lineEl.classList.remove("is-changing");
336
+ hintEl.classList.remove("is-changing");
337
+ });
338
+ };
339
+ if (statusText.textContent === displayText && hintEl.textContent === displayHint) {
340
+ if (spin) startSpinner();
341
+ else setMarker(marker);
342
+ return;
343
+ }
344
+ lineEl.classList.add("is-changing");
345
+ hintEl.classList.add("is-changing");
346
+ setTimeout(apply, 220);
347
+ }
348
+
349
+ export let currentState: string | null = null;
350
+
351
+ export function setState(state: string, payload?: any): void {
352
+ payload = payload || {};
353
+ currentState = state;
354
+ errorSlot.innerHTML = "";
355
+ statusBlock.style.display = "flex";
356
+ setTabTitle(tabTitleForState(state));
357
+ applyTransferTimeline();
358
+ switch (state) {
359
+ case "connecting":
360
+ setStatus("·", "Connecting To Wallet...", "Open your wallet if needed.", true);
361
+ break;
362
+ case "approve":
363
+ if (config.kind === "account") {
364
+ const copy = accountCopy();
365
+ setStatus("·", copy.text, copy.hint, true);
366
+ } else if (config.kind === "sign") {
367
+ const sigCopy = signCopy();
368
+ setStatus("·", sigCopy.text, sigCopy.hint, true);
369
+ } else {
370
+ const txCopy = transactionCopy();
371
+ setStatus("·", txCopy.text, txCopy.hint, true);
372
+ }
373
+ break;
374
+ case "approve-sign":
375
+ {
376
+ const sigCopy = signCopy();
377
+ setStatus("·", sigCopy.text, sigCopy.hint, true);
378
+ }
379
+ break;
380
+ case "preparing-transaction":
381
+ setStatus("·", purposeCopy().prepare!.text, purposeCopy().prepare!.hint, true);
382
+ break;
383
+ case "approve-transaction":
384
+ {
385
+ const txCopy = transactionCopy();
386
+ setStatus("·", txCopy.text, txCopy.hint, true);
387
+ }
388
+ break;
389
+ case "submitting":
390
+ if (config.kind === "account") setStatus("·", "Connecting Wallet...", "Returning to terminal.", true);
391
+ else if (config.kind === "sign") setStatus("·", "Verifying Signature...", hasNextLifecyclePrompt() ? nextLifecycleHint() : "Returning to terminal.", true);
392
+ else setStatus("·", "Submitted · Waiting For Confirmation...", "Your wallet accepted the transaction.", true);
393
+ break;
394
+ case "done":
395
+ stopSpinner();
396
+ statusMarker.innerHTML =
397
+ '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor"' +
398
+ ' stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">' +
399
+ '<polyline points="20 6 9 17 4 12"></polyline></svg>';
400
+ statusText.textContent =
401
+ config.kind === "account" ? "Connected · Returning"
402
+ : config.kind === "sign" ? (hasNextLifecyclePrompt() ? "Signed · Waiting" : "Signed · Returning")
403
+ : "Submitted · Returning";
404
+ statusHint.textContent = payload.txHash
405
+ ? (isTransactionFlow() ? "Transaction submitted. Returning." : "This tab will close shortly.")
406
+ : hasNextLifecyclePrompt() ? nextLifecycleHint() : "This tab will close shortly.";
407
+ markActiveTimelineStepDone();
408
+ break;
409
+ case "error":
410
+ stopSpinner();
411
+ statusBlock.style.display = "none";
412
+ renderError(payload);
413
+ break;
414
+ }
415
+ }
416
+
417
+ let lastWalletError: WalletErrorPayload | null = null;
418
+
419
+ function safeStringify(v: unknown, max: number): string | undefined {
420
+ if (v === undefined || v === null) return undefined;
421
+ let s: string;
422
+ try { s = typeof v === "string" ? v : JSON.stringify(v); } catch (_) { s = String(v); }
423
+ if (!s) return undefined;
424
+ return s.length > max ? s.slice(0, max) + "..." : s;
425
+ }
426
+
427
+ export function serializeWalletError(err: unknown, method: string | null): WalletErrorPayload {
428
+ const e = err as any;
429
+ const message = ((e && (e.message ?? String(err))) || "Something went wrong.").trim();
430
+ const codeRaw = e && (e.code ?? e.errorCode);
431
+ const code = codeRaw === undefined || codeRaw === null ? undefined : String(codeRaw);
432
+ const data = safeStringify(e && e.data, 500);
433
+ const causes: string[] = [];
434
+ let cur = e && e.cause;
435
+ for (let i = 0; i < 5 && cur; i++) {
436
+ const cm = ((cur && (cur.message ?? String(cur))) || "").trim();
437
+ if (cm) causes.push(cm);
438
+ cur = cur && cur.cause;
439
+ }
440
+ const out: WalletErrorPayload = { message };
441
+ if (code) out.code = code;
442
+ if (data) out.data = data;
443
+ if (causes.length) out.causes = causes;
444
+ if (method) out.method = method;
445
+ if (config.purpose) out.purpose = config.purpose;
446
+ if (config.chainIdHex) out.chainIdHex = config.chainIdHex;
447
+ return out;
448
+ }
449
+
450
+ function actionContextFor(payload: WalletErrorPayload): string {
451
+ const purpose = payload.purpose || config.purpose;
452
+ if (purpose) {
453
+ const copy = (PURPOSE_COPY as any)[purpose] as PurposeCopyEntry | undefined;
454
+ const ctx = copy && (copy as any).errorContext;
455
+ if (typeof ctx === "string" && ctx) return ctx;
456
+ }
457
+ if (payload.method) return "during " + payload.method;
458
+ return "";
459
+ }
460
+
461
+ function classifyByCode(code: string | undefined): { title: string; hint: string } | null {
462
+ if (!code) return null;
463
+ switch (code) {
464
+ case "4001": return { title: "Rejected", hint: "Press <code>enter</code> to retry or <code>esc</code> to abort." };
465
+ case "4100": return { title: "Wallet Not Authorized", hint: "Connect this site in your wallet, then retry." };
466
+ case "4200": return { title: "Method Not Supported by Wallet", hint: "Use a wallet that supports this transaction type, then retry." };
467
+ case "4900": return { title: "Wallet Disconnected", hint: "Reconnect your wallet, then retry." };
468
+ case "4901": return { title: "Wrong Network", hint: "Switch to <code>" + escapeHtml(chainLabel(config.chainIdHex)) + "</code>, then retry." };
469
+ case "-32603": return { title: "Internal Wallet RPC Error", hint: "The wallet's connected RPC failed. Try again, or switch RPC in your wallet settings." };
470
+ case "-32602": return { title: "Invalid Request Parameters", hint: "Press <code>enter</code> to retry or <code>esc</code> to abort." };
471
+ case "-32000": case "-32001": case "-32002": case "-32003": case "-32004":
472
+ case "-32005": case "-32006": case "-32007": case "-32008": case "-32009":
473
+ return { title: "Wallet RPC Error", hint: "The wallet RPC reported a server error. Try again or switch RPC in your wallet." };
474
+ default: return null;
475
+ }
476
+ }
477
+
478
+ function renderError(payload: WalletErrorPayload): void {
479
+ const msg = payload.message || "Something went wrong.";
480
+ const isNoWallet = /no wallet|window\.ethereum|metamask|rabby|brave|extension/i.test(msg);
481
+ const isUserReject = /user rejected|user denied|cancelled|canceled/i.test(msg);
482
+ const isWrongChain = /chain|network/i.test(msg) && !isNoWallet;
483
+ const isExecutionRevert = /execution reverted|revert/i.test(msg);
484
+ const isOwnerWalletRequired = /owner wallet required/i.test(msg);
485
+ const isOperatorWalletRequired = /operator wallet required/i.test(msg);
486
+ let title = "Wallet Error";
487
+ let body = msg;
488
+ let hint = "Press <code>enter</code> to retry or <code>esc</code> to abort.";
489
+ const codeClass = classifyByCode(payload.code);
490
+ if (codeClass) {
491
+ title = codeClass.title;
492
+ hint = codeClass.hint;
493
+ } else if (isOwnerWalletRequired) {
494
+ title = "Owner Wallet Required";
495
+ body = msg.replace(/^owner wallet required:\s*/i, "");
496
+ body = body ? body.charAt(0).toUpperCase() + body.slice(1) : "Switch to the owner wallet.";
497
+ hint = "Switch to the owner wallet, then retry.";
498
+ } else if (isOperatorWalletRequired) {
499
+ title = "Operator Wallet Required";
500
+ body = msg.replace(/^operator wallet required:\s*/i, "");
501
+ body = body ? body.charAt(0).toUpperCase() + body.slice(1) : "Switch to the operator wallet.";
502
+ hint = "Switch to the operator wallet, then retry.";
503
+ } else if (isNoWallet) {
504
+ title = "No Wallet";
505
+ body = "Install a wallet.";
506
+ hint = "Install a wallet, then retry.";
507
+ } else if (isUserReject) {
508
+ title = "Rejected";
509
+ body = "Request declined in wallet.";
510
+ hint = "Press <code>enter</code> to retry or <code>esc</code> to abort.";
511
+ } else if (isWrongChain) {
512
+ title = "Wrong Network";
513
+ hint = "Switch to <code>" + escapeHtml(chainLabel(config.chainIdHex)) + "</code>, then retry.";
514
+ } else if (isExecutionRevert) {
515
+ title = "Transaction Reverted";
516
+ hint = "Use the expected wallet and check ENS ownership, then retry.";
517
+ }
518
+ if (body) body = body.charAt(0).toUpperCase() + body.slice(1);
519
+ payload.title = title;
520
+ const action = actionContextFor(payload);
521
+ let html = '<p class="error-title">' + escapeHtml(title) + "</p>"
522
+ + '<p class="error-msg">' + escapeHtml(body) + "</p>";
523
+ if (action) html += '<p class="error-action">' + escapeHtml(action) + "</p>";
524
+ if (payload.causes && payload.causes.length) {
525
+ for (const cause of payload.causes) {
526
+ html += '<p class="error-cause">Caused by: ' + escapeHtml(cause) + "</p>";
527
+ }
528
+ }
529
+ html += '<p class="error-hint">' + hint + "</p>";
530
+ errorSlot.innerHTML = html;
531
+ lastWalletError = payload;
532
+ }
533
+
534
+ export function getLastWalletError(): WalletErrorPayload | null { return lastWalletError }
535
+ export function clearLastWalletError(): void { lastWalletError = null }
@@ -0,0 +1,70 @@
1
+ import type { WalletTx } from './types.js'
2
+ import { WALLET_PROVIDER_POLL_MS, WALLET_PROVIDER_WAIT_MS } from './constants.js'
3
+
4
+ let announcedEthereum: any = null;
5
+ let activeEthereum: any = null;
6
+
7
+ function rememberAnnouncedProvider(event: any): void {
8
+ const provider = event && event.detail && event.detail.provider;
9
+ if (provider && !announcedEthereum) announcedEthereum = provider;
10
+ }
11
+ window.addEventListener("eip6963:announceProvider", rememberAnnouncedProvider as EventListener);
12
+ try { window.dispatchEvent(new Event("eip6963:requestProvider")); } catch (_) { }
13
+
14
+ export function ethereumProvider(): any { return activeEthereum || (window as any).ethereum || announcedEthereum; }
15
+ export function noWalletError(): Error { return new Error("No wallet detected. Install a wallet and retry."); }
16
+
17
+ export function waitForEthereumProvider(timeoutMs?: number): Promise<any> {
18
+ const existing = ethereumProvider();
19
+ if (existing) return Promise.resolve(existing);
20
+ return new Promise((resolve, reject) => {
21
+ let settled = false;
22
+ const cleanup = () => {
23
+ clearTimeout(timer);
24
+ clearInterval(interval);
25
+ window.removeEventListener("ethereum#initialized", check);
26
+ window.removeEventListener("eip6963:announceProvider", onAnnounce as EventListener);
27
+ };
28
+ const finish = (fn: () => void) => {
29
+ if (settled) return;
30
+ settled = true;
31
+ cleanup();
32
+ fn();
33
+ };
34
+ const check = () => { const provider = ethereumProvider(); if (provider) finish(() => resolve(provider)); };
35
+ const onAnnounce = (event: any) => { rememberAnnouncedProvider(event); check(); };
36
+ const timer = setTimeout(() => finish(() => reject(noWalletError())), timeoutMs || WALLET_PROVIDER_WAIT_MS);
37
+ const interval = setInterval(check, WALLET_PROVIDER_POLL_MS);
38
+ window.addEventListener("ethereum#initialized", check);
39
+ window.addEventListener("eip6963:announceProvider", onAnnounce as EventListener);
40
+ try { window.dispatchEvent(new Event("eip6963:requestProvider")); } catch (_) { }
41
+ check();
42
+ });
43
+ }
44
+
45
+ export let currentWalletMethod: string | null = null;
46
+ export async function walletRequest(method: string, params?: any): Promise<any> {
47
+ const provider = ethereumProvider();
48
+ if (!provider) throw noWalletError();
49
+ currentWalletMethod = method;
50
+ try {
51
+ return await provider.request(params === undefined ? { method } : { method, params });
52
+ } finally {
53
+ currentWalletMethod = null;
54
+ }
55
+ }
56
+
57
+ export function buildTxParams(account: string, tx: WalletTx): Record<string, string> {
58
+ if (!tx || typeof tx.data !== "string") {
59
+ throw new Error("Transaction is missing call data.");
60
+ }
61
+ const params: Record<string, string> = { from: account, data: tx.data };
62
+ if (typeof tx.to === "string" && tx.to.length > 0) params.to = tx.to;
63
+ if (typeof tx.value === "string" && tx.value.length > 0) params.value = tx.value;
64
+ if (typeof tx.gas === "string" && tx.gas.length > 0) params.gas = tx.gas;
65
+ if (typeof tx.maxFeePerGas === "string" && tx.maxFeePerGas.length > 0) params.maxFeePerGas = tx.maxFeePerGas;
66
+ if (typeof tx.maxPriorityFeePerGas === "string" && tx.maxPriorityFeePerGas.length > 0) params.maxPriorityFeePerGas = tx.maxPriorityFeePerGas;
67
+ return params;
68
+ }
69
+ export function setActiveEthereum(provider: unknown): void { activeEthereum = provider }
70
+ export function clearCurrentWalletMethod(): void { currentWalletMethod = null }
@@ -0,0 +1,38 @@
1
+ import { injectStylesAndMarkup } from './page/markup.js'
2
+ import { startGrainient } from './page/grainient.js'
3
+ import { renderEyes } from './page/html.js'
4
+ import { bootWallet } from './page/controller.js'
5
+
6
+ injectStylesAndMarkup()
7
+
8
+ const grainCanvas = document.getElementById('grainient') as HTMLCanvasElement
9
+ if (grainCanvas) {
10
+ startGrainient(grainCanvas, {
11
+ color1: '#000422',
12
+ color2: '#d8dcfa',
13
+ color3: '#000422',
14
+ timeSpeed: 0.25,
15
+ colorBalance: 0,
16
+ warpStrength: 1,
17
+ warpFrequency: 5,
18
+ warpSpeed: 2,
19
+ warpAmplitude: 10,
20
+ blendAngle: 0,
21
+ blendSoftness: 0.05,
22
+ rotationAmount: 500,
23
+ noiseScale: 2,
24
+ grainAmount: 0.1,
25
+ grainScale: 2,
26
+ grainAnimated: false,
27
+ contrast: 1.5,
28
+ gamma: 1,
29
+ saturation: 1,
30
+ centerX: 0,
31
+ centerY: 0,
32
+ zoom: 0.9,
33
+ })
34
+ }
35
+
36
+ ;(document.getElementById('splash') as HTMLElement).innerHTML = renderEyes()
37
+
38
+ bootWallet()
@@ -0,0 +1,27 @@
1
+ const legacyOwnerRole = ['c', 'old'].join('')
2
+ const legacyOperatorRole = ['h', 'ot'].join('')
3
+
4
+ function legacyWalletPurpose(action: string, role: string, suffix = 'wallet'): string {
5
+ return `${action}-${role}-${suffix}`
6
+ }
7
+
8
+ export const LEGACY_WALLET_PURPOSE_ALIASES: Record<string, string> = {
9
+ [legacyWalletPurpose('connect', legacyOperatorRole)]: 'connect-operator-wallet',
10
+ [legacyWalletPurpose('restore', legacyOwnerRole)]: 'restore-owner-wallet',
11
+ [legacyWalletPurpose('restore', legacyOperatorRole)]: 'restore-operator-wallet',
12
+ [legacyWalletPurpose('authorize', legacyOperatorRole, 'wallet-resolver')]: 'authorize-operator-wallet-resolver',
13
+ [legacyWalletPurpose('revoke', legacyOperatorRole, 'wallet-resolver')]: 'revoke-operator-wallet-resolver',
14
+ }
15
+
16
+ export function normalizeWalletPurposeValue(value: unknown): string | undefined {
17
+ if (typeof value !== 'string') return undefined
18
+ const trimmed = value.trim()
19
+ if (!trimmed) return undefined
20
+ return LEGACY_WALLET_PURPOSE_ALIASES[trimmed] ?? trimmed
21
+ }
22
+
23
+ export function normalizeWalletPayloadPurpose(payload: Record<string, unknown>): Record<string, unknown> {
24
+ const purpose = normalizeWalletPurposeValue(payload.purpose)
25
+ if (!purpose || purpose === payload.purpose) return payload
26
+ return { ...payload, purpose }
27
+ }
@@ -27,7 +27,6 @@ import {
27
27
  formatMcpCallResult,
28
28
  formatMcpResourceResult,
29
29
  promptMessagesToText,
30
- truncateMcpOutput,
31
30
  } from './output.js'
32
31
 
33
32
  const MCP_CONNECT_TIMEOUT_MS = 10_000