@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,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stopDaemon = void 0;
4
+ const promises_1 = require("node:fs/promises");
5
+ const daemon_client_js_1 = require("./daemon-client.cjs");
6
+ const utils_js_1 = require("./utils.cjs");
7
+ /**
8
+ * Stop the daemon, preferring a graceful shutdown.
9
+ *
10
+ * Resolution order when a live daemon is present:
11
+ * 1. If the socket is responsive, request a graceful `shutdown` over it.
12
+ * 2. If the recorded PID is still alive, escalate to SIGTERM.
13
+ * 3. ...then SIGKILL.
14
+ *
15
+ * Signals (steps 2-3) are only ever sent against a PID that is observed alive.
16
+ * The socket-absent + alive-PID branch trades a small risk of signalling a
17
+ * recycled PID for the larger risk of leaving an orphan daemon holding the
18
+ * SQLite database — which `daemon purge` would otherwise wipe out from under
19
+ * it.
20
+ *
21
+ * When the socket is NOT responsive AND the recorded PID is dead (or there is
22
+ * no PID file), there is no live daemon: a lingering socket or PID file is
23
+ * stale leftovers from a daemon that already exited — typically one that
24
+ * crashed without running its own cleanup. Those files are removed and the
25
+ * stop is reported as successful, rather than failing on a daemon that is
26
+ * already gone.
27
+ *
28
+ * @param socketPath - The daemon socket path.
29
+ * @param pidPath - The daemon PID file path.
30
+ * @param log - Optional logging function for status messages.
31
+ * @returns True if the daemon was stopped (or was not running).
32
+ */
33
+ async function stopDaemon(socketPath, pidPath, log) {
34
+ const pid = await (0, utils_js_1.readPidFile)(pidPath);
35
+ const ping = await (0, daemon_client_js_1.pingDaemon)(socketPath);
36
+ const socketObserved = ping.status === 'responsive' || ping.status === 'unreachable';
37
+ const processAlive = pid !== undefined && (0, utils_js_1.isProcessAlive)(pid);
38
+ // Only `absent` and `refused` prove no live daemon; `permission`/`timeout`/
39
+ // `protocol` may be a wedged or foreign daemon, so those fall through.
40
+ const socketProvenGone = ping.status === 'absent' ||
41
+ (ping.status === 'unreachable' && ping.reason === 'refused');
42
+ if (socketProvenGone && !processAlive) {
43
+ await cleanupFile(pidPath, 'PID file', log);
44
+ await cleanupFile(socketPath, 'socket file', log);
45
+ return true;
46
+ }
47
+ log?.('Stopping daemon...');
48
+ let stopped = false;
49
+ // Strategy 1: Graceful socket-based shutdown.
50
+ if (ping.status === 'responsive') {
51
+ try {
52
+ await (0, daemon_client_js_1.sendCommand)({ socketPath, method: 'shutdown' });
53
+ }
54
+ catch (error) {
55
+ log?.(`Graceful shutdown request failed: ${String(error)}`);
56
+ }
57
+ // A quiet socket does not prove the process exited; require the recorded
58
+ // pid to be gone too, so a daemon that outlived its socket falls through to
59
+ // SIGTERM/SIGKILL rather than being orphaned.
60
+ stopped = await (0, utils_js_1.waitFor)(async () => (await (0, daemon_client_js_1.pingDaemon)(socketPath)).status !== 'responsive' &&
61
+ (pid === undefined || !(0, utils_js_1.isProcessAlive)(pid)), 5000);
62
+ }
63
+ // Strategy 2: SIGTERM. Signal when either the socket was observed or the
64
+ // recorded PID is alive; the absent+alive case typically means someone
65
+ // removed the socket from under a live daemon.
66
+ if (!stopped && processAlive && pid !== undefined) {
67
+ if (!socketObserved) {
68
+ log?.(`Socket at ${socketPath} is absent but recorded pid ${pid} is alive; signalling anyway.`);
69
+ }
70
+ try {
71
+ if ((0, utils_js_1.sendSignal)(pid, 'SIGTERM')) {
72
+ stopped = await (0, utils_js_1.waitFor)(() => !(0, utils_js_1.isProcessAlive)(pid), 5000);
73
+ }
74
+ else {
75
+ stopped = true; // Process already gone (ESRCH).
76
+ }
77
+ }
78
+ catch (error) {
79
+ log?.(`SIGTERM failed: ${String(error)}`);
80
+ }
81
+ }
82
+ // Strategy 3: SIGKILL.
83
+ if (!stopped && processAlive && pid !== undefined) {
84
+ try {
85
+ if ((0, utils_js_1.sendSignal)(pid, 'SIGKILL')) {
86
+ stopped = await (0, utils_js_1.waitFor)(() => !(0, utils_js_1.isProcessAlive)(pid), 2000);
87
+ }
88
+ else {
89
+ stopped = true; // Process already gone (ESRCH).
90
+ }
91
+ }
92
+ catch (error) {
93
+ log?.(`SIGKILL failed: ${String(error)}`);
94
+ }
95
+ }
96
+ if (stopped) {
97
+ await cleanupFile(pidPath, 'PID file', log);
98
+ await cleanupFile(socketPath, 'socket file', log);
99
+ log?.('Daemon stopped.');
100
+ }
101
+ return stopped;
102
+ }
103
+ exports.stopDaemon = stopDaemon;
104
+ /**
105
+ * Remove a file best-effort, logging any failure rather than letting it
106
+ * propagate. ENOENT is silently ignored via `force: true`.
107
+ *
108
+ * @param path - The file path to remove.
109
+ * @param label - Human-readable label for log messages.
110
+ * @param log - Optional log sink.
111
+ */
112
+ async function cleanupFile(path, label, log) {
113
+ await (0, promises_1.rm)(path, { force: true }).catch((error) => {
114
+ log?.(`Failed to remove ${label}: ${String(error)}`);
115
+ });
116
+ }
117
+ //# sourceMappingURL=stop-daemon.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-daemon.cjs","sourceRoot":"","sources":["../../src/daemon/stop-daemon.ts"],"names":[],"mappings":"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","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"]}
@@ -25,4 +25,4 @@
25
25
  * @returns True if the daemon was stopped (or was not running).
26
26
  */
27
27
  export declare function stopDaemon(socketPath: string, pidPath: string, log?: (message: string) => void): Promise<boolean>;
28
- //# sourceMappingURL=stop-daemon.d.ts.map
28
+ //# sourceMappingURL=stop-daemon.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-daemon.d.cts","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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Stop the daemon, preferring a graceful shutdown.
3
+ *
4
+ * Resolution order when a live daemon is present:
5
+ * 1. If the socket is responsive, request a graceful `shutdown` over it.
6
+ * 2. If the recorded PID is still alive, escalate to SIGTERM.
7
+ * 3. ...then SIGKILL.
8
+ *
9
+ * Signals (steps 2-3) are only ever sent against a PID that is observed alive.
10
+ * The socket-absent + alive-PID branch trades a small risk of signalling a
11
+ * recycled PID for the larger risk of leaving an orphan daemon holding the
12
+ * SQLite database — which `daemon purge` would otherwise wipe out from under
13
+ * it.
14
+ *
15
+ * When the socket is NOT responsive AND the recorded PID is dead (or there is
16
+ * no PID file), there is no live daemon: a lingering socket or PID file is
17
+ * stale leftovers from a daemon that already exited — typically one that
18
+ * crashed without running its own cleanup. Those files are removed and the
19
+ * stop is reported as successful, rather than failing on a daemon that is
20
+ * already gone.
21
+ *
22
+ * @param socketPath - The daemon socket path.
23
+ * @param pidPath - The daemon PID file path.
24
+ * @param log - Optional logging function for status messages.
25
+ * @returns True if the daemon was stopped (or was not running).
26
+ */
27
+ export declare function stopDaemon(socketPath: string, pidPath: string, log?: (message: string) => void): Promise<boolean>;
28
+ //# sourceMappingURL=stop-daemon.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-daemon.d.mts","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,6 +1,6 @@
1
- import { rm } from 'node:fs/promises';
2
- import { pingDaemon, sendCommand } from './daemon-client.js';
3
- import { isProcessAlive, readPidFile, sendSignal, waitFor } from './utils.js';
1
+ import { rm } from "node:fs/promises";
2
+ import { pingDaemon, sendCommand } from "./daemon-client.mjs";
3
+ import { isProcessAlive, readPidFile, sendSignal, waitFor } from "./utils.mjs";
4
4
  /**
5
5
  * Stop the daemon, preferring a graceful shutdown.
6
6
  *
@@ -55,7 +55,7 @@ export async function stopDaemon(socketPath, pidPath, log) {
55
55
  // pid to be gone too, so a daemon that outlived its socket falls through to
56
56
  // SIGTERM/SIGKILL rather than being orphaned.
57
57
  stopped = await waitFor(async () => (await pingDaemon(socketPath)).status !== 'responsive' &&
58
- (pid === undefined || !isProcessAlive(pid)), 5_000);
58
+ (pid === undefined || !isProcessAlive(pid)), 5000);
59
59
  }
60
60
  // Strategy 2: SIGTERM. Signal when either the socket was observed or the
61
61
  // recorded PID is alive; the absent+alive case typically means someone
@@ -66,7 +66,7 @@ export async function stopDaemon(socketPath, pidPath, log) {
66
66
  }
67
67
  try {
68
68
  if (sendSignal(pid, 'SIGTERM')) {
69
- stopped = await waitFor(() => !isProcessAlive(pid), 5_000);
69
+ stopped = await waitFor(() => !isProcessAlive(pid), 5000);
70
70
  }
71
71
  else {
72
72
  stopped = true; // Process already gone (ESRCH).
@@ -80,7 +80,7 @@ export async function stopDaemon(socketPath, pidPath, log) {
80
80
  if (!stopped && processAlive && pid !== undefined) {
81
81
  try {
82
82
  if (sendSignal(pid, 'SIGKILL')) {
83
- stopped = await waitFor(() => !isProcessAlive(pid), 2_000);
83
+ stopped = await waitFor(() => !isProcessAlive(pid), 2000);
84
84
  }
85
85
  else {
86
86
  stopped = true; // Process already gone (ESRCH).
@@ -110,4 +110,4 @@ async function cleanupFile(path, label, log) {
110
110
  log?.(`Failed to remove ${label}: ${String(error)}`);
111
111
  });
112
112
  }
113
- //# sourceMappingURL=stop-daemon.js.map
113
+ //# sourceMappingURL=stop-daemon.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-daemon.mjs","sourceRoot":"","sources":["../../src/daemon/stop-daemon.ts"],"names":[],"mappings":"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","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"]}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineHandler = void 0;
4
+ /**
5
+ * Bundle a params struct with the handler that runs once `params` is
6
+ * validated. The server invokes `run` only after `paramsStruct` accepts the
7
+ * value, so `run` can trust the type of its argument.
8
+ *
9
+ * The returned definition erases `TParams` to `unknown` so heterogeneous
10
+ * handlers can share an {@link RpcHandlerMap}.
11
+ *
12
+ * @param paramsStruct - Struct that validates `params` for this method.
13
+ * @param run - Handler invoked with the validated params.
14
+ * @returns An {@link RpcHandlerDefinition} suitable for an {@link RpcHandlerMap}.
15
+ */
16
+ function defineHandler(paramsStruct, run) {
17
+ return { paramsStruct, run };
18
+ }
19
+ exports.defineHandler = defineHandler;
20
+ //# sourceMappingURL=types.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../src/daemon/types.ts"],"names":[],"mappings":";;;AAiDA;;;;;;;;;;;GAWG;AACH,SAAgB,aAAa,CAC3B,YAA6B,EAC7B,GAAiC;IAEjC,OAAO,EAAE,YAAY,EAAE,GAAG,EAGzB,CAAC;AACJ,CAAC;AARD,sCAQC","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,6 +1,6 @@
1
- import type { Struct } from '@metamask/superstruct';
2
- import type { Json } from '@metamask/utils';
3
- import type { Password, Srp } from './secrets.js';
1
+ import type { Struct } from "@metamask/superstruct";
2
+ import type { Json } from "@metamask/utils";
3
+ import type { Password, Srp } from "./secrets.cjs";
4
4
  /**
5
5
  * Sink for daemon diagnostic messages. A backgrounded daemon's stdio may be
6
6
  * discarded, so hosts supply a logger that writes somewhere durable.
@@ -91,4 +91,4 @@ export type DaemonSpawnConfig = {
91
91
  srp: Srp;
92
92
  packageRoot: string;
93
93
  };
94
- //# sourceMappingURL=types.d.ts.map
94
+ //# sourceMappingURL=types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../../src/daemon/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,8BAA8B;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,sBAAqB;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"}
@@ -0,0 +1,94 @@
1
+ import type { Struct } from "@metamask/superstruct";
2
+ import type { Json } from "@metamask/utils";
3
+ import type { Password, Srp } from "./secrets.mjs";
4
+ /**
5
+ * Sink for daemon diagnostic messages. A backgrounded daemon's stdio may be
6
+ * discarded, so hosts supply a logger that writes somewhere durable.
7
+ */
8
+ export type Logger = (message: string) => void;
9
+ /**
10
+ * A function that handles a JSON-RPC method call after its params have been
11
+ * validated by the corresponding {@link RpcHandlerDefinition.paramsStruct}.
12
+ */
13
+ export type RpcHandler<TParams, TResult> = (params: TParams) => Promise<TResult>;
14
+ /**
15
+ * Definition for a single JSON-RPC method: the struct that validates
16
+ * incoming `params` plus the handler that runs once `params` is known to
17
+ * match.
18
+ *
19
+ * The server (see `rpc-socket-server.ts`) validates the raw `params` against
20
+ * `paramsStruct` before invoking `run`, so each handler body can trust the
21
+ * shape of its input without re-checking.
22
+ */
23
+ export type RpcHandlerDefinition<TParams, TResult extends Json | void> = {
24
+ paramsStruct: Struct<TParams>;
25
+ run: RpcHandler<TParams, TResult>;
26
+ };
27
+ /**
28
+ * A map of RPC method names to their handler definitions.
29
+ *
30
+ * `TParams` is erased to `unknown` here so definitions with different narrow
31
+ * params types (e.g. `null` vs. a tuple) can coexist in the same map. Consumers
32
+ * therefore see each `run` as accepting `unknown` and must validate `params`
33
+ * against the paired `paramsStruct` before invoking it — which is exactly what
34
+ * the server (see `rpc-socket-server.ts`) does. The concrete `TParams` is
35
+ * captured inside {@link defineHandler}, where the struct and handler are bound
36
+ * together.
37
+ */
38
+ export type RpcHandlerMap = Record<string, RpcHandlerDefinition<unknown, Json | void>>;
39
+ /**
40
+ * Bundle a params struct with the handler that runs once `params` is
41
+ * validated. The server invokes `run` only after `paramsStruct` accepts the
42
+ * value, so `run` can trust the type of its argument.
43
+ *
44
+ * The returned definition erases `TParams` to `unknown` so heterogeneous
45
+ * handlers can share an {@link RpcHandlerMap}.
46
+ *
47
+ * @param paramsStruct - Struct that validates `params` for this method.
48
+ * @param run - Handler invoked with the validated params.
49
+ * @returns An {@link RpcHandlerDefinition} suitable for an {@link RpcHandlerMap}.
50
+ */
51
+ export declare function defineHandler<TParams, TResult extends Json | void>(paramsStruct: Struct<TParams>, run: RpcHandler<TParams, TResult>): RpcHandlerDefinition<unknown, TResult>;
52
+ /**
53
+ * Typed wrapper around `wallet.messenger.call` used by the `call` RPC.
54
+ *
55
+ * The messenger is strongly typed by action name; the daemon exposes the full
56
+ * messenger surface over the socket and dispatches by string, so we consolidate
57
+ * the unsafe cast into a single, documented escape hatch instead of repeating
58
+ * it at each call site.
59
+ */
60
+ export type RpcDispatcher = (action: string, ...args: Json[]) => Promise<Json>;
61
+ /**
62
+ * Resolved paths for daemon state files.
63
+ */
64
+ export type DaemonPaths = {
65
+ socketPath: string;
66
+ pidPath: string;
67
+ logPath: string;
68
+ dbPath: string;
69
+ };
70
+ /**
71
+ * Status information returned by the daemon's `getStatus` RPC method.
72
+ */
73
+ export type DaemonStatusInfo = {
74
+ pid: number;
75
+ uptime: number;
76
+ };
77
+ /**
78
+ * Configuration passed to the daemon spawner.
79
+ *
80
+ * `password` is optional: when omitted, the daemon starts without unlocking
81
+ * the keyring, and the caller is expected to use `mm wallet unlock` before
82
+ * any keyring-bound operation. First-run startup still requires both
83
+ * `password` and `srp`; without `password`, the daemon will exit during
84
+ * startup with a clear error. `srp` is always required by the type and
85
+ * forwarded to the daemon; it is only consumed on first run.
86
+ */
87
+ export type DaemonSpawnConfig = {
88
+ dataDir: string;
89
+ infuraProjectId: string;
90
+ password?: Password;
91
+ srp: Srp;
92
+ packageRoot: string;
93
+ };
94
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../src/daemon/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,8BAA8B;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,sBAAqB;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"}
@@ -13,4 +13,4 @@
13
13
  export function defineHandler(paramsStruct, run) {
14
14
  return { paramsStruct, run };
15
15
  }
16
- //# sourceMappingURL=types.js.map
16
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.mjs","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"]}
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.waitFor = exports.sendSignal = exports.isProcessAlive = exports.readPidFile = exports.isStringArray = exports.emptyToUndefined = exports.formatJsonRpcError = exports.makeDaemonConnectionError = exports.isErrorWithCode = void 0;
4
+ const utils_1 = require("@metamask/utils");
5
+ const promises_1 = require("node:fs/promises");
6
+ /**
7
+ * Check whether an unknown error is a Node.js system error with the given code.
8
+ *
9
+ * @param error - The error to check.
10
+ * @param code - The expected error code (e.g. 'ENOENT', 'EPERM').
11
+ * @returns True if the error matches the code.
12
+ */
13
+ function isErrorWithCode(error, code) {
14
+ return (0, utils_1.isErrorWithCode)(error) && error.code === code;
15
+ }
16
+ exports.isErrorWithCode = isErrorWithCode;
17
+ /**
18
+ * Turn an error thrown while contacting the daemon socket into a user-facing
19
+ * message for `Command.error`, so every command reports the same failure the
20
+ * same way. Distinguishes a stopped daemon (`ENOENT`/`ECONNREFUSED`), a
21
+ * connection dropped mid-request (`ECONNRESET`), and a permission problem
22
+ * (`EACCES`/`EPERM`), falling back to the raw error message.
23
+ *
24
+ * @param error - The value thrown while contacting the daemon.
25
+ * @returns A human-readable explanation of the failure.
26
+ */
27
+ function makeDaemonConnectionError(error) {
28
+ if (isErrorWithCode(error, 'ENOENT') ||
29
+ isErrorWithCode(error, 'ECONNREFUSED')) {
30
+ return 'Daemon is not running. Start it with `mm daemon start`.';
31
+ }
32
+ // A reset drops an already-established connection, so the daemon was running
33
+ // — most likely it crashed mid-request. Don't tell the user to start it.
34
+ if (isErrorWithCode(error, 'ECONNRESET')) {
35
+ return ('Lost the connection to the daemon; it may have crashed while ' +
36
+ 'handling the request. Check `mm daemon status` and the daemon log.');
37
+ }
38
+ if (isErrorWithCode(error, 'EACCES') || isErrorWithCode(error, 'EPERM')) {
39
+ return ('Cannot connect to the daemon socket: permission denied. ' +
40
+ 'The socket may be owned by another user, or MM_DAEMON_DATA_DIR ' +
41
+ 'may point to a directory you cannot access.');
42
+ }
43
+ return error instanceof Error ? error.message : String(error);
44
+ }
45
+ exports.makeDaemonConnectionError = makeDaemonConnectionError;
46
+ /**
47
+ * Format the error of a JSON-RPC failure response into a user-facing message
48
+ * for `Command.error`, so every command reports RPC failures the same way.
49
+ *
50
+ * @param error - The `error` field of a JSON-RPC failure response.
51
+ * @returns The error message annotated with its numeric code.
52
+ */
53
+ function formatJsonRpcError(error) {
54
+ return `${error.message} (code ${String(error.code)})`;
55
+ }
56
+ exports.formatJsonRpcError = formatJsonRpcError;
57
+ /**
58
+ * Treat an empty string the same as `undefined` — "no value supplied". Used
59
+ * to normalise `--password ''` and `MM_WALLET_PASSWORD=''` so they trigger
60
+ * the interactive prompt rather than sending an empty string to the keyring.
61
+ *
62
+ * @param value - The value to normalise.
63
+ * @returns `undefined` if `value` is `undefined` or `''`; `value` otherwise.
64
+ */
65
+ function emptyToUndefined(value) {
66
+ return value === '' ? undefined : value;
67
+ }
68
+ exports.emptyToUndefined = emptyToUndefined;
69
+ /**
70
+ * Check whether a value is an array of strings. Used to validate untyped RPC
71
+ * results (e.g. the daemon's `listActions`) before treating them as `string[]`.
72
+ *
73
+ * @param value - The value to check.
74
+ * @returns True if the value is an array whose every element is a string.
75
+ */
76
+ function isStringArray(value) {
77
+ return (Array.isArray(value) && value.every((item) => typeof item === 'string'));
78
+ }
79
+ exports.isStringArray = isStringArray;
80
+ /**
81
+ * Read a PID from a file. The file may contain just the PID, or the PID on
82
+ * the first line followed by additional metadata (e.g. start time written by
83
+ * the daemon).
84
+ *
85
+ * @param pidPath - The PID file path.
86
+ * @returns The PID, or undefined if the file is missing or its first line is
87
+ * not a positive integer.
88
+ */
89
+ async function readPidFile(pidPath) {
90
+ let contents;
91
+ try {
92
+ contents = await (0, promises_1.readFile)(pidPath, 'utf-8');
93
+ }
94
+ catch (error) {
95
+ if (isErrorWithCode(error, 'ENOENT')) {
96
+ return undefined;
97
+ }
98
+ throw error;
99
+ }
100
+ // String.prototype.split always returns at least one element, so [0] is safe.
101
+ const pid = Number(contents.split('\n')[0].trim());
102
+ return Number.isInteger(pid) && pid > 0 ? pid : undefined;
103
+ }
104
+ exports.readPidFile = readPidFile;
105
+ /**
106
+ * Check whether a process is alive by sending signal 0.
107
+ *
108
+ * Treats `ESRCH` as "process is gone", `EPERM` as "process exists but we
109
+ * cannot signal it" (still alive from our perspective), and rethrows
110
+ * anything else so the caller can surface unexpected failures rather than
111
+ * silently assuming the process is dead.
112
+ *
113
+ * @param pid - The process ID to check.
114
+ * @returns True if the process exists.
115
+ */
116
+ function isProcessAlive(pid) {
117
+ try {
118
+ process.kill(pid, 0);
119
+ return true;
120
+ }
121
+ catch (error) {
122
+ if (isErrorWithCode(error, 'ESRCH')) {
123
+ return false;
124
+ }
125
+ if (isErrorWithCode(error, 'EPERM')) {
126
+ return true;
127
+ }
128
+ throw error;
129
+ }
130
+ }
131
+ exports.isProcessAlive = isProcessAlive;
132
+ /**
133
+ * Send a signal to a process. Returns true if the signal was sent, false if
134
+ * the process does not exist (ESRCH). Re-throws on permission errors and
135
+ * other failures.
136
+ *
137
+ * @param pid - The process ID.
138
+ * @param signal - The signal to send.
139
+ * @returns True if the signal was delivered, false if the process is gone.
140
+ */
141
+ function sendSignal(pid, signal) {
142
+ try {
143
+ process.kill(pid, signal);
144
+ return true;
145
+ }
146
+ catch (error) {
147
+ if (isErrorWithCode(error, 'ESRCH')) {
148
+ return false;
149
+ }
150
+ throw error;
151
+ }
152
+ }
153
+ exports.sendSignal = sendSignal;
154
+ /**
155
+ * Poll until a condition is met or the timeout elapses.
156
+ *
157
+ * @param check - A function that returns true when the condition is met.
158
+ * @param timeoutMs - Maximum time to wait in milliseconds.
159
+ * @returns True if the condition was met, false on timeout.
160
+ */
161
+ async function waitFor(check, timeoutMs) {
162
+ const deadline = Date.now() + timeoutMs;
163
+ while (Date.now() < deadline) {
164
+ if (await check()) {
165
+ return true;
166
+ }
167
+ await new Promise((resolve) => setTimeout(resolve, 250));
168
+ }
169
+ return false;
170
+ }
171
+ exports.waitFor = waitFor;
172
+ //# sourceMappingURL=utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.cjs","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,4 +1,5 @@
1
- import type { JsonRpcError } from '@metamask/utils';
1
+ /// <reference types="node" />
2
+ import type { JsonRpcError } from "@metamask/utils";
2
3
  /**
3
4
  * Check whether an unknown error is a Node.js system error with the given code.
4
5
  *
@@ -83,4 +84,4 @@ export declare function sendSignal(pid: number, signal: NodeJS.Signals): boolean
83
84
  * @returns True if the condition was met, false on timeout.
84
85
  */
85
86
  export declare function waitFor(check: () => boolean | Promise<boolean>, timeoutMs: number): Promise<boolean>;
86
- //# sourceMappingURL=utils.d.ts.map
87
+ //# sourceMappingURL=utils.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.cts","sourceRoot":"","sources":["../../src/daemon/utils.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,wBAAwB;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"}