agentvault 1.0.0 → 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 (293) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/README.md +1 -1
  3. package/dist/cli/commands/approve.js +5 -5
  4. package/dist/cli/commands/archive.js +5 -5
  5. package/dist/cli/commands/backup.js +5 -5
  6. package/dist/cli/commands/cloud-backup.js +12 -12
  7. package/dist/cli/commands/decrypt.js +2 -2
  8. package/dist/cli/commands/deploy.js +1 -1
  9. package/dist/cli/commands/exec.js +2 -2
  10. package/dist/cli/commands/fetch.js +4 -4
  11. package/dist/cli/commands/inference.js +5 -5
  12. package/dist/cli/commands/init.d.ts +1 -1
  13. package/dist/cli/commands/init.js +16 -16
  14. package/dist/cli/commands/list.js +4 -4
  15. package/dist/cli/commands/package.js +2 -2
  16. package/dist/cli/commands/profile.js +1 -1
  17. package/dist/cli/commands/rebuild.js +2 -2
  18. package/dist/cli/commands/show.js +1 -1
  19. package/dist/cli/commands/status.d.ts +1 -1
  20. package/dist/cli/commands/status.js +8 -8
  21. package/dist/cli/commands/trace.js +1 -1
  22. package/dist/cli/commands/wallet-export.js +1 -1
  23. package/dist/cli/commands/wallet-sign.js +1 -1
  24. package/dist/cli/commands/wallet.d.ts +1 -1
  25. package/dist/cli/commands/wallet.js +1 -1
  26. package/dist/cli/index.d.ts +2 -2
  27. package/dist/cli/index.js +3 -3
  28. package/dist/src/archival/archive-manager.d.ts +85 -0
  29. package/dist/src/archival/archive-manager.js +294 -0
  30. package/dist/src/archival/arweave-client.d.ts +88 -0
  31. package/dist/src/archival/arweave-client.js +223 -0
  32. package/dist/src/archival/index.d.ts +8 -0
  33. package/{src/archival/index.ts → dist/src/archival/index.js} +1 -1
  34. package/dist/src/backup/backup.d.ts +67 -0
  35. package/dist/src/backup/backup.js +231 -0
  36. package/dist/src/backup/index.d.ts +7 -0
  37. package/{src/backup/index.ts → dist/src/backup/index.js} +1 -1
  38. package/dist/src/cloud-storage/cloud-sync.d.ts +49 -0
  39. package/dist/src/cloud-storage/cloud-sync.js +372 -0
  40. package/dist/src/cloud-storage/index.d.ts +11 -0
  41. package/{src/cloud-storage/index.ts → dist/src/cloud-storage/index.js} +1 -1
  42. package/dist/src/cloud-storage/provider-detector.d.ts +34 -0
  43. package/dist/src/cloud-storage/provider-detector.js +158 -0
  44. package/{src/cloud-storage/types.ts → dist/src/cloud-storage/types.d.ts} +40 -53
  45. package/dist/src/cloud-storage/types.js +10 -0
  46. package/dist/src/debugging/index.d.ts +6 -0
  47. package/{src/debugging/index.ts → dist/src/debugging/index.js} +1 -1
  48. package/dist/src/debugging/logs.d.ts +32 -0
  49. package/dist/src/debugging/logs.js +158 -0
  50. package/dist/src/debugging/types.d.ts +91 -0
  51. package/dist/src/debugging/types.js +5 -0
  52. package/dist/src/deployment/deployer.d.ts +52 -0
  53. package/dist/src/deployment/deployer.js +211 -0
  54. package/dist/src/deployment/icpClient.d.ts +144 -0
  55. package/dist/src/deployment/icpClient.js +545 -0
  56. package/dist/src/deployment/index.d.ts +11 -0
  57. package/dist/src/deployment/index.js +14 -0
  58. package/dist/src/deployment/promotion.d.ts +32 -0
  59. package/dist/src/deployment/promotion.js +114 -0
  60. package/dist/src/deployment/types.d.ts +101 -0
  61. package/dist/src/deployment/types.js +5 -0
  62. package/dist/src/icp/batch.d.ts +112 -0
  63. package/dist/src/icp/batch.js +273 -0
  64. package/dist/src/icp/cycles.d.ts +29 -0
  65. package/{src/icp/cycles.ts → dist/src/icp/cycles.js} +8 -22
  66. package/dist/src/icp/environment.d.ts +60 -0
  67. package/dist/src/icp/environment.js +183 -0
  68. package/dist/src/icp/icpcli.d.ts +204 -0
  69. package/dist/src/icp/icpcli.js +374 -0
  70. package/dist/src/icp/icwasm.d.ts +94 -0
  71. package/dist/src/icp/icwasm.js +197 -0
  72. package/dist/src/icp/identity.d.ts +50 -0
  73. package/{src/icp/identity.ts → dist/src/icp/identity.js} +15 -28
  74. package/dist/src/icp/index.d.ts +16 -0
  75. package/dist/src/icp/index.js +20 -0
  76. package/dist/src/icp/optimization.d.ts +16 -0
  77. package/dist/src/icp/optimization.js +225 -0
  78. package/dist/src/icp/tokens.d.ts +24 -0
  79. package/{src/icp/tokens.ts → dist/src/icp/tokens.js} +5 -12
  80. package/dist/src/icp/tool-detector.d.ts +31 -0
  81. package/dist/src/icp/tool-detector.js +104 -0
  82. package/dist/src/icp/types.d.ts +493 -0
  83. package/dist/src/icp/types.js +7 -0
  84. package/dist/src/index.d.ts +12 -0
  85. package/dist/src/index.js +18 -0
  86. package/dist/src/inference/bittensor-client.d.ts +108 -0
  87. package/dist/src/inference/bittensor-client.js +224 -0
  88. package/dist/src/inference/index.d.ts +8 -0
  89. package/{src/inference/index.ts → dist/src/inference/index.js} +1 -1
  90. package/dist/src/inference/inference-manager.d.ts +76 -0
  91. package/dist/src/inference/inference-manager.js +228 -0
  92. package/dist/src/metrics/index.d.ts +7 -0
  93. package/{src/metrics/index.ts → dist/src/metrics/index.js} +1 -1
  94. package/dist/src/metrics/metrics.d.ts +39 -0
  95. package/dist/src/metrics/metrics.js +129 -0
  96. package/dist/src/monitoring/alerting.d.ts +51 -0
  97. package/dist/src/monitoring/alerting.js +169 -0
  98. package/dist/src/monitoring/health.d.ts +40 -0
  99. package/dist/src/monitoring/health.js +164 -0
  100. package/dist/src/monitoring/index.d.ts +10 -0
  101. package/dist/src/monitoring/index.js +12 -0
  102. package/dist/src/monitoring/info.d.ts +15 -0
  103. package/dist/src/monitoring/info.js +109 -0
  104. package/dist/src/monitoring/types.d.ts +93 -0
  105. package/dist/src/monitoring/types.js +7 -0
  106. package/dist/src/network/index.d.ts +5 -0
  107. package/{src/network/index.ts → dist/src/network/index.js} +1 -1
  108. package/dist/src/network/network-config.d.ts +31 -0
  109. package/dist/src/network/network-config.js +109 -0
  110. package/dist/src/packaging/compiler.d.ts +61 -0
  111. package/dist/src/packaging/compiler.js +562 -0
  112. package/dist/src/packaging/config-persistence.d.ts +46 -0
  113. package/dist/src/packaging/config-persistence.js +108 -0
  114. package/dist/src/packaging/config-schemas.d.ts +115 -0
  115. package/dist/src/packaging/config-schemas.js +43 -0
  116. package/dist/src/packaging/detector.d.ts +26 -0
  117. package/dist/src/packaging/detector.js +193 -0
  118. package/dist/src/packaging/index.d.ts +16 -0
  119. package/dist/src/packaging/index.js +22 -0
  120. package/dist/src/packaging/packager.d.ts +31 -0
  121. package/dist/src/packaging/packager.js +90 -0
  122. package/dist/src/packaging/parsers/clawdbot.d.ts +19 -0
  123. package/dist/src/packaging/parsers/clawdbot.js +231 -0
  124. package/dist/src/packaging/parsers/cline.d.ts +26 -0
  125. package/dist/src/packaging/parsers/cline.js +185 -0
  126. package/dist/src/packaging/parsers/generic.d.ts +27 -0
  127. package/dist/src/packaging/parsers/generic.js +228 -0
  128. package/dist/src/packaging/parsers/goose.d.ts +26 -0
  129. package/dist/src/packaging/parsers/goose.js +175 -0
  130. package/dist/src/packaging/parsers/index.d.ts +11 -0
  131. package/{src/packaging/parsers/index.ts → dist/src/packaging/parsers/index.js} +1 -1
  132. package/dist/src/packaging/serializer.d.ts +108 -0
  133. package/dist/src/packaging/serializer.js +153 -0
  134. package/dist/src/packaging/types.d.ts +131 -0
  135. package/dist/src/packaging/types.js +5 -0
  136. package/dist/src/packaging/wasmedge-compiler.d.ts +76 -0
  137. package/dist/src/packaging/wasmedge-compiler.js +349 -0
  138. package/dist/src/security/index.d.ts +11 -0
  139. package/{src/security/index.ts → dist/src/security/index.js} +1 -4
  140. package/dist/src/security/multisig.d.ts +102 -0
  141. package/dist/src/security/multisig.js +283 -0
  142. package/dist/src/security/types.d.ts +207 -0
  143. package/dist/src/security/types.js +217 -0
  144. package/dist/src/security/vetkeys.d.ts +179 -0
  145. package/dist/src/security/vetkeys.js +499 -0
  146. package/dist/src/testing/index.d.ts +6 -0
  147. package/{src/testing/index.ts → dist/src/testing/index.js} +1 -1
  148. package/dist/src/testing/local-runner.d.ts +23 -0
  149. package/dist/src/testing/local-runner.js +226 -0
  150. package/dist/src/testing/types.d.ts +98 -0
  151. package/dist/src/testing/types.js +5 -0
  152. package/dist/src/wallet/cbor-serializer.d.ts +82 -0
  153. package/dist/src/wallet/cbor-serializer.js +282 -0
  154. package/dist/src/wallet/chain-dispatcher.d.ts +112 -0
  155. package/dist/src/wallet/chain-dispatcher.js +241 -0
  156. package/dist/src/wallet/cross-chain-aggregator.d.ts +119 -0
  157. package/dist/src/wallet/cross-chain-aggregator.js +235 -0
  158. package/dist/src/wallet/index.d.ts +16 -0
  159. package/dist/src/wallet/index.js +22 -0
  160. package/dist/src/wallet/key-derivation.d.ts +117 -0
  161. package/dist/src/wallet/key-derivation.js +325 -0
  162. package/dist/src/wallet/providers/base-provider.d.ts +111 -0
  163. package/dist/src/wallet/providers/base-provider.js +58 -0
  164. package/dist/src/wallet/providers/cketh-provider.d.ts +104 -0
  165. package/dist/src/wallet/providers/cketh-provider.js +343 -0
  166. package/dist/src/wallet/providers/polkadot-provider.d.ts +115 -0
  167. package/dist/src/wallet/providers/polkadot-provider.js +407 -0
  168. package/dist/src/wallet/providers/solana-provider.d.ts +102 -0
  169. package/dist/src/wallet/providers/solana-provider.js +393 -0
  170. package/dist/src/wallet/transaction-queue.d.ts +133 -0
  171. package/dist/src/wallet/transaction-queue.js +195 -0
  172. package/dist/src/wallet/types.d.ts +167 -0
  173. package/dist/src/wallet/types.js +5 -0
  174. package/dist/src/wallet/vetkeys-adapter.d.ts +134 -0
  175. package/dist/src/wallet/vetkeys-adapter.js +313 -0
  176. package/dist/src/wallet/wallet-manager.d.ts +202 -0
  177. package/dist/src/wallet/wallet-manager.js +451 -0
  178. package/dist/src/wallet/wallet-storage.d.ts +131 -0
  179. package/dist/src/wallet/wallet-storage.js +274 -0
  180. package/macos-wallet-app/AgentVaultWallet/App/AgentVaultWalletApp.swift +54 -0
  181. package/macos-wallet-app/AgentVaultWallet/Models/AppState.swift +102 -0
  182. package/macos-wallet-app/AgentVaultWallet/Models/Chain.swift +121 -0
  183. package/macos-wallet-app/AgentVaultWallet/Models/Wallet.swift +98 -0
  184. package/macos-wallet-app/AgentVaultWallet/Resources/AgentVaultWallet.entitlements +27 -0
  185. package/macos-wallet-app/AgentVaultWallet/Resources/Info.plist +69 -0
  186. package/macos-wallet-app/AgentVaultWallet/Services/BackupService.swift +270 -0
  187. package/macos-wallet-app/AgentVaultWallet/Services/CLIBridge.swift +367 -0
  188. package/macos-wallet-app/AgentVaultWallet/Services/CryptoService.swift +157 -0
  189. package/macos-wallet-app/AgentVaultWallet/Services/FileService.swift +120 -0
  190. package/macos-wallet-app/AgentVaultWallet/Services/KeychainService.swift +219 -0
  191. package/macos-wallet-app/AgentVaultWallet/Utilities/Constants.swift +44 -0
  192. package/macos-wallet-app/AgentVaultWallet/Utilities/Extensions.swift +115 -0
  193. package/macos-wallet-app/AgentVaultWallet/ViewModels/BackupViewModel.swift +237 -0
  194. package/macos-wallet-app/AgentVaultWallet/ViewModels/CreateWalletViewModel.swift +137 -0
  195. package/macos-wallet-app/AgentVaultWallet/ViewModels/ImportWalletViewModel.swift +179 -0
  196. package/macos-wallet-app/AgentVaultWallet/ViewModels/WalletStore.swift +286 -0
  197. package/macos-wallet-app/AgentVaultWallet/Views/Backup/BackupView.swift +235 -0
  198. package/macos-wallet-app/AgentVaultWallet/Views/Backup/RestoreView.swift +316 -0
  199. package/macos-wallet-app/AgentVaultWallet/Views/Create/CreateWalletFlow.swift +438 -0
  200. package/macos-wallet-app/AgentVaultWallet/Views/Import/ImportWalletFlow.swift +399 -0
  201. package/macos-wallet-app/AgentVaultWallet/Views/MainView.swift +134 -0
  202. package/macos-wallet-app/AgentVaultWallet/Views/Settings/SettingsView.swift +276 -0
  203. package/macos-wallet-app/AgentVaultWallet/Views/Sidebar/SidebarView.swift +133 -0
  204. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/DashboardView.swift +233 -0
  205. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/WalletDetailView.swift +281 -0
  206. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/WalletListView.swift +280 -0
  207. package/macos-wallet-app/AgentVaultWallet/Views/Welcome/WelcomeView.swift +176 -0
  208. package/macos-wallet-app/Makefile +47 -0
  209. package/macos-wallet-app/project.yml +40 -0
  210. package/macos-wallet-app/setup.sh +73 -0
  211. package/package.json +10 -2
  212. package/backups/agentvault-backup-test-agent-2026-02-12T17-54-28-967Z.json +0 -28
  213. package/backups/agentvault-backup-test-agent-2026-02-12T17-54-29-032Z.backup +0 -1
  214. package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-373Z.json +0 -28
  215. package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-428Z.backup +0 -1
  216. package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-132Z.json +0 -28
  217. package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-247Z.backup +0 -1
  218. package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-216Z.json +0 -28
  219. package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-283Z.backup +0 -1
  220. package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-772Z.backup +0 -1
  221. package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-793Z.json +0 -28
  222. package/backups/test-backup.json +0 -28
  223. package/scripts/dev-dashboard.mjs +0 -84
  224. package/site/README.md +0 -63
  225. package/site/docusaurus.config.ts +0 -148
  226. package/site/package-lock.json +0 -18383
  227. package/site/package.json +0 -47
  228. package/site/sidebars.ts +0 -86
  229. package/site/static/.gitkeep +0 -0
  230. package/site/static/img/logo.svg +0 -28
  231. package/site/static/img/og-image.svg +0 -35
  232. package/src/archival/archive-manager.ts +0 -372
  233. package/src/archival/arweave-client.ts +0 -289
  234. package/src/backup/backup.ts +0 -315
  235. package/src/cloud-storage/cloud-sync.ts +0 -461
  236. package/src/cloud-storage/provider-detector.ts +0 -198
  237. package/src/debugging/logs.ts +0 -193
  238. package/src/debugging/types.ts +0 -100
  239. package/src/deployment/deployer.ts +0 -274
  240. package/src/deployment/icpClient.ts +0 -620
  241. package/src/deployment/index.ts +0 -46
  242. package/src/deployment/promotion.ts +0 -161
  243. package/src/deployment/types.ts +0 -111
  244. package/src/icp/batch.ts +0 -374
  245. package/src/icp/environment.ts +0 -215
  246. package/src/icp/icpcli.ts +0 -438
  247. package/src/icp/icwasm.ts +0 -222
  248. package/src/icp/index.ts +0 -94
  249. package/src/icp/optimization.ts +0 -242
  250. package/src/icp/tool-detector.ts +0 -110
  251. package/src/icp/types.ts +0 -574
  252. package/src/index.ts +0 -25
  253. package/src/inference/bittensor-client.ts +0 -304
  254. package/src/inference/inference-manager.ts +0 -327
  255. package/src/metrics/metrics.ts +0 -186
  256. package/src/monitoring/alerting.ts +0 -190
  257. package/src/monitoring/health.ts +0 -197
  258. package/src/monitoring/index.ts +0 -38
  259. package/src/monitoring/info.ts +0 -114
  260. package/src/monitoring/types.ts +0 -99
  261. package/src/network/network-config.ts +0 -129
  262. package/src/packaging/compiler.ts +0 -647
  263. package/src/packaging/config-persistence.ts +0 -135
  264. package/src/packaging/config-schemas.ts +0 -156
  265. package/src/packaging/detector.ts +0 -220
  266. package/src/packaging/index.ts +0 -90
  267. package/src/packaging/packager.ts +0 -118
  268. package/src/packaging/parsers/clawdbot.ts +0 -278
  269. package/src/packaging/parsers/cline.ts +0 -223
  270. package/src/packaging/parsers/generic.ts +0 -266
  271. package/src/packaging/parsers/goose.ts +0 -214
  272. package/src/packaging/serializer.ts +0 -260
  273. package/src/packaging/types.ts +0 -144
  274. package/src/packaging/wasmedge-compiler.ts +0 -406
  275. package/src/security/multisig.ts +0 -415
  276. package/src/security/types.ts +0 -416
  277. package/src/security/vetkeys.ts +0 -655
  278. package/src/testing/local-runner.ts +0 -264
  279. package/src/testing/types.ts +0 -104
  280. package/src/wallet/cbor-serializer.ts +0 -323
  281. package/src/wallet/chain-dispatcher.ts +0 -313
  282. package/src/wallet/cross-chain-aggregator.ts +0 -346
  283. package/src/wallet/index.ts +0 -76
  284. package/src/wallet/key-derivation.ts +0 -425
  285. package/src/wallet/providers/base-provider.ts +0 -154
  286. package/src/wallet/providers/cketh-provider.ts +0 -434
  287. package/src/wallet/providers/polkadot-provider.ts +0 -503
  288. package/src/wallet/providers/solana-provider.ts +0 -490
  289. package/src/wallet/transaction-queue.ts +0 -284
  290. package/src/wallet/types.ts +0 -178
  291. package/src/wallet/vetkeys-adapter.ts +0 -431
  292. package/src/wallet/wallet-manager.ts +0 -597
  293. 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
+ }