@metamask-previews/wallet-cli 0.0.0-preview-b3db565c8 → 0.0.0-preview-8bfa290fb

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 (264) hide show
  1. package/CHANGELOG.md +4 -8
  2. package/dist/commands/daemon/call.cjs +79 -0
  3. package/dist/commands/daemon/call.cjs.map +1 -0
  4. package/dist/commands/daemon/{call.d.ts → call.d.cts} +2 -2
  5. package/dist/commands/daemon/call.d.cts.map +1 -0
  6. package/dist/commands/daemon/call.d.mts +14 -0
  7. package/dist/commands/daemon/call.d.mts.map +1 -0
  8. package/dist/commands/daemon/{call.js → call.mjs} +31 -30
  9. package/dist/commands/daemon/call.mjs.map +1 -0
  10. package/dist/commands/daemon/list.cjs +48 -0
  11. package/dist/commands/daemon/list.cjs.map +1 -0
  12. package/dist/commands/daemon/{list.d.ts → list.d.cts} +2 -2
  13. package/dist/commands/daemon/list.d.cts.map +1 -0
  14. package/dist/commands/daemon/list.d.mts +7 -0
  15. package/dist/commands/daemon/list.d.mts.map +1 -0
  16. package/dist/commands/daemon/{list.js → list.mjs} +10 -9
  17. package/dist/commands/daemon/list.mjs.map +1 -0
  18. package/dist/commands/daemon/purge.cjs +58 -0
  19. package/dist/commands/daemon/purge.cjs.map +1 -0
  20. package/dist/commands/daemon/{purge.d.ts → purge.d.cts} +2 -2
  21. package/dist/commands/daemon/purge.d.cts.map +1 -0
  22. package/dist/commands/daemon/purge.d.mts +10 -0
  23. package/dist/commands/daemon/purge.d.mts.map +1 -0
  24. package/dist/commands/daemon/{purge.js → purge.mjs} +20 -19
  25. package/dist/commands/daemon/purge.mjs.map +1 -0
  26. package/dist/commands/daemon/start.cjs +52 -0
  27. package/dist/commands/daemon/{start.js.map → start.cjs.map} +1 -1
  28. package/dist/commands/daemon/{start.d.ts → start.d.cts} +2 -2
  29. package/dist/commands/daemon/start.d.cts.map +1 -0
  30. package/dist/commands/daemon/start.d.mts +12 -0
  31. package/dist/commands/daemon/start.d.mts.map +1 -0
  32. package/dist/commands/daemon/start.mjs +50 -0
  33. package/dist/commands/daemon/start.mjs.map +1 -0
  34. package/dist/commands/daemon/status.cjs +59 -0
  35. package/dist/commands/daemon/status.cjs.map +1 -0
  36. package/dist/commands/daemon/{status.d.ts → status.d.cts} +2 -2
  37. package/dist/commands/daemon/status.d.cts.map +1 -0
  38. package/dist/commands/daemon/status.d.mts +7 -0
  39. package/dist/commands/daemon/status.d.mts.map +1 -0
  40. package/dist/commands/daemon/{status.js → status.mjs} +11 -10
  41. package/dist/commands/daemon/status.mjs.map +1 -0
  42. package/dist/commands/daemon/stop.cjs +26 -0
  43. package/dist/commands/daemon/stop.cjs.map +1 -0
  44. package/dist/commands/daemon/{stop.d.ts → stop.d.cts} +2 -2
  45. package/dist/commands/daemon/stop.d.cts.map +1 -0
  46. package/dist/commands/daemon/stop.d.mts +7 -0
  47. package/dist/commands/daemon/stop.d.mts.map +1 -0
  48. package/dist/commands/daemon/{stop.js → stop.mjs} +10 -9
  49. package/dist/commands/daemon/stop.mjs.map +1 -0
  50. package/dist/commands/wallet/send.cjs +332 -0
  51. package/dist/commands/wallet/send.cjs.map +1 -0
  52. package/dist/commands/wallet/{send.d.ts → send.d.cts} +3 -3
  53. package/dist/commands/wallet/send.d.cts.map +1 -0
  54. package/dist/commands/wallet/send.d.mts +42 -0
  55. package/dist/commands/wallet/send.d.mts.map +1 -0
  56. package/dist/commands/wallet/{send.js → send.mjs} +123 -111
  57. package/dist/commands/wallet/send.mjs.map +1 -0
  58. package/dist/commands/wallet/unlock.cjs +75 -0
  59. package/dist/commands/wallet/unlock.cjs.map +1 -0
  60. package/dist/commands/wallet/{unlock.d.ts → unlock.d.cts} +2 -2
  61. package/dist/commands/wallet/unlock.d.cts.map +1 -0
  62. package/dist/commands/wallet/unlock.d.mts +11 -0
  63. package/dist/commands/wallet/unlock.d.mts.map +1 -0
  64. package/dist/commands/wallet/{unlock.js → unlock.mjs} +30 -29
  65. package/dist/commands/wallet/unlock.mjs.map +1 -0
  66. package/dist/daemon/auto-approval.cjs +86 -0
  67. package/dist/daemon/auto-approval.cjs.map +1 -0
  68. package/dist/daemon/{auto-approval.d.ts → auto-approval.d.cts} +3 -3
  69. package/dist/daemon/auto-approval.d.cts.map +1 -0
  70. package/dist/daemon/auto-approval.d.mts +22 -0
  71. package/dist/daemon/auto-approval.d.mts.map +1 -0
  72. package/dist/daemon/{auto-approval.js → auto-approval.mjs} +1 -1
  73. package/dist/daemon/auto-approval.mjs.map +1 -0
  74. package/dist/daemon/daemon-client.cjs +149 -0
  75. package/dist/daemon/daemon-client.cjs.map +1 -0
  76. package/dist/daemon/{daemon-client.d.ts → daemon-client.d.cts} +2 -2
  77. package/dist/daemon/daemon-client.d.cts.map +1 -0
  78. package/dist/daemon/daemon-client.d.mts +84 -0
  79. package/dist/daemon/daemon-client.d.mts.map +1 -0
  80. package/dist/daemon/{daemon-client.js → daemon-client.mjs} +8 -8
  81. package/dist/daemon/daemon-client.mjs.map +1 -0
  82. package/dist/daemon/daemon-entry.cjs +278 -0
  83. package/dist/daemon/daemon-entry.cjs.map +1 -0
  84. package/dist/daemon/daemon-entry.d.cts +2 -0
  85. package/dist/daemon/daemon-entry.d.cts.map +1 -0
  86. package/dist/daemon/daemon-entry.d.mts +2 -0
  87. package/dist/daemon/daemon-entry.d.mts.map +1 -0
  88. package/dist/daemon/{daemon-entry.js → daemon-entry.mjs} +12 -12
  89. package/dist/daemon/daemon-entry.mjs.map +1 -0
  90. package/dist/daemon/daemon-spawn.cjs +138 -0
  91. package/dist/daemon/daemon-spawn.cjs.map +1 -0
  92. package/dist/daemon/{daemon-spawn.d.ts → daemon-spawn.d.cts} +2 -2
  93. package/dist/daemon/daemon-spawn.d.cts.map +1 -0
  94. package/dist/daemon/daemon-spawn.d.mts +28 -0
  95. package/dist/daemon/daemon-spawn.d.mts.map +1 -0
  96. package/dist/daemon/{daemon-spawn.js → daemon-spawn.mjs} +7 -7
  97. package/dist/daemon/daemon-spawn.mjs.map +1 -0
  98. package/dist/daemon/data-dir.cjs +23 -0
  99. package/dist/daemon/data-dir.cjs.map +1 -0
  100. package/dist/daemon/{data-dir.d.ts → data-dir.d.cts} +1 -1
  101. package/dist/daemon/data-dir.d.cts.map +1 -0
  102. package/dist/daemon/data-dir.d.mts +14 -0
  103. package/dist/daemon/data-dir.d.mts.map +1 -0
  104. package/dist/daemon/{data-dir.js → data-dir.mjs} +3 -3
  105. package/dist/daemon/data-dir.mjs.map +1 -0
  106. package/dist/daemon/paths.cjs +20 -0
  107. package/dist/daemon/paths.cjs.map +1 -0
  108. package/dist/daemon/{paths.d.ts → paths.d.cts} +2 -2
  109. package/dist/daemon/paths.d.cts.map +1 -0
  110. package/dist/daemon/paths.d.mts +9 -0
  111. package/dist/daemon/paths.d.mts.map +1 -0
  112. package/dist/daemon/{paths.js → paths.mjs} +2 -2
  113. package/dist/daemon/paths.mjs.map +1 -0
  114. package/dist/daemon/prompts.cjs +55 -0
  115. package/dist/daemon/prompts.cjs.map +1 -0
  116. package/dist/daemon/{prompts.d.ts → prompts.d.cts} +1 -1
  117. package/dist/daemon/prompts.d.cts.map +1 -0
  118. package/dist/daemon/prompts.d.mts +31 -0
  119. package/dist/daemon/prompts.d.mts.map +1 -0
  120. package/dist/daemon/{prompts.js → prompts.mjs} +4 -4
  121. package/dist/daemon/prompts.mjs.map +1 -0
  122. package/dist/daemon/rpc-socket-server.cjs +284 -0
  123. package/dist/daemon/rpc-socket-server.cjs.map +1 -0
  124. package/dist/daemon/{rpc-socket-server.d.ts → rpc-socket-server.d.cts} +2 -2
  125. package/dist/daemon/rpc-socket-server.d.cts.map +1 -0
  126. package/dist/daemon/rpc-socket-server.d.mts +44 -0
  127. package/dist/daemon/rpc-socket-server.d.mts.map +1 -0
  128. package/dist/daemon/{rpc-socket-server.js → rpc-socket-server.mjs} +8 -8
  129. package/dist/daemon/rpc-socket-server.mjs.map +1 -0
  130. package/dist/daemon/secrets.cjs +137 -0
  131. package/dist/daemon/secrets.cjs.map +1 -0
  132. package/dist/daemon/{secrets.d.ts → secrets.d.cts} +1 -1
  133. package/dist/daemon/secrets.d.cts.map +1 -0
  134. package/dist/daemon/secrets.d.mts +72 -0
  135. package/dist/daemon/secrets.d.mts.map +1 -0
  136. package/dist/daemon/{secrets.js → secrets.mjs} +23 -11
  137. package/dist/daemon/secrets.mjs.map +1 -0
  138. package/dist/daemon/send-transaction.cjs +147 -0
  139. package/dist/daemon/send-transaction.cjs.map +1 -0
  140. package/dist/daemon/{send-transaction.d.ts → send-transaction.d.cts} +9 -9
  141. package/dist/daemon/send-transaction.d.cts.map +1 -0
  142. package/dist/daemon/send-transaction.d.mts +102 -0
  143. package/dist/daemon/send-transaction.d.mts.map +1 -0
  144. package/dist/daemon/{send-transaction.js → send-transaction.mjs} +3 -3
  145. package/dist/daemon/send-transaction.mjs.map +1 -0
  146. package/dist/daemon/socket-line.cjs +93 -0
  147. package/dist/daemon/socket-line.cjs.map +1 -0
  148. package/dist/daemon/{socket-line.d.ts → socket-line.d.cts} +5 -2
  149. package/dist/daemon/socket-line.d.cts.map +1 -0
  150. package/dist/daemon/socket-line.d.mts +21 -0
  151. package/dist/daemon/socket-line.d.mts.map +1 -0
  152. package/dist/daemon/{socket-line.js → socket-line.mjs} +1 -1
  153. package/dist/daemon/socket-line.mjs.map +1 -0
  154. package/dist/daemon/stop-daemon.cjs +117 -0
  155. package/dist/daemon/stop-daemon.cjs.map +1 -0
  156. package/dist/daemon/{stop-daemon.d.ts → stop-daemon.d.cts} +1 -1
  157. package/dist/daemon/stop-daemon.d.cts.map +1 -0
  158. package/dist/daemon/stop-daemon.d.mts +28 -0
  159. package/dist/daemon/stop-daemon.d.mts.map +1 -0
  160. package/dist/daemon/{stop-daemon.js → stop-daemon.mjs} +7 -7
  161. package/dist/daemon/stop-daemon.mjs.map +1 -0
  162. package/dist/daemon/types.cjs +20 -0
  163. package/dist/daemon/types.cjs.map +1 -0
  164. package/dist/daemon/{types.d.ts → types.d.cts} +4 -4
  165. package/dist/daemon/types.d.cts.map +1 -0
  166. package/dist/daemon/types.d.mts +94 -0
  167. package/dist/daemon/types.d.mts.map +1 -0
  168. package/dist/daemon/{types.js → types.mjs} +1 -1
  169. package/dist/daemon/types.mjs.map +1 -0
  170. package/dist/daemon/utils.cjs +172 -0
  171. package/dist/daemon/utils.cjs.map +1 -0
  172. package/dist/daemon/{utils.d.ts → utils.d.cts} +3 -2
  173. package/dist/daemon/utils.d.cts.map +1 -0
  174. package/dist/daemon/utils.d.mts +87 -0
  175. package/dist/daemon/utils.d.mts.map +1 -0
  176. package/dist/daemon/{utils.js → utils.mjs} +3 -3
  177. package/dist/daemon/utils.mjs.map +1 -0
  178. package/dist/daemon/wallet-factory.cjs +341 -0
  179. package/dist/daemon/wallet-factory.cjs.map +1 -0
  180. package/dist/daemon/{wallet-factory.d.ts → wallet-factory.d.cts} +4 -4
  181. package/dist/daemon/wallet-factory.d.cts.map +1 -0
  182. package/dist/daemon/wallet-factory.d.mts +64 -0
  183. package/dist/daemon/wallet-factory.d.mts.map +1 -0
  184. package/dist/daemon/{wallet-factory.js → wallet-factory.mjs} +12 -12
  185. package/dist/daemon/wallet-factory.mjs.map +1 -0
  186. package/dist/persistence/KeyValueStore.cjs +79 -0
  187. package/dist/persistence/KeyValueStore.cjs.map +1 -0
  188. package/dist/persistence/{KeyValueStore.d.ts → KeyValueStore.d.cts} +2 -2
  189. package/dist/persistence/KeyValueStore.d.cts.map +1 -0
  190. package/dist/persistence/KeyValueStore.d.mts +18 -0
  191. package/dist/persistence/KeyValueStore.d.mts.map +1 -0
  192. package/dist/persistence/KeyValueStore.mjs +79 -0
  193. package/dist/persistence/KeyValueStore.mjs.map +1 -0
  194. package/dist/persistence/persistence.cjs +204 -0
  195. package/dist/persistence/persistence.cjs.map +1 -0
  196. package/dist/persistence/{persistence.d.ts → persistence.d.cts} +5 -5
  197. package/dist/persistence/persistence.d.cts.map +1 -0
  198. package/dist/persistence/persistence.d.mts +45 -0
  199. package/dist/persistence/persistence.d.mts.map +1 -0
  200. package/dist/persistence/{persistence.js → persistence.mjs} +2 -2
  201. package/dist/persistence/persistence.mjs.map +1 -0
  202. package/dist/test/run-command.cjs +73 -0
  203. package/dist/test/run-command.cjs.map +1 -0
  204. package/dist/test/{run-command.d.ts → run-command.d.cts} +4 -4
  205. package/dist/test/run-command.d.cts.map +1 -0
  206. package/dist/test/run-command.d.mts +27 -0
  207. package/dist/test/run-command.d.mts.map +1 -0
  208. package/dist/test/{run-command.js → run-command.mjs} +3 -3
  209. package/dist/test/run-command.mjs.map +1 -0
  210. package/package.json +15 -19
  211. package/dist/commands/daemon/call.d.ts.map +0 -1
  212. package/dist/commands/daemon/call.js.map +0 -1
  213. package/dist/commands/daemon/list.d.ts.map +0 -1
  214. package/dist/commands/daemon/list.js.map +0 -1
  215. package/dist/commands/daemon/purge.d.ts.map +0 -1
  216. package/dist/commands/daemon/purge.js.map +0 -1
  217. package/dist/commands/daemon/start.d.ts.map +0 -1
  218. package/dist/commands/daemon/start.js +0 -49
  219. package/dist/commands/daemon/status.d.ts.map +0 -1
  220. package/dist/commands/daemon/status.js.map +0 -1
  221. package/dist/commands/daemon/stop.d.ts.map +0 -1
  222. package/dist/commands/daemon/stop.js.map +0 -1
  223. package/dist/commands/wallet/send.d.ts.map +0 -1
  224. package/dist/commands/wallet/send.js.map +0 -1
  225. package/dist/commands/wallet/unlock.d.ts.map +0 -1
  226. package/dist/commands/wallet/unlock.js.map +0 -1
  227. package/dist/daemon/auto-approval.d.ts.map +0 -1
  228. package/dist/daemon/auto-approval.js.map +0 -1
  229. package/dist/daemon/daemon-client.d.ts.map +0 -1
  230. package/dist/daemon/daemon-client.js.map +0 -1
  231. package/dist/daemon/daemon-entry.d.ts +0 -2
  232. package/dist/daemon/daemon-entry.d.ts.map +0 -1
  233. package/dist/daemon/daemon-entry.js.map +0 -1
  234. package/dist/daemon/daemon-spawn.d.ts.map +0 -1
  235. package/dist/daemon/daemon-spawn.js.map +0 -1
  236. package/dist/daemon/data-dir.d.ts.map +0 -1
  237. package/dist/daemon/data-dir.js.map +0 -1
  238. package/dist/daemon/paths.d.ts.map +0 -1
  239. package/dist/daemon/paths.js.map +0 -1
  240. package/dist/daemon/prompts.d.ts.map +0 -1
  241. package/dist/daemon/prompts.js.map +0 -1
  242. package/dist/daemon/rpc-socket-server.d.ts.map +0 -1
  243. package/dist/daemon/rpc-socket-server.js.map +0 -1
  244. package/dist/daemon/secrets.d.ts.map +0 -1
  245. package/dist/daemon/secrets.js.map +0 -1
  246. package/dist/daemon/send-transaction.d.ts.map +0 -1
  247. package/dist/daemon/send-transaction.js.map +0 -1
  248. package/dist/daemon/socket-line.d.ts.map +0 -1
  249. package/dist/daemon/socket-line.js.map +0 -1
  250. package/dist/daemon/stop-daemon.d.ts.map +0 -1
  251. package/dist/daemon/stop-daemon.js.map +0 -1
  252. package/dist/daemon/types.d.ts.map +0 -1
  253. package/dist/daemon/types.js.map +0 -1
  254. package/dist/daemon/utils.d.ts.map +0 -1
  255. package/dist/daemon/utils.js.map +0 -1
  256. package/dist/daemon/wallet-factory.d.ts.map +0 -1
  257. package/dist/daemon/wallet-factory.js.map +0 -1
  258. package/dist/persistence/KeyValueStore.d.ts.map +0 -1
  259. package/dist/persistence/KeyValueStore.js +0 -59
  260. package/dist/persistence/KeyValueStore.js.map +0 -1
  261. package/dist/persistence/persistence.d.ts.map +0 -1
  262. package/dist/persistence/persistence.js.map +0 -1
  263. package/dist/test/run-command.d.ts.map +0 -1
  264. package/dist/test/run-command.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"daemon-entry.js","sourceRoot":"","sources":["../../src/daemon/daemon-entry.ts"],"names":[],"mappings":"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","sourcesContent":["import { define, literal } from '@metamask/superstruct';\nimport type { Json } from '@metamask/utils';\nimport type { Wallet } from '@metamask/wallet';\nimport { appendFile, readFile, rm, writeFile } from 'node:fs/promises';\n\nimport { pingDaemon } from './daemon-client.js';\nimport { ensureOwnerOnlyDirectory } from './data-dir.js';\nimport { getDaemonPaths } from './paths.js';\nimport { startRpcSocketServer } from './rpc-socket-server.js';\nimport type { RpcSocketServerHandle } from './rpc-socket-server.js';\nimport { Password, Srp } from './secrets.js';\nimport {\n runSendTransaction,\n SendTransactionParamsStruct,\n} from './send-transaction.js';\nimport { defineHandler } from './types.js';\nimport type {\n DaemonStatusInfo,\n Logger,\n RpcDispatcher,\n RpcHandlerMap,\n} from './types.js';\nimport { isErrorWithCode, isProcessAlive, readPidFile } from './utils.js';\nimport { createWallet } from './wallet-factory.js';\n\n/**\n * Params struct for the `call` RPC method. `params` must be a non-empty array\n * whose first element is the messenger action name; remaining elements are\n * positional action arguments forwarded as-is to `messenger.call`.\n */\nconst callParamsStruct = define<[string, ...unknown[]]>(\n 'CallParams',\n (value) => {\n if (!Array.isArray(value)) {\n return 'Expected an array';\n }\n if (value.length === 0) {\n return 'Expected a non-empty array';\n }\n if (typeof value[0] !== 'string') {\n return 'Expected the first element to be a string action name';\n }\n return true;\n },\n);\n\nconst startTime = Date.now();\n\nmain().catch((error: unknown) => {\n process.stderr.write(`Daemon fatal: ${String(error)}\\n`);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n const dataDir = process.env.MM_DAEMON_DATA_DIR;\n if (!dataDir) {\n throw new Error('MM_DAEMON_DATA_DIR environment variable is required');\n }\n\n const infuraProjectId = process.env.INFURA_PROJECT_ID;\n if (!infuraProjectId) {\n throw new Error('INFURA_PROJECT_ID environment variable is required');\n }\n\n // Password is optional: when absent, the daemon starts without unlocking\n // the keyring (e.g. when the user prefers to call `mm wallet unlock`\n // interactively rather than embed the password in their environment).\n // First-run startup still requires a password; wallet-factory enforces\n // that and surfaces a clear error.\n const passwordRaw = process.env.MM_WALLET_PASSWORD;\n\n const srpRaw = process.env.MM_WALLET_SRP;\n if (!srpRaw) {\n throw new Error('MM_WALLET_SRP environment variable is required');\n }\n\n // Scrub before validation so a throw from Password.from / Srp.from (bad\n // value) does not leave the raw secrets in the long-lived daemon's env.\n delete process.env.MM_WALLET_PASSWORD;\n delete process.env.MM_WALLET_SRP;\n\n const password = passwordRaw ? Password.from(passwordRaw) : undefined;\n const srp = Srp.from(srpRaw);\n\n await ensureOwnerOnlyDirectory(dataDir);\n\n const {\n socketPath: defaultSocketPath,\n pidPath,\n logPath,\n dbPath,\n } = getDaemonPaths(dataDir);\n const socketPath = process.env.MM_DAEMON_SOCKET_PATH ?? defaultSocketPath;\n\n const log = makeLogger(logPath);\n log('Starting daemon...');\n\n // Pre-flight: refuse to take over if a responsive daemon already owns this\n // socket. If the existing PID file is stale (or the socket is dead), clean\n // it up so the exclusive PID-file write below has a chance to succeed.\n await claimDaemonSlot(pidPath, socketPath, log);\n\n const pidFileContents = `${process.pid}\\n${startTime}\\n`;\n\n // Claim the slot atomically BEFORE opening the SQLite database or\n // constructing the Wallet. Two concurrent `daemon start` invocations can\n // both pass `claimDaemonSlot` (the gap between its preflight and the slot\n // write is racy); without this ordering, both would open `wallet.db` and\n // both would run first-run SRP import before one loses the wx race.\n try {\n await writeFile(pidPath, pidFileContents, { flag: 'wx' });\n } catch (error) {\n throw error instanceof Error\n ? Object.assign(error, {\n message: `Failed to claim daemon slot at ${pidPath}: ${error.message}`,\n })\n : /* istanbul ignore next -- node:fs/promises always rejects with an Error */\n new Error(\n `Failed to claim daemon slot at ${pidPath}: ${String(error)}`,\n );\n }\n\n let wallet: Wallet | undefined;\n let dispose: (() => Promise<void>) | undefined;\n let handle: RpcSocketServerHandle | undefined;\n\n try {\n ({ wallet, dispose } = await createWallet({\n databasePath: dbPath,\n password,\n srp,\n infuraProjectId,\n log,\n }));\n\n const constructedWallet = wallet;\n // Arbitrary messenger dispatch is intentional: the CLI exposes the full\n // messenger surface over a Unix socket inside the per-user oclif data\n // directory. The dataDir is chmodded to 0o700 above and the socket to\n // 0o600 by the RPC server on bind, so only the owning user can open them,\n // but there is no in-process auth check beyond that filesystem-permission\n // barrier. The messenger is strongly typed by action name; we narrow it\n // once here to the RpcDispatcher shape the `call` handler needs.\n const dispatch = constructedWallet.messenger.call.bind(\n constructedWallet.messenger,\n ) as unknown as RpcDispatcher;\n\n const handlers: RpcHandlerMap = {\n getStatus: defineHandler(\n literal(null),\n async (): Promise<DaemonStatusInfo> => ({\n pid: process.pid,\n uptime: Math.floor((Date.now() - startTime) / 1000),\n }),\n ),\n call: defineHandler(callParamsStruct, async (params) => {\n const [action, ...args] = params;\n return await dispatch(action, ...(args as Json[]));\n }),\n // Exposes the callable surface for discovery: it grows silently as\n // controllers are wired, so consumers need a way to see it without a\n // hand-kept catalog that would rot.\n listActions: defineHandler(\n literal(null),\n async (): Promise<Json> =>\n constructedWallet.messenger.getRegisteredActionTypes(),\n ),\n // Dedicated send handler; see `runSendTransaction` for why the generic\n // `call` dispatch cannot carry a broadcast result.\n sendTransaction: defineHandler(\n SendTransactionParamsStruct,\n async (params) =>\n runSendTransaction(constructedWallet.messenger, params),\n ),\n };\n\n // `startRpcSocketServer` restricts the socket to the owner (chmod 0o600)\n // on bind and never leaves a live server/socket behind if it rejects, so\n // the catch below has nothing of its own to close.\n handle = await startRpcSocketServer({\n socketPath,\n handlers,\n onShutdown: async () => shutdown('RPC shutdown'),\n log,\n });\n } catch (error) {\n // `dispose` is undefined only when `createWallet` itself threw — it has\n // already torn down its own store in that case.\n if (dispose) {\n await dispose();\n }\n // Only remove the PID file if it's still ours (we may have lost the race\n // and the file now belongs to another daemon).\n await removeOwnedPidFile(pidPath, pidFileContents).catch(\n (rmError: unknown) => {\n log(`Failed to remove PID file during cleanup: ${String(rmError)}`);\n },\n );\n throw error;\n }\n\n // Stable non-undefined refs for the shutdown closures (TS won't narrow the\n // outer `let`s across closure escape).\n const activeHandle = handle;\n const activeDispose = dispose;\n\n log(`Daemon started. Socket: ${socketPath}`);\n\n let shutdownPromise: Promise<void> | undefined;\n\n /**\n * Shut down the daemon idempotently. Concurrent calls coalesce.\n *\n * @param reason - A label describing why shutdown was triggered.\n * @returns A promise that resolves when shutdown completes.\n */\n async function shutdown(reason: string): Promise<void> {\n if (shutdownPromise === undefined) {\n log(`Shutting down (${reason})...`);\n shutdownPromise = (async (): Promise<void> => {\n try {\n await activeHandle.close();\n } catch (closeError) {\n log(`handle.close() failed: ${String(closeError)}`);\n }\n try {\n await activeDispose();\n } catch (disposeError) {\n log(`dispose() failed during shutdown: ${String(disposeError)}`);\n }\n await Promise.all([\n removeOwnedPidFile(pidPath, pidFileContents).catch(\n (rmError: unknown) => {\n log(`Failed to remove PID file: ${String(rmError)}`);\n },\n ),\n rm(socketPath, { force: true }).catch((rmError: unknown) => {\n log(`Failed to remove socket file: ${String(rmError)}`);\n }),\n ]);\n })();\n }\n return shutdownPromise;\n }\n\n process.on('SIGTERM', () => {\n /* istanbul ignore next -- `shutdown` logs each step internally; the catch only guards against an unhandled rejection from the signal handler. */\n shutdown('SIGTERM').catch(() => undefined);\n });\n process.on('SIGINT', () => {\n /* istanbul ignore next -- `shutdown` logs each step internally; the catch only guards against an unhandled rejection from the signal handler. */\n shutdown('SIGINT').catch(() => undefined);\n });\n}\n\n/**\n * Refuse to start if a responsive daemon already owns the socket. Otherwise\n * clear any stale PID/socket files so the exclusive PID-file write can\n * proceed.\n *\n * @param pidPath - The PID file path.\n * @param socketPath - The socket path.\n * @param log - Logger for diagnostic messages.\n */\nasync function claimDaemonSlot(\n pidPath: string,\n socketPath: string,\n log: Logger,\n): Promise<void> {\n const existingPid = await readPidFile(pidPath);\n const ping = await pingDaemon(socketPath);\n\n if (ping.status === 'responsive') {\n const pidPart =\n existingPid === undefined\n ? '(no PID file present)'\n : `(pid ${existingPid})`;\n throw new Error(`A daemon is already running on ${socketPath} ${pidPart}`);\n }\n\n // Refuse to clobber when the recorded PID is still alive, regardless of\n // whether the socket exists. Possible scenarios:\n // - `unreachable`: wedged or mid-startup sibling daemon (socket present\n // but not responding to JSON-RPC).\n // - `absent`: a sibling daemon that hasn't yet bound its socket, or one\n // whose socket was manually removed. In either case, removing its PID\n // file would orphan it from `daemon stop`.\n if (existingPid !== undefined && isProcessAlive(existingPid)) {\n const detail =\n ping.status === 'unreachable'\n ? `socket at ${socketPath} is unresponsive (${ping.error.message})`\n : `no socket at ${socketPath}, but pid is still alive`;\n throw new Error(\n `A daemon is already running (pid ${existingPid}): ${detail}. ` +\n `Run \\`mm daemon stop\\` (or \\`mm daemon purge\\`) before starting a new daemon.`,\n );\n }\n\n if (ping.status === 'unreachable') {\n log(`Removing stale socket at ${socketPath} (${ping.error.message}).`);\n }\n // Always clear both files before claiming the slot. The PID file may be\n // corrupt (truncated, partial write from a crashed run); without this, the\n // exclusive `wx` write below would fail with EEXIST and the daemon could\n // not start until a human manually deleted the file.\n await Promise.all([\n rm(pidPath, { force: true }),\n rm(socketPath, { force: true }),\n ]);\n}\n\n/**\n * Remove the PID file only if it still contains our exact contents. Guards\n * against a racing daemon's PID file being removed by this daemon during\n * cleanup.\n *\n * @param pidPath - Path to the PID file.\n * @param expectedContents - The contents we wrote when claiming the slot.\n */\nasync function removeOwnedPidFile(\n pidPath: string,\n expectedContents: string,\n): Promise<void> {\n let actual: string;\n try {\n actual = await readFile(pidPath, 'utf-8');\n } catch (error: unknown) {\n if (isErrorWithCode(error, 'ENOENT')) {\n return;\n }\n throw error;\n }\n if (actual === expectedContents) {\n await rm(pidPath, { force: true });\n }\n}\n\n/**\n * Create a file logger that appends timestamped lines to `logPath`, falling\n * back to stderr if the append fails.\n *\n * @param logPath - The log file path.\n * @returns A logging function.\n */\nfunction makeLogger(logPath: string): Logger {\n return (message: string): void => {\n const line = `[${new Date().toISOString()}] ${message}\\n`;\n appendFile(logPath, line).catch((error: unknown) => {\n process.stderr.write(`[log write failed: ${String(error)}] ${message}\\n`);\n });\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"daemon-spawn.d.ts","sourceRoot":"","sources":["../../src/daemon/daemon-spawn.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAKpD;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAuH7B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"daemon-spawn.js","sourceRoot":"","sources":["../../src/daemon/daemon-spawn.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'node:child_process';\nimport { closeSync, existsSync, openSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { pingDaemon } from './daemon-client.js';\nimport { ensureOwnerOnlyDirectory } from './data-dir.js';\nimport { getDaemonPaths } from './paths.js';\nimport type { DaemonSpawnConfig } from './types.js';\n\nconst POLL_INTERVAL_MS = 100;\nconst MAX_POLLS = 300; // 30 seconds\n\n/**\n * Outcome of {@link ensureDaemon}.\n *\n * - `'already-running'`: a responsive daemon was found at the configured\n * socket path. The supplied flags (`infuraProjectId`, `password`, `srp`)\n * were NOT applied to that daemon; the caller should surface this so a\n * user who is trying to change them isn't silently ignored.\n * - `'started'`: a new daemon was spawned and is now responsive.\n */\nexport type EnsureDaemonResult = {\n state: 'already-running' | 'started';\n socketPath: string;\n};\n\n/**\n * Ensure the daemon is running. If a responsive daemon already exists, return\n * `'already-running'` (caller decides how to surface that). Otherwise spawn\n * one as a detached process and wait until the socket becomes responsive.\n *\n * Refuses to spawn when pinging the existing socket fails with anything other\n * than `ENOENT` (wedged or foreign daemon) — taking over could orphan the\n * existing process and corrupt its PID file.\n *\n * @param config - Spawn configuration.\n * @returns The state of the daemon and the socket path it's listening on.\n */\nexport async function ensureDaemon(\n config: DaemonSpawnConfig,\n): Promise<EnsureDaemonResult> {\n const { socketPath, logPath } = getDaemonPaths(config.dataDir);\n\n const initialPing = await pingDaemon(socketPath);\n switch (initialPing.status) {\n case 'responsive':\n return { state: 'already-running', socketPath };\n case 'unreachable':\n if (initialPing.reason === 'permission') {\n throw new Error(\n `Refusing to start: the socket at ${socketPath} is owned by another user. ` +\n `Choose a different data directory (MM_DAEMON_DATA_DIR) or remove the socket manually. ` +\n `(${initialPing.error.message})`,\n );\n }\n throw new Error(\n `Refusing to start: a daemon socket already exists at ${socketPath} but is unresponsive. ` +\n `Run \\`mm daemon stop\\` (or \\`mm daemon purge\\`) before starting a new daemon. ` +\n `(${initialPing.error.message})`,\n );\n case 'absent':\n break;\n /* istanbul ignore next -- exhaustiveness guard; unreachable for the current PingResult union */\n default: {\n const exhaustiveCheck: never = initialPing;\n throw new Error(\n `Unexpected daemon ping status: ${String(exhaustiveCheck)}`,\n );\n }\n }\n\n process.stderr.write('Starting daemon...\\n');\n\n const { entryPath, args } = resolveEntryPoint(config.packageRoot);\n\n // Create the data directory before opening the log file inside it. The daemon\n // entry also does this, but only after spawn — opening the log first would\n // ENOENT on a fresh data directory.\n await ensureOwnerOnlyDirectory(config.dataDir);\n\n // Redirect the detached daemon's stderr to its log file rather than\n // discarding it, so a crash after startup stays diagnosable. stdout stays\n // ignored — structured status goes through the file logger.\n const logFileDescriptor = openSync(logPath, 'a');\n\n const childEnv: NodeJS.ProcessEnv = {\n ...process.env,\n MM_DAEMON_DATA_DIR: config.dataDir,\n MM_DAEMON_SOCKET_PATH: socketPath,\n INFURA_PROJECT_ID: config.infuraProjectId,\n MM_WALLET_SRP: config.srp.unwrap(),\n };\n // `childEnv` spreads `process.env`, so an inherited `MM_WALLET_PASSWORD`\n // would otherwise leak into the child and unlock a daemon the caller meant\n // to start locked. When no password was supplied, delete the inherited key\n // so the daemon comes up locked; otherwise overwrite it with the supplied\n // value.\n if (config.password === undefined) {\n delete childEnv.MM_WALLET_PASSWORD;\n } else {\n childEnv.MM_WALLET_PASSWORD = config.password.unwrap();\n }\n\n const child = spawn(process.execPath, [...args, entryPath], {\n detached: true,\n stdio: ['ignore', 'ignore', logFileDescriptor],\n env: childEnv,\n });\n // The child dup'd the file descriptor into its stderr, so drop the parent's\n // copy. Safe on the success path: `spawn` reports failures via the 'error'\n // event, not a synchronous throw.\n closeSync(logFileDescriptor);\n\n type StartupOutcome =\n | { kind: 'pending' }\n | { kind: 'error'; error: Error }\n | { kind: 'exited'; code: number | null; signal: NodeJS.Signals | null };\n\n // A failed spawn (bad interpreter, EACCES, ENOENT) emits 'error' and may\n // never emit 'exit', so 'error' is recorded first and not overwritten by a\n // later 'exit' — the loop surfaces the real cause instead of hanging.\n const outcome: { current: StartupOutcome } = { current: { kind: 'pending' } };\n\n child.on('error', (error: Error) => {\n process.stderr.write(`Failed to spawn daemon process: ${String(error)}\\n`);\n outcome.current = { kind: 'error', error };\n });\n child.on('exit', (code, signal) => {\n if (outcome.current.kind === 'pending') {\n outcome.current = { kind: 'exited', code, signal };\n }\n });\n child.unref();\n\n for (let i = 0; i < MAX_POLLS; i++) {\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n const settled = outcome.current;\n if (settled.kind === 'error') {\n throw new Error(\n `Failed to spawn daemon process: ${settled.error.message}. ` +\n `Check the daemon log at ${logPath}.`,\n );\n }\n if (settled.kind === 'exited') {\n throw new Error(\n `Daemon process exited during startup (code=${String(settled.code)}, signal=${String(settled.signal)}). ` +\n `Check the daemon log at ${logPath}.`,\n );\n }\n const ping = await pingDaemon(socketPath);\n if (ping.status === 'responsive') {\n process.stderr.write('Daemon ready.\\n');\n return { state: 'started', socketPath };\n }\n }\n\n throw new Error(\n `Daemon did not start within ${(MAX_POLLS * POLL_INTERVAL_MS) / 1000}s`,\n );\n}\n\n/**\n * Resolve the daemon entry point path and any extra Node.js args needed.\n *\n * In production, uses the compiled dist output. In development, uses tsx\n * to run TypeScript source directly.\n *\n * @param packageRoot - The root directory of the wallet-cli package.\n * @returns The entry path and any extra node args.\n */\nfunction resolveEntryPoint(packageRoot: string): {\n entryPath: string;\n args: string[];\n} {\n const distEntry = join(packageRoot, 'dist', 'daemon', 'daemon-entry.mjs');\n if (existsSync(distEntry)) {\n return { entryPath: distEntry, args: [] };\n }\n\n const srcEntry = join(packageRoot, 'src', 'daemon', 'daemon-entry.ts');\n return {\n entryPath: srcEntry,\n args: ['--import', 'tsx'],\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"data-dir.d.ts","sourceRoot":"","sources":["../../src/daemon/data-dir.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7E"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"data-dir.js","sourceRoot":"","sources":["../../src/daemon/data-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,OAAe;IAC5D,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrD,MAAM,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC","sourcesContent":["import { mkdirSync } from 'node:fs';\nimport { chmod } from 'node:fs/promises';\n\n/**\n * Create the daemon's data directory (if it does not exist) and restrict it to\n * the owning user.\n *\n * The mode is `0o700` (owner-only). The daemon exposes the full wallet\n * messenger over the socket inside this directory, so anyone who can traverse\n * the dir can also `connect()` to the socket. Restricting to the owning user is\n * the only access-control boundary. We `chmod` after `mkdir` because the `mode`\n * option is ignored when the directory already exists.\n *\n * @param dataDir - The data directory to create and lock down.\n */\nexport async function ensureOwnerOnlyDirectory(dataDir: string): Promise<void> {\n mkdirSync(dataDir, { recursive: true, mode: 0o700 });\n await chmod(dataDir, 0o700);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/daemon/paths.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAO3D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/daemon/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QACxC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC;QACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC;QACpC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;KACnC,CAAC;AACJ,CAAC","sourcesContent":["import { join } from 'node:path';\n\nimport type { DaemonPaths } from './types.js';\n\n/**\n * Resolve paths for daemon state files within the given data directory.\n *\n * @param dataDir - The base data directory (e.g. oclif config.dataDir).\n * @returns Resolved paths for socket, PID file, log file, and database file.\n */\nexport function getDaemonPaths(dataDir: string): DaemonPaths {\n return {\n socketPath: join(dataDir, 'daemon.sock'),\n pidPath: join(dataDir, 'daemon.pid'),\n logPath: join(dataDir, 'daemon.log'),\n dbPath: join(dataDir, 'wallet.db'),\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/daemon/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CAMrD;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMnE;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAMtD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/daemon/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/D,OAAO,OAAO,CAAC;QACb,OAAO,EAAE,2DAA2D;QACpE,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/D,OAAO,OAAO,CAAC;QACb,OAAO,EAAE,GAAG,OAAO,+BAA+B;QAClD,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACjE,OAAO,QAAQ,CAAC;QACd,OAAO,EAAE,kBAAkB;QAC3B,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Ask the user to confirm the destructive `daemon purge` operation.\n *\n * Wraps `@inquirer/confirm` in a dynamic import so this CommonJS-compiled\n * package can interop with that ESM-only dependency, and so tests can mock\n * the prompt without going through jest's ESM mock machinery.\n *\n * @returns True if the user confirmed.\n */\nexport async function confirmPurge(): Promise<boolean> {\n const { default: confirm } = await import('@inquirer/confirm');\n return confirm({\n message: 'This will stop the daemon and delete all state. Continue?',\n default: false,\n });\n}\n\n/**\n * Ask the user to confirm broadcasting a transaction. Used by `mm wallet send`\n * when neither `--yes` nor `--dry-run` was passed. Same dynamic-import +\n * ESM-interop pattern as {@link confirmPurge}. Defaults to `false` so an\n * accidental bare Enter never broadcasts.\n *\n * @param summary - A human-readable description of the transaction to send\n * (recipient, amount, network), shown above the prompt.\n * @returns True if the user confirmed.\n */\nexport async function confirmSend(summary: string): Promise<boolean> {\n const { default: confirm } = await import('@inquirer/confirm');\n return confirm({\n message: `${summary}\\nBroadcast this transaction?`,\n default: false,\n });\n}\n\n/**\n * Prompt the user for the wallet password, with input masked. Used by\n * `mm wallet unlock` when the user did not pass `--password` or set the\n * `MM_WALLET_PASSWORD` env var. Same dynamic-import + ESM-interop pattern\n * as {@link confirmPurge}.\n *\n * @returns The password the user typed.\n */\nexport async function promptPassword(): Promise<string> {\n const { default: password } = await import('@inquirer/password');\n return password({\n message: 'Wallet password:',\n mask: true,\n });\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"rpc-socket-server.d.ts","sourceRoot":"","sources":["../../src/daemon/rpc-socket-server.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,QAAQ,EAAE,aAAa,CAAC;IACxB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/C;;;;;OAKG;IACH,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC/C,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,UAAU,EACV,QAAQ,EACR,UAAU,EACV,GAAG,GACJ,EAAE,2BAA2B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAqF9D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"rpc-socket-server.js","sourceRoot":"","sources":["../../src/daemon/rpc-socket-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { rpcErrors } from '@metamask/rpc-errors';\nimport { validate as validateStruct } from '@metamask/superstruct';\nimport type {\n JsonRpcId,\n JsonRpcParams,\n JsonRpcResponse,\n} from '@metamask/utils';\nimport { hasProperty, isJsonRpcRequest } from '@metamask/utils';\nimport { chmod, unlink } from 'node:fs/promises';\nimport { createServer } from 'node:net';\nimport type { Server } from 'node:net';\n\nimport type { RpcHandlerMap } from './types.js';\nimport { isErrorWithCode } from './utils.js';\n\nconst CONNECTION_TIMEOUT_MS = 30_000;\n\n/**\n * Handle returned by {@link startRpcSocketServer}.\n */\nexport type RpcSocketServerHandle = {\n close: () => Promise<void>;\n};\n\n/**\n * Options for {@link startRpcSocketServer}.\n */\nexport type StartRpcSocketServerOptions = {\n /** The Unix socket path to listen on. */\n socketPath: string;\n /** Map of RPC method names to handler functions. */\n handlers: RpcHandlerMap;\n /** Callback invoked when a `shutdown` RPC is received. */\n onShutdown?: (() => Promise<void>) | undefined;\n /**\n * Optional logger for server-side diagnostics (unexpected socket errors,\n * unhandled handler rejections, `onShutdown` callback failures). Without\n * this, failures fall back to `process.stderr.write`, which is discarded\n * when the daemon is spawned with `stdio: 'ignore'`.\n */\n log?: ((message: string) => void) | undefined;\n};\n\n/**\n * Start a Unix socket server that processes JSON-RPC requests.\n *\n * Each connection reads one newline-delimited JSON-RPC request, processes it\n * via the provided handler map, writes a JSON-RPC response, and closes.\n *\n * The special `shutdown` method is intercepted before handler dispatch and\n * triggers the provided {@link StartRpcSocketServerOptions.onShutdown} callback\n * after responding.\n *\n * @param options - Server options.\n * @param options.socketPath - The Unix socket path to listen on.\n * @param options.handlers - Map of RPC method names to handler functions.\n * @param options.onShutdown - Optional callback invoked when a `shutdown` RPC is received.\n * @param options.log - Optional logger for server-side diagnostics.\n * @returns A handle with a `close()` function for cleanup.\n */\nexport async function startRpcSocketServer({\n socketPath,\n handlers,\n onShutdown,\n log,\n}: StartRpcSocketServerOptions): Promise<RpcSocketServerHandle> {\n const logFn = log ?? defaultLog;\n\n const server = createServer((socket) => {\n let buffer = '';\n\n // Destroy connections that never send a complete request line. `unref` so\n // the timer alone cannot keep the event loop alive at shutdown.\n const timer = setTimeout(() => {\n socket.destroy();\n }, CONNECTION_TIMEOUT_MS);\n timer.unref();\n\n /**\n * Clear the idle-connection timer. Called from data, close, and error\n * paths so the timer never outlives the connection itself.\n */\n const clearIdleTimer = (): void => {\n clearTimeout(timer);\n };\n\n const onData = (data: Buffer): void => {\n buffer += data.toString();\n const idx = buffer.indexOf('\\n');\n if (idx === -1) {\n return;\n }\n\n clearIdleTimer();\n\n // One request per connection.\n socket.removeListener('data', onData);\n\n const line = buffer.slice(0, idx);\n const remaining = buffer.slice(idx + 1);\n buffer = '';\n\n if (remaining.length > 0) {\n socket.end(\n `${JSON.stringify({\n jsonrpc: '2.0',\n error: rpcErrors\n .invalidRequest({\n message: 'Only one request per connection is allowed',\n })\n .serialize(),\n })}\\n`,\n );\n return;\n }\n\n handleRequest(handlers, line, onShutdown, logFn)\n .then((response) => {\n socket.end(`${JSON.stringify(response)}\\n`);\n return undefined;\n })\n .catch((dispatchError: unknown) => {\n logFn(`Unhandled RPC dispatch error: ${String(dispatchError)}`);\n socket.end(\n `${JSON.stringify({\n jsonrpc: '2.0',\n error: rpcErrors\n .internal({ message: 'Internal error' })\n .serialize(),\n })}\\n`,\n );\n });\n };\n socket.on('data', onData);\n socket.once('close', clearIdleTimer);\n socket.on('error', (socketError: NodeJS.ErrnoException) => {\n clearIdleTimer();\n const { code } = socketError;\n if (code === 'EPIPE' || code === 'ECONNRESET') {\n return; // Expected during probe/disconnect.\n }\n logFn(`Unexpected socket error: ${String(socketError)}`);\n });\n });\n\n await listen(server, socketPath);\n\n return {\n close: async (): Promise<void> => closeServer(server),\n };\n}\n\n/**\n * Default fallback logger: writes to stderr. Daemons spawned with\n * `stdio: 'ignore'` should always pass an explicit `log`.\n *\n * @param message - The message to log.\n */\nfunction defaultLog(message: string): void {\n process.stderr.write(`${message}\\n`);\n}\n\n/**\n * Handle a single JSON-RPC request line, intercepting the `shutdown` method.\n *\n * @param handlers - The RPC handler map.\n * @param line - The raw JSON line from the socket.\n * @param onShutdown - Optional shutdown callback.\n * @param log - Logger for diagnostic messages.\n * @returns A JSON-RPC response object.\n */\nasync function handleRequest(\n handlers: RpcHandlerMap,\n line: string,\n onShutdown: (() => Promise<void>) | undefined,\n log: (message: string) => void,\n): Promise<JsonRpcResponse> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return {\n jsonrpc: '2.0',\n id: null,\n error: rpcErrors.parse({ message: 'Parse error' }).serialize(),\n };\n }\n\n if (!isJsonRpcRequest(parsed)) {\n const id: JsonRpcId =\n typeof parsed === 'object' &&\n parsed !== null &&\n hasProperty(parsed, 'id') &&\n isValidJsonRpcId(parsed.id)\n ? parsed.id\n : null;\n return {\n jsonrpc: '2.0',\n id,\n error: rpcErrors\n .invalidRequest({ message: 'Invalid JSON-RPC request' })\n .serialize(),\n };\n }\n\n const { id, method, params } = parsed;\n\n try {\n if (method === 'shutdown') {\n if (onShutdown) {\n setTimeout(() => {\n onShutdown().catch((error: unknown) => {\n log(`onShutdown callback failed: ${String(error)}`);\n });\n }, 0);\n }\n return { jsonrpc: '2.0', id, result: { status: 'shutting down' } };\n }\n\n const handler = Object.prototype.hasOwnProperty.call(handlers, method)\n ? handlers[method]\n : undefined;\n if (!handler) {\n return {\n jsonrpc: '2.0',\n id,\n error: rpcErrors\n .methodNotFound({ message: `Method not found: ${method}` })\n .serialize(),\n };\n }\n\n const [structError, validatedParams] = validateStruct(\n coerceHandlerParams(params),\n handler.paramsStruct,\n );\n if (structError !== undefined) {\n return {\n jsonrpc: '2.0',\n id,\n error: rpcErrors\n .invalidParams({\n message: `Invalid params for ${method}: ${structError.message}`,\n })\n .serialize(),\n };\n }\n\n const result = await handler.run(validatedParams);\n return { jsonrpc: '2.0', id, result: result ?? null };\n } catch (error) {\n log(`RPC handler \"${method}\" failed: ${String(error)}`);\n if (isRpcError(error)) {\n return { jsonrpc: '2.0', id, error };\n }\n const message = error instanceof Error ? error.message : 'Internal error';\n return {\n jsonrpc: '2.0',\n id,\n error: rpcErrors.internal({ message }).serialize(),\n };\n }\n}\n\n/**\n * Narrow `params` to the shape handlers expect. JSON-RPC 2.0 requires\n * `params`, when present, to be an array or object; both are valid `Json`.\n *\n * @param params - The validated `params` field from a JSON-RPC request.\n * @returns The same value, or `null` when absent.\n */\nfunction coerceHandlerParams(\n params: JsonRpcParams | undefined,\n): JsonRpcParams | null {\n return params ?? null;\n}\n\n/**\n * Per JSON-RPC 2.0, `id` must be a string, number, or null. Used when\n * salvaging an `id` from a parse-success-but-not-valid-request payload.\n *\n * @param value - The candidate id.\n * @returns True if the value is an acceptable JSON-RPC id.\n */\nfunction isValidJsonRpcId(value: unknown): value is JsonRpcId {\n return (\n value === null || typeof value === 'string' || typeof value === 'number'\n );\n}\n\n/**\n * Check if an error is an RPC error with a numeric code.\n *\n * @param error - The error to check.\n * @returns True if the error has a numeric code property.\n */\nfunction isRpcError(\n error: unknown,\n): error is { code: number; message: string } {\n return (\n typeof error === 'object' &&\n error !== null &&\n hasProperty(error, 'code') &&\n typeof error.code === 'number' &&\n hasProperty(error, 'message') &&\n typeof error.message === 'string'\n );\n}\n\n/**\n * Start listening on a Unix socket path, removing any stale socket file.\n *\n * @param server - The net.Server instance.\n * @param socketPath - The Unix socket path.\n */\nasync function listen(server: Server, socketPath: string): Promise<void> {\n try {\n await unlink(socketPath);\n } catch (error) {\n if (!isErrorWithCode(error, 'ENOENT')) {\n throw error;\n }\n }\n\n await new Promise<void>((resolve, reject) => {\n server.on('error', reject);\n server.listen(socketPath, () => {\n server.removeListener('error', reject);\n resolve();\n });\n });\n\n // Restrict the socket to its owner. The daemon hosts an unlocked wallet, so\n // a world-connectable socket would let any local user drive it. listen()\n // creates the socket with umask-derived (typically world-accessible) perms.\n try {\n await chmod(socketPath, 0o600);\n } catch (error) {\n // Never leave a possibly world-accessible socket listening with no handle\n // to close it. Cleanup is best-effort so the chmod failure still surfaces.\n await closeServer(server).catch(() => undefined);\n await unlink(socketPath).catch(() => undefined);\n throw error;\n }\n}\n\n/**\n * Close a server, resolving once it stops accepting connections.\n *\n * @param server - The net.Server instance.\n */\nasync function closeServer(server: Server): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/daemon/secrets.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,cAAc,eAA2C,CAAC;AAIhE;;;;;;;;GAQG;AACH,qBAAa,QAAQ;;IAKnB,OAAO,eAEN;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAKnC;IAED;;;;;OAKG;IACH,MAAM,IAAI,MAAM,CAEf;IAED,QAAQ,IAAI,MAAM,CAEjB;IAED,MAAM,IAAI,MAAM,CAEf;IAED,CAAC,cAAc,CAAC,IAAI,MAAM,CAEzB;CACF;AAED;;;;;;;GAOG;AACH,qBAAa,GAAG;;IAKd,OAAO,eAEN;IAED;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAoB9B;IAED;;;;OAIG;IACH,MAAM,IAAI,MAAM,CAEf;IAED,QAAQ,IAAI,MAAM,CAEjB;IAED,MAAM,IAAI,MAAM,CAEf;IAED,CAAC,cAAc,CAAC,IAAI,MAAM,CAEzB;CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/daemon/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,iDAAiD,CAAC;AAE3E,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B,MAAM,qBAAqB,GAAsB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAEtE,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAEhE,MAAM,YAAY,GAAwB,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;AAE5D;;;;;;;;GAQG;AACH,MAAM,OAAO,QAAQ;IACV,MAAM,CAAS;IAExB,uCAAuC;IACvC,gDAAgD;IAChD,YAAoB,KAAa;QAC/B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,IAAI,CAAC,KAAa;QACvB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,QAAQ;QACN,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM;QACJ,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,CAAC,cAAc,CAAC;QACd,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG;IACL,MAAM,CAAS;IAExB,uCAAuC;IACvC,gDAAgD;IAChD,YAAoB,KAAa;QAC/B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,IAAI,CAAC,KAAa;QACvB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CACb,mEAAmE,KAAK,CAAC,MAAM,GAAG,CACnF,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,QAAQ;QACN,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM;QACJ,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,CAAC,cAAc,CAAC;QACd,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF","sourcesContent":["import { validateMnemonic } from '@metamask/scure-bip39';\nimport { wordlist } from '@metamask/scure-bip39/dist/wordlists/english.js';\n\nconst REDACTED = '[redacted]';\n\nconst VALID_SRP_WORD_COUNTS: readonly number[] = [12, 15, 18, 21, 24];\n\nconst INSPECT_CUSTOM = Symbol.for('nodejs.util.inspect.custom');\n\nconst WORDLIST_SET: ReadonlySet<string> = new Set(wordlist);\n\n/**\n * Opaque wrapper around a wallet password.\n *\n * Constructed via {@link Password.from}, which validates the input. The\n * underlying string is only reachable through {@link Password.unwrap}; every\n * other path (`toString`, `JSON.stringify`, `util.inspect`, template-literal\n * interpolation) yields `[redacted]`. This makes accidental logging produce a\n * harmless placeholder instead of leaking the secret.\n */\nexport class Password {\n readonly #value: string;\n\n // See .from() for why this is private.\n // eslint-disable-next-line no-restricted-syntax\n private constructor(value: string) {\n this.#value = value;\n }\n\n /**\n * Wrap a non-empty string as a {@link Password}.\n *\n * Matches the `@metamask/keyring-controller` convention: any non-empty\n * string is acceptable; minimum-length policy is left to the keyring.\n *\n * @param value - The raw password string.\n * @returns A redacting {@link Password} wrapper.\n * @throws If `value` is empty.\n */\n static from(value: string): Password {\n if (value.length === 0) {\n throw new Error('Password must be a non-empty string');\n }\n return new Password(value);\n }\n\n /**\n * Reveal the underlying password string. Call this only at trust boundaries\n * (e.g. handing the value to the keyring or to a child-process env var).\n *\n * @returns The original password string.\n */\n unwrap(): string {\n return this.#value;\n }\n\n toString(): string {\n return REDACTED;\n }\n\n toJSON(): string {\n return REDACTED;\n }\n\n [INSPECT_CUSTOM](): string {\n return REDACTED;\n }\n}\n\n/**\n * Opaque wrapper around a BIP-39 secret recovery phrase.\n *\n * Constructed via {@link Srp.from}, which validates the word count\n * (12/15/18/21/24) and that every word is present in the BIP-39 English\n * wordlist. The underlying string is only reachable through {@link Srp.unwrap};\n * every other path yields `[redacted]`.\n */\nexport class Srp {\n readonly #value: string;\n\n // See .from() for why this is private.\n // eslint-disable-next-line no-restricted-syntax\n private constructor(value: string) {\n this.#value = value;\n }\n\n /**\n * Validate and wrap a BIP-39 mnemonic phrase.\n *\n * Whitespace is normalized (trimmed and collapsed) before validation so that\n * copy-pasted phrases with accidental leading/trailing/extra spaces are\n * accepted. Catching malformed input here (rather than letting it reach\n * `KeyringController:createNewVaultAndRestore`) produces a clearer error.\n *\n * @param value - The raw mnemonic string.\n * @returns A redacting {@link Srp} wrapper containing the normalized phrase.\n * @throws If the word count is not one of 12/15/18/21/24, if any word is\n * not present in the BIP-39 English wordlist, or if the checksum is invalid.\n */\n static from(value: string): Srp {\n const words = value.trim().split(/\\s+/u);\n if (!VALID_SRP_WORD_COUNTS.includes(words.length)) {\n throw new Error(\n `Secret recovery phrase must be 12, 15, 18, 21, or 24 words (got ${words.length})`,\n );\n }\n for (const word of words) {\n if (!WORDLIST_SET.has(word)) {\n throw new Error(\n 'Secret recovery phrase contains a word not in the BIP-39 English wordlist',\n );\n }\n }\n\n const normalized = words.join(' ');\n if (!validateMnemonic(normalized, wordlist)) {\n throw new Error('Secret recovery phrase has an invalid checksum');\n }\n return new Srp(normalized);\n }\n\n /**\n * Reveal the underlying mnemonic string. Call this only at trust boundaries.\n *\n * @returns The original mnemonic string.\n */\n unwrap(): string {\n return this.#value;\n }\n\n toString(): string {\n return REDACTED;\n }\n\n toJSON(): string {\n return REDACTED;\n }\n\n [INSPECT_CUSTOM](): string {\n return REDACTED;\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"send-transaction.d.ts","sourceRoot":"","sources":["../../src/daemon/send-transaction.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGnD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACd,MAAM,kBAAkB,CAAC;AAqB1B;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;EAmCvC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;EAM5C,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,oCAAoC;;;;;;;;EAI/C,CAAC;AAEH,MAAM,MAAM,2BAA2B,GAAG,KAAK,CAC7C,OAAO,iCAAiC,CACzC,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG,KAAK,CAChD,OAAO,oCAAoC,CAC5C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B,2BAA2B,GAC3B,8BAA8B,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,EACjE,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAiEhC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"send-transaction.js","sourceRoot":"","sources":["../../src/daemon/send-transaction.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n boolean,\n define,\n literal,\n nonempty,\n object,\n optional,\n refine,\n string,\n} from '@metamask/superstruct';\nimport type { Infer } from '@metamask/superstruct';\nimport { isValidHexAddress, StrictHexStruct } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\nimport type {\n DefaultActions,\n DefaultEvents,\n RootMessenger,\n} from '@metamask/wallet';\n\n/**\n * Origin recorded on daemon-initiated transactions. Mirrors\n * `ORIGIN_METAMASK` from `@metamask/controller-utils` (a single string\n * constant, inlined here to avoid pulling that whole package in as a\n * dependency for one value).\n */\nconst INTERNAL_ORIGIN = 'metamask';\n\n/**\n * Struct for a `0x`-prefixed 20-byte hex address. `isValidHexAddress` accepts\n * an all-lowercase address or a valid EIP-55 checksummed one, so a plain\n * lowercase paste works while a mistyped mixed-case address is rejected.\n */\nconst AddressStruct = define<Hex>('Address', (value) =>\n typeof value === 'string' && isValidHexAddress(value as Hex)\n ? true\n : 'Expected a 0x-prefixed 20-byte hex address',\n);\n\n/**\n * Params struct for the `sendTransaction` RPC method.\n *\n * The transaction fields (`to`, `value`, `data`, gas overrides) are canonical\n * `0x`-prefixed hex — any unit conversion (e.g. ether → wei) is the CLI\n * command's job, so the daemon boundary stays unambiguous. Exactly one of\n * `networkClientId` / `chainId` selects the network client; the refinement\n * below rejects supplying both or neither. `dryRun` short-circuits before\n * broadcast (see {@link runSendTransaction}).\n */\nexport const SendTransactionParamsStruct = refine(\n object({\n to: AddressStruct,\n from: optional(AddressStruct),\n value: optional(StrictHexStruct),\n data: optional(StrictHexStruct),\n gas: optional(StrictHexStruct),\n maxFeePerGas: optional(StrictHexStruct),\n maxPriorityFeePerGas: optional(StrictHexStruct),\n gasPrice: optional(StrictHexStruct),\n networkClientId: optional(nonempty(string())),\n chainId: optional(StrictHexStruct),\n dryRun: optional(boolean()),\n }),\n 'SendTransactionParams',\n (value) => {\n if (\n (value.networkClientId === undefined) ===\n (value.chainId === undefined)\n ) {\n return 'Exactly one of `networkClientId` or `chainId` must be provided';\n }\n // `gasPrice` (legacy) and the EIP-1559 fields are mutually exclusive:\n // `TransactionController` rejects the combination at broadcast, so reject\n // it here at the boundary instead — otherwise a dry-run and the\n // confirmation preview would show a plan that always fails at send.\n if (\n value.gasPrice !== undefined &&\n (value.maxFeePerGas !== undefined ||\n value.maxPriorityFeePerGas !== undefined)\n ) {\n return '`gasPrice` cannot be combined with `maxFeePerGas` or `maxPriorityFeePerGas`';\n }\n return true;\n },\n);\n\nexport type SendTransactionParams = Infer<typeof SendTransactionParamsStruct>;\n\n/**\n * The resolved plan a `dryRun` returns: the network client and sender the\n * daemon would use, without adding or broadcasting anything. Exported as a\n * struct so the CLI can validate the payload it reads back over JSON-RPC\n * (which erases types on the wire) rather than trusting its shape.\n */\nexport const SendTransactionDryRunResultStruct = object({\n dryRun: literal(true),\n from: StrictHexStruct,\n to: StrictHexStruct,\n value: StrictHexStruct,\n networkClientId: string(),\n});\n\n/**\n * The outcome of a broadcast send: the on-chain hash plus the id/status the\n * daemon tracks the transaction under. Exported as a struct for the same\n * client-side validation reason as {@link SendTransactionDryRunResultStruct}.\n */\nexport const SendTransactionBroadcastResultStruct = object({\n transactionHash: string(),\n transactionId: string(),\n status: string(),\n});\n\nexport type SendTransactionDryRunResult = Infer<\n typeof SendTransactionDryRunResultStruct\n>;\n\nexport type SendTransactionBroadcastResult = Infer<\n typeof SendTransactionBroadcastResultStruct\n>;\n\nexport type SendTransactionResult =\n | SendTransactionDryRunResult\n | SendTransactionBroadcastResult;\n\n/**\n * Add and broadcast a transaction through the daemon-hosted\n * `TransactionController`, returning a serializable result.\n *\n * `TransactionController:addTransaction` returns `{ transactionMeta, result }`\n * where `result` is a `Promise<hash>` that resolves once the transaction is\n * signed and broadcast. That promise is not JSON-serializable, so it cannot\n * travel back over the daemon's JSON-RPC socket via the generic `call`\n * dispatch — hence this dedicated handler, which awaits the broadcast\n * server-side and returns only the resolved hash (and id/status).\n *\n * The network client is resolved from `chainId` (via `NetworkController`) when\n * `networkClientId` is not given directly, and `from` defaults to the selected\n * account. The transaction is submitted as `isInternal` (the daemon is driven\n * only by its owner over the `0600` same-user socket), which skips\n * origin/permitted-account validation; its approval request is auto-accepted\n * by the daemon's auto-approval subscription.\n *\n * When `dryRun` is set, the network client and sender are resolved and the\n * params validated, but nothing is added or broadcast — the resolved plan is\n * returned so the CLI can preview it before the user confirms.\n *\n * @param messenger - The wallet root messenger.\n * @param params - Validated `sendTransaction` params.\n * @returns The dry-run plan, or the broadcast hash with its id and status.\n */\nexport async function runSendTransaction(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n params: SendTransactionParams,\n): Promise<SendTransactionResult> {\n const {\n to,\n value = '0x0',\n data,\n gas,\n maxFeePerGas,\n maxPriorityFeePerGas,\n gasPrice,\n networkClientId: networkClientIdParam,\n chainId,\n dryRun,\n } = params;\n\n // The struct guarantees exactly one of `networkClientId` / `chainId`, so\n // `chainId` is defined whenever `networkClientId` is not.\n const networkClientId =\n networkClientIdParam ??\n messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n chainId as Hex,\n );\n\n const from =\n params.from ??\n (messenger.call('AccountsController:getSelectedAccount').address as Hex);\n\n if (dryRun) {\n return { dryRun: true, from, to, value, networkClientId };\n }\n\n const txParams = {\n from,\n to,\n value,\n ...(data === undefined ? {} : { data }),\n ...(gas === undefined ? {} : { gas }),\n ...(maxFeePerGas === undefined ? {} : { maxFeePerGas }),\n ...(maxPriorityFeePerGas === undefined ? {} : { maxPriorityFeePerGas }),\n ...(gasPrice === undefined ? {} : { gasPrice }),\n };\n\n const { result, transactionMeta } = await messenger.call(\n 'TransactionController:addTransaction',\n txParams,\n { networkClientId, origin: INTERNAL_ORIGIN, isInternal: true },\n );\n\n // `result` resolves only once the transaction is signed and broadcast (see\n // the function JSDoc); awaiting it here is what this handler exists for.\n const transactionHash = await result;\n\n // Re-read the live record for the current status: `transactionMeta` is the\n // creation snapshot (still `unapproved`). If the record is already gone,\n // fall back to `submitted` — reaching this point means `result` resolved, so\n // the transaction was broadcast, never merely `unapproved`.\n const [current] = messenger.call('TransactionController:getTransactions', {\n searchCriteria: { id: transactionMeta.id },\n });\n\n return {\n transactionHash,\n transactionId: transactionMeta.id,\n status: current?.status ?? 'submitted',\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"socket-line.d.ts","sourceRoot":"","sources":["../../src/daemon/socket-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAsDjB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"socket-line.js","sourceRoot":"","sources":["../../src/daemon/socket-line.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Socket } from 'node:net';\n\n/**\n * Write a newline-delimited line to a socket.\n *\n * @param socket - The socket to write to.\n * @param line - The line to write (without trailing newline).\n */\nexport async function writeLine(socket: Socket, line: string): Promise<void> {\n return new Promise((resolve, reject) => {\n // A socket 'error' can fire while the write is in flight without ever\n // reaching the write callback; without this listener Node would treat it\n // as an unhandled 'error' event and crash the process.\n const onError = (error: Error): void => {\n socket.removeListener('error', onError);\n reject(error);\n };\n socket.once('error', onError);\n\n socket.write(`${line}\\n`, (error) => {\n if (error) {\n // A failed write (e.g. EPIPE) is delivered BOTH here AND as a later\n // 'error' event. Leave `onError` attached to handle that event —\n // detaching here would let Node treat it as unhandled and crash the\n // process. This reject settles the promise; `onError` detaches itself\n // when/if the event arrives.\n reject(error);\n return;\n }\n socket.removeListener('error', onError);\n resolve();\n });\n });\n}\n\n/**\n * Read a single newline-delimited line from a socket.\n *\n * @param socket - The socket to read from.\n * @param timeoutMs - Optional timeout in milliseconds. Rejects with a timeout\n * error if no complete line is received within the limit.\n * @returns The line read (without trailing newline).\n */\nexport async function readLine(\n socket: Socket,\n timeoutMs?: number,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n let buffer = '';\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n if (timeoutMs !== undefined) {\n timer = setTimeout(() => {\n cleanup();\n reject(new Error('Socket read timed out'));\n }, timeoutMs);\n }\n\n const onData = (data: Buffer): void => {\n buffer += data.toString();\n const idx = buffer.indexOf('\\n');\n if (idx !== -1) {\n cleanup();\n resolve(buffer.slice(0, idx));\n }\n };\n\n const onError = (error: Error): void => {\n cleanup();\n reject(error);\n };\n\n const onEnd = (): void => {\n cleanup();\n reject(new Error('Socket closed before response received'));\n };\n\n const onClose = (): void => {\n cleanup();\n reject(new Error('Socket closed before response received'));\n };\n\n /**\n * Remove listeners registered by this call and clear the timeout.\n */\n function cleanup(): void {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n socket.removeListener('data', onData);\n socket.removeListener('error', onError);\n socket.removeListener('end', onEnd);\n socket.removeListener('close', onClose);\n }\n\n socket.on('data', onData);\n socket.once('error', onError);\n socket.once('end', onEnd);\n socket.once('close', onClose);\n });\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stop-daemon.d.ts","sourceRoot":"","sources":["../../src/daemon/stop-daemon.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC9B,OAAO,CAAC,OAAO,CAAC,CAiFlB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stop-daemon.js","sourceRoot":"","sources":["../../src/daemon/stop-daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,UAAkB,EAClB,OAAe,EACf,GAA+B;IAE/B,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,cAAc,GAClB,IAAI,CAAC,MAAM,KAAK,YAAY,IAAI,IAAI,CAAC,MAAM,KAAK,aAAa,CAAC;IAChE,MAAM,YAAY,GAAG,GAAG,KAAK,SAAS,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;IAE9D,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,gBAAgB,GACpB,IAAI,CAAC,MAAM,KAAK,QAAQ;QACxB,CAAC,IAAI,CAAC,MAAM,KAAK,aAAa,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IAE/D,IAAI,gBAAgB,IAAI,CAAC,YAAY,EAAE,CAAC;QACtC,MAAM,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAC5C,MAAM,WAAW,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,EAAE,CAAC,oBAAoB,CAAC,CAAC;IAE5B,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,8CAA8C;IAC9C,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,EAAE,CAAC,qCAAqC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,yEAAyE;QACzE,4EAA4E;QAC5E,8CAA8C;QAC9C,OAAO,GAAG,MAAM,OAAO,CACrB,KAAK,IAAI,EAAE,CACT,CAAC,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,KAAK,YAAY;YACtD,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC7C,KAAK,CACN,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,uEAAuE;IACvE,+CAA+C;IAC/C,IAAI,CAAC,OAAO,IAAI,YAAY,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QAClD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,GAAG,EAAE,CACH,aAAa,UAAU,+BAA+B,GAAG,+BAA+B,CACzF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC;gBAC/B,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,IAAI,CAAC,CAAC,gCAAgC;YAClD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,EAAE,CAAC,mBAAmB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,IAAI,CAAC,OAAO,IAAI,YAAY,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QAClD,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC;gBAC/B,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,IAAI,CAAC,CAAC,gCAAgC;YAClD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,EAAE,CAAC,mBAAmB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAC5C,MAAM,WAAW,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QAClD,GAAG,EAAE,CAAC,iBAAiB,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,WAAW,CACxB,IAAY,EACZ,KAAa,EACb,GAA4C;IAE5C,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACvD,GAAG,EAAE,CAAC,oBAAoB,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { rm } from 'node:fs/promises';\n\nimport { pingDaemon, sendCommand } from './daemon-client.js';\nimport { isProcessAlive, readPidFile, sendSignal, waitFor } from './utils.js';\n\n/**\n * Stop the daemon, preferring a graceful shutdown.\n *\n * Resolution order when a live daemon is present:\n * 1. If the socket is responsive, request a graceful `shutdown` over it.\n * 2. If the recorded PID is still alive, escalate to SIGTERM.\n * 3. ...then SIGKILL.\n *\n * Signals (steps 2-3) are only ever sent against a PID that is observed alive.\n * The socket-absent + alive-PID branch trades a small risk of signalling a\n * recycled PID for the larger risk of leaving an orphan daemon holding the\n * SQLite database — which `daemon purge` would otherwise wipe out from under\n * it.\n *\n * When the socket is NOT responsive AND the recorded PID is dead (or there is\n * no PID file), there is no live daemon: a lingering socket or PID file is\n * stale leftovers from a daemon that already exited — typically one that\n * crashed without running its own cleanup. Those files are removed and the\n * stop is reported as successful, rather than failing on a daemon that is\n * already gone.\n *\n * @param socketPath - The daemon socket path.\n * @param pidPath - The daemon PID file path.\n * @param log - Optional logging function for status messages.\n * @returns True if the daemon was stopped (or was not running).\n */\nexport async function stopDaemon(\n socketPath: string,\n pidPath: string,\n log?: (message: string) => void,\n): Promise<boolean> {\n const pid = await readPidFile(pidPath);\n const ping = await pingDaemon(socketPath);\n const socketObserved =\n ping.status === 'responsive' || ping.status === 'unreachable';\n const processAlive = pid !== undefined && isProcessAlive(pid);\n\n // Only `absent` and `refused` prove no live daemon; `permission`/`timeout`/\n // `protocol` may be a wedged or foreign daemon, so those fall through.\n const socketProvenGone =\n ping.status === 'absent' ||\n (ping.status === 'unreachable' && ping.reason === 'refused');\n\n if (socketProvenGone && !processAlive) {\n await cleanupFile(pidPath, 'PID file', log);\n await cleanupFile(socketPath, 'socket file', log);\n return true;\n }\n\n log?.('Stopping daemon...');\n\n let stopped = false;\n\n // Strategy 1: Graceful socket-based shutdown.\n if (ping.status === 'responsive') {\n try {\n await sendCommand({ socketPath, method: 'shutdown' });\n } catch (error) {\n log?.(`Graceful shutdown request failed: ${String(error)}`);\n }\n // A quiet socket does not prove the process exited; require the recorded\n // pid to be gone too, so a daemon that outlived its socket falls through to\n // SIGTERM/SIGKILL rather than being orphaned.\n stopped = await waitFor(\n async () =>\n (await pingDaemon(socketPath)).status !== 'responsive' &&\n (pid === undefined || !isProcessAlive(pid)),\n 5_000,\n );\n }\n\n // Strategy 2: SIGTERM. Signal when either the socket was observed or the\n // recorded PID is alive; the absent+alive case typically means someone\n // removed the socket from under a live daemon.\n if (!stopped && processAlive && pid !== undefined) {\n if (!socketObserved) {\n log?.(\n `Socket at ${socketPath} is absent but recorded pid ${pid} is alive; signalling anyway.`,\n );\n }\n try {\n if (sendSignal(pid, 'SIGTERM')) {\n stopped = await waitFor(() => !isProcessAlive(pid), 5_000);\n } else {\n stopped = true; // Process already gone (ESRCH).\n }\n } catch (error) {\n log?.(`SIGTERM failed: ${String(error)}`);\n }\n }\n\n // Strategy 3: SIGKILL.\n if (!stopped && processAlive && pid !== undefined) {\n try {\n if (sendSignal(pid, 'SIGKILL')) {\n stopped = await waitFor(() => !isProcessAlive(pid), 2_000);\n } else {\n stopped = true; // Process already gone (ESRCH).\n }\n } catch (error) {\n log?.(`SIGKILL failed: ${String(error)}`);\n }\n }\n\n if (stopped) {\n await cleanupFile(pidPath, 'PID file', log);\n await cleanupFile(socketPath, 'socket file', log);\n log?.('Daemon stopped.');\n }\n\n return stopped;\n}\n\n/**\n * Remove a file best-effort, logging any failure rather than letting it\n * propagate. ENOENT is silently ignored via `force: true`.\n *\n * @param path - The file path to remove.\n * @param label - Human-readable label for log messages.\n * @param log - Optional log sink.\n */\nasync function cleanupFile(\n path: string,\n label: string,\n log: ((message: string) => void) | undefined,\n): Promise<void> {\n await rm(path, { force: true }).catch((error: unknown) => {\n log?.(`Failed to remove ${label}: ${String(error)}`);\n });\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/daemon/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAElD;;;GAGG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAE/C;;;GAGG;AACH,MAAM,MAAM,UAAU,CAAC,OAAO,EAAE,OAAO,IAAI,CACzC,MAAM,EAAE,OAAO,KACZ,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,CAAC,OAAO,EAAE,OAAO,SAAS,IAAI,GAAG,IAAI,IAAI;IACvE,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9B,GAAG,EAAE,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;CACnC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,MAAM,EACN,oBAAoB,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC,CAC3C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,SAAS,IAAI,GAAG,IAAI,EAChE,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,EAC7B,GAAG,EAAE,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,GAChC,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAKxC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,GAAG,EAAE,GAAG,CAAC;IACT,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/daemon/types.ts"],"names":[],"mappings":"AAiDA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,YAA6B,EAC7B,GAAiC;IAEjC,OAAO,EAAE,YAAY,EAAE,GAAG,EAGzB,CAAC;AACJ,CAAC","sourcesContent":["import type { Struct } from '@metamask/superstruct';\nimport type { Json } from '@metamask/utils';\n\nimport type { Password, Srp } from './secrets.js';\n\n/**\n * Sink for daemon diagnostic messages. A backgrounded daemon's stdio may be\n * discarded, so hosts supply a logger that writes somewhere durable.\n */\nexport type Logger = (message: string) => void;\n\n/**\n * A function that handles a JSON-RPC method call after its params have been\n * validated by the corresponding {@link RpcHandlerDefinition.paramsStruct}.\n */\nexport type RpcHandler<TParams, TResult> = (\n params: TParams,\n) => Promise<TResult>;\n\n/**\n * Definition for a single JSON-RPC method: the struct that validates\n * incoming `params` plus the handler that runs once `params` is known to\n * match.\n *\n * The server (see `rpc-socket-server.ts`) validates the raw `params` against\n * `paramsStruct` before invoking `run`, so each handler body can trust the\n * shape of its input without re-checking.\n */\nexport type RpcHandlerDefinition<TParams, TResult extends Json | void> = {\n paramsStruct: Struct<TParams>;\n run: RpcHandler<TParams, TResult>;\n};\n\n/**\n * A map of RPC method names to their handler definitions.\n *\n * `TParams` is erased to `unknown` here so definitions with different narrow\n * params types (e.g. `null` vs. a tuple) can coexist in the same map. Consumers\n * therefore see each `run` as accepting `unknown` and must validate `params`\n * against the paired `paramsStruct` before invoking it — which is exactly what\n * the server (see `rpc-socket-server.ts`) does. The concrete `TParams` is\n * captured inside {@link defineHandler}, where the struct and handler are bound\n * together.\n */\nexport type RpcHandlerMap = Record<\n string,\n RpcHandlerDefinition<unknown, Json | void>\n>;\n\n/**\n * Bundle a params struct with the handler that runs once `params` is\n * validated. The server invokes `run` only after `paramsStruct` accepts the\n * value, so `run` can trust the type of its argument.\n *\n * The returned definition erases `TParams` to `unknown` so heterogeneous\n * handlers can share an {@link RpcHandlerMap}.\n *\n * @param paramsStruct - Struct that validates `params` for this method.\n * @param run - Handler invoked with the validated params.\n * @returns An {@link RpcHandlerDefinition} suitable for an {@link RpcHandlerMap}.\n */\nexport function defineHandler<TParams, TResult extends Json | void>(\n paramsStruct: Struct<TParams>,\n run: RpcHandler<TParams, TResult>,\n): RpcHandlerDefinition<unknown, TResult> {\n return { paramsStruct, run } as unknown as RpcHandlerDefinition<\n unknown,\n TResult\n >;\n}\n\n/**\n * Typed wrapper around `wallet.messenger.call` used by the `call` RPC.\n *\n * The messenger is strongly typed by action name; the daemon exposes the full\n * messenger surface over the socket and dispatches by string, so we consolidate\n * the unsafe cast into a single, documented escape hatch instead of repeating\n * it at each call site.\n */\nexport type RpcDispatcher = (action: string, ...args: Json[]) => Promise<Json>;\n\n/**\n * Resolved paths for daemon state files.\n */\nexport type DaemonPaths = {\n socketPath: string;\n pidPath: string;\n logPath: string;\n dbPath: string;\n};\n\n/**\n * Status information returned by the daemon's `getStatus` RPC method.\n */\nexport type DaemonStatusInfo = {\n pid: number;\n uptime: number;\n};\n\n/**\n * Configuration passed to the daemon spawner.\n *\n * `password` is optional: when omitted, the daemon starts without unlocking\n * the keyring, and the caller is expected to use `mm wallet unlock` before\n * any keyring-bound operation. First-run startup still requires both\n * `password` and `srp`; without `password`, the daemon will exit during\n * startup with a clear error. `srp` is always required by the type and\n * forwarded to the daemon; it is only consumed on first run.\n */\nexport type DaemonSpawnConfig = {\n dataDir: string;\n infuraProjectId: string;\n password?: Password;\n srp: Srp;\n packageRoot: string;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/daemon/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIpD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAuBhE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAE9D;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,GAAG,SAAS,CAEpB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,EAAE,CAI/D;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAa7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAanD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAUvE;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,KAAK,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EACvC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CASlB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/daemon/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcError } from '@metamask/utils';\nimport { isErrorWithCode as hasErrorCode } from '@metamask/utils';\nimport { readFile } from 'node:fs/promises';\n\n/**\n * Check whether an unknown error is a Node.js system error with the given code.\n *\n * @param error - The error to check.\n * @param code - The expected error code (e.g. 'ENOENT', 'EPERM').\n * @returns True if the error matches the code.\n */\nexport function isErrorWithCode(error: unknown, code: string): boolean {\n return hasErrorCode(error) && error.code === code;\n}\n\n/**\n * Turn an error thrown while contacting the daemon socket into a user-facing\n * message for `Command.error`, so every command reports the same failure the\n * same way. Distinguishes a stopped daemon (`ENOENT`/`ECONNREFUSED`), a\n * connection dropped mid-request (`ECONNRESET`), and a permission problem\n * (`EACCES`/`EPERM`), falling back to the raw error message.\n *\n * @param error - The value thrown while contacting the daemon.\n * @returns A human-readable explanation of the failure.\n */\nexport function makeDaemonConnectionError(error: unknown): string {\n if (\n isErrorWithCode(error, 'ENOENT') ||\n isErrorWithCode(error, 'ECONNREFUSED')\n ) {\n return 'Daemon is not running. Start it with `mm daemon start`.';\n }\n // A reset drops an already-established connection, so the daemon was running\n // — most likely it crashed mid-request. Don't tell the user to start it.\n if (isErrorWithCode(error, 'ECONNRESET')) {\n return (\n 'Lost the connection to the daemon; it may have crashed while ' +\n 'handling the request. Check `mm daemon status` and the daemon log.'\n );\n }\n if (isErrorWithCode(error, 'EACCES') || isErrorWithCode(error, 'EPERM')) {\n return (\n 'Cannot connect to the daemon socket: permission denied. ' +\n 'The socket may be owned by another user, or MM_DAEMON_DATA_DIR ' +\n 'may point to a directory you cannot access.'\n );\n }\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Format the error of a JSON-RPC failure response into a user-facing message\n * for `Command.error`, so every command reports RPC failures the same way.\n *\n * @param error - The `error` field of a JSON-RPC failure response.\n * @returns The error message annotated with its numeric code.\n */\nexport function formatJsonRpcError(error: JsonRpcError): string {\n return `${error.message} (code ${String(error.code)})`;\n}\n\n/**\n * Treat an empty string the same as `undefined` — \"no value supplied\". Used\n * to normalise `--password ''` and `MM_WALLET_PASSWORD=''` so they trigger\n * the interactive prompt rather than sending an empty string to the keyring.\n *\n * @param value - The value to normalise.\n * @returns `undefined` if `value` is `undefined` or `''`; `value` otherwise.\n */\nexport function emptyToUndefined(\n value: string | undefined,\n): string | undefined {\n return value === '' ? undefined : value;\n}\n\n/**\n * Check whether a value is an array of strings. Used to validate untyped RPC\n * results (e.g. the daemon's `listActions`) before treating them as `string[]`.\n *\n * @param value - The value to check.\n * @returns True if the value is an array whose every element is a string.\n */\nexport function isStringArray(value: unknown): value is string[] {\n return (\n Array.isArray(value) && value.every((item) => typeof item === 'string')\n );\n}\n\n/**\n * Read a PID from a file. The file may contain just the PID, or the PID on\n * the first line followed by additional metadata (e.g. start time written by\n * the daemon).\n *\n * @param pidPath - The PID file path.\n * @returns The PID, or undefined if the file is missing or its first line is\n * not a positive integer.\n */\nexport async function readPidFile(\n pidPath: string,\n): Promise<number | undefined> {\n let contents: string;\n try {\n contents = await readFile(pidPath, 'utf-8');\n } catch (error: unknown) {\n if (isErrorWithCode(error, 'ENOENT')) {\n return undefined;\n }\n throw error;\n }\n // String.prototype.split always returns at least one element, so [0] is safe.\n const pid = Number(contents.split('\\n')[0].trim());\n return Number.isInteger(pid) && pid > 0 ? pid : undefined;\n}\n\n/**\n * Check whether a process is alive by sending signal 0.\n *\n * Treats `ESRCH` as \"process is gone\", `EPERM` as \"process exists but we\n * cannot signal it\" (still alive from our perspective), and rethrows\n * anything else so the caller can surface unexpected failures rather than\n * silently assuming the process is dead.\n *\n * @param pid - The process ID to check.\n * @returns True if the process exists.\n */\nexport function isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error: unknown) {\n if (isErrorWithCode(error, 'ESRCH')) {\n return false;\n }\n if (isErrorWithCode(error, 'EPERM')) {\n return true;\n }\n throw error;\n }\n}\n\n/**\n * Send a signal to a process. Returns true if the signal was sent, false if\n * the process does not exist (ESRCH). Re-throws on permission errors and\n * other failures.\n *\n * @param pid - The process ID.\n * @param signal - The signal to send.\n * @returns True if the signal was delivered, false if the process is gone.\n */\nexport function sendSignal(pid: number, signal: NodeJS.Signals): boolean {\n try {\n process.kill(pid, signal);\n return true;\n } catch (error: unknown) {\n if (isErrorWithCode(error, 'ESRCH')) {\n return false;\n }\n throw error;\n }\n}\n\n/**\n * Poll until a condition is met or the timeout elapses.\n *\n * @param check - A function that returns true when the condition is met.\n * @param timeoutMs - Maximum time to wait in milliseconds.\n * @returns True if the condition was met, false on timeout.\n */\nexport async function waitFor(\n check: () => boolean | Promise<boolean>,\n timeoutMs: number,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await check()) {\n return true;\n }\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n return false;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wallet-factory.d.ts","sourceRoot":"","sources":["../../src/daemon/wallet-factory.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,MAAM,EACP,MAAM,kBAAkB,CAAC;AAY1B,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIzC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,GAAG,EAAE,GAAG,CAAC;IACT,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAmIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAsB,YAAY,CAAC,EACjC,YAAY,EACZ,QAAQ,EACR,GAAG,EACH,eAAe,EACf,GAAG,GACJ,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAwHlD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wallet-factory.js","sourceRoot":"","sources":["../../src/daemon/wallet-factory.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDefaultAnalyticsControllerState } from '@metamask/analytics-controller';\nimport type {\n AnalyticsControllerGetStateAction,\n AnalyticsControllerTrackEventAction,\n} from '@metamask/analytics-controller';\nimport { ConfigRegistryApiEnv } from '@metamask/config-registry-controller';\nimport { Messenger } from '@metamask/messenger';\nimport {\n ClientConfigApiService,\n ClientType,\n DistributionType,\n EnvironmentType,\n} from '@metamask/remote-feature-flag-controller';\nimport { InMemoryStorageAdapter } from '@metamask/storage-service';\nimport type { Json } from '@metamask/utils';\nimport {\n AlwaysOnlineAdapter,\n importSecretRecoveryPhrase,\n Wallet,\n} from '@metamask/wallet';\nimport type {\n DefaultActions,\n DefaultEvents,\n RootMessenger,\n WalletOptions,\n} from '@metamask/wallet';\nimport { rm } from 'node:fs/promises';\n\nimport { KeyValueStore } from '../persistence/KeyValueStore.js';\nimport { loadState, subscribeToChanges } from '../persistence/persistence.js';\nimport { subscribeToAutoApproval } from './auto-approval.js';\nimport type { Password, Srp } from './secrets.js';\nimport type { Logger } from './types.js';\n\nconst IN_MEMORY_DATABASE_PATH = ':memory:';\n\nexport type CreateWalletConfig = {\n databasePath: string;\n password?: Password;\n srp: Srp;\n infuraProjectId: string;\n log?: Logger;\n};\n\nexport type CreateWalletResult = {\n wallet: Wallet;\n /**\n * Tear down everything `createWallet` set up, in the order that keeps\n * in-flight persistence writes valid: stop the state-change subscription,\n * destroy the wallet, then close the store (closing first would cause a\n * teardown-time persistence write to fail). Resilient — a failure in any\n * step is logged and the remaining steps still run — and idempotent (repeat\n * calls coalesce onto the same teardown).\n */\n dispose: () => Promise<void>;\n};\n\n/**\n * Build the per-instance options the daemon's `Wallet` is constructed with.\n *\n * Returns a fresh set on every call so the metadata probe and the real wallet\n * never share adapter/service instances (the probe is destroyed, which may\n * tear those instances down).\n *\n * Only the slots wired on `@metamask/wallet` today are populated:\n * - `storageService` — backed by an in-memory adapter. This is the wallet's\n * large-blob store (snap source, caches), distinct from the SQLite\n * `KeyValueStore` that persists controller state; no wired controller\n * offloads durable data to it yet, so in-memory suffices.\n * - `connectivityController` — the `AlwaysOnlineAdapter` exported for\n * node-like hosts that have no platform connectivity signal.\n * - `networkController` — the Infura project ID used to reach Infura RPC\n * endpoints.\n * - `remoteFeatureFlagController` — a `ClientConfigApiService` fetching real\n * flags over the network.\n * - `approvalController` — a no-op `showApprovalRequest` (the daemon is\n * headless); pending requests are accepted separately by the auto-approval\n * subscription `createWallet` installs, not by this hook.\n * - `gasFeeController` — only `clientId: 'cli'` (sent as `X-Client-Id` to the\n * gas API); the API endpoints, poll interval, and compatibility callbacks are\n * left at the wallet package's platform-agnostic defaults (the default\n * `EIP1559APIEndpoint` is already the production URL, so the daemon does not\n * re-specify it).\n * - `transactionController` — swaps processing disabled and no client hooks;\n * see the slot's inline comment for why the daemon relies on the\n * controller's defaults for everything else.\n *\n * The optional `keyringController` slot is intentionally omitted so the\n * controller's built-in defaults (e.g. the PBKDF2 encryptor) apply.\n *\n * @param infuraProjectId - The Infura project ID for the `NetworkController`.\n * @returns The `instanceOptions` for the `Wallet` constructor.\n */\nfunction buildInstanceOptions(\n infuraProjectId: string,\n): WalletOptions['instanceOptions'] {\n return {\n configRegistryApiService: {\n env: ConfigRegistryApiEnv.PRD,\n },\n approvalController: {\n // The daemon is headless, so there is no UI to open: requests are\n // resolved by the auto-approval subscription (see `subscribeToAutoApproval`)\n // rather than by this hook, which stays a no-op.\n // TODO: surface approval requests over the daemon transport.\n showApprovalRequest: (): undefined => undefined,\n },\n connectivityController: {\n connectivityAdapter: new AlwaysOnlineAdapter(),\n },\n gasFeeController: {\n // Identifies the CLI to the gas estimation API via the `X-Client-Id`\n // header.\n clientId: 'cli',\n },\n networkController: {\n infuraProjectId,\n },\n remoteFeatureFlagController: {\n clientConfigApiService: new ClientConfigApiService({\n fetch: globalThis.fetch,\n config: {\n // TODO: switch to a CLI-specific `ClientType` once one exists; until\n // then `Extension` buckets feature flags as an extension client.\n client: ClientType.Extension,\n distribution: DistributionType.Main,\n environment: EnvironmentType.Production,\n },\n }),\n getMetaMetricsId: (): string => 'cli',\n clientVersion: '0.0.0',\n },\n storageService: {\n storage: new InMemoryStorageAdapter(),\n },\n transactionController: {\n // The CLI exposes no swaps surface, so skip the swaps-specific\n // post-processing a full wallet client runs (mobile makes the same\n // choice; the extension keeps swaps enabled).\n disableSwaps: true,\n // No CLI-specific transaction hooks: the controller's built-in publish\n // path broadcasts through the wired `NetworkController` provider, and the\n // remaining hooks (metrics, notifications, gas-fee tokens) are client-UI\n // concerns the headless daemon has no equivalent for. Every other option\n // (`getPermittedAccounts`, `isSimulationEnabled`, `trace`, …) is left at\n // the controller's default.\n hooks: {},\n },\n };\n}\n\n/**\n * Register handlers for the `AnalyticsController` actions that\n * `NetworkController` calls to emit RPC service analytics. The daemon does not\n * report analytics, so these do nothing: `getState` returns an empty analytics\n * ID, which stops the controller before it tracks anything, and `trackEvent` is\n * a no-op. Registering them keeps those messenger calls from throwing.\n *\n * @param messenger - The wallet's root messenger.\n */\nfunction registerAnalyticsStubHandlers(messenger: Wallet['messenger']): void {\n // Register on an `AnalyticsController`-namespaced messenger, not the root: a\n // messenger can only register handlers for its own namespace, and doing so\n // delegates them up to the root, where `NetworkController` can reach them.\n const analyticsMessenger = new Messenger<\n 'AnalyticsController',\n AnalyticsControllerGetStateAction | AnalyticsControllerTrackEventAction,\n never,\n RootMessenger<DefaultActions, DefaultEvents>\n >({\n namespace: 'AnalyticsController',\n parent: messenger as RootMessenger<DefaultActions, DefaultEvents>,\n });\n analyticsMessenger.registerActionHandler(\n 'AnalyticsController:getState',\n () => ({\n ...getDefaultAnalyticsControllerState(),\n analyticsId: '',\n }),\n );\n analyticsMessenger.registerActionHandler(\n 'AnalyticsController:trackEvent',\n () => undefined,\n );\n}\n\n/**\n * Create a configured `Wallet` for daemon use, backed by a SQLite key-value\n * store for controller-state persistence.\n *\n * Loads any previously-persisted controller state from the store, seeds the\n * wallet with it, then subscribes the store to subsequent state changes so all\n * persist-flagged properties are written through.\n *\n * If the store does not yet contain a keyring vault (first run), the supplied\n * secret recovery phrase is imported using the supplied password. On\n * subsequent runs, the persisted vault is reused: when a password is\n * supplied, the wallet is unlocked via `KeyringController:submitPassword` so\n * keyring-bound messenger actions work immediately; when no password is\n * supplied, the wallet starts locked and the caller is expected to invoke\n * `mm wallet unlock` before any keyring-bound operation. First-run startup\n * without a password is rejected (the SRP cannot be imported without one).\n * On a subsequent run, a wrong password rejects startup with a `Failed to\n * unlock the persisted vault` error that wraps the `submitPassword` rejection.\n *\n * On any failure after the store is opened, the store is closed (and the wallet\n * destroyed, if constructed). On a first-run failure, the on-disk database is\n * also removed so a retry does not latch onto an orphaned partial vault — this\n * covers in-process failures only; a hard crash (SIGKILL/power loss) mid-import\n * can still leave a vault on disk.\n *\n * @param config - Wallet configuration.\n * @param config.databasePath - Path to the SQLite database file (or\n * `':memory:'` for ephemeral use).\n * @param config.password - The wallet password. Optional on subsequent runs;\n * when omitted, the daemon starts with a locked keyring. Required on first\n * run (to import the SRP).\n * @param config.srp - The secret recovery phrase (BIP-39 mnemonic). Used\n * only on first run.\n * @param config.infuraProjectId - The Infura project ID for the\n * `NetworkController`.\n * @param config.log - Optional logger for persistence-write and teardown\n * failures. Without it, failures fall back to `console.error` (which a detached\n * daemon's `stdio: 'ignore'` discards).\n * @returns The `Wallet` and a `dispose` handle that tears it down.\n */\nexport async function createWallet({\n databasePath,\n password,\n srp,\n infuraProjectId,\n log,\n}: CreateWalletConfig): Promise<CreateWalletResult> {\n const logFn = log ?? ((message: string): void => console.error(message));\n const store = new KeyValueStore(databasePath);\n let wallet: Wallet | undefined;\n let persistenceUnsubscribe: (() => void) | undefined;\n let autoApprovalUnsubscribe: (() => void) | undefined;\n let wasFirstRun = false;\n\n try {\n const state = await loadPersistedState(store, infuraProjectId, logFn);\n wasFirstRun = !hasPersistedKeyring(state);\n\n // Validate the first-run precondition BEFORE constructing the wallet,\n // so a doomed startup doesn't build a Wallet (and wire persistence\n // handlers) just to tear it down.\n if (wasFirstRun && password === undefined) {\n throw new Error(\n 'A password is required on first run to import the secret recovery phrase. ' +\n 'Pass `--password` (or `MM_WALLET_PASSWORD`) on `mm daemon start`.',\n );\n }\n\n wallet = new Wallet({\n state,\n instanceOptions: buildInstanceOptions(infuraProjectId),\n });\n registerAnalyticsStubHandlers(wallet.messenger);\n persistenceUnsubscribe = subscribeToChanges(\n wallet.messenger,\n wallet.controllerMetadata,\n store,\n logFn,\n );\n\n // Installed before `init` so any approval raised during initialization is covered.\n autoApprovalUnsubscribe = subscribeToAutoApproval(wallet.messenger, logFn);\n\n // Complete post-construction controller setup before serving requests —\n // e.g. `NetworkController.init` applies the selected network so a provider\n // is available. `init` settles every step independently; a rejected step\n // leaves the wallet only partially usable, so abort startup (the catch\n // below tears down and, on first run, removes the partial database) rather\n // than serving a degraded daemon.\n const initResults = await wallet.init();\n const initFailures = initResults.filter(\n (result): result is PromiseRejectedResult => result.status === 'rejected',\n );\n for (const failure of initFailures) {\n logFn(`Wallet init step failed: ${String(failure.reason)}`);\n }\n if (initFailures.length > 0) {\n const firstReason = String(initFailures[0].reason);\n throw new Error(\n `Wallet initialization failed (${initFailures.length} step(s)); refusing to serve a partially initialized wallet. First failure: ${firstReason}`,\n );\n }\n\n if (wasFirstRun) {\n // The precondition check above throws when `wasFirstRun && password ===\n // undefined`, so `password` is defined here. TS does not correlate the\n // two separate variables, so it cannot narrow `password` from\n // `wasFirstRun` alone — hence the assertion.\n await importSecretRecoveryPhrase(\n wallet,\n (password as Password).unwrap(),\n srp.unwrap(),\n );\n } else if (password !== undefined) {\n try {\n await wallet.messenger.call(\n 'KeyringController:submitPassword',\n password.unwrap(),\n );\n } catch (error) {\n throw new Error(\n `Failed to unlock the persisted vault: ${String(error)}`,\n );\n }\n }\n\n let disposePromise: Promise<void> | undefined;\n return {\n wallet,\n dispose: async () =>\n (disposePromise ??= teardown(\n persistenceUnsubscribe,\n autoApprovalUnsubscribe,\n wallet,\n store,\n logFn,\n )),\n };\n } catch (error) {\n await teardown(\n persistenceUnsubscribe,\n autoApprovalUnsubscribe,\n wallet,\n store,\n logFn,\n );\n\n if (wasFirstRun && databasePath !== IN_MEMORY_DATABASE_PATH) {\n // Best-effort cleanup of the on-disk SQLite files (main, WAL, SHM) so a\n // partially-persisted KeyringController vault cannot mislead the next run\n // into skipping SRP import. Covers in-process failures only — a crash\n // (SIGKILL/power loss) mid-import leaves the vault on disk.\n await Promise.all(\n [databasePath, `${databasePath}-wal`, `${databasePath}-shm`].map(\n (path) =>\n rm(path, { force: true }).catch((rmError: unknown) => {\n logFn(\n `Failed to remove ${path} during first-run cleanup: ${String(rmError)}`,\n );\n }),\n ),\n );\n }\n\n throw error;\n }\n}\n\n/**\n * Load persisted controller state, filtered to currently persist-flagged\n * properties.\n *\n * `loadState` filters against the live controller metadata, but that metadata\n * is only knowable from a constructed `Wallet` — and its output is what seeds\n * the real wallet. So this constructs a short-lived probe purely to read the\n * metadata, then tears it down.\n *\n * TODO: drop the probe once `@metamask/wallet` exposes controller metadata\n * without constructing a `Wallet`.\n *\n * @param store - The key-value store to read from.\n * @param infuraProjectId - The Infura project ID for the probe's\n * `NetworkController`.\n * @param logFn - Logger for a probe-teardown failure.\n * @returns The filtered persisted state, suitable for the `Wallet` `state`\n * option.\n */\nasync function loadPersistedState(\n store: KeyValueStore,\n infuraProjectId: string,\n logFn: Logger,\n): Promise<Record<string, Record<string, Json>>> {\n const probe = new Wallet({\n instanceOptions: buildInstanceOptions(infuraProjectId),\n });\n registerAnalyticsStubHandlers(probe.messenger);\n try {\n return loadState(store, probe.controllerMetadata);\n } finally {\n await probe.destroy().catch((error: unknown) => {\n logFn(`Metadata probe destroy failed: ${String(error)}`);\n });\n }\n}\n\n/**\n * Best-effort unsubscribe: run the function if present, logging (and\n * swallowing) any error so a later teardown step still runs.\n *\n * @param unsubscribe - The unsubscribe function, if one was registered.\n * @param label - Human-readable subscription name for the failure log.\n * @param logFn - Logger for a failure.\n */\nfunction runUnsubscribe(\n unsubscribe: (() => void) | undefined,\n label: string,\n logFn: Logger,\n): void {\n if (!unsubscribe) {\n return;\n }\n try {\n unsubscribe();\n } catch (error) {\n logFn(\n `${label} unsubscribe failed during teardown — subscription may remain ` +\n `live until the process exits: ${String(error)}`,\n );\n }\n}\n\n/**\n * Persistence-safe teardown of a wallet and its store; see {@link\n * CreateWalletResult.dispose} for the ordering rationale. Each step is\n * best-effort, so a failure is logged and the remaining steps still run.\n *\n * @param persistenceUnsubscribe - The persistence-subscription unsubscribe\n * function, if one was registered.\n * @param autoApprovalUnsubscribe - The auto-approval-subscription unsubscribe\n * function, if one was registered.\n * @param wallet - The wallet to destroy, if one was constructed.\n * @param store - The store to close.\n * @param logFn - Logger for step failures.\n */\nasync function teardown(\n persistenceUnsubscribe: (() => void) | undefined,\n autoApprovalUnsubscribe: (() => void) | undefined,\n wallet: Wallet | undefined,\n store: KeyValueStore,\n logFn: Logger,\n): Promise<void> {\n runUnsubscribe(persistenceUnsubscribe, 'Persistence', logFn);\n runUnsubscribe(autoApprovalUnsubscribe, 'Auto-approval', logFn);\n if (wallet) {\n try {\n await wallet.destroy();\n } catch (error) {\n logFn(`wallet.destroy() failed during teardown: ${String(error)}`);\n }\n }\n try {\n store.close();\n } catch (error) {\n logFn(`store.close() failed during teardown: ${String(error)}`);\n }\n}\n\n/**\n * Determine whether the loaded state already contains a keyring vault.\n *\n * The KeyringController persists its `vault` once an SRP has been imported, so\n * its presence indicates that first-run setup completed before.\n *\n * @param state - The state loaded from the key-value store.\n * @returns True if a KeyringController vault string is present.\n */\nfunction hasPersistedKeyring(\n state: Record<string, Record<string, unknown>>,\n): boolean {\n return typeof state.KeyringController?.vault === 'string';\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"KeyValueStore.d.ts","sourceRoot":"","sources":["../../src/persistence/KeyValueStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAG5C;;;;;;GAMG;AACH,qBAAa,aAAa;;IAWxB,YAAY,YAAY,EAAE,MAAM,EAa/B;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAUjC;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI,CAElC;IAED,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAW7B;IAED,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAExB;IAED,KAAK,IAAI,IAAI,CAEZ;CACF"}
@@ -1,59 +0,0 @@
1
- import Sqlite from 'better-sqlite3';
2
- /**
3
- * A synchronous key-value store backed by better-sqlite3.
4
- *
5
- * Uses a single `kv` table with TEXT key (primary key) and TEXT value
6
- * (JSON-serialized). Intended as the persistence backend for wallet
7
- * controller state.
8
- */
9
- export class KeyValueStore {
10
- #db;
11
- #getStmt;
12
- #setStmt;
13
- #deleteStmt;
14
- #getAllStmt;
15
- constructor(databasePath) {
16
- this.#db = new Sqlite(databasePath);
17
- this.#db.pragma('journal_mode = WAL');
18
- this.#db.exec('CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)');
19
- this.#getStmt = this.#db.prepare('SELECT value FROM kv WHERE key = ?');
20
- this.#setStmt = this.#db.prepare('INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)');
21
- this.#deleteStmt = this.#db.prepare('DELETE FROM kv WHERE key = ?');
22
- this.#getAllStmt = this.#db.prepare('SELECT key, value FROM kv');
23
- }
24
- get(key) {
25
- const row = this.#getStmt.get(key);
26
- if (!row) {
27
- return undefined;
28
- }
29
- try {
30
- return JSON.parse(row.value);
31
- }
32
- catch {
33
- throw new Error(`Failed to parse stored value for key '${key}'`);
34
- }
35
- }
36
- set(key, value) {
37
- this.#setStmt.run(key, JSON.stringify(value));
38
- }
39
- getAll() {
40
- const rows = this.#getAllStmt.all();
41
- const result = {};
42
- for (const row of rows) {
43
- try {
44
- result[row.key] = JSON.parse(row.value);
45
- }
46
- catch {
47
- throw new Error(`Failed to parse stored value for key '${row.key}'`);
48
- }
49
- }
50
- return result;
51
- }
52
- delete(key) {
53
- this.#deleteStmt.run(key);
54
- }
55
- close() {
56
- this.#db.close();
57
- }
58
- }
59
- //# sourceMappingURL=KeyValueStore.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"KeyValueStore.js","sourceRoot":"","sources":["../../src/persistence/KeyValueStore.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,gBAAgB,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IACf,GAAG,CAAkB;IAErB,QAAQ,CAA4D;IAEpE,QAAQ,CAA2C;IAEnD,WAAW,CAAmC;IAE9C,WAAW,CAAuD;IAE3E,YAAY,YAAoB;QAC9B,IAAI,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG,CAAC,IAAI,CACX,2EAA2E,CAC5E,CAAC;QAEF,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC;QACvE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAC9B,sDAAsD,CACvD,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;QACpE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACnE,CAAC;IAED,GAAG,CAAC,GAAW;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,yCAAyC,GAAG,GAAG,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAW;QAC1B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,MAAM,GAAyB,EAAE,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1C,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,yCAAyC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;CACF","sourcesContent":["import type { Json } from '@metamask/utils';\nimport Sqlite from 'better-sqlite3';\n\n/**\n * A synchronous key-value store backed by better-sqlite3.\n *\n * Uses a single `kv` table with TEXT key (primary key) and TEXT value\n * (JSON-serialized). Intended as the persistence backend for wallet\n * controller state.\n */\nexport class KeyValueStore {\n readonly #db: Sqlite.Database;\n\n readonly #getStmt: Sqlite.Statement<[string], { value: string } | undefined>;\n\n readonly #setStmt: Sqlite.Statement<[string, string], void>;\n\n readonly #deleteStmt: Sqlite.Statement<[string], void>;\n\n readonly #getAllStmt: Sqlite.Statement<[], { key: string; value: string }>;\n\n constructor(databasePath: string) {\n this.#db = new Sqlite(databasePath);\n this.#db.pragma('journal_mode = WAL');\n this.#db.exec(\n 'CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)',\n );\n\n this.#getStmt = this.#db.prepare('SELECT value FROM kv WHERE key = ?');\n this.#setStmt = this.#db.prepare(\n 'INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)',\n );\n this.#deleteStmt = this.#db.prepare('DELETE FROM kv WHERE key = ?');\n this.#getAllStmt = this.#db.prepare('SELECT key, value FROM kv');\n }\n\n get(key: string): Json | undefined {\n const row = this.#getStmt.get(key);\n if (!row) {\n return undefined;\n }\n try {\n return JSON.parse(row.value);\n } catch {\n throw new Error(`Failed to parse stored value for key '${key}'`);\n }\n }\n\n set(key: string, value: Json): void {\n this.#setStmt.run(key, JSON.stringify(value));\n }\n\n getAll(): Record<string, Json> {\n const rows = this.#getAllStmt.all();\n const result: Record<string, Json> = {};\n for (const row of rows) {\n try {\n result[row.key] = JSON.parse(row.value);\n } catch {\n throw new Error(`Failed to parse stored value for key '${row.key}'`);\n }\n }\n return result;\n }\n\n delete(key: string): void {\n this.#deleteStmt.run(key);\n }\n\n close(): void {\n this.#db.close();\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../src/persistence/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACd,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAsBxD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,aAAa,EACpB,kBAAkB,EAAE,QAAQ,CAC1B,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,CAClD,GACA,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CA8BtC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,EACjE,kBAAkB,EAAE,QAAQ,CAC1B,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,CAClD,EACD,KAAK,EAAE,aAAa,EACpB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC9B,MAAM,IAAI,CAwDZ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/persistence/persistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StateMetadataConstraint } from '@metamask/base-controller';\nimport { hasProperty } from '@metamask/utils';\nimport type { Json } from '@metamask/utils';\nimport type {\n DefaultActions,\n DefaultEvents,\n RootMessenger,\n} from '@metamask/wallet';\nimport type { Patch } from 'immer';\n\nimport type { KeyValueStore } from './KeyValueStore.js';\n\n/**\n * Handler for a controller's `stateChanged` event: the new controller state and\n * the Immer patches describing what changed.\n */\ntype StateChangedHandler = (\n state: Record<string, Json>,\n patches: Patch[],\n) => void;\n\n/**\n * Construct a store key from a controller name and property name.\n *\n * @param controllerName - The controller name.\n * @param propertyName - The property name.\n * @returns The store key in the format `ControllerName.propertyName`.\n */\nfunction storeKey(controllerName: string, propertyName: string): string {\n return `${controllerName}.${propertyName}`;\n}\n\n/**\n * Load persisted state from the key-value store and reconstruct it as\n * a record keyed by controller name.\n *\n * Keys in the store follow the format `ControllerName.propertyName`.\n * This function groups them into `{ [controllerName]: { [propertyName]: value } }`.\n *\n * Only properties that are currently persist-flagged in `controllerMetadata`\n * are rehydrated. Rows for controllers or properties that no longer exist — or\n * whose `persist` flag has since been disabled — are ignored. This keeps\n * loading symmetric with {@link subscribeToChanges}, which only ever writes\n * persist-flagged properties: without the filter, a migration that stops\n * persisting a property would leave its stale row on disk to be resurrected\n * into the `Wallet` constructor state on the next restart.\n *\n * @param store - The key-value store to read from.\n * @param controllerMetadata - A map from controller name to its state metadata,\n * used to filter out keys that are no longer persist-flagged.\n * @returns A record of controller states, keyed by controller name, suitable\n * for the `state` option of the `Wallet` constructor.\n */\nexport function loadState(\n store: KeyValueStore,\n controllerMetadata: Readonly<\n Record<string, Readonly<StateMetadataConstraint>>\n >,\n): Record<string, Record<string, Json>> {\n const allPairs = store.getAll();\n const state: Record<string, Record<string, Json>> = {};\n\n for (const [key, value] of Object.entries(allPairs)) {\n const dotIndex = key.indexOf('.');\n if (dotIndex === -1) {\n throw new Error(\n `Invalid key in store: '${key}'. Expected format 'ControllerName.propertyName'.`,\n );\n }\n const controllerName = key.slice(0, dotIndex);\n const propertyName = key.slice(dotIndex + 1);\n\n if (!controllerName || !propertyName) {\n throw new Error(\n `Invalid key in store: '${key}'. Both controller name and property name must be non-empty.`,\n );\n }\n\n if (!isPersisted(controllerMetadata[controllerName], propertyName)) {\n continue;\n }\n\n if (!state[controllerName]) {\n state[controllerName] = {};\n }\n state[controllerName][propertyName] = value;\n }\n return state;\n}\n\n/**\n * Subscribe to all controller `stateChanged` events and persist changes\n * to the key-value store.\n *\n * For each controller's metadata, this function determines which state\n * properties are persist-flagged. When a `stateChanged` event fires, it uses\n * the Immer patches to identify which top-level properties changed, filters\n * to only persist-flagged properties, and writes them to the store.\n *\n * @param messenger - The root messenger to subscribe on.\n * @param controllerMetadata - A map from controller name to its state metadata.\n * @param store - The key-value store to write to.\n * @param log - Optional logger for persistence-write failures. Defaults to\n * `console.error` when omitted. A daemon host should supply its own logger,\n * since a backgrounded daemon's stdio may be discarded.\n * @returns A function that unsubscribes all persistence handlers.\n */\nexport function subscribeToChanges(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n controllerMetadata: Readonly<\n Record<string, Readonly<StateMetadataConstraint>>\n >,\n store: KeyValueStore,\n log?: (message: string) => void,\n): () => void {\n const unsubscribers: (() => void)[] = [];\n const logFn =\n log ??\n ((message: string): void => {\n console.error(message);\n });\n\n for (const [controllerName, metadata] of Object.entries(controllerMetadata)) {\n const persistedProperties = getPersistPropertyNames(metadata);\n if (persistedProperties.size === 0) {\n continue;\n }\n\n const eventType = `${controllerName}:stateChanged`;\n\n const handler: StateChangedHandler = (state, patches) => {\n const changed = getChangedProperties(patches, persistedProperties);\n\n for (const prop of changed) {\n const key = storeKey(controllerName, prop);\n const removed = !hasProperty(state, prop);\n\n // Derive the value before the try/catch so a throwing `StateDeriver`\n // surfaces as its own error instead of a misreported write failure.\n const persistFlag = metadata[prop]?.persist;\n const value =\n !removed && typeof persistFlag === 'function'\n ? persistFlag(state[prop] as never)\n : state[prop];\n\n try {\n if (removed) {\n store.delete(key);\n } else {\n store.set(key, value);\n }\n } catch (error) {\n // TODO: Surface persistence-write failures up the stack so callers\n // can decide to halt rather than continue with diverging in-memory\n // and on-disk state. For now, log and continue.\n logFn(`Failed to persist state for ${key}: ${String(error)}`);\n }\n }\n };\n\n unsubscribers.push(subscribeToStateChanged(messenger, eventType, handler));\n }\n\n const unsubscribeAll = (): void => {\n while (unsubscribers.length > 0) {\n unsubscribers.pop()?.();\n }\n };\n\n return unsubscribeAll;\n}\n\n/**\n * Subscribe a handler to a controller's `stateChanged` event.\n *\n * The event name is built from a runtime controller name, so it widens to\n * `string` and cannot be proven to be a literal member of the messenger's event\n * union at compile time. This helper localizes that single unavoidable cast\n * behind a typed {@link StateChangedHandler}, so the `(state, patches)` payload\n * shape stays compile-checked at every call site instead of being erased by a\n * statement-level `@ts-expect-error`.\n *\n * @param messenger - The root messenger to subscribe on.\n * @param eventType - The `${controllerName}:stateChanged` event name.\n * @param handler - The state-change handler to register.\n * @returns A function that unsubscribes the handler.\n */\nfunction subscribeToStateChanged(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n eventType: string,\n handler: StateChangedHandler,\n): () => void {\n const subscriber = messenger as unknown as {\n subscribe: (eventType: string, handler: StateChangedHandler) => void;\n unsubscribe: (eventType: string, handler: StateChangedHandler) => void;\n };\n subscriber.subscribe(eventType, handler);\n return () => {\n subscriber.unsubscribe(eventType, handler);\n };\n}\n\n/**\n * Determine whether a property is currently persist-flagged.\n *\n * The `persist` flag is truthy when it is `true` or a `StateDeriver` function,\n * and falsy when it is `false` or when the controller or property is absent\n * from the metadata. `loadState` and `subscribeToChanges` share this predicate\n * so the read and write paths can never disagree on what counts as persisted.\n *\n * @param metadata - The controller's state metadata, or `undefined` when the\n * controller is absent from the metadata map.\n * @param propertyName - The property name to check.\n * @returns `true` if the property should be persisted.\n */\nfunction isPersisted(\n metadata: Readonly<StateMetadataConstraint> | undefined,\n propertyName: string,\n): boolean {\n return Boolean(metadata?.[propertyName]?.persist);\n}\n\n/**\n * Get the set of property names whose `persist` metadata is truthy\n * (either `true` or a `StateDeriver` function).\n *\n * @param metadata - The controller's state metadata.\n * @returns A set of property names that should be persisted.\n */\nfunction getPersistPropertyNames(\n metadata: StateMetadataConstraint,\n): ReadonlySet<string> {\n const names = new Set<string>();\n for (const key of Object.keys(metadata)) {\n if (isPersisted(metadata, key)) {\n names.add(key);\n }\n }\n return names;\n}\n\n/**\n * Extracts the set of persist-flagged top-level property names that changed\n * from an array of Immer patches.\n *\n * If any patch has an empty path (indicating a root state replacement),\n * all persist-flagged properties are returned.\n *\n * @param patches - Immer patches from a state update.\n * @param persistedProperties - The set of persist-flagged property names.\n * @returns A set of top-level property names that were modified.\n */\nfunction getChangedProperties(\n patches: Patch[],\n persistedProperties: ReadonlySet<string>,\n): ReadonlySet<string> {\n const changed = new Set<string>();\n for (const patch of patches) {\n if (patch.path.length === 0) {\n return persistedProperties;\n }\n\n const prop = String(patch.path[0]);\n if (persistedProperties.has(prop)) {\n changed.add(prop);\n }\n }\n return changed;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"run-command.d.ts","sourceRoot":"","sources":["../../src/test/run-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,KAAK,WAAW,GAAG,KACjB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,MAAM,KACX,OAAO,GAAG;IACb,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9B,CAAC;AAKF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,YAAY,EAAE,WAAW,EACzB,IAAI,GAAE,MAAM,EAAO,GAClB,OAAO,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC7B,CAAC,CAkDD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"run-command.js","sourceRoot":"","sources":["../../src/test/run-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAS9C,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAC9C,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,YAAyB,EACzB,IAAI,GAAa,EAAE;IAMnB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,KAA2B,CAAC;IAEhC,MAAM,UAAU,GAAG;QACjB,OAAO,EAAE,aAAa;QACtB,IAAI,EAAE,iBAAiB;QACvB,GAAG,EAAE,IAAI;QACT,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,YAAY;QACrB,KAAK,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,YAAY,EAAE;QAC9D,WAAW,EAAE,GAAG,EAAE,CAAC,SAAS;QAC5B,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QACtD,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;QAC7B,eAAe,EAAE,GAAG,EAAE,CAAC,EAAE;QACzB,gBAAgB,EAAE,GAAG,EAAE,CAAC,EAAE;QAC1B,gBAAgB,EAAE,GAAG,EAAE,CAAC,KAAK;QAC7B,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,gBAAgB,EAAE,KAAK;KACH,CAAC;IAEvB,MAAM,MAAM,GAAG,IAAI;SAChB,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC;SAC/B,kBAAkB,CAAC,CAAC,OAAO,GAAY,EAAE,EAAE,EAAE;QAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;IACnC,CAAC,CAAC,CAAC;IACL,MAAM,QAAQ,GAAG,IAAI;SAClB,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC;SACjC,kBAAkB,CAAC,CAAC,KAAqB,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QAClE,MAAM,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEL,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACpD,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,MAAe,EAAE,CAAC;QACzB,IAAI,MAAM,YAAY,QAAQ,EAAE,CAAC;YAC/B,KAAK,GAAG,MAAM,CAAC;YACf,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,IAAI,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,CAAC;QACf,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,WAAW,EAAE,CAAC;QACrB,QAAQ,CAAC,WAAW,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACnC,CAAC","sourcesContent":["import { Command } from '@oclif/core';\nimport type { Config } from '@oclif/core';\nimport { CLIError } from '@oclif/core/errors';\n\ntype CommandCtor = new (\n argv: string[],\n config: Config,\n) => Command & {\n _run: () => Promise<unknown>;\n};\n\nconst TEST_DATA_DIR = '/tmp/mm-cli-test-data';\nconst TEST_PACKAGE_ROOT = '/tmp/mm-cli-test-root';\n\n/**\n * Invoke an oclif command class with the given argv and return the captured\n * stdout/stderr/error so tests can assert on them.\n *\n * Bypasses `Command.run`'s static plugin-loading path (which requires a real\n * `Config.load`) by constructing the command instance directly with a\n * hand-rolled `Config` and invoking the protected `_run`. Spies on the\n * Command prototype so `this.log` and `this.error` go to local buffers\n * instead of stdout/stderr.\n *\n * @param CommandClass - The command class (a subclass of `@oclif/core` Command).\n * @param argv - Command-line tokens (flags + positional args).\n * @returns Captured stdout, stderr, and any `this.error()` payload.\n */\nexport async function runCommand(\n CommandClass: CommandCtor,\n argv: string[] = [],\n): Promise<{\n stdout: string;\n stderr: string;\n error: CLIError | undefined;\n}> {\n let stdout = '';\n let stderr = '';\n let error: CLIError | undefined;\n\n const fakeConfig = {\n dataDir: TEST_DATA_DIR,\n root: TEST_PACKAGE_ROOT,\n bin: 'mm',\n name: '@metamask/wallet-cli',\n version: '0.0.0-test',\n pjson: { name: '@metamask/wallet-cli', version: '0.0.0-test' },\n findCommand: () => undefined,\n runHook: async () => ({ successes: [], failures: [] }),\n scopedEnvVar: () => undefined,\n scopedEnvVarKey: () => '',\n scopedEnvVarKeys: () => [],\n scopedEnvVarTrue: () => false,\n plugins: new Map(),\n flexibleTaxonomy: false,\n } as unknown as Config;\n\n const logSpy = jest\n .spyOn(Command.prototype, 'log')\n .mockImplementation((message: unknown = '') => {\n stdout += `${String(message)}\\n`;\n });\n const errorSpy = jest\n .spyOn(Command.prototype, 'error')\n .mockImplementation((input: string | Error) => {\n const message = typeof input === 'string' ? input : input.message;\n throw new CLIError(message);\n });\n\n try {\n const instance = new CommandClass(argv, fakeConfig);\n await instance._run();\n } catch (caught: unknown) {\n if (caught instanceof CLIError) {\n error = caught;\n stderr += `${caught.message}\\n`;\n } else {\n throw caught;\n }\n } finally {\n logSpy.mockRestore();\n errorSpy.mockRestore();\n }\n\n return { stdout, stderr, error };\n}\n"]}