@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-approval.cjs","sourceRoot":"","sources":["../../src/daemon/auto-approval.ts"],"names":[],"mappings":";;;AA2BA;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,uBAAuB,CACrC,SAAiE,EACjE,GAAY;IAEZ,MAAM,KAAK,GACT,GAAG;QACH,CAAC,CAAC,OAAe,EAAQ,EAAE;YACzB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IAEL,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,MAAM,UAAU,GAAG,CAAC,EAAU,EAAE,KAAc,EAAQ,EAAE;QACtD,KAAK,CAAC,0CAA0C,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,EAAU,EAAQ,EAAE;QACzC,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEjB,IAAI,CAAC;YACH,SAAS;iBACN,IAAI,CAAC,kCAAkC,EAAE,EAAE,CAAC;iBAC5C,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;iBAChD,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,kEAAkE;YAClE,uEAAuE;YACvE,uEAAuE;YACvE,8DAA8D;YAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACpB,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,OAAO,GAA+B,CAAC,KAAK,EAAE,EAAE;QACpD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACrD,aAAa,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,+BAA+B,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AA5CD,0DA4CC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,+BAA+B,CACtC,SAAiE,EACjE,OAAmC;IAEnC,MAAM,UAAU,GAAG,SAMlB,CAAC;IACF,UAAU,CAAC,SAAS,CAAC,iCAAiC,EAAE,OAAO,CAAC,CAAC;IACjE,OAAO,GAAG,EAAE;QACV,UAAU,CAAC,WAAW,CAAC,iCAAiC,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type {\n DefaultActions,\n DefaultEvents,\n RootMessenger,\n} from '@metamask/wallet';\n\nimport type { Logger } from './types.js';\n\n/**\n * The slice of `ApprovalController` state this module reads. The full\n * state-change payload is the controller's `ApprovalControllerState`; auto\n * approval only needs the ids of the pending requests, so it looks at\n * `pendingApprovals` alone (the request bodies are irrelevant to accepting\n * them).\n */\ntype PendingApprovalsState = {\n pendingApprovals: Record<string, unknown>;\n};\n\n/**\n * Handler for `ApprovalController`'s state-change event, narrowed to the slice\n * auto approval reads. The `Patch[]` second argument from the raw event is\n * intentionally absent — auto approval re-accepts on every state change and\n * does not filter by patch.\n */\ntype ApprovalStateChangeHandler = (state: PendingApprovalsState) => void;\n\n/**\n * Subscribe the daemon to auto-accept every pending approval request.\n *\n * Without this, any `ApprovalController:addRequest` call hangs forever on a\n * headless daemon. **Everything is approved without a prompt** — trust boundary\n * is the `0600` same-user socket. A scoped policy is tracked in\n * {@link https://github.com/MetaMask/core/issues/9513}.\n *\n * The `inFlight` guard makes accepting each id idempotent: `acceptRequest`\n * deletes the request on resolve, which itself re-emits `stateChanged`. Without\n * it the same id would be accepted twice and the second accept would reject.\n * Both sync throws and async rejections are logged and swallowed so one bad\n * request cannot crash the daemon or wedge the subscription.\n *\n * @param messenger - The wallet root messenger.\n * @param log - Optional logger for accept failures. Defaults to `console.error`.\n * @returns A function that unsubscribes the auto-approval handler.\n */\nexport function subscribeToAutoApproval(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n log?: Logger,\n): () => void {\n const logFn =\n log ??\n ((message: string): void => {\n console.error(message);\n });\n\n const inFlight = new Set<string>();\n\n const logFailure = (id: string, error: unknown): void => {\n logFn(`Failed to auto-accept approval request ${id}: ${String(error)}`);\n };\n\n const acceptRequest = (id: string): void => {\n if (inFlight.has(id)) {\n return;\n }\n inFlight.add(id);\n\n try {\n messenger\n .call('ApprovalController:acceptRequest', id)\n .catch((error: unknown) => logFailure(id, error))\n .finally(() => inFlight.delete(id));\n } catch (error) {\n // Synchronous throw means the Promise chain above was never built\n // (no .catch/.finally attached), so clean up the in-flight guard here.\n // The error is intentionally suppressed — a race-condition reject must\n // not crash the daemon or permanently wedge the subscription.\n inFlight.delete(id);\n logFailure(id, error);\n }\n };\n\n const handler: ApprovalStateChangeHandler = (state) => {\n for (const id of Object.keys(state.pendingApprovals)) {\n acceptRequest(id);\n }\n };\n\n return subscribeToApprovalStateChanged(messenger, handler);\n}\n\n/**\n * Subscribe a handler to `ApprovalController:stateChanged`.\n *\n * `ApprovalControllerEvents` only declares `ApprovalController:stateChange`\n * (via `ControllerStateChangeEvent`), not the non-deprecated\n * `ApprovalController:stateChanged` variant that `BaseController` publishes at\n * runtime. This helper localizes the unavoidable cast — using the same\n * technique as `subscribeToStateChanged` in the persistence layer — behind a\n * typed {@link ApprovalStateChangeHandler}, keeping the `state` payload\n * compile-checked at the call site instead of erased by a statement-level\n * `@ts-expect-error`.\n *\n * TODO: Remove this cast once `ApprovalControllerEvents` includes\n * `ControllerStateChangedEvent` (`:stateChanged`), matching the union already\n * exported by `BaseController`.\n *\n * @param messenger - The wallet root messenger.\n * @param handler - The state-change handler to register.\n * @returns A function that unsubscribes the handler.\n */\nfunction subscribeToApprovalStateChanged(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n handler: ApprovalStateChangeHandler,\n): () => void {\n const subscriber = messenger as unknown as {\n subscribe: (eventType: string, handler: ApprovalStateChangeHandler) => void;\n unsubscribe: (\n eventType: string,\n handler: ApprovalStateChangeHandler,\n ) => void;\n };\n subscriber.subscribe('ApprovalController:stateChanged', handler);\n return () => {\n subscriber.unsubscribe('ApprovalController:stateChanged', handler);\n };\n}\n"]}
@@ -1,5 +1,5 @@
1
- import type { DefaultActions, DefaultEvents, RootMessenger } from '@metamask/wallet';
2
- import type { Logger } from './types.js';
1
+ import type { DefaultActions, DefaultEvents, RootMessenger } from "@metamask/wallet";
2
+ import type { Logger } from "./types.cjs";
3
3
  /**
4
4
  * Subscribe the daemon to auto-accept every pending approval request.
5
5
  *
@@ -19,4 +19,4 @@ import type { Logger } from './types.js';
19
19
  * @returns A function that unsubscribes the auto-approval handler.
20
20
  */
21
21
  export declare function subscribeToAutoApproval(messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>, log?: Logger): () => void;
22
- //# sourceMappingURL=auto-approval.d.ts.map
22
+ //# sourceMappingURL=auto-approval.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-approval.d.cts","sourceRoot":"","sources":["../../src/daemon/auto-approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACd,yBAAyB;AAE1B,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAmB;AAqBzC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,EACjE,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,IAAI,CAyCZ"}
@@ -0,0 +1,22 @@
1
+ import type { DefaultActions, DefaultEvents, RootMessenger } from "@metamask/wallet";
2
+ import type { Logger } from "./types.mjs";
3
+ /**
4
+ * Subscribe the daemon to auto-accept every pending approval request.
5
+ *
6
+ * Without this, any `ApprovalController:addRequest` call hangs forever on a
7
+ * headless daemon. **Everything is approved without a prompt** — trust boundary
8
+ * is the `0600` same-user socket. A scoped policy is tracked in
9
+ * {@link https://github.com/MetaMask/core/issues/9513}.
10
+ *
11
+ * The `inFlight` guard makes accepting each id idempotent: `acceptRequest`
12
+ * deletes the request on resolve, which itself re-emits `stateChanged`. Without
13
+ * it the same id would be accepted twice and the second accept would reject.
14
+ * Both sync throws and async rejections are logged and swallowed so one bad
15
+ * request cannot crash the daemon or wedge the subscription.
16
+ *
17
+ * @param messenger - The wallet root messenger.
18
+ * @param log - Optional logger for accept failures. Defaults to `console.error`.
19
+ * @returns A function that unsubscribes the auto-approval handler.
20
+ */
21
+ export declare function subscribeToAutoApproval(messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>, log?: Logger): () => void;
22
+ //# sourceMappingURL=auto-approval.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-approval.d.mts","sourceRoot":"","sources":["../../src/daemon/auto-approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACd,yBAAyB;AAE1B,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAmB;AAqBzC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,EACjE,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,IAAI,CAyCZ"}
@@ -79,4 +79,4 @@ function subscribeToApprovalStateChanged(messenger, handler) {
79
79
  subscriber.unsubscribe('ApprovalController:stateChanged', handler);
80
80
  };
81
81
  }
82
- //# sourceMappingURL=auto-approval.js.map
82
+ //# sourceMappingURL=auto-approval.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-approval.mjs","sourceRoot":"","sources":["../../src/daemon/auto-approval.ts"],"names":[],"mappings":"AA2BA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,uBAAuB,CACrC,SAAiE,EACjE,GAAY;IAEZ,MAAM,KAAK,GACT,GAAG;QACH,CAAC,CAAC,OAAe,EAAQ,EAAE;YACzB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IAEL,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,MAAM,UAAU,GAAG,CAAC,EAAU,EAAE,KAAc,EAAQ,EAAE;QACtD,KAAK,CAAC,0CAA0C,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,EAAU,EAAQ,EAAE;QACzC,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEjB,IAAI,CAAC;YACH,SAAS;iBACN,IAAI,CAAC,kCAAkC,EAAE,EAAE,CAAC;iBAC5C,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;iBAChD,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,kEAAkE;YAClE,uEAAuE;YACvE,uEAAuE;YACvE,8DAA8D;YAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACpB,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,OAAO,GAA+B,CAAC,KAAK,EAAE,EAAE;QACpD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACrD,aAAa,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,+BAA+B,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,+BAA+B,CACtC,SAAiE,EACjE,OAAmC;IAEnC,MAAM,UAAU,GAAG,SAMlB,CAAC;IACF,UAAU,CAAC,SAAS,CAAC,iCAAiC,EAAE,OAAO,CAAC,CAAC;IACjE,OAAO,GAAG,EAAE;QACV,UAAU,CAAC,WAAW,CAAC,iCAAiC,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type {\n DefaultActions,\n DefaultEvents,\n RootMessenger,\n} from '@metamask/wallet';\n\nimport type { Logger } from './types.js';\n\n/**\n * The slice of `ApprovalController` state this module reads. The full\n * state-change payload is the controller's `ApprovalControllerState`; auto\n * approval only needs the ids of the pending requests, so it looks at\n * `pendingApprovals` alone (the request bodies are irrelevant to accepting\n * them).\n */\ntype PendingApprovalsState = {\n pendingApprovals: Record<string, unknown>;\n};\n\n/**\n * Handler for `ApprovalController`'s state-change event, narrowed to the slice\n * auto approval reads. The `Patch[]` second argument from the raw event is\n * intentionally absent — auto approval re-accepts on every state change and\n * does not filter by patch.\n */\ntype ApprovalStateChangeHandler = (state: PendingApprovalsState) => void;\n\n/**\n * Subscribe the daemon to auto-accept every pending approval request.\n *\n * Without this, any `ApprovalController:addRequest` call hangs forever on a\n * headless daemon. **Everything is approved without a prompt** — trust boundary\n * is the `0600` same-user socket. A scoped policy is tracked in\n * {@link https://github.com/MetaMask/core/issues/9513}.\n *\n * The `inFlight` guard makes accepting each id idempotent: `acceptRequest`\n * deletes the request on resolve, which itself re-emits `stateChanged`. Without\n * it the same id would be accepted twice and the second accept would reject.\n * Both sync throws and async rejections are logged and swallowed so one bad\n * request cannot crash the daemon or wedge the subscription.\n *\n * @param messenger - The wallet root messenger.\n * @param log - Optional logger for accept failures. Defaults to `console.error`.\n * @returns A function that unsubscribes the auto-approval handler.\n */\nexport function subscribeToAutoApproval(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n log?: Logger,\n): () => void {\n const logFn =\n log ??\n ((message: string): void => {\n console.error(message);\n });\n\n const inFlight = new Set<string>();\n\n const logFailure = (id: string, error: unknown): void => {\n logFn(`Failed to auto-accept approval request ${id}: ${String(error)}`);\n };\n\n const acceptRequest = (id: string): void => {\n if (inFlight.has(id)) {\n return;\n }\n inFlight.add(id);\n\n try {\n messenger\n .call('ApprovalController:acceptRequest', id)\n .catch((error: unknown) => logFailure(id, error))\n .finally(() => inFlight.delete(id));\n } catch (error) {\n // Synchronous throw means the Promise chain above was never built\n // (no .catch/.finally attached), so clean up the in-flight guard here.\n // The error is intentionally suppressed — a race-condition reject must\n // not crash the daemon or permanently wedge the subscription.\n inFlight.delete(id);\n logFailure(id, error);\n }\n };\n\n const handler: ApprovalStateChangeHandler = (state) => {\n for (const id of Object.keys(state.pendingApprovals)) {\n acceptRequest(id);\n }\n };\n\n return subscribeToApprovalStateChanged(messenger, handler);\n}\n\n/**\n * Subscribe a handler to `ApprovalController:stateChanged`.\n *\n * `ApprovalControllerEvents` only declares `ApprovalController:stateChange`\n * (via `ControllerStateChangeEvent`), not the non-deprecated\n * `ApprovalController:stateChanged` variant that `BaseController` publishes at\n * runtime. This helper localizes the unavoidable cast — using the same\n * technique as `subscribeToStateChanged` in the persistence layer — behind a\n * typed {@link ApprovalStateChangeHandler}, keeping the `state` payload\n * compile-checked at the call site instead of erased by a statement-level\n * `@ts-expect-error`.\n *\n * TODO: Remove this cast once `ApprovalControllerEvents` includes\n * `ControllerStateChangedEvent` (`:stateChanged`), matching the union already\n * exported by `BaseController`.\n *\n * @param messenger - The wallet root messenger.\n * @param handler - The state-change handler to register.\n * @returns A function that unsubscribes the handler.\n */\nfunction subscribeToApprovalStateChanged(\n messenger: Readonly<RootMessenger<DefaultActions, DefaultEvents>>,\n handler: ApprovalStateChangeHandler,\n): () => void {\n const subscriber = messenger as unknown as {\n subscribe: (eventType: string, handler: ApprovalStateChangeHandler) => void;\n unsubscribe: (\n eventType: string,\n handler: ApprovalStateChangeHandler,\n ) => void;\n };\n subscriber.subscribe('ApprovalController:stateChanged', handler);\n return () => {\n subscriber.unsubscribe('ApprovalController:stateChanged', handler);\n };\n}\n"]}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pingDaemon = exports.sendCommand = void 0;
4
+ const utils_1 = require("@metamask/utils");
5
+ const node_crypto_1 = require("node:crypto");
6
+ const node_net_1 = require("node:net");
7
+ const socket_line_js_1 = require("./socket-line.cjs");
8
+ const utils_js_1 = require("./utils.cjs");
9
+ const DEFAULT_TIMEOUT_MS = 30000;
10
+ async function connectSocket(socketPath) {
11
+ return new Promise((resolve, reject) => {
12
+ const socket = (0, node_net_1.createConnection)(socketPath, () => {
13
+ socket.removeAllListeners('error');
14
+ resolve(socket);
15
+ });
16
+ socket.on('error', (error) => {
17
+ // A failed connect never reaches the caller, so destroy the socket here
18
+ // to avoid leaking its file descriptor.
19
+ socket.destroy();
20
+ reject(error);
21
+ });
22
+ });
23
+ }
24
+ /**
25
+ * Send a JSON-RPC request to the daemon over a Unix socket and return the
26
+ * response.
27
+ *
28
+ * Opens a connection, writes one JSON-RPC request line, reads one JSON-RPC
29
+ * response line, then closes the connection. Retries once after a short delay
30
+ * only on `ECONNREFUSED` — the connection was never established, so the daemon
31
+ * provably never received the request and re-sending is safe. Does not retry
32
+ * on `ECONNRESET`, which can drop after the daemon has already received and
33
+ * acted on the request: blindly re-sending could execute a non-idempotent
34
+ * action (e.g. a transaction broadcast) twice, so it is surfaced to the caller
35
+ * instead. Verifies that the response `id` matches the outgoing request `id`.
36
+ *
37
+ * @param options - Command options.
38
+ * @param options.socketPath - The Unix socket path.
39
+ * @param options.method - The RPC method name.
40
+ * @param options.params - Optional method parameters.
41
+ * @param options.timeoutMs - Read timeout in milliseconds.
42
+ * @returns The parsed JSON-RPC response.
43
+ */
44
+ async function sendCommand({ socketPath, method, params, timeoutMs, }) {
45
+ const id = (0, node_crypto_1.randomUUID)();
46
+ const request = {
47
+ jsonrpc: '2.0',
48
+ id,
49
+ method,
50
+ ...(params === undefined ? {} : { params }),
51
+ };
52
+ const effectiveTimeout = timeoutMs ?? DEFAULT_TIMEOUT_MS;
53
+ const attempt = async () => {
54
+ const socket = await connectSocket(socketPath);
55
+ try {
56
+ await (0, socket_line_js_1.writeLine)(socket, JSON.stringify(request));
57
+ const responseLine = await (0, socket_line_js_1.readLine)(socket, effectiveTimeout);
58
+ const parsed = JSON.parse(responseLine);
59
+ (0, utils_1.assertIsJsonRpcResponse)(parsed);
60
+ if (parsed.id !== id) {
61
+ throw new Error(`JSON-RPC response id ${JSON.stringify(parsed.id)} does not match request id ${JSON.stringify(id)}`);
62
+ }
63
+ return parsed;
64
+ }
65
+ finally {
66
+ socket.destroy();
67
+ }
68
+ };
69
+ try {
70
+ return await attempt();
71
+ }
72
+ catch (error) {
73
+ // Only retry on ECONNREFUSED: the connection was never established, so the
74
+ // daemon provably never received the request and re-sending is safe.
75
+ // ECONNRESET can drop *after* the daemon received and began (or finished)
76
+ // processing the request, so blindly re-sending a non-idempotent request
77
+ // (e.g. a transaction broadcast) could execute it twice. Surface it to the
78
+ // caller instead of retrying.
79
+ if (!(0, utils_js_1.isErrorWithCode)(error, 'ECONNREFUSED')) {
80
+ throw error;
81
+ }
82
+ await new Promise((resolve) => setTimeout(resolve, 100));
83
+ return attempt();
84
+ }
85
+ }
86
+ exports.sendCommand = sendCommand;
87
+ /**
88
+ * Normalize an unknown throw into a real Error instance so that downstream
89
+ * consumers (which read `.message`) cannot crash on string/object throws.
90
+ *
91
+ * @param error - The caught value.
92
+ * @returns An Error mirroring the caught value.
93
+ */
94
+ function toError(error) {
95
+ return error instanceof Error ? error : new Error(String(error));
96
+ }
97
+ /**
98
+ * Categorise an unreachable error by Node errno / message shape so callers can
99
+ * make decisions per failure mode rather than parsing message strings.
100
+ *
101
+ * @param error - The caught value.
102
+ * @returns A {@link PingUnreachableReason} label.
103
+ */
104
+ function classifyUnreachable(error) {
105
+ if ((0, utils_js_1.isErrorWithCode)(error, 'ECONNREFUSED') ||
106
+ (0, utils_js_1.isErrorWithCode)(error, 'ECONNRESET')) {
107
+ return 'refused';
108
+ }
109
+ if ((0, utils_js_1.isErrorWithCode)(error, 'EACCES') || (0, utils_js_1.isErrorWithCode)(error, 'EPERM')) {
110
+ return 'permission';
111
+ }
112
+ if (error instanceof Error && error.message === 'Socket read timed out') {
113
+ return 'timeout';
114
+ }
115
+ if (error instanceof Error &&
116
+ (error.message.includes('JSON-RPC response id') ||
117
+ /Expected .* JSON-RPC/u.test(error.message) ||
118
+ error.name === 'SyntaxError')) {
119
+ return 'protocol';
120
+ }
121
+ return 'other';
122
+ }
123
+ /**
124
+ * Check whether the daemon is running by sending a lightweight `getStatus`
125
+ * RPC call. Distinguishes "no daemon present" (socket file missing) from
126
+ * "daemon present but unreachable" (socket file exists but the daemon is
127
+ * wedged, mid-shutdown, or owned by a different user).
128
+ *
129
+ * @param socketPath - The Unix socket path.
130
+ * @returns A {@link PingResult} describing the daemon's reachability.
131
+ */
132
+ async function pingDaemon(socketPath) {
133
+ try {
134
+ await sendCommand({ socketPath, method: 'getStatus', timeoutMs: 3000 });
135
+ return { status: 'responsive' };
136
+ }
137
+ catch (error) {
138
+ if ((0, utils_js_1.isErrorWithCode)(error, 'ENOENT')) {
139
+ return { status: 'absent' };
140
+ }
141
+ return {
142
+ status: 'unreachable',
143
+ reason: classifyUnreachable(error),
144
+ error: toError(error),
145
+ };
146
+ }
147
+ }
148
+ exports.pingDaemon = pingDaemon;
149
+ //# sourceMappingURL=daemon-client.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-client.cjs","sourceRoot":"","sources":["../../src/daemon/daemon-client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcParams, JsonRpcResponse } from '@metamask/utils';\nimport { assertIsJsonRpcResponse } from '@metamask/utils';\nimport { randomUUID } from 'node:crypto';\nimport { createConnection } from 'node:net';\nimport type { Socket } from 'node:net';\n\nimport { readLine, writeLine } from './socket-line.js';\nimport { isErrorWithCode } from './utils.js';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Options for {@link sendCommand}.\n */\ntype SendCommandOptions = {\n /** The Unix socket path. */\n socketPath: string;\n /** The RPC method name. */\n method: string;\n /** Optional method parameters (object or positional array). */\n params?: JsonRpcParams | undefined;\n /** Response read timeout in milliseconds (default: 30 000). */\n timeoutMs?: number | undefined;\n};\n\nasync function connectSocket(socketPath: string): Promise<Socket> {\n return new Promise((resolve, reject) => {\n const socket = createConnection(socketPath, () => {\n socket.removeAllListeners('error');\n resolve(socket);\n });\n socket.on('error', (error) => {\n // A failed connect never reaches the caller, so destroy the socket here\n // to avoid leaking its file descriptor.\n socket.destroy();\n reject(error);\n });\n });\n}\n\n/**\n * Send a JSON-RPC request to the daemon over a Unix socket and return the\n * response.\n *\n * Opens a connection, writes one JSON-RPC request line, reads one JSON-RPC\n * response line, then closes the connection. Retries once after a short delay\n * only on `ECONNREFUSED` — the connection was never established, so the daemon\n * provably never received the request and re-sending is safe. Does not retry\n * on `ECONNRESET`, which can drop after the daemon has already received and\n * acted on the request: blindly re-sending could execute a non-idempotent\n * action (e.g. a transaction broadcast) twice, so it is surfaced to the caller\n * instead. Verifies that the response `id` matches the outgoing request `id`.\n *\n * @param options - Command options.\n * @param options.socketPath - The Unix socket path.\n * @param options.method - The RPC method name.\n * @param options.params - Optional method parameters.\n * @param options.timeoutMs - Read timeout in milliseconds.\n * @returns The parsed JSON-RPC response.\n */\nexport async function sendCommand({\n socketPath,\n method,\n params,\n timeoutMs,\n}: SendCommandOptions): Promise<JsonRpcResponse> {\n const id = randomUUID();\n const request = {\n jsonrpc: '2.0',\n id,\n method,\n ...(params === undefined ? {} : { params }),\n };\n\n const effectiveTimeout = timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const attempt = async (): Promise<JsonRpcResponse> => {\n const socket = await connectSocket(socketPath);\n try {\n await writeLine(socket, JSON.stringify(request));\n const responseLine = await readLine(socket, effectiveTimeout);\n const parsed: unknown = JSON.parse(responseLine);\n assertIsJsonRpcResponse(parsed);\n if (parsed.id !== id) {\n throw new Error(\n `JSON-RPC response id ${JSON.stringify(parsed.id)} does not match request id ${JSON.stringify(id)}`,\n );\n }\n return parsed;\n } finally {\n socket.destroy();\n }\n };\n\n try {\n return await attempt();\n } catch (error: unknown) {\n // Only retry on ECONNREFUSED: the connection was never established, so the\n // daemon provably never received the request and re-sending is safe.\n // ECONNRESET can drop *after* the daemon received and began (or finished)\n // processing the request, so blindly re-sending a non-idempotent request\n // (e.g. a transaction broadcast) could execute it twice. Surface it to the\n // caller instead of retrying.\n if (!isErrorWithCode(error, 'ECONNREFUSED')) {\n throw error;\n }\n await new Promise((resolve) => setTimeout(resolve, 100));\n return attempt();\n }\n}\n\n/**\n * Why an unreachable daemon cannot be queried.\n *\n * - `'refused'`: the connection could not be completed — `ECONNREFUSED`\n * (retried once) or `ECONNRESET` (a mid-request drop, not retried). Typical\n * of a daemon that has crashed or is mid-restart.\n * - `'timeout'`: the daemon accepted the connection but did not respond within\n * the read timeout — most likely wedged on a long-running operation.\n * - `'permission'`: the socket exists but cannot be opened (`EACCES` / `EPERM`).\n * The daemon almost certainly belongs to another user.\n * - `'protocol'`: the daemon responded but the response did not parse as a\n * valid JSON-RPC response, or the response id did not match.\n * - `'other'`: anything else.\n */\nexport type PingUnreachableReason =\n | 'refused'\n | 'timeout'\n | 'permission'\n | 'protocol'\n | 'other';\n\n/**\n * Outcome of a daemon health check.\n *\n * - `'responsive'`: the daemon answered a `getStatus` RPC.\n * - `'absent'`: the socket connect attempt failed with `ENOENT`, i.e. no\n * socket file exists at the path. No daemon present.\n * - `'unreachable'`: any other non-success outcome. The daemon may still be\n * alive but is not responding. Callers should not silently take over the\n * slot or assume the daemon is dead. The `reason` field categorises the\n * failure so callers can distinguish a wedged sibling daemon from a\n * foreign-user daemon from a transient crash. User-initiated stop / purge\n * flows may still escalate to signals against the recorded PID.\n */\nexport type PingResult =\n | { status: 'responsive' }\n | { status: 'absent' }\n | { status: 'unreachable'; reason: PingUnreachableReason; error: Error };\n\n/**\n * Normalize an unknown throw into a real Error instance so that downstream\n * consumers (which read `.message`) cannot crash on string/object throws.\n *\n * @param error - The caught value.\n * @returns An Error mirroring the caught value.\n */\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n/**\n * Categorise an unreachable error by Node errno / message shape so callers can\n * make decisions per failure mode rather than parsing message strings.\n *\n * @param error - The caught value.\n * @returns A {@link PingUnreachableReason} label.\n */\nfunction classifyUnreachable(error: unknown): PingUnreachableReason {\n if (\n isErrorWithCode(error, 'ECONNREFUSED') ||\n isErrorWithCode(error, 'ECONNRESET')\n ) {\n return 'refused';\n }\n if (isErrorWithCode(error, 'EACCES') || isErrorWithCode(error, 'EPERM')) {\n return 'permission';\n }\n if (error instanceof Error && error.message === 'Socket read timed out') {\n return 'timeout';\n }\n if (\n error instanceof Error &&\n (error.message.includes('JSON-RPC response id') ||\n /Expected .* JSON-RPC/u.test(error.message) ||\n error.name === 'SyntaxError')\n ) {\n return 'protocol';\n }\n return 'other';\n}\n\n/**\n * Check whether the daemon is running by sending a lightweight `getStatus`\n * RPC call. Distinguishes \"no daemon present\" (socket file missing) from\n * \"daemon present but unreachable\" (socket file exists but the daemon is\n * wedged, mid-shutdown, or owned by a different user).\n *\n * @param socketPath - The Unix socket path.\n * @returns A {@link PingResult} describing the daemon's reachability.\n */\nexport async function pingDaemon(socketPath: string): Promise<PingResult> {\n try {\n await sendCommand({ socketPath, method: 'getStatus', timeoutMs: 3_000 });\n return { status: 'responsive' };\n } catch (error: unknown) {\n if (isErrorWithCode(error, 'ENOENT')) {\n return { status: 'absent' };\n }\n return {\n status: 'unreachable',\n reason: classifyUnreachable(error),\n error: toError(error),\n };\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { JsonRpcParams, JsonRpcResponse } from '@metamask/utils';
1
+ import type { JsonRpcParams, JsonRpcResponse } from "@metamask/utils";
2
2
  /**
3
3
  * Options for {@link sendCommand}.
4
4
  */
@@ -81,4 +81,4 @@ export type PingResult = {
81
81
  */
82
82
  export declare function pingDaemon(socketPath: string): Promise<PingResult>;
83
83
  export {};
84
- //# sourceMappingURL=daemon-client.d.ts.map
84
+ //# sourceMappingURL=daemon-client.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-client.d.cts","sourceRoot":"","sources":["../../src/daemon/daemon-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,wBAAwB;AAWtE;;GAEG;AACH,KAAK,kBAAkB,GAAG;IACxB,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAiBF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAAC,EAChC,UAAU,EACV,MAAM,EACN,MAAM,EACN,SAAS,GACV,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC,CA4C/C;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,qBAAqB,GAC7B,SAAS,GACT,SAAS,GACT,YAAY,GACZ,UAAU,GACV,OAAO,CAAC;AAEZ;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AA4C3E;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAcxE"}
@@ -0,0 +1,84 @@
1
+ import type { JsonRpcParams, JsonRpcResponse } from "@metamask/utils";
2
+ /**
3
+ * Options for {@link sendCommand}.
4
+ */
5
+ type SendCommandOptions = {
6
+ /** The Unix socket path. */
7
+ socketPath: string;
8
+ /** The RPC method name. */
9
+ method: string;
10
+ /** Optional method parameters (object or positional array). */
11
+ params?: JsonRpcParams | undefined;
12
+ /** Response read timeout in milliseconds (default: 30 000). */
13
+ timeoutMs?: number | undefined;
14
+ };
15
+ /**
16
+ * Send a JSON-RPC request to the daemon over a Unix socket and return the
17
+ * response.
18
+ *
19
+ * Opens a connection, writes one JSON-RPC request line, reads one JSON-RPC
20
+ * response line, then closes the connection. Retries once after a short delay
21
+ * only on `ECONNREFUSED` — the connection was never established, so the daemon
22
+ * provably never received the request and re-sending is safe. Does not retry
23
+ * on `ECONNRESET`, which can drop after the daemon has already received and
24
+ * acted on the request: blindly re-sending could execute a non-idempotent
25
+ * action (e.g. a transaction broadcast) twice, so it is surfaced to the caller
26
+ * instead. Verifies that the response `id` matches the outgoing request `id`.
27
+ *
28
+ * @param options - Command options.
29
+ * @param options.socketPath - The Unix socket path.
30
+ * @param options.method - The RPC method name.
31
+ * @param options.params - Optional method parameters.
32
+ * @param options.timeoutMs - Read timeout in milliseconds.
33
+ * @returns The parsed JSON-RPC response.
34
+ */
35
+ export declare function sendCommand({ socketPath, method, params, timeoutMs, }: SendCommandOptions): Promise<JsonRpcResponse>;
36
+ /**
37
+ * Why an unreachable daemon cannot be queried.
38
+ *
39
+ * - `'refused'`: the connection could not be completed — `ECONNREFUSED`
40
+ * (retried once) or `ECONNRESET` (a mid-request drop, not retried). Typical
41
+ * of a daemon that has crashed or is mid-restart.
42
+ * - `'timeout'`: the daemon accepted the connection but did not respond within
43
+ * the read timeout — most likely wedged on a long-running operation.
44
+ * - `'permission'`: the socket exists but cannot be opened (`EACCES` / `EPERM`).
45
+ * The daemon almost certainly belongs to another user.
46
+ * - `'protocol'`: the daemon responded but the response did not parse as a
47
+ * valid JSON-RPC response, or the response id did not match.
48
+ * - `'other'`: anything else.
49
+ */
50
+ export type PingUnreachableReason = 'refused' | 'timeout' | 'permission' | 'protocol' | 'other';
51
+ /**
52
+ * Outcome of a daemon health check.
53
+ *
54
+ * - `'responsive'`: the daemon answered a `getStatus` RPC.
55
+ * - `'absent'`: the socket connect attempt failed with `ENOENT`, i.e. no
56
+ * socket file exists at the path. No daemon present.
57
+ * - `'unreachable'`: any other non-success outcome. The daemon may still be
58
+ * alive but is not responding. Callers should not silently take over the
59
+ * slot or assume the daemon is dead. The `reason` field categorises the
60
+ * failure so callers can distinguish a wedged sibling daemon from a
61
+ * foreign-user daemon from a transient crash. User-initiated stop / purge
62
+ * flows may still escalate to signals against the recorded PID.
63
+ */
64
+ export type PingResult = {
65
+ status: 'responsive';
66
+ } | {
67
+ status: 'absent';
68
+ } | {
69
+ status: 'unreachable';
70
+ reason: PingUnreachableReason;
71
+ error: Error;
72
+ };
73
+ /**
74
+ * Check whether the daemon is running by sending a lightweight `getStatus`
75
+ * RPC call. Distinguishes "no daemon present" (socket file missing) from
76
+ * "daemon present but unreachable" (socket file exists but the daemon is
77
+ * wedged, mid-shutdown, or owned by a different user).
78
+ *
79
+ * @param socketPath - The Unix socket path.
80
+ * @returns A {@link PingResult} describing the daemon's reachability.
81
+ */
82
+ export declare function pingDaemon(socketPath: string): Promise<PingResult>;
83
+ export {};
84
+ //# sourceMappingURL=daemon-client.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-client.d.mts","sourceRoot":"","sources":["../../src/daemon/daemon-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,wBAAwB;AAWtE;;GAEG;AACH,KAAK,kBAAkB,GAAG;IACxB,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAiBF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAAC,EAChC,UAAU,EACV,MAAM,EACN,MAAM,EACN,SAAS,GACV,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC,CA4C/C;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,qBAAqB,GAC7B,SAAS,GACT,SAAS,GACT,YAAY,GACZ,UAAU,GACV,OAAO,CAAC;AAEZ;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AA4C3E;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAcxE"}
@@ -1,9 +1,9 @@
1
- import { assertIsJsonRpcResponse } from '@metamask/utils';
2
- import { randomUUID } from 'node:crypto';
3
- import { createConnection } from 'node:net';
4
- import { readLine, writeLine } from './socket-line.js';
5
- import { isErrorWithCode } from './utils.js';
6
- const DEFAULT_TIMEOUT_MS = 30_000;
1
+ import { assertIsJsonRpcResponse } from "@metamask/utils";
2
+ import { randomUUID } from "node:crypto";
3
+ import { createConnection } from "node:net";
4
+ import { readLine, writeLine } from "./socket-line.mjs";
5
+ import { isErrorWithCode } from "./utils.mjs";
6
+ const DEFAULT_TIMEOUT_MS = 30000;
7
7
  async function connectSocket(socketPath) {
8
8
  return new Promise((resolve, reject) => {
9
9
  const socket = createConnection(socketPath, () => {
@@ -127,7 +127,7 @@ function classifyUnreachable(error) {
127
127
  */
128
128
  export async function pingDaemon(socketPath) {
129
129
  try {
130
- await sendCommand({ socketPath, method: 'getStatus', timeoutMs: 3_000 });
130
+ await sendCommand({ socketPath, method: 'getStatus', timeoutMs: 3000 });
131
131
  return { status: 'responsive' };
132
132
  }
133
133
  catch (error) {
@@ -141,4 +141,4 @@ export async function pingDaemon(socketPath) {
141
141
  };
142
142
  }
143
143
  }
144
- //# sourceMappingURL=daemon-client.js.map
144
+ //# sourceMappingURL=daemon-client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-client.mjs","sourceRoot":"","sources":["../../src/daemon/daemon-client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcParams, JsonRpcResponse } from '@metamask/utils';\nimport { assertIsJsonRpcResponse } from '@metamask/utils';\nimport { randomUUID } from 'node:crypto';\nimport { createConnection } from 'node:net';\nimport type { Socket } from 'node:net';\n\nimport { readLine, writeLine } from './socket-line.js';\nimport { isErrorWithCode } from './utils.js';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Options for {@link sendCommand}.\n */\ntype SendCommandOptions = {\n /** The Unix socket path. */\n socketPath: string;\n /** The RPC method name. */\n method: string;\n /** Optional method parameters (object or positional array). */\n params?: JsonRpcParams | undefined;\n /** Response read timeout in milliseconds (default: 30 000). */\n timeoutMs?: number | undefined;\n};\n\nasync function connectSocket(socketPath: string): Promise<Socket> {\n return new Promise((resolve, reject) => {\n const socket = createConnection(socketPath, () => {\n socket.removeAllListeners('error');\n resolve(socket);\n });\n socket.on('error', (error) => {\n // A failed connect never reaches the caller, so destroy the socket here\n // to avoid leaking its file descriptor.\n socket.destroy();\n reject(error);\n });\n });\n}\n\n/**\n * Send a JSON-RPC request to the daemon over a Unix socket and return the\n * response.\n *\n * Opens a connection, writes one JSON-RPC request line, reads one JSON-RPC\n * response line, then closes the connection. Retries once after a short delay\n * only on `ECONNREFUSED` — the connection was never established, so the daemon\n * provably never received the request and re-sending is safe. Does not retry\n * on `ECONNRESET`, which can drop after the daemon has already received and\n * acted on the request: blindly re-sending could execute a non-idempotent\n * action (e.g. a transaction broadcast) twice, so it is surfaced to the caller\n * instead. Verifies that the response `id` matches the outgoing request `id`.\n *\n * @param options - Command options.\n * @param options.socketPath - The Unix socket path.\n * @param options.method - The RPC method name.\n * @param options.params - Optional method parameters.\n * @param options.timeoutMs - Read timeout in milliseconds.\n * @returns The parsed JSON-RPC response.\n */\nexport async function sendCommand({\n socketPath,\n method,\n params,\n timeoutMs,\n}: SendCommandOptions): Promise<JsonRpcResponse> {\n const id = randomUUID();\n const request = {\n jsonrpc: '2.0',\n id,\n method,\n ...(params === undefined ? {} : { params }),\n };\n\n const effectiveTimeout = timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const attempt = async (): Promise<JsonRpcResponse> => {\n const socket = await connectSocket(socketPath);\n try {\n await writeLine(socket, JSON.stringify(request));\n const responseLine = await readLine(socket, effectiveTimeout);\n const parsed: unknown = JSON.parse(responseLine);\n assertIsJsonRpcResponse(parsed);\n if (parsed.id !== id) {\n throw new Error(\n `JSON-RPC response id ${JSON.stringify(parsed.id)} does not match request id ${JSON.stringify(id)}`,\n );\n }\n return parsed;\n } finally {\n socket.destroy();\n }\n };\n\n try {\n return await attempt();\n } catch (error: unknown) {\n // Only retry on ECONNREFUSED: the connection was never established, so the\n // daemon provably never received the request and re-sending is safe.\n // ECONNRESET can drop *after* the daemon received and began (or finished)\n // processing the request, so blindly re-sending a non-idempotent request\n // (e.g. a transaction broadcast) could execute it twice. Surface it to the\n // caller instead of retrying.\n if (!isErrorWithCode(error, 'ECONNREFUSED')) {\n throw error;\n }\n await new Promise((resolve) => setTimeout(resolve, 100));\n return attempt();\n }\n}\n\n/**\n * Why an unreachable daemon cannot be queried.\n *\n * - `'refused'`: the connection could not be completed — `ECONNREFUSED`\n * (retried once) or `ECONNRESET` (a mid-request drop, not retried). Typical\n * of a daemon that has crashed or is mid-restart.\n * - `'timeout'`: the daemon accepted the connection but did not respond within\n * the read timeout — most likely wedged on a long-running operation.\n * - `'permission'`: the socket exists but cannot be opened (`EACCES` / `EPERM`).\n * The daemon almost certainly belongs to another user.\n * - `'protocol'`: the daemon responded but the response did not parse as a\n * valid JSON-RPC response, or the response id did not match.\n * - `'other'`: anything else.\n */\nexport type PingUnreachableReason =\n | 'refused'\n | 'timeout'\n | 'permission'\n | 'protocol'\n | 'other';\n\n/**\n * Outcome of a daemon health check.\n *\n * - `'responsive'`: the daemon answered a `getStatus` RPC.\n * - `'absent'`: the socket connect attempt failed with `ENOENT`, i.e. no\n * socket file exists at the path. No daemon present.\n * - `'unreachable'`: any other non-success outcome. The daemon may still be\n * alive but is not responding. Callers should not silently take over the\n * slot or assume the daemon is dead. The `reason` field categorises the\n * failure so callers can distinguish a wedged sibling daemon from a\n * foreign-user daemon from a transient crash. User-initiated stop / purge\n * flows may still escalate to signals against the recorded PID.\n */\nexport type PingResult =\n | { status: 'responsive' }\n | { status: 'absent' }\n | { status: 'unreachable'; reason: PingUnreachableReason; error: Error };\n\n/**\n * Normalize an unknown throw into a real Error instance so that downstream\n * consumers (which read `.message`) cannot crash on string/object throws.\n *\n * @param error - The caught value.\n * @returns An Error mirroring the caught value.\n */\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n/**\n * Categorise an unreachable error by Node errno / message shape so callers can\n * make decisions per failure mode rather than parsing message strings.\n *\n * @param error - The caught value.\n * @returns A {@link PingUnreachableReason} label.\n */\nfunction classifyUnreachable(error: unknown): PingUnreachableReason {\n if (\n isErrorWithCode(error, 'ECONNREFUSED') ||\n isErrorWithCode(error, 'ECONNRESET')\n ) {\n return 'refused';\n }\n if (isErrorWithCode(error, 'EACCES') || isErrorWithCode(error, 'EPERM')) {\n return 'permission';\n }\n if (error instanceof Error && error.message === 'Socket read timed out') {\n return 'timeout';\n }\n if (\n error instanceof Error &&\n (error.message.includes('JSON-RPC response id') ||\n /Expected .* JSON-RPC/u.test(error.message) ||\n error.name === 'SyntaxError')\n ) {\n return 'protocol';\n }\n return 'other';\n}\n\n/**\n * Check whether the daemon is running by sending a lightweight `getStatus`\n * RPC call. Distinguishes \"no daemon present\" (socket file missing) from\n * \"daemon present but unreachable\" (socket file exists but the daemon is\n * wedged, mid-shutdown, or owned by a different user).\n *\n * @param socketPath - The Unix socket path.\n * @returns A {@link PingResult} describing the daemon's reachability.\n */\nexport async function pingDaemon(socketPath: string): Promise<PingResult> {\n try {\n await sendCommand({ socketPath, method: 'getStatus', timeoutMs: 3_000 });\n return { status: 'responsive' };\n } catch (error: unknown) {\n if (isErrorWithCode(error, 'ENOENT')) {\n return { status: 'absent' };\n }\n return {\n status: 'unreachable',\n reason: classifyUnreachable(error),\n error: toError(error),\n };\n }\n}\n"]}