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,367 @@
1
+ import Foundation
2
+
3
+ /// Bridges the macOS GUI to the AgentVault Node.js CLI.
4
+ /// Runs CLI commands as child processes and parses their output.
5
+ actor CLIBridge {
6
+
7
+ enum CLIError: LocalizedError {
8
+ case nodeNotFound
9
+ case cliNotFound
10
+ case commandFailed(String)
11
+ case parseError(String)
12
+ case timeout
13
+
14
+ var errorDescription: String? {
15
+ switch self {
16
+ case .nodeNotFound:
17
+ return "Node.js is not installed. Please install Node.js 18+ from nodejs.org or via Homebrew."
18
+ case .cliNotFound:
19
+ return "AgentVault CLI not found. Run 'npm install' in the AgentVault directory."
20
+ case .commandFailed(let msg):
21
+ return "CLI command failed: \(msg)"
22
+ case .parseError(let msg):
23
+ return "Failed to parse CLI output: \(msg)"
24
+ case .timeout:
25
+ return "Command timed out after 60 seconds."
26
+ }
27
+ }
28
+ }
29
+
30
+ /// Cached path to the AgentVault project root
31
+ private var projectRoot: String?
32
+
33
+ /// Discover the AgentVault project root directory
34
+ func findProjectRoot() -> String? {
35
+ if let cached = projectRoot { return cached }
36
+
37
+ // Check common locations
38
+ let candidates = [
39
+ // Relative to the app bundle
40
+ Bundle.main.bundlePath + "/../../../../",
41
+ // Home directory
42
+ NSHomeDirectory() + "/AgentVault",
43
+ // Common dev paths
44
+ "/usr/local/src/AgentVault",
45
+ NSHomeDirectory() + "/Developer/AgentVault",
46
+ NSHomeDirectory() + "/Projects/AgentVault",
47
+ NSHomeDirectory() + "/Code/AgentVault",
48
+ ]
49
+
50
+ for path in candidates {
51
+ let packageJSON = (path as NSString).appendingPathComponent("package.json")
52
+ if FileManager.default.fileExists(atPath: packageJSON) {
53
+ // Verify it's actually AgentVault
54
+ if let data = FileManager.default.contents(atPath: packageJSON),
55
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
56
+ let name = json["name"] as? String,
57
+ name.contains("agentvault") {
58
+ let resolved = (path as NSString).standardizingPath
59
+ projectRoot = resolved
60
+ return resolved
61
+ }
62
+ }
63
+ }
64
+ return nil
65
+ }
66
+
67
+ /// Set the project root manually (from Settings)
68
+ func setProjectRoot(_ path: String) {
69
+ projectRoot = path
70
+ }
71
+
72
+ // MARK: - Environment Checks
73
+
74
+ func checkEnvironment() async -> EnvironmentStatus {
75
+ var status = EnvironmentStatus()
76
+
77
+ // Check Node.js
78
+ if let result = try? await run("node", args: ["--version"]) {
79
+ status.nodeInstalled = true
80
+ status.nodeVersion = result.trimmingCharacters(in: .whitespacesAndNewlines)
81
+ }
82
+
83
+ // Check npm
84
+ if let _ = try? await run("npm", args: ["--version"]) {
85
+ status.npmInstalled = true
86
+ }
87
+
88
+ // Check AgentVault CLI
89
+ if let root = findProjectRoot() {
90
+ let cliEntry = (root as NSString).appendingPathComponent("cli/index.ts")
91
+ status.agentVaultInstalled = FileManager.default.fileExists(atPath: cliEntry)
92
+ if status.agentVaultInstalled {
93
+ status.agentVaultVersion = "local"
94
+ }
95
+ }
96
+
97
+ return status
98
+ }
99
+
100
+ // MARK: - Wallet Operations
101
+
102
+ /// Generate a new wallet for the given chain
103
+ func generateWallet(chain: Chain, name: String) async throws -> CLIWalletResult {
104
+ let root = try requireProjectRoot()
105
+
106
+ let output = try await runCLI(
107
+ root: root,
108
+ args: ["wallet", "generate", "--chain", chain.rawValue.lowercased(), "--name", name, "--json"]
109
+ )
110
+
111
+ return try parseCLIWalletResult(output, chain: chain)
112
+ }
113
+
114
+ /// Import a wallet from a mnemonic phrase
115
+ func importFromMnemonic(chain: Chain, mnemonic: String, name: String) async throws -> CLIWalletResult {
116
+ let root = try requireProjectRoot()
117
+
118
+ let output = try await runCLI(
119
+ root: root,
120
+ args: ["wallet", "import", "--chain", chain.rawValue.lowercased(),
121
+ "--mnemonic", mnemonic, "--name", name, "--json"]
122
+ )
123
+
124
+ return try parseCLIWalletResult(output, chain: chain)
125
+ }
126
+
127
+ /// Import a wallet from a private key
128
+ func importFromPrivateKey(chain: Chain, privateKey: String, name: String) async throws -> CLIWalletResult {
129
+ let root = try requireProjectRoot()
130
+
131
+ let output = try await runCLI(
132
+ root: root,
133
+ args: ["wallet", "import", "--chain", chain.rawValue.lowercased(),
134
+ "--private-key", privateKey, "--name", name, "--json"]
135
+ )
136
+
137
+ return try parseCLIWalletResult(output, chain: chain)
138
+ }
139
+
140
+ /// Import a wallet from a JWK file (Arweave)
141
+ func importFromJWK(filePath: String, name: String) async throws -> CLIWalletResult {
142
+ let root = try requireProjectRoot()
143
+
144
+ let output = try await runCLI(
145
+ root: root,
146
+ args: ["wallet", "import", "--chain", "ar", "--jwk-file", filePath, "--name", name, "--json"]
147
+ )
148
+
149
+ return try parseCLIWalletResult(output, chain: .arweave)
150
+ }
151
+
152
+ /// Import from PEM file (ICP)
153
+ func importFromPEM(filePath: String, name: String) async throws -> CLIWalletResult {
154
+ let root = try requireProjectRoot()
155
+
156
+ let output = try await runCLI(
157
+ root: root,
158
+ args: ["wallet", "import", "--chain", "icp", "--pem-file", filePath, "--name", name, "--json"]
159
+ )
160
+
161
+ return try parseCLIWalletResult(output, chain: .icp)
162
+ }
163
+
164
+ /// Import from keystore JSON (Ethereum)
165
+ func importFromKeystore(filePath: String, password: String, name: String) async throws -> CLIWalletResult {
166
+ let root = try requireProjectRoot()
167
+
168
+ let output = try await runCLI(
169
+ root: root,
170
+ args: ["wallet", "import", "--chain", "eth", "--keystore", filePath,
171
+ "--password", password, "--name", name, "--json"]
172
+ )
173
+
174
+ return try parseCLIWalletResult(output, chain: .ethereum)
175
+ }
176
+
177
+ /// Get wallet balance
178
+ func getBalance(chain: Chain, address: String) async throws -> String {
179
+ let root = try requireProjectRoot()
180
+
181
+ let output = try await runCLI(
182
+ root: root,
183
+ args: ["wallet", "balance", "--chain", chain.rawValue.lowercased(), "--address", address, "--json"]
184
+ )
185
+
186
+ if let data = output.data(using: .utf8),
187
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
188
+ let balance = json["balance"] as? String {
189
+ return balance
190
+ }
191
+
192
+ // Fallback: try to extract balance from plain text
193
+ let trimmed = output.trimmingCharacters(in: .whitespacesAndNewlines)
194
+ if !trimmed.isEmpty { return trimmed }
195
+ return "0"
196
+ }
197
+
198
+ /// Export wallet data
199
+ func exportWallet(walletId: String, format: String, outputPath: String) async throws -> String {
200
+ let root = try requireProjectRoot()
201
+
202
+ return try await runCLI(
203
+ root: root,
204
+ args: ["wallet-export", "--id", walletId, "--format", format, "--output", outputPath, "--json"]
205
+ )
206
+ }
207
+
208
+ /// Create a backup of all wallets
209
+ func createBackup(outputPath: String, password: String) async throws -> String {
210
+ let root = try requireProjectRoot()
211
+
212
+ return try await runCLI(
213
+ root: root,
214
+ args: ["backup", "create", "--output", outputPath, "--password", password, "--json"]
215
+ )
216
+ }
217
+
218
+ /// Restore wallets from a backup
219
+ func restoreBackup(inputPath: String, password: String) async throws -> String {
220
+ let root = try requireProjectRoot()
221
+
222
+ return try await runCLI(
223
+ root: root,
224
+ args: ["backup", "restore", "--input", inputPath, "--password", password, "--json"]
225
+ )
226
+ }
227
+
228
+ // MARK: - Process Execution
229
+
230
+ private func requireProjectRoot() throws -> String {
231
+ guard let root = findProjectRoot() else {
232
+ throw CLIError.cliNotFound
233
+ }
234
+ return root
235
+ }
236
+
237
+ /// Run the AgentVault CLI via tsx
238
+ private func runCLI(root: String, args: [String]) async throws -> String {
239
+ let tsxPath = (root as NSString).appendingPathComponent("node_modules/.bin/tsx")
240
+ let cliEntry = (root as NSString).appendingPathComponent("cli/index.ts")
241
+
242
+ let command: String
243
+ let fullArgs: [String]
244
+
245
+ if FileManager.default.fileExists(atPath: tsxPath) {
246
+ command = tsxPath
247
+ fullArgs = [cliEntry] + args
248
+ } else {
249
+ // Fallback: use npx tsx
250
+ command = "npx"
251
+ fullArgs = ["tsx", cliEntry] + args
252
+ }
253
+
254
+ return try await run(command, args: fullArgs, cwd: root)
255
+ }
256
+
257
+ /// Execute a process and capture its stdout
258
+ @discardableResult
259
+ private func run(_ command: String, args: [String] = [], cwd: String? = nil) async throws -> String {
260
+ try await withCheckedThrowingContinuation { continuation in
261
+ let process = Process()
262
+ let pipe = Pipe()
263
+
264
+ // Resolve command path
265
+ if command.hasPrefix("/") || command.hasPrefix(".") {
266
+ process.executableURL = URL(fileURLWithPath: command)
267
+ } else {
268
+ process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
269
+ process.arguments = [command] + args
270
+ }
271
+
272
+ if process.executableURL?.lastPathComponent != "env" {
273
+ process.arguments = args
274
+ }
275
+
276
+ if let cwd = cwd {
277
+ process.currentDirectoryURL = URL(fileURLWithPath: cwd)
278
+ }
279
+
280
+ // Inherit PATH from user environment
281
+ var env = ProcessInfo.processInfo.environment
282
+ let additionalPaths = [
283
+ "/usr/local/bin",
284
+ "/opt/homebrew/bin",
285
+ NSHomeDirectory() + "/.nvm/versions/node/current/bin",
286
+ NSHomeDirectory() + "/.volta/bin",
287
+ NSHomeDirectory() + "/.fnm/current/bin",
288
+ ]
289
+ let currentPath = env["PATH"] ?? "/usr/bin:/bin"
290
+ env["PATH"] = (additionalPaths + [currentPath]).joined(separator: ":")
291
+ process.environment = env
292
+
293
+ process.standardOutput = pipe
294
+ process.standardError = pipe
295
+
296
+ process.terminationHandler = { proc in
297
+ let data = pipe.fileHandleForReading.readDataToEndOfFile()
298
+ let output = String(data: data, encoding: .utf8) ?? ""
299
+
300
+ if proc.terminationStatus == 0 {
301
+ continuation.resume(returning: output)
302
+ } else {
303
+ continuation.resume(throwing: CLIError.commandFailed(output))
304
+ }
305
+ }
306
+
307
+ do {
308
+ try process.run()
309
+ } catch {
310
+ continuation.resume(throwing: CLIError.commandFailed(error.localizedDescription))
311
+ }
312
+ }
313
+ }
314
+
315
+ // MARK: - Parsing
316
+
317
+ private func parseCLIWalletResult(_ output: String, chain: Chain) throws -> CLIWalletResult {
318
+ // Try JSON parse first
319
+ if let data = output.data(using: .utf8),
320
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
321
+ return CLIWalletResult(
322
+ address: json["address"] as? String ?? "",
323
+ mnemonic: json["mnemonic"] as? String,
324
+ privateKey: json["privateKey"] as? String,
325
+ publicKey: json["publicKey"] as? String,
326
+ chain: chain
327
+ )
328
+ }
329
+
330
+ // Fallback: parse text output
331
+ var address = ""
332
+ var mnemonic: String?
333
+ var privateKey: String?
334
+
335
+ for line in output.components(separatedBy: .newlines) {
336
+ let trimmed = line.trimmingCharacters(in: .whitespaces)
337
+ if trimmed.lowercased().contains("address:") || trimmed.lowercased().contains("principal:") {
338
+ address = trimmed.components(separatedBy: ":").dropFirst().joined(separator: ":").trimmingCharacters(in: .whitespaces)
339
+ } else if trimmed.lowercased().contains("mnemonic:") {
340
+ mnemonic = trimmed.components(separatedBy: ":").dropFirst().joined(separator: ":").trimmingCharacters(in: .whitespaces)
341
+ } else if trimmed.lowercased().contains("private") && trimmed.contains(":") {
342
+ privateKey = trimmed.components(separatedBy: ":").dropFirst().joined(separator: ":").trimmingCharacters(in: .whitespaces)
343
+ }
344
+ }
345
+
346
+ guard !address.isEmpty else {
347
+ throw CLIError.parseError("Could not extract wallet address from CLI output")
348
+ }
349
+
350
+ return CLIWalletResult(
351
+ address: address,
352
+ mnemonic: mnemonic,
353
+ privateKey: privateKey,
354
+ publicKey: nil,
355
+ chain: chain
356
+ )
357
+ }
358
+ }
359
+
360
+ /// Parsed result from CLI wallet operations
361
+ struct CLIWalletResult {
362
+ let address: String
363
+ let mnemonic: String?
364
+ let privateKey: String?
365
+ let publicKey: String?
366
+ let chain: Chain
367
+ }
@@ -0,0 +1,157 @@
1
+ import Foundation
2
+ import CryptoKit
3
+
4
+ /// Native cryptographic operations for wallet management.
5
+ /// Uses Apple CryptoKit for encryption, hashing, and key derivation.
6
+ final class CryptoService {
7
+
8
+ static let shared = CryptoService()
9
+
10
+ private init() {}
11
+
12
+ // MARK: - Encryption (AES-256-GCM)
13
+
14
+ struct EncryptedData: Codable {
15
+ let ciphertext: Data
16
+ let nonce: Data
17
+ let tag: Data
18
+ let salt: Data
19
+ }
20
+
21
+ /// Encrypt data with a password using AES-256-GCM + PBKDF2 key derivation
22
+ func encrypt(data: Data, password: String) throws -> EncryptedData {
23
+ let salt = generateSalt()
24
+ let key = try deriveKey(from: password, salt: salt)
25
+ let nonce = AES.GCM.Nonce()
26
+
27
+ let sealed = try AES.GCM.seal(data, using: key, nonce: nonce)
28
+
29
+ guard let combined = sealed.combined else {
30
+ throw CryptoError.encryptionFailed
31
+ }
32
+
33
+ // Extract components from the combined representation
34
+ // Combined = nonce (12 bytes) + ciphertext + tag (16 bytes)
35
+ let nonceData = Data(nonce)
36
+ let ciphertext = combined.dropFirst(12).dropLast(16)
37
+ let tag = combined.suffix(16)
38
+
39
+ return EncryptedData(
40
+ ciphertext: Data(ciphertext),
41
+ nonce: nonceData,
42
+ tag: Data(tag),
43
+ salt: salt
44
+ )
45
+ }
46
+
47
+ /// Decrypt data with a password
48
+ func decrypt(encrypted: EncryptedData, password: String) throws -> Data {
49
+ let key = try deriveKey(from: password, salt: encrypted.salt)
50
+
51
+ let nonce = try AES.GCM.Nonce(data: encrypted.nonce)
52
+ let sealedBox = try AES.GCM.SealedBox(
53
+ nonce: nonce,
54
+ ciphertext: encrypted.ciphertext,
55
+ tag: encrypted.tag
56
+ )
57
+
58
+ return try AES.GCM.open(sealedBox, using: key)
59
+ }
60
+
61
+ // MARK: - Key Derivation
62
+
63
+ /// Derive a symmetric key from a password using HKDF (CryptoKit approach)
64
+ private func deriveKey(from password: String, salt: Data) throws -> SymmetricKey {
65
+ guard let passwordData = password.data(using: .utf8) else {
66
+ throw CryptoError.invalidPassword
67
+ }
68
+
69
+ // Use HKDF with SHA-256 for key derivation
70
+ let inputKey = SymmetricKey(data: passwordData)
71
+ let derivedKey = HKDF<SHA256>.deriveKey(
72
+ inputKeyMaterial: inputKey,
73
+ salt: salt,
74
+ info: "AgentVault Wallet Backup".data(using: .utf8)!,
75
+ outputByteCount: 32
76
+ )
77
+
78
+ return derivedKey
79
+ }
80
+
81
+ // MARK: - Hashing
82
+
83
+ /// SHA-256 hash of data
84
+ func sha256(_ data: Data) -> Data {
85
+ Data(SHA256.hash(data: data))
86
+ }
87
+
88
+ /// SHA-256 hash of a string
89
+ func sha256(_ string: String) -> String {
90
+ let data = Data(string.utf8)
91
+ let hash = SHA256.hash(data: data)
92
+ return hash.compactMap { String(format: "%02x", $0) }.joined()
93
+ }
94
+
95
+ // MARK: - Random Generation
96
+
97
+ /// Generate a cryptographically secure random salt (32 bytes)
98
+ func generateSalt() -> Data {
99
+ var bytes = [UInt8](repeating: 0, count: 32)
100
+ _ = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
101
+ return Data(bytes)
102
+ }
103
+
104
+ /// Generate a random password/passphrase of given length
105
+ func generateRandomPassword(length: Int = 32) -> String {
106
+ let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*"
107
+ var password = ""
108
+ var randomBytes = [UInt8](repeating: 0, count: length)
109
+ _ = SecRandomCopyBytes(kSecRandomDefault, length, &randomBytes)
110
+
111
+ for byte in randomBytes {
112
+ let index = Int(byte) % chars.count
113
+ password.append(chars[chars.index(chars.startIndex, offsetBy: index)])
114
+ }
115
+ return password
116
+ }
117
+
118
+ // MARK: - Validation
119
+
120
+ /// Validate a BIP39 mnemonic phrase (basic word count check)
121
+ func isValidMnemonic(_ phrase: String) -> Bool {
122
+ let words = phrase.trimmingCharacters(in: .whitespacesAndNewlines)
123
+ .components(separatedBy: .whitespaces)
124
+ .filter { !$0.isEmpty }
125
+ return words.count == 12 || words.count == 24
126
+ }
127
+
128
+ /// Validate an Ethereum private key format
129
+ func isValidEthPrivateKey(_ key: String) -> Bool {
130
+ let cleaned = key.hasPrefix("0x") ? String(key.dropFirst(2)) : key
131
+ return cleaned.count == 64 && cleaned.allSatisfy { $0.isHexDigit }
132
+ }
133
+
134
+ /// Validate an Ethereum address format
135
+ func isValidEthAddress(_ address: String) -> Bool {
136
+ let cleaned = address.hasPrefix("0x") ? String(address.dropFirst(2)) : address
137
+ return cleaned.count == 40 && cleaned.allSatisfy { $0.isHexDigit }
138
+ }
139
+
140
+ // MARK: - Errors
141
+
142
+ enum CryptoError: LocalizedError {
143
+ case encryptionFailed
144
+ case decryptionFailed
145
+ case invalidPassword
146
+ case keyDerivationFailed
147
+
148
+ var errorDescription: String? {
149
+ switch self {
150
+ case .encryptionFailed: return "Encryption failed"
151
+ case .decryptionFailed: return "Decryption failed — wrong password?"
152
+ case .invalidPassword: return "Invalid password"
153
+ case .keyDerivationFailed: return "Key derivation failed"
154
+ }
155
+ }
156
+ }
157
+ }
@@ -0,0 +1,120 @@
1
+ import Foundation
2
+
3
+ /// Manages persistent storage of wallet metadata on the filesystem.
4
+ /// Secrets are NOT stored here — they live in Keychain only.
5
+ final class FileService {
6
+
7
+ static let shared = FileService()
8
+
9
+ private let fileManager = FileManager.default
10
+
11
+ private init() {}
12
+
13
+ // MARK: - App Support Directory
14
+
15
+ /// The application's data directory
16
+ var appDataDirectory: URL {
17
+ let appSupport = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
18
+ let appDir = appSupport.appendingPathComponent("AgentVaultWallet", isDirectory: true)
19
+ try? fileManager.createDirectory(at: appDir, withIntermediateDirectories: true)
20
+ return appDir
21
+ }
22
+
23
+ /// Path to the wallet metadata file
24
+ private var walletsFilePath: URL {
25
+ appDataDirectory.appendingPathComponent("wallets.json")
26
+ }
27
+
28
+ // MARK: - Wallet Metadata Persistence
29
+
30
+ /// Save wallet list to disk (metadata only, no secrets)
31
+ func saveWallets(_ wallets: [Wallet]) throws {
32
+ let encoder = JSONEncoder()
33
+ encoder.dateEncodingStrategy = .iso8601
34
+ encoder.outputFormatting = [.prettyPrinted]
35
+ let data = try encoder.encode(wallets)
36
+ try data.write(to: walletsFilePath, options: [.atomic])
37
+ }
38
+
39
+ /// Load wallet list from disk
40
+ func loadWallets() throws -> [Wallet] {
41
+ guard fileManager.fileExists(atPath: walletsFilePath.path) else {
42
+ return []
43
+ }
44
+
45
+ let data = try Data(contentsOf: walletsFilePath)
46
+ let decoder = JSONDecoder()
47
+ decoder.dateDecodingStrategy = .iso8601
48
+ return try decoder.decode([Wallet].self, from: data)
49
+ }
50
+
51
+ /// Delete a specific wallet's metadata from the stored list
52
+ func deleteWallet(withId id: UUID) throws {
53
+ var wallets = try loadWallets()
54
+ wallets.removeAll { $0.id == id }
55
+ try saveWallets(wallets)
56
+ }
57
+
58
+ // MARK: - File Dialogs
59
+
60
+ /// Suggest a save location for backup files
61
+ var suggestedBackupURL: URL {
62
+ let desktop = fileManager.urls(for: .desktopDirectory, in: .userDomainMask).first!
63
+ let formatter = DateFormatter()
64
+ formatter.dateFormat = "yyyy-MM-dd"
65
+ let filename = "AgentVault-Backup-\(formatter.string(from: Date())).avbackup"
66
+ return desktop.appendingPathComponent(filename)
67
+ }
68
+
69
+ // MARK: - Import File Validation
70
+
71
+ /// Read and validate a JWK file
72
+ func readJWKFile(at url: URL) throws -> Data {
73
+ let data = try Data(contentsOf: url)
74
+
75
+ // Basic validation: should be valid JSON with "n" and "d" fields (RSA key)
76
+ guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
77
+ json["n"] != nil,
78
+ json["kty"] as? String == "RSA" else {
79
+ throw FileError.invalidJWKFile
80
+ }
81
+
82
+ return data
83
+ }
84
+
85
+ /// Read and validate a PEM file
86
+ func readPEMFile(at url: URL) throws -> String {
87
+ let content = try String(contentsOf: url, encoding: .utf8)
88
+ guard content.contains("BEGIN") && content.contains("KEY") else {
89
+ throw FileError.invalidPEMFile
90
+ }
91
+ return content
92
+ }
93
+
94
+ /// Read and validate an Ethereum keystore JSON file
95
+ func readKeystoreFile(at url: URL) throws -> Data {
96
+ let data = try Data(contentsOf: url)
97
+ guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
98
+ json["crypto"] != nil || json["Crypto"] != nil else {
99
+ throw FileError.invalidKeystoreFile
100
+ }
101
+ return data
102
+ }
103
+
104
+ enum FileError: LocalizedError {
105
+ case invalidJWKFile
106
+ case invalidPEMFile
107
+ case invalidKeystoreFile
108
+
109
+ var errorDescription: String? {
110
+ switch self {
111
+ case .invalidJWKFile:
112
+ return "The selected file is not a valid Arweave JWK wallet file"
113
+ case .invalidPEMFile:
114
+ return "The selected file is not a valid PEM identity file"
115
+ case .invalidKeystoreFile:
116
+ return "The selected file is not a valid Ethereum keystore file"
117
+ }
118
+ }
119
+ }
120
+ }