@wlfi-agent/cli 1.4.13 → 1.4.15

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 (289) hide show
  1. package/Cargo.lock +3968 -0
  2. package/Cargo.toml +50 -0
  3. package/README.md +426 -6
  4. package/crates/vault-cli-admin/Cargo.toml +26 -0
  5. package/crates/vault-cli-admin/src/io_utils.rs +500 -0
  6. package/crates/vault-cli-admin/src/main.rs +3990 -0
  7. package/crates/vault-cli-admin/src/shared_config.rs +624 -0
  8. package/crates/vault-cli-admin/src/tui/amounts.rs +180 -0
  9. package/crates/vault-cli-admin/src/tui/token_rpc.rs +250 -0
  10. package/crates/vault-cli-admin/src/tui/utils.rs +82 -0
  11. package/crates/vault-cli-admin/src/tui.rs +3410 -0
  12. package/crates/vault-cli-agent/Cargo.toml +24 -0
  13. package/crates/vault-cli-agent/src/io_utils.rs +576 -0
  14. package/crates/vault-cli-agent/src/main.rs +833 -0
  15. package/crates/vault-cli-daemon/Cargo.toml +28 -0
  16. package/crates/vault-cli-daemon/src/bin/wlfi-agent-system-keychain.rs +216 -0
  17. package/crates/vault-cli-daemon/src/main.rs +644 -0
  18. package/crates/vault-cli-daemon/src/relay_sync.rs +894 -0
  19. package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +167 -0
  20. package/crates/vault-daemon/Cargo.toml +32 -0
  21. package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +1041 -0
  22. package/crates/vault-daemon/src/daemon_parts/core_helpers.rs +1256 -0
  23. package/crates/vault-daemon/src/daemon_parts/types_api_rpc.rs +622 -0
  24. package/crates/vault-daemon/src/lib.rs +54 -0
  25. package/crates/vault-daemon/src/persistence.rs +441 -0
  26. package/crates/vault-daemon/src/tests.rs +237 -0
  27. package/crates/vault-daemon/src/tests_parts/part1.rs +1224 -0
  28. package/crates/vault-daemon/src/tests_parts/part2.rs +1021 -0
  29. package/crates/vault-daemon/src/tests_parts/part3.rs +835 -0
  30. package/crates/vault-daemon/src/tests_parts/part4.rs +604 -0
  31. package/crates/vault-domain/Cargo.toml +20 -0
  32. package/crates/vault-domain/src/action.rs +849 -0
  33. package/crates/vault-domain/src/address.rs +51 -0
  34. package/crates/vault-domain/src/approval.rs +90 -0
  35. package/crates/vault-domain/src/constants.rs +4 -0
  36. package/crates/vault-domain/src/error.rs +54 -0
  37. package/crates/vault-domain/src/keys.rs +71 -0
  38. package/crates/vault-domain/src/lib.rs +42 -0
  39. package/crates/vault-domain/src/nonce.rs +102 -0
  40. package/crates/vault-domain/src/policy.rs +172 -0
  41. package/crates/vault-domain/src/request.rs +53 -0
  42. package/crates/vault-domain/src/scope.rs +24 -0
  43. package/crates/vault-domain/src/session.rs +50 -0
  44. package/crates/vault-domain/src/signature.rs +34 -0
  45. package/crates/vault-domain/src/tests.rs +651 -0
  46. package/crates/vault-domain/src/u128_as_decimal_string.rs +44 -0
  47. package/crates/vault-policy/Cargo.toml +17 -0
  48. package/crates/vault-policy/src/engine.rs +301 -0
  49. package/crates/vault-policy/src/error.rs +81 -0
  50. package/crates/vault-policy/src/lib.rs +17 -0
  51. package/crates/vault-policy/src/report.rs +34 -0
  52. package/crates/vault-policy/src/tests.rs +891 -0
  53. package/crates/vault-policy/src/tests_explain.rs +78 -0
  54. package/crates/vault-sdk-agent/Cargo.toml +21 -0
  55. package/crates/vault-sdk-agent/src/lib.rs +711 -0
  56. package/crates/vault-signer/Cargo.toml +25 -0
  57. package/crates/vault-signer/src/lib.rs +731 -0
  58. package/crates/vault-signer/tests/secure_enclave_acl.rs +54 -0
  59. package/crates/vault-transport-unix/Cargo.toml +24 -0
  60. package/crates/vault-transport-unix/src/lib.rs +1640 -0
  61. package/crates/vault-transport-xpc/Cargo.toml +25 -0
  62. package/crates/vault-transport-xpc/src/client_codec_api.rs +635 -0
  63. package/crates/vault-transport-xpc/src/lib.rs +680 -0
  64. package/crates/vault-transport-xpc/src/tests.rs +818 -0
  65. package/crates/vault-transport-xpc/tests/e2e_flow.rs +773 -0
  66. package/dist/cli.cjs +35088 -0
  67. package/dist/cli.cjs.map +1 -0
  68. package/package.json +45 -41
  69. package/packages/cache/.turbo/turbo-build.log +52 -0
  70. package/packages/cache/dist/chunk-2QFWMUXT.cjs +43 -0
  71. package/packages/cache/dist/chunk-2QFWMUXT.cjs.map +1 -0
  72. package/packages/cache/dist/chunk-4U63TZTQ.js +43 -0
  73. package/packages/cache/dist/chunk-4U63TZTQ.js.map +1 -0
  74. package/packages/cache/dist/chunk-ALQ6H7KG.cjs +404 -0
  75. package/packages/cache/dist/chunk-ALQ6H7KG.cjs.map +1 -0
  76. package/packages/cache/dist/chunk-FGJEEF5N.js +404 -0
  77. package/packages/cache/dist/chunk-FGJEEF5N.js.map +1 -0
  78. package/packages/cache/dist/chunk-UYNEHZHB.cjs +45 -0
  79. package/packages/cache/dist/chunk-UYNEHZHB.cjs.map +1 -0
  80. package/packages/cache/dist/chunk-VXVMPG3W.js +45 -0
  81. package/packages/cache/dist/chunk-VXVMPG3W.js.map +1 -0
  82. package/packages/cache/dist/client/index.cjs +11 -0
  83. package/packages/cache/dist/client/index.cjs.map +1 -0
  84. package/packages/cache/dist/client/index.d.cts +15 -0
  85. package/packages/cache/dist/client/index.d.ts +15 -0
  86. package/packages/cache/dist/client/index.js +11 -0
  87. package/packages/cache/dist/client/index.js.map +1 -0
  88. package/packages/cache/dist/errors/index.cjs +11 -0
  89. package/packages/cache/dist/errors/index.cjs.map +1 -0
  90. package/packages/cache/dist/errors/index.d.cts +26 -0
  91. package/packages/cache/dist/errors/index.d.ts +26 -0
  92. package/packages/cache/dist/errors/index.js +11 -0
  93. package/packages/cache/dist/errors/index.js.map +1 -0
  94. package/packages/cache/dist/index.cjs +29 -0
  95. package/packages/cache/dist/index.cjs.map +1 -0
  96. package/packages/cache/dist/index.d.cts +4 -0
  97. package/packages/cache/dist/index.d.ts +4 -0
  98. package/packages/cache/dist/index.js +29 -0
  99. package/packages/cache/dist/index.js.map +1 -0
  100. package/packages/cache/dist/service/index.cjs +15 -0
  101. package/packages/cache/dist/service/index.cjs.map +1 -0
  102. package/packages/cache/dist/service/index.d.cts +184 -0
  103. package/packages/cache/dist/service/index.d.ts +184 -0
  104. package/packages/cache/dist/service/index.js +15 -0
  105. package/packages/cache/dist/service/index.js.map +1 -0
  106. package/packages/cache/node_modules/.bin/jiti +17 -0
  107. package/packages/cache/node_modules/.bin/tsc +17 -0
  108. package/packages/cache/node_modules/.bin/tsserver +17 -0
  109. package/packages/cache/node_modules/.bin/tsup +17 -0
  110. package/packages/cache/node_modules/.bin/tsup-node +17 -0
  111. package/packages/cache/node_modules/.bin/tsx +17 -0
  112. package/packages/cache/node_modules/.bin/vitest +17 -0
  113. package/packages/cache/package.json +48 -0
  114. package/packages/cache/src/client/index.ts +56 -0
  115. package/packages/cache/src/errors/index.ts +53 -0
  116. package/packages/cache/src/index.ts +3 -0
  117. package/packages/cache/src/service/index.test.ts +263 -0
  118. package/packages/cache/src/service/index.ts +678 -0
  119. package/packages/cache/tsconfig.json +13 -0
  120. package/packages/cache/tsup.config.ts +13 -0
  121. package/packages/cache/vitest.config.ts +16 -0
  122. package/packages/config/.turbo/turbo-build.log +18 -0
  123. package/packages/config/dist/index.cjs +1037 -0
  124. package/packages/config/dist/index.cjs.map +1 -0
  125. package/packages/config/dist/index.d.ts +131 -0
  126. package/packages/config/node_modules/.bin/jiti +17 -0
  127. package/packages/config/node_modules/.bin/tsc +17 -0
  128. package/packages/config/node_modules/.bin/tsserver +17 -0
  129. package/packages/config/node_modules/.bin/tsup +17 -0
  130. package/packages/config/node_modules/.bin/tsup-node +17 -0
  131. package/packages/config/node_modules/.bin/tsx +17 -0
  132. package/packages/config/package.json +21 -0
  133. package/packages/config/src/index.js +1 -0
  134. package/packages/config/src/index.ts +1282 -0
  135. package/packages/config/tsconfig.json +4 -0
  136. package/packages/rpc/.turbo/turbo-build.log +32 -0
  137. package/packages/rpc/dist/_esm-BCLXDO2R.cjs +3660 -0
  138. package/packages/rpc/dist/_esm-BCLXDO2R.cjs.map +1 -0
  139. package/packages/rpc/dist/ccip-OWJLAW55.cjs +16 -0
  140. package/packages/rpc/dist/ccip-OWJLAW55.cjs.map +1 -0
  141. package/packages/rpc/dist/chunk-APQIFZ3B.cjs +6247 -0
  142. package/packages/rpc/dist/chunk-APQIFZ3B.cjs.map +1 -0
  143. package/packages/rpc/dist/chunk-CDO2GWRD.cjs +410 -0
  144. package/packages/rpc/dist/chunk-CDO2GWRD.cjs.map +1 -0
  145. package/packages/rpc/dist/chunk-QGTNTFJ7.cjs +2249 -0
  146. package/packages/rpc/dist/chunk-QGTNTFJ7.cjs.map +1 -0
  147. package/packages/rpc/dist/chunk-TZDTAHWR.cjs +44 -0
  148. package/packages/rpc/dist/chunk-TZDTAHWR.cjs.map +1 -0
  149. package/packages/rpc/dist/index.cjs +7342 -0
  150. package/packages/rpc/dist/index.cjs.map +1 -0
  151. package/packages/rpc/dist/index.d.ts +3857 -0
  152. package/packages/rpc/dist/secp256k1-WCNM675D.cjs +18 -0
  153. package/packages/rpc/dist/secp256k1-WCNM675D.cjs.map +1 -0
  154. package/packages/rpc/node_modules/.bin/jiti +17 -0
  155. package/packages/rpc/node_modules/.bin/tsc +17 -0
  156. package/packages/rpc/node_modules/.bin/tsserver +17 -0
  157. package/packages/rpc/node_modules/.bin/tsup +17 -0
  158. package/packages/rpc/node_modules/.bin/tsup-node +17 -0
  159. package/packages/rpc/node_modules/.bin/tsx +17 -0
  160. package/packages/rpc/package.json +25 -0
  161. package/packages/rpc/src/index.ts +206 -0
  162. package/packages/rpc/tsconfig.json +4 -0
  163. package/packages/typescript/base.json +36 -0
  164. package/packages/typescript/nextjs.json +17 -0
  165. package/packages/typescript/package.json +10 -0
  166. package/packages/ui/.turbo/turbo-build.log +44 -0
  167. package/packages/ui/dist/chunk-MOAFBKSA.js +11 -0
  168. package/packages/ui/dist/chunk-MOAFBKSA.js.map +1 -0
  169. package/packages/ui/dist/components/badge.d.ts +12 -0
  170. package/packages/ui/dist/components/badge.js +31 -0
  171. package/packages/ui/dist/components/badge.js.map +1 -0
  172. package/packages/ui/dist/components/button.d.ts +13 -0
  173. package/packages/ui/dist/components/button.js +40 -0
  174. package/packages/ui/dist/components/button.js.map +1 -0
  175. package/packages/ui/dist/components/card.d.ts +10 -0
  176. package/packages/ui/dist/components/card.js +39 -0
  177. package/packages/ui/dist/components/card.js.map +1 -0
  178. package/packages/ui/dist/components/input.d.ts +5 -0
  179. package/packages/ui/dist/components/input.js +28 -0
  180. package/packages/ui/dist/components/input.js.map +1 -0
  181. package/packages/ui/dist/components/label.d.ts +5 -0
  182. package/packages/ui/dist/components/label.js +13 -0
  183. package/packages/ui/dist/components/label.js.map +1 -0
  184. package/packages/ui/dist/components/separator.d.ts +5 -0
  185. package/packages/ui/dist/components/separator.js +13 -0
  186. package/packages/ui/dist/components/separator.js.map +1 -0
  187. package/packages/ui/dist/components/textarea.d.ts +5 -0
  188. package/packages/ui/dist/components/textarea.js +27 -0
  189. package/packages/ui/dist/components/textarea.js.map +1 -0
  190. package/packages/ui/dist/tailwind.d.ts +56 -0
  191. package/packages/ui/dist/tailwind.js +60 -0
  192. package/packages/ui/dist/tailwind.js.map +1 -0
  193. package/packages/ui/dist/utils/cn.d.ts +5 -0
  194. package/packages/ui/dist/utils/cn.js +7 -0
  195. package/packages/ui/dist/utils/cn.js.map +1 -0
  196. package/packages/ui/node_modules/.bin/jiti +17 -0
  197. package/packages/ui/node_modules/.bin/tsc +17 -0
  198. package/packages/ui/node_modules/.bin/tsserver +17 -0
  199. package/packages/ui/node_modules/.bin/tsup +17 -0
  200. package/packages/ui/node_modules/.bin/tsup-node +17 -0
  201. package/packages/ui/node_modules/.bin/tsx +17 -0
  202. package/packages/ui/package.json +69 -0
  203. package/packages/ui/src/components/badge.tsx +27 -0
  204. package/packages/ui/src/components/button.tsx +40 -0
  205. package/packages/ui/src/components/card.tsx +31 -0
  206. package/packages/ui/src/components/input.tsx +21 -0
  207. package/packages/ui/src/components/label.tsx +6 -0
  208. package/packages/ui/src/components/separator.tsx +6 -0
  209. package/packages/ui/src/components/textarea.tsx +20 -0
  210. package/packages/ui/src/globals.css +70 -0
  211. package/packages/ui/src/tailwind.ts +56 -0
  212. package/packages/ui/src/utils/cn.ts +6 -0
  213. package/packages/ui/tsconfig.json +20 -0
  214. package/packages/ui/tsup.config.ts +20 -0
  215. package/pnpm-workspace.yaml +4 -0
  216. package/scripts/install-rust-binaries.mjs +84 -0
  217. package/scripts/launchd/install-user-daemon.sh +358 -0
  218. package/scripts/launchd/run-vault-daemon.sh +5 -0
  219. package/scripts/launchd/run-wlfi-agent-daemon.sh +73 -0
  220. package/scripts/launchd/uninstall-user-daemon.sh +103 -0
  221. package/src/cli.ts +2121 -0
  222. package/src/lib/admin-guard.js +1 -0
  223. package/src/lib/admin-guard.ts +185 -0
  224. package/src/lib/admin-passthrough.ts +33 -0
  225. package/src/lib/admin-reset.ts +751 -0
  226. package/src/lib/admin-setup.ts +1612 -0
  227. package/src/lib/agent-auth-clear.js +1 -0
  228. package/src/lib/agent-auth-clear.ts +58 -0
  229. package/src/lib/agent-auth-forwarding.js +1 -0
  230. package/src/lib/agent-auth-forwarding.ts +149 -0
  231. package/src/lib/agent-auth-migrate.js +1 -0
  232. package/src/lib/agent-auth-migrate.ts +150 -0
  233. package/src/lib/agent-auth-revoke.ts +103 -0
  234. package/src/lib/agent-auth-rotate.ts +107 -0
  235. package/src/lib/agent-auth-token.js +1 -0
  236. package/src/lib/agent-auth-token.ts +25 -0
  237. package/src/lib/agent-auth.ts +89 -0
  238. package/src/lib/asset-broadcast.js +1 -0
  239. package/src/lib/asset-broadcast.ts +285 -0
  240. package/src/lib/bootstrap-artifacts.js +1 -0
  241. package/src/lib/bootstrap-artifacts.ts +205 -0
  242. package/src/lib/bootstrap-credentials.js +1 -0
  243. package/src/lib/bootstrap-credentials.ts +832 -0
  244. package/src/lib/config-amounts.js +1 -0
  245. package/src/lib/config-amounts.ts +189 -0
  246. package/src/lib/config-mutation.ts +27 -0
  247. package/src/lib/fs-trust.js +1 -0
  248. package/src/lib/fs-trust.ts +537 -0
  249. package/src/lib/keychain.js +1 -0
  250. package/src/lib/keychain.ts +225 -0
  251. package/src/lib/local-admin-access.ts +106 -0
  252. package/src/lib/network-selection.js +1 -0
  253. package/src/lib/network-selection.ts +71 -0
  254. package/src/lib/passthrough-security.js +1 -0
  255. package/src/lib/passthrough-security.ts +114 -0
  256. package/src/lib/rpc-guard.js +1 -0
  257. package/src/lib/rpc-guard.ts +7 -0
  258. package/src/lib/rust-spawn-options.js +1 -0
  259. package/src/lib/rust-spawn-options.ts +98 -0
  260. package/src/lib/rust.js +1 -0
  261. package/src/lib/rust.ts +143 -0
  262. package/src/lib/signed-tx.js +1 -0
  263. package/src/lib/signed-tx.ts +116 -0
  264. package/src/lib/status-repair-cli.ts +116 -0
  265. package/src/lib/sudo.js +1 -0
  266. package/src/lib/sudo.ts +172 -0
  267. package/src/lib/vault-password-forwarding.js +1 -0
  268. package/src/lib/vault-password-forwarding.ts +155 -0
  269. package/src/lib/wallet-profile.js +1 -0
  270. package/src/lib/wallet-profile.ts +332 -0
  271. package/src/lib/wallet-repair.js +1 -0
  272. package/src/lib/wallet-repair.ts +304 -0
  273. package/src/lib/wallet-setup.js +1 -0
  274. package/src/lib/wallet-setup.ts +1466 -0
  275. package/src/lib/wallet-status.js +1 -0
  276. package/src/lib/wallet-status.ts +640 -0
  277. package/tsconfig.base.json +17 -0
  278. package/tsconfig.json +10 -0
  279. package/tsup.config.ts +25 -0
  280. package/turbo.json +41 -0
  281. package/LICENSE.md +0 -1
  282. package/dist/wlfa/index.cjs +0 -250
  283. package/dist/wlfa/index.d.cts +0 -1
  284. package/dist/wlfa/index.d.ts +0 -1
  285. package/dist/wlfa/index.js +0 -250
  286. package/dist/wlfc/index.cjs +0 -1839
  287. package/dist/wlfc/index.d.cts +0 -1
  288. package/dist/wlfc/index.d.ts +0 -1
  289. package/dist/wlfc/index.js +0 -1839
@@ -0,0 +1 @@
1
+ export * from './wallet-profile.ts';
@@ -0,0 +1,332 @@
1
+ import { resolveChainProfile, type WalletProfile, type WlfiConfig } from '../../packages/config/src/index.js';
2
+ import type { Address, Hex } from 'viem';
3
+ import { isAddress } from 'viem';
4
+ import { publicKeyToAddress } from 'viem/accounts';
5
+ import { listAutoGeneratedBootstrapArtifacts } from './bootstrap-artifacts.js';
6
+ import {
7
+ readBootstrapSetupSummaryFile,
8
+ type BootstrapSetupSummary,
9
+ } from './bootstrap-credentials.js';
10
+
11
+ function presentString(value: string | undefined | null): string | undefined {
12
+ const normalized = value?.trim();
13
+ return normalized ? normalized : undefined;
14
+ }
15
+
16
+ function renderError(error: unknown): string {
17
+ return error instanceof Error ? error.message : String(error);
18
+ }
19
+
20
+ function normalizePublicKeyHex(value: string): Hex {
21
+ const normalized = value.trim();
22
+ return (normalized.startsWith('0x') ? normalized : `0x${normalized}`) as Hex;
23
+ }
24
+
25
+ function deriveWalletAddress(vaultPublicKey: string): string | undefined {
26
+ try {
27
+ return publicKeyToAddress(normalizePublicKeyHex(vaultPublicKey));
28
+ } catch {
29
+ return undefined;
30
+ }
31
+ }
32
+
33
+ function findMatchingBootstrapSummary(profile: WalletProfile | undefined): BootstrapSetupSummary | undefined {
34
+ if (!profile) {
35
+ return undefined;
36
+ }
37
+
38
+ const persistedAgentKeyId = presentString(profile.agentKeyId);
39
+ const persistedPublicKey = presentString(profile.vaultPublicKey);
40
+ const persistedAddress = presentString(profile.address) ?? (
41
+ persistedPublicKey ? deriveWalletAddress(persistedPublicKey) : undefined
42
+ );
43
+
44
+ return listAutoGeneratedBootstrapArtifacts()
45
+ .filter(
46
+ (artifact) =>
47
+ (artifact.status === 'plaintext' || artifact.status === 'redacted') && !artifact.leaseExpired,
48
+ )
49
+ .map((artifact) => readBootstrapSetupSummaryFile(artifact.path))
50
+ .find((summary) => {
51
+ if (persistedAgentKeyId && summary.agentKeyId === persistedAgentKeyId) {
52
+ return true;
53
+ }
54
+
55
+ if (persistedPublicKey && summary.vaultPublicKey === persistedPublicKey) {
56
+ return true;
57
+ }
58
+
59
+ const summaryAddress = deriveWalletAddress(summary.vaultPublicKey);
60
+ return Boolean(persistedAddress && summaryAddress && summaryAddress === persistedAddress);
61
+ });
62
+ }
63
+
64
+ export function walletProfileFromBootstrapSummary(summary: BootstrapSetupSummary): WalletProfile {
65
+ return {
66
+ vaultKeyId: presentString(summary.vaultKeyId) ?? summary.vaultKeyId,
67
+ vaultPublicKey: presentString(summary.vaultPublicKey) ?? summary.vaultPublicKey,
68
+ address: deriveWalletAddress(summary.vaultPublicKey),
69
+ agentKeyId: presentString(summary.agentKeyId),
70
+ policyAttachment: presentString(summary.policyAttachment) ?? summary.policyAttachment,
71
+ attachedPolicyIds: summary.attachedPolicyIds.length > 0 ? [...summary.attachedPolicyIds] : undefined,
72
+ policyNote: presentString(summary.policyNote),
73
+ networkScope: presentString(summary.networkScope),
74
+ assetScope: presentString(summary.assetScope),
75
+ recipientScope: presentString(summary.recipientScope),
76
+ };
77
+ }
78
+
79
+ export function resolveWalletProfile(config: WlfiConfig): WalletProfile {
80
+ if (config.wallet?.vaultPublicKey && config.wallet?.policyAttachment) {
81
+ const matchingBootstrap = findMatchingBootstrapSummary(config.wallet);
82
+ return {
83
+ ...config.wallet,
84
+ vaultKeyId: config.wallet.vaultKeyId ?? matchingBootstrap?.vaultKeyId,
85
+ address: config.wallet.address ?? deriveWalletAddress(config.wallet.vaultPublicKey),
86
+ };
87
+ }
88
+
89
+ const bootstrapArtifact = listAutoGeneratedBootstrapArtifacts().find(
90
+ (artifact) =>
91
+ (artifact.status === 'plaintext' || artifact.status === 'redacted') && !artifact.leaseExpired,
92
+ );
93
+ if (!bootstrapArtifact) {
94
+ throw new Error(
95
+ 'wallet metadata is unavailable; rerun `wlfi-agent admin setup` or import a bootstrap file first',
96
+ );
97
+ }
98
+
99
+ return walletProfileFromBootstrapSummary(readBootstrapSetupSummaryFile(bootstrapArtifact.path));
100
+ }
101
+
102
+ export function resolveWalletAddress(config: WlfiConfig): Address {
103
+ const profile = resolveWalletProfile(config);
104
+ const address = presentString(profile.address) ?? deriveWalletAddress(profile.vaultPublicKey);
105
+ if (!address || !isAddress(address)) {
106
+ throw new Error(
107
+ 'wallet address is unavailable; rerun `wlfi-agent admin setup` or import a bootstrap file first',
108
+ );
109
+ }
110
+
111
+ return address;
112
+ }
113
+
114
+ export interface WalletBalanceEntry {
115
+ tokenKey: string;
116
+ symbol: string;
117
+ name?: string;
118
+ chainKey: string;
119
+ chainName: string;
120
+ chainId: number;
121
+ rpcUrl: string;
122
+ kind: 'native' | 'erc20';
123
+ tokenAddress: string;
124
+ decimals: number;
125
+ balance?: {
126
+ raw: string;
127
+ formatted: string;
128
+ };
129
+ error?: string;
130
+ }
131
+
132
+ export interface WalletProfileWithBalances extends WalletProfile {
133
+ balances: WalletBalanceEntry[];
134
+ }
135
+
136
+ interface ResolveWalletProfileWithBalancesDeps {
137
+ getNativeBalance: (rpcUrl: string, address: Address) => Promise<{
138
+ raw: bigint;
139
+ formatted: string;
140
+ }>;
141
+ getTokenBalance: (rpcUrl: string, token: Address, owner: Address, decimals?: number) => Promise<{
142
+ raw: bigint;
143
+ decimals: number;
144
+ name: string | null;
145
+ symbol: string | null;
146
+ formatted: string;
147
+ }>;
148
+ }
149
+
150
+ interface WalletBalanceTarget {
151
+ tokenKey: string;
152
+ symbol: string;
153
+ name?: string;
154
+ chainKey: string;
155
+ chainName: string;
156
+ chainId: number;
157
+ rpcUrl: string;
158
+ isNative: boolean;
159
+ tokenAddress: string;
160
+ decimals: number;
161
+ }
162
+
163
+ function collectWalletBalanceTargets(config: WlfiConfig): WalletBalanceTarget[] {
164
+ return Object.entries(config.tokens ?? {})
165
+ .flatMap(([tokenKey, tokenProfile]) =>
166
+ Object.entries(tokenProfile.chains ?? {}).flatMap(([chainKey, chainProfile]) => {
167
+ const resolvedChain = resolveChainProfile(chainKey, config);
168
+ const rpcUrl = presentString(resolvedChain?.rpcUrl);
169
+ if (!rpcUrl) {
170
+ return [];
171
+ }
172
+
173
+ return [{
174
+ tokenKey,
175
+ symbol: tokenProfile.symbol,
176
+ name: tokenProfile.name,
177
+ chainKey,
178
+ chainName: resolvedChain?.name ?? chainKey,
179
+ chainId: chainProfile.chainId,
180
+ rpcUrl,
181
+ isNative: chainProfile.isNative,
182
+ tokenAddress: chainProfile.isNative
183
+ ? 'native'
184
+ : presentString(chainProfile.address) ?? '',
185
+ decimals: chainProfile.decimals,
186
+ }];
187
+ }),
188
+ )
189
+ .sort(
190
+ (left, right) =>
191
+ left.chainId - right.chainId
192
+ || left.chainKey.localeCompare(right.chainKey)
193
+ || left.symbol.localeCompare(right.symbol)
194
+ || left.tokenKey.localeCompare(right.tokenKey),
195
+ );
196
+ }
197
+
198
+ export async function resolveWalletProfileWithBalances(
199
+ config: WlfiConfig,
200
+ deps: ResolveWalletProfileWithBalancesDeps,
201
+ ): Promise<WalletProfileWithBalances> {
202
+ const profile = resolveWalletProfile(config);
203
+ const owner = resolveWalletAddress(config);
204
+ const nativeBalance = deps.getNativeBalance;
205
+ const erc20Balance = deps.getTokenBalance;
206
+
207
+ const balances = await Promise.all(
208
+ collectWalletBalanceTargets(config).map(async (target): Promise<WalletBalanceEntry> => {
209
+ if (target.isNative) {
210
+ try {
211
+ const balance = await nativeBalance(target.rpcUrl, owner);
212
+ return {
213
+ tokenKey: target.tokenKey,
214
+ symbol: target.symbol,
215
+ name: target.name,
216
+ chainKey: target.chainKey,
217
+ chainName: target.chainName,
218
+ chainId: target.chainId,
219
+ rpcUrl: target.rpcUrl,
220
+ kind: 'native',
221
+ tokenAddress: target.tokenAddress,
222
+ decimals: target.decimals,
223
+ balance: {
224
+ raw: balance.raw.toString(),
225
+ formatted: balance.formatted,
226
+ },
227
+ };
228
+ } catch (error) {
229
+ return {
230
+ tokenKey: target.tokenKey,
231
+ symbol: target.symbol,
232
+ name: target.name,
233
+ chainKey: target.chainKey,
234
+ chainName: target.chainName,
235
+ chainId: target.chainId,
236
+ rpcUrl: target.rpcUrl,
237
+ kind: 'native',
238
+ tokenAddress: target.tokenAddress,
239
+ decimals: target.decimals,
240
+ error: renderError(error),
241
+ };
242
+ }
243
+ }
244
+
245
+ if (!isAddress(target.tokenAddress)) {
246
+ return {
247
+ tokenKey: target.tokenKey,
248
+ symbol: target.symbol,
249
+ name: target.name,
250
+ chainKey: target.chainKey,
251
+ chainName: target.chainName,
252
+ chainId: target.chainId,
253
+ rpcUrl: target.rpcUrl,
254
+ kind: 'erc20',
255
+ tokenAddress: target.tokenAddress || '<unset>',
256
+ decimals: target.decimals,
257
+ error: `configured token address for ${target.tokenKey} on ${target.chainKey} is invalid`,
258
+ };
259
+ }
260
+
261
+ try {
262
+ const balance = await erc20Balance(target.rpcUrl, target.tokenAddress as Address, owner, target.decimals);
263
+ return {
264
+ tokenKey: target.tokenKey,
265
+ symbol: balance.symbol ?? target.symbol,
266
+ name: balance.name ?? target.name,
267
+ chainKey: target.chainKey,
268
+ chainName: target.chainName,
269
+ chainId: target.chainId,
270
+ rpcUrl: target.rpcUrl,
271
+ kind: 'erc20',
272
+ tokenAddress: target.tokenAddress,
273
+ decimals: balance.decimals,
274
+ balance: {
275
+ raw: balance.raw.toString(),
276
+ formatted: balance.formatted,
277
+ },
278
+ };
279
+ } catch (error) {
280
+ return {
281
+ tokenKey: target.tokenKey,
282
+ symbol: target.symbol,
283
+ name: target.name,
284
+ chainKey: target.chainKey,
285
+ chainName: target.chainName,
286
+ chainId: target.chainId,
287
+ rpcUrl: target.rpcUrl,
288
+ kind: 'erc20',
289
+ tokenAddress: target.tokenAddress,
290
+ decimals: target.decimals,
291
+ error: renderError(error),
292
+ };
293
+ }
294
+ }),
295
+ );
296
+
297
+ return {
298
+ ...profile,
299
+ balances,
300
+ };
301
+ }
302
+
303
+ export function formatWalletProfileText(profile: WalletProfile | WalletProfileWithBalances): string {
304
+ const lines = [
305
+ `Public Key: ${profile.vaultPublicKey}`,
306
+ profile.address ? `Address: ${profile.address}` : null,
307
+ profile.agentKeyId ? `Agent Key ID: ${profile.agentKeyId}` : null,
308
+ `Policy Attachment: ${profile.policyAttachment}`,
309
+ profile.policyNote ? `Policy Note: ${profile.policyNote}` : null,
310
+ profile.networkScope ? `Network Scope: ${profile.networkScope}` : null,
311
+ profile.assetScope ? `Asset Scope: ${profile.assetScope}` : null,
312
+ profile.recipientScope ? `Recipient Scope: ${profile.recipientScope}` : null,
313
+ profile.attachedPolicyIds?.length
314
+ ? `Attached Policy IDs: ${profile.attachedPolicyIds.join(', ')}`
315
+ : null,
316
+ ];
317
+
318
+ if ('balances' in profile && Array.isArray(profile.balances) && profile.balances.length > 0) {
319
+ lines.push('Balances:');
320
+ lines.push(
321
+ ...profile.balances.map((balance) => {
322
+ const label = `${balance.symbol} on ${balance.chainKey} (${balance.chainId})`;
323
+ if (balance.error) {
324
+ return `- ${label}: error: ${balance.error}`;
325
+ }
326
+ return `- ${label}: ${balance.balance?.formatted ?? '0'}`;
327
+ }),
328
+ );
329
+ }
330
+
331
+ return lines.filter((line): line is string => Boolean(line)).join('\n');
332
+ }
@@ -0,0 +1 @@
1
+ export * from './wallet-repair.ts';
@@ -0,0 +1,304 @@
1
+ import { readConfig, type WlfiConfig } from '../../packages/config/src/index.js';
2
+ import {
3
+ type MigrateLegacyAgentAuthInput,
4
+ type MigrateLegacyAgentAuthResult,
5
+ migrateLegacyAgentAuthToken,
6
+ } from './agent-auth-migrate.js';
7
+ import {
8
+ type BootstrapArtifactCheck,
9
+ cleanupAutoGeneratedBootstrapArtifacts,
10
+ } from './bootstrap-artifacts.js';
11
+ import { assertValidAgentKeyId } from './keychain.js';
12
+ import { getWalletStatus, type WalletStatusResult } from './wallet-status.js';
13
+
14
+ export interface RepairWalletStateOptions {
15
+ agentKeyId?: string;
16
+ overwriteKeychain?: boolean;
17
+ redactBootstrap?: boolean;
18
+ }
19
+
20
+ export interface WalletRepairLegacyAgentAuthResult {
21
+ attempted: boolean;
22
+ action: 'none' | 'migrated' | 'scrubbed-config' | 'skipped';
23
+ agentKeyId: string | null;
24
+ reason: string | null;
25
+ keychain: {
26
+ service: string | null;
27
+ stored: boolean;
28
+ overwritten: boolean;
29
+ alreadyPresent: boolean;
30
+ matchedExisting: boolean;
31
+ } | null;
32
+ }
33
+
34
+ export interface WalletRepairBootstrapArtifactsResult {
35
+ attempted: boolean;
36
+ action: 'deleted' | 'redacted';
37
+ wlfiHome: string | null;
38
+ files: Array<
39
+ BootstrapArtifactCheck & {
40
+ cleanup: 'deleted' | 'redacted' | 'skipped';
41
+ }
42
+ >;
43
+ error: string | null;
44
+ }
45
+
46
+ export interface WalletRepairResult {
47
+ before: WalletStatusResult;
48
+ after: WalletStatusResult;
49
+ legacyAgentAuth: WalletRepairLegacyAgentAuthResult;
50
+ bootstrapArtifacts: WalletRepairBootstrapArtifactsResult;
51
+ fixedWarnings: string[];
52
+ remainingWarnings: string[];
53
+ newWarnings: string[];
54
+ }
55
+
56
+ interface RepairWalletStateDeps {
57
+ platform?: NodeJS.Platform;
58
+ readConfig?: () => WlfiConfig;
59
+ getWalletStatus?: () => WalletStatusResult;
60
+ migrateLegacyAgentAuthToken?: (
61
+ input: MigrateLegacyAgentAuthInput,
62
+ ) => MigrateLegacyAgentAuthResult;
63
+ cleanupAutoGeneratedBootstrapArtifacts?: (action: 'deleted' | 'redacted') => {
64
+ wlfiHome: string;
65
+ action: 'deleted' | 'redacted';
66
+ files: Array<
67
+ BootstrapArtifactCheck & {
68
+ cleanup: 'deleted' | 'redacted' | 'skipped';
69
+ }
70
+ >;
71
+ };
72
+ }
73
+
74
+ function renderError(error: unknown): string {
75
+ return error instanceof Error ? error.message : String(error);
76
+ }
77
+
78
+ function presentSecret(value: string | undefined): string | null {
79
+ if (typeof value !== 'string') {
80
+ return null;
81
+ }
82
+
83
+ return value.trim().length > 0 ? value : null;
84
+ }
85
+
86
+ function resolveResultAgentKeyId(
87
+ explicitAgentKeyId: string | undefined,
88
+ status: WalletStatusResult,
89
+ ): string | null {
90
+ if (explicitAgentKeyId) {
91
+ return explicitAgentKeyId;
92
+ }
93
+
94
+ return status.agent.agentKeyId;
95
+ }
96
+
97
+ function describeLegacyMigration(result: MigrateLegacyAgentAuthResult): string {
98
+ if (!result.keychain.stored) {
99
+ return 'matching macOS Keychain token already existed, so plaintext config storage was scrubbed';
100
+ }
101
+
102
+ if (result.keychain.overwritten) {
103
+ return 'legacy config secret replaced a different macOS Keychain token after explicit confirmation, then plaintext config storage was scrubbed';
104
+ }
105
+
106
+ return 'legacy config secret was moved into macOS Keychain and scrubbed from plaintext config storage';
107
+ }
108
+
109
+ function repairLegacyAgentAuth(
110
+ input: RepairWalletStateOptions,
111
+ before: WalletStatusResult,
112
+ deps: RepairWalletStateDeps,
113
+ ): WalletRepairLegacyAgentAuthResult {
114
+ const platform = deps.platform ?? process.platform;
115
+ const loadConfig = deps.readConfig ?? readConfig;
116
+ const migrate = deps.migrateLegacyAgentAuthToken ?? migrateLegacyAgentAuthToken;
117
+ const explicitAgentKeyId = input.agentKeyId ? assertValidAgentKeyId(input.agentKeyId) : undefined;
118
+
119
+ let config: WlfiConfig;
120
+ try {
121
+ config = loadConfig();
122
+ } catch (error) {
123
+ return {
124
+ attempted: false,
125
+ action: 'skipped',
126
+ agentKeyId: resolveResultAgentKeyId(explicitAgentKeyId, before),
127
+ reason: `config is unreadable: ${renderError(error)}`,
128
+ keychain: null,
129
+ };
130
+ }
131
+
132
+ if (!presentSecret(config.agentAuthToken)) {
133
+ return {
134
+ attempted: false,
135
+ action: 'none',
136
+ agentKeyId: resolveResultAgentKeyId(explicitAgentKeyId, before),
137
+ reason: null,
138
+ keychain: null,
139
+ };
140
+ }
141
+
142
+ if (platform !== 'darwin') {
143
+ return {
144
+ attempted: false,
145
+ action: 'skipped',
146
+ agentKeyId: resolveResultAgentKeyId(explicitAgentKeyId, before),
147
+ reason:
148
+ 'legacy agentAuthToken cannot be repaired automatically without macOS Keychain access',
149
+ keychain: null,
150
+ };
151
+ }
152
+
153
+ try {
154
+ const result = migrate({
155
+ agentKeyId: explicitAgentKeyId,
156
+ overwriteKeychain: input.overwriteKeychain,
157
+ });
158
+ return {
159
+ attempted: true,
160
+ action: result.keychain.stored ? 'migrated' : 'scrubbed-config',
161
+ agentKeyId: result.agentKeyId,
162
+ reason: describeLegacyMigration(result),
163
+ keychain: {
164
+ service: result.keychain.service,
165
+ stored: result.keychain.stored,
166
+ overwritten: result.keychain.overwritten,
167
+ alreadyPresent: result.keychain.alreadyPresent,
168
+ matchedExisting: result.keychain.matchedExisting,
169
+ },
170
+ };
171
+ } catch (error) {
172
+ return {
173
+ attempted: true,
174
+ action: 'skipped',
175
+ agentKeyId: resolveResultAgentKeyId(explicitAgentKeyId, before),
176
+ reason: renderError(error),
177
+ keychain: null,
178
+ };
179
+ }
180
+ }
181
+
182
+ function repairBootstrapArtifacts(
183
+ input: RepairWalletStateOptions,
184
+ deps: RepairWalletStateDeps,
185
+ ): WalletRepairBootstrapArtifactsResult {
186
+ const cleanup =
187
+ deps.cleanupAutoGeneratedBootstrapArtifacts ?? cleanupAutoGeneratedBootstrapArtifacts;
188
+ const action = input.redactBootstrap ? 'redacted' : 'deleted';
189
+
190
+ try {
191
+ const result = cleanup(action);
192
+ return {
193
+ attempted: true,
194
+ action: result.action,
195
+ wlfiHome: result.wlfiHome,
196
+ files: result.files,
197
+ error: null,
198
+ };
199
+ } catch (error) {
200
+ return {
201
+ attempted: true,
202
+ action,
203
+ wlfiHome: null,
204
+ files: [],
205
+ error: renderError(error),
206
+ };
207
+ }
208
+ }
209
+
210
+ function summarizeLegacyAgentAuth(result: WalletRepairLegacyAgentAuthResult): string {
211
+ switch (result.action) {
212
+ case 'none':
213
+ return 'no plaintext config token was present';
214
+ case 'migrated':
215
+ return `migrated for ${result.agentKeyId ?? '<unknown>'}`;
216
+ case 'scrubbed-config':
217
+ return `scrubbed plaintext config storage for ${result.agentKeyId ?? '<unknown>'}`;
218
+ case 'skipped':
219
+ return result.reason ? `skipped: ${result.reason}` : 'skipped';
220
+ default:
221
+ return result.reason ?? result.action;
222
+ }
223
+ }
224
+
225
+ function summarizeBootstrapArtifacts(result: WalletRepairBootstrapArtifactsResult): string {
226
+ if (result.error) {
227
+ return `cleanup failed: ${result.error}`;
228
+ }
229
+ if (result.files.length === 0) {
230
+ return 'no auto-generated bootstrap files found';
231
+ }
232
+
233
+ const deleted = result.files.filter((file) => file.cleanup === 'deleted').length;
234
+ const redacted = result.files.filter((file) => file.cleanup === 'redacted').length;
235
+ const skipped = result.files.filter((file) => file.cleanup === 'skipped').length;
236
+ const parts = [`${deleted} deleted`, `${redacted} redacted`];
237
+ if (skipped > 0) {
238
+ parts.push(`${skipped} skipped`);
239
+ }
240
+ return parts.join(', ');
241
+ }
242
+
243
+ export function formatWalletRepairText(result: WalletRepairResult): string {
244
+ const lines = [
245
+ 'wallet repair complete',
246
+ `legacy agent auth: ${summarizeLegacyAgentAuth(result.legacyAgentAuth)}`,
247
+ `bootstrap artifacts: ${summarizeBootstrapArtifacts(result.bootstrapArtifacts)}`,
248
+ ];
249
+
250
+ if (result.fixedWarnings.length === 0) {
251
+ lines.push('fixed warnings: none');
252
+ } else {
253
+ lines.push(`fixed warnings (${result.fixedWarnings.length}):`);
254
+ for (const warning of result.fixedWarnings) {
255
+ lines.push(`- ${warning}`);
256
+ }
257
+ }
258
+
259
+ if (result.remainingWarnings.length === 0) {
260
+ lines.push('remaining warnings: none');
261
+ } else {
262
+ lines.push(`remaining warnings (${result.remainingWarnings.length}):`);
263
+ for (const warning of result.remainingWarnings) {
264
+ lines.push(`- ${warning}`);
265
+ }
266
+ }
267
+
268
+ if (result.newWarnings.length > 0) {
269
+ lines.push(`new warnings (${result.newWarnings.length}):`);
270
+ for (const warning of result.newWarnings) {
271
+ lines.push(`- ${warning}`);
272
+ }
273
+ }
274
+
275
+ return lines.join('\n');
276
+ }
277
+
278
+ export function repairWalletState(
279
+ input: RepairWalletStateOptions = {},
280
+ deps: RepairWalletStateDeps = {},
281
+ ): WalletRepairResult {
282
+ const readStatus = deps.getWalletStatus ?? getWalletStatus;
283
+ const before = readStatus();
284
+ const legacyAgentAuth = repairLegacyAgentAuth(input, before, deps);
285
+ const bootstrapArtifacts = repairBootstrapArtifacts(input, deps);
286
+ const after = readStatus();
287
+
288
+ const fixedWarnings = before.security.warnings.filter(
289
+ (warning) => !after.security.warnings.includes(warning),
290
+ );
291
+ const newWarnings = after.security.warnings.filter(
292
+ (warning) => !before.security.warnings.includes(warning),
293
+ );
294
+
295
+ return {
296
+ before,
297
+ after,
298
+ legacyAgentAuth,
299
+ bootstrapArtifacts,
300
+ fixedWarnings,
301
+ remainingWarnings: after.security.warnings,
302
+ newWarnings,
303
+ };
304
+ }
@@ -0,0 +1 @@
1
+ export * from './wallet-setup.ts';