agentvault 1.0.1 → 1.0.2

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 (292) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/dist/cli/commands/approve.js +5 -5
  3. package/dist/cli/commands/archive.js +5 -5
  4. package/dist/cli/commands/backup.js +5 -5
  5. package/dist/cli/commands/cloud-backup.js +12 -12
  6. package/dist/cli/commands/decrypt.js +2 -2
  7. package/dist/cli/commands/deploy.js +1 -1
  8. package/dist/cli/commands/exec.js +2 -2
  9. package/dist/cli/commands/fetch.js +4 -4
  10. package/dist/cli/commands/inference.js +5 -5
  11. package/dist/cli/commands/init.d.ts +1 -1
  12. package/dist/cli/commands/init.js +16 -16
  13. package/dist/cli/commands/list.js +4 -4
  14. package/dist/cli/commands/package.js +2 -2
  15. package/dist/cli/commands/profile.js +1 -1
  16. package/dist/cli/commands/rebuild.js +2 -2
  17. package/dist/cli/commands/show.js +1 -1
  18. package/dist/cli/commands/status.d.ts +1 -1
  19. package/dist/cli/commands/status.js +8 -8
  20. package/dist/cli/commands/trace.js +1 -1
  21. package/dist/cli/commands/wallet-export.js +1 -1
  22. package/dist/cli/commands/wallet-sign.js +1 -1
  23. package/dist/cli/commands/wallet.d.ts +1 -1
  24. package/dist/cli/commands/wallet.js +1 -1
  25. package/dist/cli/index.d.ts +2 -2
  26. package/dist/cli/index.js +3 -3
  27. package/dist/src/archival/archive-manager.d.ts +85 -0
  28. package/dist/src/archival/archive-manager.js +294 -0
  29. package/dist/src/archival/arweave-client.d.ts +88 -0
  30. package/dist/src/archival/arweave-client.js +223 -0
  31. package/dist/src/archival/index.d.ts +8 -0
  32. package/{src/archival/index.ts → dist/src/archival/index.js} +1 -1
  33. package/dist/src/backup/backup.d.ts +67 -0
  34. package/dist/src/backup/backup.js +231 -0
  35. package/dist/src/backup/index.d.ts +7 -0
  36. package/{src/backup/index.ts → dist/src/backup/index.js} +1 -1
  37. package/dist/src/cloud-storage/cloud-sync.d.ts +49 -0
  38. package/dist/src/cloud-storage/cloud-sync.js +372 -0
  39. package/dist/src/cloud-storage/index.d.ts +11 -0
  40. package/{src/cloud-storage/index.ts → dist/src/cloud-storage/index.js} +1 -1
  41. package/dist/src/cloud-storage/provider-detector.d.ts +34 -0
  42. package/dist/src/cloud-storage/provider-detector.js +158 -0
  43. package/{src/cloud-storage/types.ts → dist/src/cloud-storage/types.d.ts} +40 -53
  44. package/dist/src/cloud-storage/types.js +10 -0
  45. package/dist/src/debugging/index.d.ts +6 -0
  46. package/{src/debugging/index.ts → dist/src/debugging/index.js} +1 -1
  47. package/dist/src/debugging/logs.d.ts +32 -0
  48. package/dist/src/debugging/logs.js +158 -0
  49. package/dist/src/debugging/types.d.ts +91 -0
  50. package/dist/src/debugging/types.js +5 -0
  51. package/dist/src/deployment/deployer.d.ts +52 -0
  52. package/dist/src/deployment/deployer.js +211 -0
  53. package/dist/src/deployment/icpClient.d.ts +144 -0
  54. package/dist/src/deployment/icpClient.js +545 -0
  55. package/dist/src/deployment/index.d.ts +11 -0
  56. package/dist/src/deployment/index.js +14 -0
  57. package/dist/src/deployment/promotion.d.ts +32 -0
  58. package/dist/src/deployment/promotion.js +114 -0
  59. package/dist/src/deployment/types.d.ts +101 -0
  60. package/dist/src/deployment/types.js +5 -0
  61. package/dist/src/icp/batch.d.ts +112 -0
  62. package/dist/src/icp/batch.js +273 -0
  63. package/dist/src/icp/cycles.d.ts +29 -0
  64. package/{src/icp/cycles.ts → dist/src/icp/cycles.js} +8 -22
  65. package/dist/src/icp/environment.d.ts +60 -0
  66. package/dist/src/icp/environment.js +183 -0
  67. package/dist/src/icp/icpcli.d.ts +204 -0
  68. package/dist/src/icp/icpcli.js +374 -0
  69. package/dist/src/icp/icwasm.d.ts +94 -0
  70. package/dist/src/icp/icwasm.js +197 -0
  71. package/dist/src/icp/identity.d.ts +50 -0
  72. package/{src/icp/identity.ts → dist/src/icp/identity.js} +15 -28
  73. package/dist/src/icp/index.d.ts +16 -0
  74. package/dist/src/icp/index.js +20 -0
  75. package/dist/src/icp/optimization.d.ts +16 -0
  76. package/dist/src/icp/optimization.js +225 -0
  77. package/dist/src/icp/tokens.d.ts +24 -0
  78. package/{src/icp/tokens.ts → dist/src/icp/tokens.js} +5 -12
  79. package/dist/src/icp/tool-detector.d.ts +31 -0
  80. package/dist/src/icp/tool-detector.js +104 -0
  81. package/dist/src/icp/types.d.ts +493 -0
  82. package/dist/src/icp/types.js +7 -0
  83. package/dist/src/index.d.ts +12 -0
  84. package/dist/src/index.js +18 -0
  85. package/dist/src/inference/bittensor-client.d.ts +108 -0
  86. package/dist/src/inference/bittensor-client.js +224 -0
  87. package/dist/src/inference/index.d.ts +8 -0
  88. package/{src/inference/index.ts → dist/src/inference/index.js} +1 -1
  89. package/dist/src/inference/inference-manager.d.ts +76 -0
  90. package/dist/src/inference/inference-manager.js +228 -0
  91. package/dist/src/metrics/index.d.ts +7 -0
  92. package/{src/metrics/index.ts → dist/src/metrics/index.js} +1 -1
  93. package/dist/src/metrics/metrics.d.ts +39 -0
  94. package/dist/src/metrics/metrics.js +129 -0
  95. package/dist/src/monitoring/alerting.d.ts +51 -0
  96. package/dist/src/monitoring/alerting.js +169 -0
  97. package/dist/src/monitoring/health.d.ts +40 -0
  98. package/dist/src/monitoring/health.js +164 -0
  99. package/dist/src/monitoring/index.d.ts +10 -0
  100. package/dist/src/monitoring/index.js +12 -0
  101. package/dist/src/monitoring/info.d.ts +15 -0
  102. package/dist/src/monitoring/info.js +109 -0
  103. package/dist/src/monitoring/types.d.ts +93 -0
  104. package/dist/src/monitoring/types.js +7 -0
  105. package/dist/src/network/index.d.ts +5 -0
  106. package/{src/network/index.ts → dist/src/network/index.js} +1 -1
  107. package/dist/src/network/network-config.d.ts +31 -0
  108. package/dist/src/network/network-config.js +109 -0
  109. package/dist/src/packaging/compiler.d.ts +61 -0
  110. package/dist/src/packaging/compiler.js +562 -0
  111. package/dist/src/packaging/config-persistence.d.ts +46 -0
  112. package/dist/src/packaging/config-persistence.js +108 -0
  113. package/dist/src/packaging/config-schemas.d.ts +115 -0
  114. package/dist/src/packaging/config-schemas.js +43 -0
  115. package/dist/src/packaging/detector.d.ts +26 -0
  116. package/dist/src/packaging/detector.js +193 -0
  117. package/dist/src/packaging/index.d.ts +16 -0
  118. package/dist/src/packaging/index.js +22 -0
  119. package/dist/src/packaging/packager.d.ts +31 -0
  120. package/dist/src/packaging/packager.js +90 -0
  121. package/dist/src/packaging/parsers/clawdbot.d.ts +19 -0
  122. package/dist/src/packaging/parsers/clawdbot.js +231 -0
  123. package/dist/src/packaging/parsers/cline.d.ts +26 -0
  124. package/dist/src/packaging/parsers/cline.js +185 -0
  125. package/dist/src/packaging/parsers/generic.d.ts +27 -0
  126. package/dist/src/packaging/parsers/generic.js +228 -0
  127. package/dist/src/packaging/parsers/goose.d.ts +26 -0
  128. package/dist/src/packaging/parsers/goose.js +175 -0
  129. package/dist/src/packaging/parsers/index.d.ts +11 -0
  130. package/{src/packaging/parsers/index.ts → dist/src/packaging/parsers/index.js} +1 -1
  131. package/dist/src/packaging/serializer.d.ts +108 -0
  132. package/dist/src/packaging/serializer.js +153 -0
  133. package/dist/src/packaging/types.d.ts +131 -0
  134. package/dist/src/packaging/types.js +5 -0
  135. package/dist/src/packaging/wasmedge-compiler.d.ts +76 -0
  136. package/dist/src/packaging/wasmedge-compiler.js +349 -0
  137. package/dist/src/security/index.d.ts +11 -0
  138. package/{src/security/index.ts → dist/src/security/index.js} +1 -4
  139. package/dist/src/security/multisig.d.ts +102 -0
  140. package/dist/src/security/multisig.js +283 -0
  141. package/dist/src/security/types.d.ts +207 -0
  142. package/dist/src/security/types.js +217 -0
  143. package/dist/src/security/vetkeys.d.ts +179 -0
  144. package/dist/src/security/vetkeys.js +499 -0
  145. package/dist/src/testing/index.d.ts +6 -0
  146. package/{src/testing/index.ts → dist/src/testing/index.js} +1 -1
  147. package/dist/src/testing/local-runner.d.ts +23 -0
  148. package/dist/src/testing/local-runner.js +226 -0
  149. package/dist/src/testing/types.d.ts +98 -0
  150. package/dist/src/testing/types.js +5 -0
  151. package/dist/src/wallet/cbor-serializer.d.ts +82 -0
  152. package/dist/src/wallet/cbor-serializer.js +282 -0
  153. package/dist/src/wallet/chain-dispatcher.d.ts +112 -0
  154. package/dist/src/wallet/chain-dispatcher.js +241 -0
  155. package/dist/src/wallet/cross-chain-aggregator.d.ts +119 -0
  156. package/dist/src/wallet/cross-chain-aggregator.js +235 -0
  157. package/dist/src/wallet/index.d.ts +16 -0
  158. package/dist/src/wallet/index.js +22 -0
  159. package/dist/src/wallet/key-derivation.d.ts +117 -0
  160. package/dist/src/wallet/key-derivation.js +325 -0
  161. package/dist/src/wallet/providers/base-provider.d.ts +111 -0
  162. package/dist/src/wallet/providers/base-provider.js +58 -0
  163. package/dist/src/wallet/providers/cketh-provider.d.ts +104 -0
  164. package/dist/src/wallet/providers/cketh-provider.js +343 -0
  165. package/dist/src/wallet/providers/polkadot-provider.d.ts +115 -0
  166. package/dist/src/wallet/providers/polkadot-provider.js +407 -0
  167. package/dist/src/wallet/providers/solana-provider.d.ts +102 -0
  168. package/dist/src/wallet/providers/solana-provider.js +393 -0
  169. package/dist/src/wallet/transaction-queue.d.ts +133 -0
  170. package/dist/src/wallet/transaction-queue.js +195 -0
  171. package/dist/src/wallet/types.d.ts +167 -0
  172. package/dist/src/wallet/types.js +5 -0
  173. package/dist/src/wallet/vetkeys-adapter.d.ts +134 -0
  174. package/dist/src/wallet/vetkeys-adapter.js +313 -0
  175. package/dist/src/wallet/wallet-manager.d.ts +202 -0
  176. package/dist/src/wallet/wallet-manager.js +451 -0
  177. package/dist/src/wallet/wallet-storage.d.ts +131 -0
  178. package/dist/src/wallet/wallet-storage.js +274 -0
  179. package/macos-wallet-app/AgentVaultWallet/App/AgentVaultWalletApp.swift +54 -0
  180. package/macos-wallet-app/AgentVaultWallet/Models/AppState.swift +102 -0
  181. package/macos-wallet-app/AgentVaultWallet/Models/Chain.swift +121 -0
  182. package/macos-wallet-app/AgentVaultWallet/Models/Wallet.swift +98 -0
  183. package/macos-wallet-app/AgentVaultWallet/Resources/AgentVaultWallet.entitlements +27 -0
  184. package/macos-wallet-app/AgentVaultWallet/Resources/Info.plist +69 -0
  185. package/macos-wallet-app/AgentVaultWallet/Services/BackupService.swift +270 -0
  186. package/macos-wallet-app/AgentVaultWallet/Services/CLIBridge.swift +367 -0
  187. package/macos-wallet-app/AgentVaultWallet/Services/CryptoService.swift +157 -0
  188. package/macos-wallet-app/AgentVaultWallet/Services/FileService.swift +120 -0
  189. package/macos-wallet-app/AgentVaultWallet/Services/KeychainService.swift +219 -0
  190. package/macos-wallet-app/AgentVaultWallet/Utilities/Constants.swift +44 -0
  191. package/macos-wallet-app/AgentVaultWallet/Utilities/Extensions.swift +115 -0
  192. package/macos-wallet-app/AgentVaultWallet/ViewModels/BackupViewModel.swift +237 -0
  193. package/macos-wallet-app/AgentVaultWallet/ViewModels/CreateWalletViewModel.swift +137 -0
  194. package/macos-wallet-app/AgentVaultWallet/ViewModels/ImportWalletViewModel.swift +179 -0
  195. package/macos-wallet-app/AgentVaultWallet/ViewModels/WalletStore.swift +286 -0
  196. package/macos-wallet-app/AgentVaultWallet/Views/Backup/BackupView.swift +235 -0
  197. package/macos-wallet-app/AgentVaultWallet/Views/Backup/RestoreView.swift +316 -0
  198. package/macos-wallet-app/AgentVaultWallet/Views/Create/CreateWalletFlow.swift +438 -0
  199. package/macos-wallet-app/AgentVaultWallet/Views/Import/ImportWalletFlow.swift +399 -0
  200. package/macos-wallet-app/AgentVaultWallet/Views/MainView.swift +134 -0
  201. package/macos-wallet-app/AgentVaultWallet/Views/Settings/SettingsView.swift +276 -0
  202. package/macos-wallet-app/AgentVaultWallet/Views/Sidebar/SidebarView.swift +133 -0
  203. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/DashboardView.swift +233 -0
  204. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/WalletDetailView.swift +281 -0
  205. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/WalletListView.swift +280 -0
  206. package/macos-wallet-app/AgentVaultWallet/Views/Welcome/WelcomeView.swift +176 -0
  207. package/macos-wallet-app/Makefile +47 -0
  208. package/macos-wallet-app/project.yml +40 -0
  209. package/macos-wallet-app/setup.sh +73 -0
  210. package/package.json +10 -2
  211. package/backups/agentvault-backup-test-agent-2026-02-12T17-54-28-967Z.json +0 -28
  212. package/backups/agentvault-backup-test-agent-2026-02-12T17-54-29-032Z.backup +0 -1
  213. package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-373Z.json +0 -28
  214. package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-428Z.backup +0 -1
  215. package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-132Z.json +0 -28
  216. package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-247Z.backup +0 -1
  217. package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-216Z.json +0 -28
  218. package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-283Z.backup +0 -1
  219. package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-772Z.backup +0 -1
  220. package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-793Z.json +0 -28
  221. package/backups/test-backup.json +0 -28
  222. package/scripts/dev-dashboard.mjs +0 -84
  223. package/site/README.md +0 -63
  224. package/site/docusaurus.config.ts +0 -148
  225. package/site/package-lock.json +0 -18383
  226. package/site/package.json +0 -47
  227. package/site/sidebars.ts +0 -86
  228. package/site/static/.gitkeep +0 -0
  229. package/site/static/img/logo.svg +0 -28
  230. package/site/static/img/og-image.svg +0 -35
  231. package/src/archival/archive-manager.ts +0 -372
  232. package/src/archival/arweave-client.ts +0 -289
  233. package/src/backup/backup.ts +0 -315
  234. package/src/cloud-storage/cloud-sync.ts +0 -461
  235. package/src/cloud-storage/provider-detector.ts +0 -198
  236. package/src/debugging/logs.ts +0 -193
  237. package/src/debugging/types.ts +0 -100
  238. package/src/deployment/deployer.ts +0 -274
  239. package/src/deployment/icpClient.ts +0 -620
  240. package/src/deployment/index.ts +0 -46
  241. package/src/deployment/promotion.ts +0 -161
  242. package/src/deployment/types.ts +0 -111
  243. package/src/icp/batch.ts +0 -374
  244. package/src/icp/environment.ts +0 -215
  245. package/src/icp/icpcli.ts +0 -438
  246. package/src/icp/icwasm.ts +0 -222
  247. package/src/icp/index.ts +0 -94
  248. package/src/icp/optimization.ts +0 -242
  249. package/src/icp/tool-detector.ts +0 -110
  250. package/src/icp/types.ts +0 -574
  251. package/src/index.ts +0 -25
  252. package/src/inference/bittensor-client.ts +0 -304
  253. package/src/inference/inference-manager.ts +0 -327
  254. package/src/metrics/metrics.ts +0 -186
  255. package/src/monitoring/alerting.ts +0 -190
  256. package/src/monitoring/health.ts +0 -197
  257. package/src/monitoring/index.ts +0 -38
  258. package/src/monitoring/info.ts +0 -114
  259. package/src/monitoring/types.ts +0 -99
  260. package/src/network/network-config.ts +0 -129
  261. package/src/packaging/compiler.ts +0 -647
  262. package/src/packaging/config-persistence.ts +0 -135
  263. package/src/packaging/config-schemas.ts +0 -156
  264. package/src/packaging/detector.ts +0 -220
  265. package/src/packaging/index.ts +0 -90
  266. package/src/packaging/packager.ts +0 -118
  267. package/src/packaging/parsers/clawdbot.ts +0 -278
  268. package/src/packaging/parsers/cline.ts +0 -223
  269. package/src/packaging/parsers/generic.ts +0 -266
  270. package/src/packaging/parsers/goose.ts +0 -214
  271. package/src/packaging/serializer.ts +0 -260
  272. package/src/packaging/types.ts +0 -144
  273. package/src/packaging/wasmedge-compiler.ts +0 -406
  274. package/src/security/multisig.ts +0 -415
  275. package/src/security/types.ts +0 -416
  276. package/src/security/vetkeys.ts +0 -655
  277. package/src/testing/local-runner.ts +0 -264
  278. package/src/testing/types.ts +0 -104
  279. package/src/wallet/cbor-serializer.ts +0 -323
  280. package/src/wallet/chain-dispatcher.ts +0 -313
  281. package/src/wallet/cross-chain-aggregator.ts +0 -346
  282. package/src/wallet/index.ts +0 -76
  283. package/src/wallet/key-derivation.ts +0 -425
  284. package/src/wallet/providers/base-provider.ts +0 -154
  285. package/src/wallet/providers/cketh-provider.ts +0 -434
  286. package/src/wallet/providers/polkadot-provider.ts +0 -503
  287. package/src/wallet/providers/solana-provider.ts +0 -490
  288. package/src/wallet/transaction-queue.ts +0 -284
  289. package/src/wallet/types.ts +0 -178
  290. package/src/wallet/vetkeys-adapter.ts +0 -431
  291. package/src/wallet/wallet-manager.ts +0 -597
  292. package/src/wallet/wallet-storage.ts +0 -380
@@ -0,0 +1,219 @@
1
+ import Foundation
2
+ import Security
3
+
4
+ /// Manages secure storage of wallet secrets in the macOS Keychain.
5
+ /// Private keys and mnemonics are stored here, never on disk in plaintext.
6
+ final class KeychainService {
7
+
8
+ static let shared = KeychainService()
9
+
10
+ private let servicePrefix = "com.agentvault.wallet"
11
+
12
+ enum KeychainError: LocalizedError {
13
+ case saveFailed(OSStatus)
14
+ case readFailed(OSStatus)
15
+ case deleteFailed(OSStatus)
16
+ case dataConversionFailed
17
+ case itemNotFound
18
+
19
+ var errorDescription: String? {
20
+ switch self {
21
+ case .saveFailed(let status):
22
+ return "Keychain save failed (status: \(status))"
23
+ case .readFailed(let status):
24
+ return "Keychain read failed (status: \(status))"
25
+ case .deleteFailed(let status):
26
+ return "Keychain delete failed (status: \(status))"
27
+ case .dataConversionFailed:
28
+ return "Failed to convert data for Keychain storage"
29
+ case .itemNotFound:
30
+ return "Item not found in Keychain"
31
+ }
32
+ }
33
+ }
34
+
35
+ private init() {}
36
+
37
+ // MARK: - Mnemonic Storage
38
+
39
+ /// Save a mnemonic phrase for a wallet
40
+ func saveMnemonic(_ mnemonic: String, forWalletId id: UUID) throws {
41
+ let key = mnemonicKey(for: id)
42
+ try saveString(mnemonic, forKey: key)
43
+ }
44
+
45
+ /// Retrieve a mnemonic phrase for a wallet
46
+ func getMnemonic(forWalletId id: UUID) throws -> String {
47
+ let key = mnemonicKey(for: id)
48
+ return try getString(forKey: key)
49
+ }
50
+
51
+ /// Delete a stored mnemonic
52
+ func deleteMnemonic(forWalletId id: UUID) throws {
53
+ let key = mnemonicKey(for: id)
54
+ try deleteItem(forKey: key)
55
+ }
56
+
57
+ /// Check if a mnemonic exists for a wallet
58
+ func hasMnemonic(forWalletId id: UUID) -> Bool {
59
+ let key = mnemonicKey(for: id)
60
+ return (try? getString(forKey: key)) != nil
61
+ }
62
+
63
+ // MARK: - Private Key Storage
64
+
65
+ /// Save a private key for a wallet
66
+ func savePrivateKey(_ key: String, forWalletId id: UUID) throws {
67
+ let storageKey = privateKeyKey(for: id)
68
+ try saveString(key, forKey: storageKey)
69
+ }
70
+
71
+ /// Retrieve a private key for a wallet
72
+ func getPrivateKey(forWalletId id: UUID) throws -> String {
73
+ let key = privateKeyKey(for: id)
74
+ return try getString(forKey: key)
75
+ }
76
+
77
+ /// Delete a stored private key
78
+ func deletePrivateKey(forWalletId id: UUID) throws {
79
+ let key = privateKeyKey(for: id)
80
+ try deleteItem(forKey: key)
81
+ }
82
+
83
+ // MARK: - JWK Storage (Arweave)
84
+
85
+ /// Save JWK data for an Arweave wallet
86
+ func saveJWK(_ jwkData: Data, forWalletId id: UUID) throws {
87
+ let key = jwkKey(for: id)
88
+ try saveData(jwkData, forKey: key)
89
+ }
90
+
91
+ /// Retrieve JWK data for an Arweave wallet
92
+ func getJWK(forWalletId id: UUID) throws -> Data {
93
+ let key = jwkKey(for: id)
94
+ return try getData(forKey: key)
95
+ }
96
+
97
+ /// Delete stored JWK data
98
+ func deleteJWK(forWalletId id: UUID) throws {
99
+ let key = jwkKey(for: id)
100
+ try deleteItem(forKey: key)
101
+ }
102
+
103
+ // MARK: - Backup Password
104
+
105
+ /// Save the backup encryption password hint (NOT the actual password)
106
+ func saveBackupPasswordHint(_ hint: String) throws {
107
+ try saveString(hint, forKey: "\(servicePrefix).backup-hint")
108
+ }
109
+
110
+ func getBackupPasswordHint() -> String? {
111
+ try? getString(forKey: "\(servicePrefix).backup-hint")
112
+ }
113
+
114
+ // MARK: - Bulk Operations
115
+
116
+ /// Delete all secrets for a wallet
117
+ func deleteAllSecrets(forWalletId id: UUID) {
118
+ try? deleteMnemonic(forWalletId: id)
119
+ try? deletePrivateKey(forWalletId: id)
120
+ try? deleteJWK(forWalletId: id)
121
+ }
122
+
123
+ /// Delete everything from our Keychain namespace
124
+ func purgeAll() {
125
+ let query: [String: Any] = [
126
+ kSecClass as String: kSecClassGenericPassword,
127
+ kSecAttrService as String: servicePrefix,
128
+ ]
129
+ SecItemDelete(query as CFDictionary)
130
+ }
131
+
132
+ // MARK: - Private Helpers
133
+
134
+ private func mnemonicKey(for id: UUID) -> String {
135
+ "\(servicePrefix).mnemonic.\(id.uuidString)"
136
+ }
137
+
138
+ private func privateKeyKey(for id: UUID) -> String {
139
+ "\(servicePrefix).privatekey.\(id.uuidString)"
140
+ }
141
+
142
+ private func jwkKey(for id: UUID) -> String {
143
+ "\(servicePrefix).jwk.\(id.uuidString)"
144
+ }
145
+
146
+ private func saveString(_ value: String, forKey key: String) throws {
147
+ guard let data = value.data(using: .utf8) else {
148
+ throw KeychainError.dataConversionFailed
149
+ }
150
+ try saveData(data, forKey: key)
151
+ }
152
+
153
+ private func getString(forKey key: String) throws -> String {
154
+ let data = try getData(forKey: key)
155
+ guard let string = String(data: data, encoding: .utf8) else {
156
+ throw KeychainError.dataConversionFailed
157
+ }
158
+ return string
159
+ }
160
+
161
+ private func saveData(_ data: Data, forKey key: String) throws {
162
+ // Delete existing item first
163
+ try? deleteItem(forKey: key)
164
+
165
+ let query: [String: Any] = [
166
+ kSecClass as String: kSecClassGenericPassword,
167
+ kSecAttrService as String: servicePrefix,
168
+ kSecAttrAccount as String: key,
169
+ kSecValueData as String: data,
170
+ kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
171
+ kSecAttrLabel as String: "AgentVault Wallet Secret",
172
+ kSecAttrDescription as String: "Encrypted wallet key material",
173
+ ]
174
+
175
+ let status = SecItemAdd(query as CFDictionary, nil)
176
+ guard status == errSecSuccess else {
177
+ throw KeychainError.saveFailed(status)
178
+ }
179
+ }
180
+
181
+ private func getData(forKey key: String) throws -> Data {
182
+ let query: [String: Any] = [
183
+ kSecClass as String: kSecClassGenericPassword,
184
+ kSecAttrService as String: servicePrefix,
185
+ kSecAttrAccount as String: key,
186
+ kSecReturnData as String: true,
187
+ kSecMatchLimit as String: kSecMatchLimitOne,
188
+ ]
189
+
190
+ var result: AnyObject?
191
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
192
+
193
+ guard status == errSecSuccess else {
194
+ if status == errSecItemNotFound {
195
+ throw KeychainError.itemNotFound
196
+ }
197
+ throw KeychainError.readFailed(status)
198
+ }
199
+
200
+ guard let data = result as? Data else {
201
+ throw KeychainError.dataConversionFailed
202
+ }
203
+
204
+ return data
205
+ }
206
+
207
+ private func deleteItem(forKey key: String) throws {
208
+ let query: [String: Any] = [
209
+ kSecClass as String: kSecClassGenericPassword,
210
+ kSecAttrService as String: servicePrefix,
211
+ kSecAttrAccount as String: key,
212
+ ]
213
+
214
+ let status = SecItemDelete(query as CFDictionary)
215
+ guard status == errSecSuccess || status == errSecItemNotFound else {
216
+ throw KeychainError.deleteFailed(status)
217
+ }
218
+ }
219
+ }
@@ -0,0 +1,44 @@
1
+ import Foundation
2
+
3
+ /// Application-wide constants
4
+ enum AppConstants {
5
+ static let appName = "AgentVault Wallet"
6
+ static let appVersion = "1.0.0"
7
+ static let buildNumber = "1"
8
+
9
+ /// Keychain service identifier
10
+ static let keychainService = "com.agentvault.wallet"
11
+
12
+ /// Backup file extension
13
+ static let backupFileExtension = "avbackup"
14
+
15
+ /// Minimum password length for backups
16
+ static let minimumBackupPasswordLength = 8
17
+
18
+ /// How long to keep clipboard data before auto-clearing (seconds)
19
+ static let clipboardClearDelay: TimeInterval = 60
20
+
21
+ /// Maximum wallets per chain (soft limit)
22
+ static let maxWalletsPerChain = 50
23
+
24
+ /// Supported mnemonic word counts
25
+ static let supportedMnemonicLengths: Set<Int> = [12, 24]
26
+
27
+ /// Network endpoints (for reference; actual connections go through CLI)
28
+ enum Networks {
29
+ static let icpMainnet = "https://ic0.app"
30
+ static let icpLocal = "http://127.0.0.1:4943"
31
+ static let ethMainnet = "https://eth.llamarpc.com"
32
+ static let ethSepolia = "https://rpc.sepolia.org"
33
+ static let arweaveGateway = "https://arweave.net"
34
+ }
35
+
36
+ /// UserDefaults keys
37
+ enum Defaults {
38
+ static let hasCompletedOnboarding = "hasCompletedOnboarding"
39
+ static let agentVaultCLIPath = "agentVaultCLIPath"
40
+ static let autoRefreshBalances = "autoRefreshBalances"
41
+ static let defaultChain = "defaultChain"
42
+ static let lastBackupDate = "lastBackupDate"
43
+ }
44
+ }
@@ -0,0 +1,115 @@
1
+ import SwiftUI
2
+
3
+ // MARK: - Date Extensions
4
+
5
+ extension Date {
6
+ /// Format as a relative time string ("2 hours ago", "just now")
7
+ var relativeString: String {
8
+ let formatter = RelativeDateTimeFormatter()
9
+ formatter.unitsStyle = .abbreviated
10
+ return formatter.localizedString(for: self, relativeTo: Date())
11
+ }
12
+
13
+ /// Format as ISO 8601 string for backup files
14
+ var iso8601String: String {
15
+ ISO8601DateFormatter().string(from: self)
16
+ }
17
+ }
18
+
19
+ // MARK: - String Extensions
20
+
21
+ extension String {
22
+ /// Truncate the middle of a string for display
23
+ func truncatedMiddle(maxLength: Int = 20) -> String {
24
+ guard count > maxLength else { return self }
25
+ let halfLength = (maxLength - 3) / 2
26
+ return "\(prefix(halfLength))...\(suffix(halfLength))"
27
+ }
28
+
29
+ /// Check if string is a valid hexadecimal string
30
+ var isValidHex: Bool {
31
+ let cleaned = hasPrefix("0x") ? String(dropFirst(2)) : self
32
+ return !cleaned.isEmpty && cleaned.allSatisfy { $0.isHexDigit }
33
+ }
34
+ }
35
+
36
+ // MARK: - Data Extensions
37
+
38
+ extension Data {
39
+ /// Hex string representation
40
+ var hexString: String {
41
+ map { String(format: "%02x", $0) }.joined()
42
+ }
43
+
44
+ /// Initialize from hex string
45
+ init?(hexString: String) {
46
+ let cleaned = hexString.hasPrefix("0x") ? String(hexString.dropFirst(2)) : hexString
47
+ let length = cleaned.count / 2
48
+ var data = Data(capacity: length)
49
+ var index = cleaned.startIndex
50
+
51
+ for _ in 0..<length {
52
+ let nextIndex = cleaned.index(index, offsetBy: 2)
53
+ guard let byte = UInt8(cleaned[index..<nextIndex], radix: 16) else {
54
+ return nil
55
+ }
56
+ data.append(byte)
57
+ index = nextIndex
58
+ }
59
+
60
+ self = data
61
+ }
62
+ }
63
+
64
+ // MARK: - View Extensions
65
+
66
+ extension View {
67
+ /// Apply a card-style background
68
+ func cardStyle() -> some View {
69
+ self
70
+ .padding(16)
71
+ .background(.background, in: RoundedRectangle(cornerRadius: 12))
72
+ .overlay(
73
+ RoundedRectangle(cornerRadius: 12)
74
+ .stroke(Color.secondary.opacity(0.15))
75
+ )
76
+ .shadow(color: .black.opacity(0.03), radius: 4, y: 2)
77
+ }
78
+
79
+ /// Conditional modifier
80
+ @ViewBuilder
81
+ func `if`<Transform: View>(_ condition: Bool, transform: (Self) -> Transform) -> some View {
82
+ if condition {
83
+ transform(self)
84
+ } else {
85
+ self
86
+ }
87
+ }
88
+ }
89
+
90
+ // MARK: - Color Extensions
91
+
92
+ extension Color {
93
+ /// Create a color from a hex string
94
+ init(hex: String) {
95
+ let cleaned = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
96
+ var int: UInt64 = 0
97
+ Scanner(string: cleaned).scanHexInt64(&int)
98
+ let r, g, b, a: UInt64
99
+ switch cleaned.count {
100
+ case 6:
101
+ (r, g, b, a) = (int >> 16, int >> 8 & 0xFF, int & 0xFF, 255)
102
+ case 8:
103
+ (r, g, b, a) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
104
+ default:
105
+ (r, g, b, a) = (0, 0, 0, 255)
106
+ }
107
+ self.init(
108
+ .sRGB,
109
+ red: Double(r) / 255,
110
+ green: Double(g) / 255,
111
+ blue: Double(b) / 255,
112
+ opacity: Double(a) / 255
113
+ )
114
+ }
115
+ }
@@ -0,0 +1,237 @@
1
+ import SwiftUI
2
+
3
+ /// Manages backup and restore workflows
4
+ @MainActor
5
+ final class BackupViewModel: ObservableObject {
6
+
7
+ // MARK: - Backup State
8
+
9
+ @Published var backupPassword: String = ""
10
+ @Published var backupPasswordConfirm: String = ""
11
+ @Published var selectedWalletsForBackup: Set<UUID> = []
12
+ @Published var isCreatingBackup: Bool = false
13
+ @Published var backupComplete: Bool = false
14
+ @Published var backupFilePath: String?
15
+
16
+ // MARK: - Restore State
17
+
18
+ @Published var restoreFileURL: URL?
19
+ @Published var restorePassword: String = ""
20
+ @Published var backupMetadata: WalletBackup?
21
+ @Published var isRestoring: Bool = false
22
+ @Published var restoreComplete: Bool = false
23
+ @Published var restoredCount: Int = 0
24
+
25
+ // MARK: - Common
26
+
27
+ @Published var errorMessage: String?
28
+ @Published var showFileImporter: Bool = false
29
+ @Published var showFileSaver: Bool = false
30
+ @Published var existingBackups: [BackupFileInfo] = []
31
+
32
+ private let backupService = BackupService.shared
33
+
34
+ // MARK: - Backup Validation
35
+
36
+ var isBackupPasswordValid: Bool {
37
+ backupPassword.count >= 8 && backupPassword == backupPasswordConfirm
38
+ }
39
+
40
+ var hasSelectedWallets: Bool {
41
+ !selectedWalletsForBackup.isEmpty
42
+ }
43
+
44
+ var canCreateBackup: Bool {
45
+ isBackupPasswordValid && hasSelectedWallets && !isCreatingBackup
46
+ }
47
+
48
+ var passwordStrength: PasswordStrength {
49
+ PasswordStrength.evaluate(backupPassword)
50
+ }
51
+
52
+ // MARK: - Restore Validation
53
+
54
+ var canRestore: Bool {
55
+ restoreFileURL != nil && !restorePassword.isEmpty && !isRestoring
56
+ }
57
+
58
+ var hasBackupMetadata: Bool {
59
+ backupMetadata != nil
60
+ }
61
+
62
+ // MARK: - Backup Operations
63
+
64
+ func selectAllWallets(from wallets: [Wallet]) {
65
+ selectedWalletsForBackup = Set(wallets.map(\.id))
66
+ }
67
+
68
+ func deselectAllWallets() {
69
+ selectedWalletsForBackup.removeAll()
70
+ }
71
+
72
+ func createBackup(wallets: [Wallet]) async {
73
+ isCreatingBackup = true
74
+ errorMessage = nil
75
+
76
+ do {
77
+ let walletsToBackup = wallets.filter { selectedWalletsForBackup.contains($0.id) }
78
+ let data = try backupService.createBackup(wallets: walletsToBackup, password: backupPassword)
79
+
80
+ let filename = backupService.defaultBackupFilename()
81
+ let fileURL = backupService.defaultBackupDirectory.appendingPathComponent(filename)
82
+
83
+ try backupService.writeBackup(data, to: fileURL)
84
+
85
+ backupFilePath = fileURL.path
86
+ backupComplete = true
87
+ isCreatingBackup = false
88
+ } catch {
89
+ errorMessage = error.localizedDescription
90
+ isCreatingBackup = false
91
+ }
92
+ }
93
+
94
+ /// Save backup to a user-chosen location
95
+ func saveBackupToLocation(wallets: [Wallet], url: URL) async {
96
+ isCreatingBackup = true
97
+ errorMessage = nil
98
+
99
+ do {
100
+ let walletsToBackup = wallets.filter { selectedWalletsForBackup.contains($0.id) }
101
+ let data = try backupService.createBackup(wallets: walletsToBackup, password: backupPassword)
102
+ try backupService.writeBackup(data, to: url)
103
+
104
+ backupFilePath = url.path
105
+ backupComplete = true
106
+ isCreatingBackup = false
107
+ } catch {
108
+ errorMessage = error.localizedDescription
109
+ isCreatingBackup = false
110
+ }
111
+ }
112
+
113
+ // MARK: - Restore Operations
114
+
115
+ func loadBackupMetadata() {
116
+ guard let url = restoreFileURL else { return }
117
+ errorMessage = nil
118
+
119
+ do {
120
+ backupMetadata = try backupService.readBackupMetadata(from: url)
121
+ } catch {
122
+ errorMessage = error.localizedDescription
123
+ backupMetadata = nil
124
+ }
125
+ }
126
+
127
+ func restoreFromBackup(store: WalletStore) async {
128
+ guard let backup = backupMetadata else { return }
129
+ isRestoring = true
130
+ errorMessage = nil
131
+
132
+ do {
133
+ let secrets = try backupService.restoreSecrets(from: backup, password: restorePassword)
134
+ try backupService.saveRestoredSecrets(secrets)
135
+
136
+ // Re-create wallet entries
137
+ var restored = 0
138
+ for entry in backup.wallets {
139
+ // Skip if wallet already exists
140
+ if store.wallets.contains(where: { $0.address == entry.address && $0.chain == entry.chain }) {
141
+ continue
142
+ }
143
+
144
+ let wallet = Wallet(
145
+ id: entry.id,
146
+ name: entry.name,
147
+ chain: entry.chain,
148
+ address: entry.address,
149
+ isImported: true,
150
+ derivationPath: entry.derivationPath,
151
+ hasMnemonicBackup: entry.hasMnemonicBackup
152
+ )
153
+ store.wallets.append(wallet)
154
+ restored += 1
155
+ }
156
+
157
+ store.saveWallets()
158
+ restoredCount = restored
159
+ restoreComplete = true
160
+ isRestoring = false
161
+ } catch {
162
+ errorMessage = error.localizedDescription
163
+ isRestoring = false
164
+ }
165
+ }
166
+
167
+ func loadExistingBackups() {
168
+ existingBackups = backupService.listBackups()
169
+ }
170
+
171
+ func reset() {
172
+ backupPassword = ""
173
+ backupPasswordConfirm = ""
174
+ selectedWalletsForBackup.removeAll()
175
+ isCreatingBackup = false
176
+ backupComplete = false
177
+ backupFilePath = nil
178
+ restoreFileURL = nil
179
+ restorePassword = ""
180
+ backupMetadata = nil
181
+ isRestoring = false
182
+ restoreComplete = false
183
+ restoredCount = 0
184
+ errorMessage = nil
185
+ }
186
+ }
187
+
188
+ /// Password strength indicator
189
+ enum PasswordStrength {
190
+ case weak, fair, good, strong
191
+
192
+ var label: String {
193
+ switch self {
194
+ case .weak: return "Weak"
195
+ case .fair: return "Fair"
196
+ case .good: return "Good"
197
+ case .strong: return "Strong"
198
+ }
199
+ }
200
+
201
+ var color: Color {
202
+ switch self {
203
+ case .weak: return .red
204
+ case .fair: return .orange
205
+ case .good: return .yellow
206
+ case .strong: return .green
207
+ }
208
+ }
209
+
210
+ var progress: Double {
211
+ switch self {
212
+ case .weak: return 0.25
213
+ case .fair: return 0.5
214
+ case .good: return 0.75
215
+ case .strong: return 1.0
216
+ }
217
+ }
218
+
219
+ static func evaluate(_ password: String) -> PasswordStrength {
220
+ guard !password.isEmpty else { return .weak }
221
+
222
+ var score = 0
223
+ if password.count >= 8 { score += 1 }
224
+ if password.count >= 12 { score += 1 }
225
+ if password.rangeOfCharacter(from: .uppercaseLetters) != nil { score += 1 }
226
+ if password.rangeOfCharacter(from: .lowercaseLetters) != nil { score += 1 }
227
+ if password.rangeOfCharacter(from: .decimalDigits) != nil { score += 1 }
228
+ if password.rangeOfCharacter(from: CharacterSet(charactersIn: "!@#$%^&*()_+-=[]{}|;:,.<>?")) != nil { score += 1 }
229
+
230
+ switch score {
231
+ case 0...2: return .weak
232
+ case 3: return .fair
233
+ case 4...5: return .good
234
+ default: return .strong
235
+ }
236
+ }
237
+ }