@metamask-previews/wallet-cli 0.0.0-preview-4f4c98e → 0.0.0-preview-eb263b0ee

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 (263) hide show
  1. package/CHANGELOG.md +1 -2
  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} +6 -6
  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} +6 -6
  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} +7 -7
  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.cjs.map +1 -0
  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.js → start.mjs} +4 -4
  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} +6 -6
  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} +6 -6
  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} +9 -9
  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} +7 -7
  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} +6 -6
  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} +7 -7
  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} +3 -3
  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} +4 -4
  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} +11 -11
  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.js → KeyValueStore.mjs} +9 -2
  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 +8 -12
  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.map +0 -1
  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.map +0 -1
  260. package/dist/persistence/persistence.d.ts.map +0 -1
  261. package/dist/persistence/persistence.js.map +0 -1
  262. package/dist/test/run-command.d.ts.map +0 -1
  263. package/dist/test/run-command.js.map +0 -1
@@ -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,8 +1,8 @@
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';
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
6
  const DEFAULT_TIMEOUT_MS = 30000;
7
7
  async function connectSocket(socketPath) {
8
8
  return new Promise((resolve, reject) => {
@@ -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"]}
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const superstruct_1 = require("@metamask/superstruct");
4
+ const promises_1 = require("node:fs/promises");
5
+ const daemon_client_js_1 = require("./daemon-client.cjs");
6
+ const data_dir_js_1 = require("./data-dir.cjs");
7
+ const paths_js_1 = require("./paths.cjs");
8
+ const rpc_socket_server_js_1 = require("./rpc-socket-server.cjs");
9
+ const secrets_js_1 = require("./secrets.cjs");
10
+ const send_transaction_js_1 = require("./send-transaction.cjs");
11
+ const types_js_1 = require("./types.cjs");
12
+ const utils_js_1 = require("./utils.cjs");
13
+ const wallet_factory_js_1 = require("./wallet-factory.cjs");
14
+ /**
15
+ * Params struct for the `call` RPC method. `params` must be a non-empty array
16
+ * whose first element is the messenger action name; remaining elements are
17
+ * positional action arguments forwarded as-is to `messenger.call`.
18
+ */
19
+ const callParamsStruct = (0, superstruct_1.define)('CallParams', (value) => {
20
+ if (!Array.isArray(value)) {
21
+ return 'Expected an array';
22
+ }
23
+ if (value.length === 0) {
24
+ return 'Expected a non-empty array';
25
+ }
26
+ if (typeof value[0] !== 'string') {
27
+ return 'Expected the first element to be a string action name';
28
+ }
29
+ return true;
30
+ });
31
+ const startTime = Date.now();
32
+ main().catch((error) => {
33
+ process.stderr.write(`Daemon fatal: ${String(error)}\n`);
34
+ process.exitCode = 1;
35
+ });
36
+ async function main() {
37
+ const dataDir = process.env.MM_DAEMON_DATA_DIR;
38
+ if (!dataDir) {
39
+ throw new Error('MM_DAEMON_DATA_DIR environment variable is required');
40
+ }
41
+ const infuraProjectId = process.env.INFURA_PROJECT_ID;
42
+ if (!infuraProjectId) {
43
+ throw new Error('INFURA_PROJECT_ID environment variable is required');
44
+ }
45
+ // Password is optional: when absent, the daemon starts without unlocking
46
+ // the keyring (e.g. when the user prefers to call `mm wallet unlock`
47
+ // interactively rather than embed the password in their environment).
48
+ // First-run startup still requires a password; wallet-factory enforces
49
+ // that and surfaces a clear error.
50
+ const passwordRaw = process.env.MM_WALLET_PASSWORD;
51
+ const srpRaw = process.env.MM_WALLET_SRP;
52
+ if (!srpRaw) {
53
+ throw new Error('MM_WALLET_SRP environment variable is required');
54
+ }
55
+ // Scrub before validation so a throw from Password.from / Srp.from (bad
56
+ // value) does not leave the raw secrets in the long-lived daemon's env.
57
+ delete process.env.MM_WALLET_PASSWORD;
58
+ delete process.env.MM_WALLET_SRP;
59
+ const password = passwordRaw ? secrets_js_1.Password.from(passwordRaw) : undefined;
60
+ const srp = secrets_js_1.Srp.from(srpRaw);
61
+ await (0, data_dir_js_1.ensureOwnerOnlyDirectory)(dataDir);
62
+ const { socketPath: defaultSocketPath, pidPath, logPath, dbPath, } = (0, paths_js_1.getDaemonPaths)(dataDir);
63
+ const socketPath = process.env.MM_DAEMON_SOCKET_PATH ?? defaultSocketPath;
64
+ const log = makeLogger(logPath);
65
+ log('Starting daemon...');
66
+ // Pre-flight: refuse to take over if a responsive daemon already owns this
67
+ // socket. If the existing PID file is stale (or the socket is dead), clean
68
+ // it up so the exclusive PID-file write below has a chance to succeed.
69
+ await claimDaemonSlot(pidPath, socketPath, log);
70
+ const pidFileContents = `${process.pid}\n${startTime}\n`;
71
+ // Claim the slot atomically BEFORE opening the SQLite database or
72
+ // constructing the Wallet. Two concurrent `daemon start` invocations can
73
+ // both pass `claimDaemonSlot` (the gap between its preflight and the slot
74
+ // write is racy); without this ordering, both would open `wallet.db` and
75
+ // both would run first-run SRP import before one loses the wx race.
76
+ try {
77
+ await (0, promises_1.writeFile)(pidPath, pidFileContents, { flag: 'wx' });
78
+ }
79
+ catch (error) {
80
+ throw error instanceof Error
81
+ ? Object.assign(error, {
82
+ message: `Failed to claim daemon slot at ${pidPath}: ${error.message}`,
83
+ })
84
+ : /* istanbul ignore next -- node:fs/promises always rejects with an Error */
85
+ new Error(`Failed to claim daemon slot at ${pidPath}: ${String(error)}`);
86
+ }
87
+ let wallet;
88
+ let dispose;
89
+ let handle;
90
+ try {
91
+ ({ wallet, dispose } = await (0, wallet_factory_js_1.createWallet)({
92
+ databasePath: dbPath,
93
+ password,
94
+ srp,
95
+ infuraProjectId,
96
+ log,
97
+ }));
98
+ const constructedWallet = wallet;
99
+ // Arbitrary messenger dispatch is intentional: the CLI exposes the full
100
+ // messenger surface over a Unix socket inside the per-user oclif data
101
+ // directory. The dataDir is chmodded to 0o700 above and the socket to
102
+ // 0o600 by the RPC server on bind, so only the owning user can open them,
103
+ // but there is no in-process auth check beyond that filesystem-permission
104
+ // barrier. The messenger is strongly typed by action name; we narrow it
105
+ // once here to the RpcDispatcher shape the `call` handler needs.
106
+ const dispatch = constructedWallet.messenger.call.bind(constructedWallet.messenger);
107
+ const handlers = {
108
+ getStatus: (0, types_js_1.defineHandler)((0, superstruct_1.literal)(null), async () => ({
109
+ pid: process.pid,
110
+ uptime: Math.floor((Date.now() - startTime) / 1000),
111
+ })),
112
+ call: (0, types_js_1.defineHandler)(callParamsStruct, async (params) => {
113
+ const [action, ...args] = params;
114
+ return await dispatch(action, ...args);
115
+ }),
116
+ // Exposes the callable surface for discovery: it grows silently as
117
+ // controllers are wired, so consumers need a way to see it without a
118
+ // hand-kept catalog that would rot.
119
+ listActions: (0, types_js_1.defineHandler)((0, superstruct_1.literal)(null), async () => constructedWallet.messenger.getRegisteredActionTypes()),
120
+ // Dedicated send handler; see `runSendTransaction` for why the generic
121
+ // `call` dispatch cannot carry a broadcast result.
122
+ sendTransaction: (0, types_js_1.defineHandler)(send_transaction_js_1.SendTransactionParamsStruct, async (params) => (0, send_transaction_js_1.runSendTransaction)(constructedWallet.messenger, params)),
123
+ };
124
+ // `startRpcSocketServer` restricts the socket to the owner (chmod 0o600)
125
+ // on bind and never leaves a live server/socket behind if it rejects, so
126
+ // the catch below has nothing of its own to close.
127
+ handle = await (0, rpc_socket_server_js_1.startRpcSocketServer)({
128
+ socketPath,
129
+ handlers,
130
+ onShutdown: async () => shutdown('RPC shutdown'),
131
+ log,
132
+ });
133
+ }
134
+ catch (error) {
135
+ // `dispose` is undefined only when `createWallet` itself threw — it has
136
+ // already torn down its own store in that case.
137
+ if (dispose) {
138
+ await dispose();
139
+ }
140
+ // Only remove the PID file if it's still ours (we may have lost the race
141
+ // and the file now belongs to another daemon).
142
+ await removeOwnedPidFile(pidPath, pidFileContents).catch((rmError) => {
143
+ log(`Failed to remove PID file during cleanup: ${String(rmError)}`);
144
+ });
145
+ throw error;
146
+ }
147
+ // Stable non-undefined refs for the shutdown closures (TS won't narrow the
148
+ // outer `let`s across closure escape).
149
+ const activeHandle = handle;
150
+ const activeDispose = dispose;
151
+ log(`Daemon started. Socket: ${socketPath}`);
152
+ let shutdownPromise;
153
+ /**
154
+ * Shut down the daemon idempotently. Concurrent calls coalesce.
155
+ *
156
+ * @param reason - A label describing why shutdown was triggered.
157
+ * @returns A promise that resolves when shutdown completes.
158
+ */
159
+ async function shutdown(reason) {
160
+ if (shutdownPromise === undefined) {
161
+ log(`Shutting down (${reason})...`);
162
+ shutdownPromise = (async () => {
163
+ try {
164
+ await activeHandle.close();
165
+ }
166
+ catch (closeError) {
167
+ log(`handle.close() failed: ${String(closeError)}`);
168
+ }
169
+ try {
170
+ await activeDispose();
171
+ }
172
+ catch (disposeError) {
173
+ log(`dispose() failed during shutdown: ${String(disposeError)}`);
174
+ }
175
+ await Promise.all([
176
+ removeOwnedPidFile(pidPath, pidFileContents).catch((rmError) => {
177
+ log(`Failed to remove PID file: ${String(rmError)}`);
178
+ }),
179
+ (0, promises_1.rm)(socketPath, { force: true }).catch((rmError) => {
180
+ log(`Failed to remove socket file: ${String(rmError)}`);
181
+ }),
182
+ ]);
183
+ })();
184
+ }
185
+ return shutdownPromise;
186
+ }
187
+ process.on('SIGTERM', () => {
188
+ /* istanbul ignore next -- `shutdown` logs each step internally; the catch only guards against an unhandled rejection from the signal handler. */
189
+ shutdown('SIGTERM').catch(() => undefined);
190
+ });
191
+ process.on('SIGINT', () => {
192
+ /* istanbul ignore next -- `shutdown` logs each step internally; the catch only guards against an unhandled rejection from the signal handler. */
193
+ shutdown('SIGINT').catch(() => undefined);
194
+ });
195
+ }
196
+ /**
197
+ * Refuse to start if a responsive daemon already owns the socket. Otherwise
198
+ * clear any stale PID/socket files so the exclusive PID-file write can
199
+ * proceed.
200
+ *
201
+ * @param pidPath - The PID file path.
202
+ * @param socketPath - The socket path.
203
+ * @param log - Logger for diagnostic messages.
204
+ */
205
+ async function claimDaemonSlot(pidPath, socketPath, log) {
206
+ const existingPid = await (0, utils_js_1.readPidFile)(pidPath);
207
+ const ping = await (0, daemon_client_js_1.pingDaemon)(socketPath);
208
+ if (ping.status === 'responsive') {
209
+ const pidPart = existingPid === undefined
210
+ ? '(no PID file present)'
211
+ : `(pid ${existingPid})`;
212
+ throw new Error(`A daemon is already running on ${socketPath} ${pidPart}`);
213
+ }
214
+ // Refuse to clobber when the recorded PID is still alive, regardless of
215
+ // whether the socket exists. Possible scenarios:
216
+ // - `unreachable`: wedged or mid-startup sibling daemon (socket present
217
+ // but not responding to JSON-RPC).
218
+ // - `absent`: a sibling daemon that hasn't yet bound its socket, or one
219
+ // whose socket was manually removed. In either case, removing its PID
220
+ // file would orphan it from `daemon stop`.
221
+ if (existingPid !== undefined && (0, utils_js_1.isProcessAlive)(existingPid)) {
222
+ const detail = ping.status === 'unreachable'
223
+ ? `socket at ${socketPath} is unresponsive (${ping.error.message})`
224
+ : `no socket at ${socketPath}, but pid is still alive`;
225
+ throw new Error(`A daemon is already running (pid ${existingPid}): ${detail}. ` +
226
+ `Run \`mm daemon stop\` (or \`mm daemon purge\`) before starting a new daemon.`);
227
+ }
228
+ if (ping.status === 'unreachable') {
229
+ log(`Removing stale socket at ${socketPath} (${ping.error.message}).`);
230
+ }
231
+ // Always clear both files before claiming the slot. The PID file may be
232
+ // corrupt (truncated, partial write from a crashed run); without this, the
233
+ // exclusive `wx` write below would fail with EEXIST and the daemon could
234
+ // not start until a human manually deleted the file.
235
+ await Promise.all([
236
+ (0, promises_1.rm)(pidPath, { force: true }),
237
+ (0, promises_1.rm)(socketPath, { force: true }),
238
+ ]);
239
+ }
240
+ /**
241
+ * Remove the PID file only if it still contains our exact contents. Guards
242
+ * against a racing daemon's PID file being removed by this daemon during
243
+ * cleanup.
244
+ *
245
+ * @param pidPath - Path to the PID file.
246
+ * @param expectedContents - The contents we wrote when claiming the slot.
247
+ */
248
+ async function removeOwnedPidFile(pidPath, expectedContents) {
249
+ let actual;
250
+ try {
251
+ actual = await (0, promises_1.readFile)(pidPath, 'utf-8');
252
+ }
253
+ catch (error) {
254
+ if ((0, utils_js_1.isErrorWithCode)(error, 'ENOENT')) {
255
+ return;
256
+ }
257
+ throw error;
258
+ }
259
+ if (actual === expectedContents) {
260
+ await (0, promises_1.rm)(pidPath, { force: true });
261
+ }
262
+ }
263
+ /**
264
+ * Create a file logger that appends timestamped lines to `logPath`, falling
265
+ * back to stderr if the append fails.
266
+ *
267
+ * @param logPath - The log file path.
268
+ * @returns A logging function.
269
+ */
270
+ function makeLogger(logPath) {
271
+ return (message) => {
272
+ const line = `[${new Date().toISOString()}] ${message}\n`;
273
+ (0, promises_1.appendFile)(logPath, line).catch((error) => {
274
+ process.stderr.write(`[log write failed: ${String(error)}] ${message}\n`);
275
+ });
276
+ };
277
+ }
278
+ //# sourceMappingURL=daemon-entry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-entry.cjs","sourceRoot":"","sources":["../../src/daemon/daemon-entry.ts"],"names":[],"mappings":"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","sourcesContent":["import { define, literal } from '@metamask/superstruct';\nimport type { Json } from '@metamask/utils';\nimport type { Wallet } from '@metamask/wallet';\nimport { appendFile, readFile, rm, writeFile } from 'node:fs/promises';\n\nimport { pingDaemon } from './daemon-client.js';\nimport { ensureOwnerOnlyDirectory } from './data-dir.js';\nimport { getDaemonPaths } from './paths.js';\nimport { startRpcSocketServer } from './rpc-socket-server.js';\nimport type { RpcSocketServerHandle } from './rpc-socket-server.js';\nimport { Password, Srp } from './secrets.js';\nimport {\n runSendTransaction,\n SendTransactionParamsStruct,\n} from './send-transaction.js';\nimport { defineHandler } from './types.js';\nimport type {\n DaemonStatusInfo,\n Logger,\n RpcDispatcher,\n RpcHandlerMap,\n} from './types.js';\nimport { isErrorWithCode, isProcessAlive, readPidFile } from './utils.js';\nimport { createWallet } from './wallet-factory.js';\n\n/**\n * Params struct for the `call` RPC method. `params` must be a non-empty array\n * whose first element is the messenger action name; remaining elements are\n * positional action arguments forwarded as-is to `messenger.call`.\n */\nconst callParamsStruct = define<[string, ...unknown[]]>(\n 'CallParams',\n (value) => {\n if (!Array.isArray(value)) {\n return 'Expected an array';\n }\n if (value.length === 0) {\n return 'Expected a non-empty array';\n }\n if (typeof value[0] !== 'string') {\n return 'Expected the first element to be a string action name';\n }\n return true;\n },\n);\n\nconst startTime = Date.now();\n\nmain().catch((error: unknown) => {\n process.stderr.write(`Daemon fatal: ${String(error)}\\n`);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n const dataDir = process.env.MM_DAEMON_DATA_DIR;\n if (!dataDir) {\n throw new Error('MM_DAEMON_DATA_DIR environment variable is required');\n }\n\n const infuraProjectId = process.env.INFURA_PROJECT_ID;\n if (!infuraProjectId) {\n throw new Error('INFURA_PROJECT_ID environment variable is required');\n }\n\n // Password is optional: when absent, the daemon starts without unlocking\n // the keyring (e.g. when the user prefers to call `mm wallet unlock`\n // interactively rather than embed the password in their environment).\n // First-run startup still requires a password; wallet-factory enforces\n // that and surfaces a clear error.\n const passwordRaw = process.env.MM_WALLET_PASSWORD;\n\n const srpRaw = process.env.MM_WALLET_SRP;\n if (!srpRaw) {\n throw new Error('MM_WALLET_SRP environment variable is required');\n }\n\n // Scrub before validation so a throw from Password.from / Srp.from (bad\n // value) does not leave the raw secrets in the long-lived daemon's env.\n delete process.env.MM_WALLET_PASSWORD;\n delete process.env.MM_WALLET_SRP;\n\n const password = passwordRaw ? Password.from(passwordRaw) : undefined;\n const srp = Srp.from(srpRaw);\n\n await ensureOwnerOnlyDirectory(dataDir);\n\n const {\n socketPath: defaultSocketPath,\n pidPath,\n logPath,\n dbPath,\n } = getDaemonPaths(dataDir);\n const socketPath = process.env.MM_DAEMON_SOCKET_PATH ?? defaultSocketPath;\n\n const log = makeLogger(logPath);\n log('Starting daemon...');\n\n // Pre-flight: refuse to take over if a responsive daemon already owns this\n // socket. If the existing PID file is stale (or the socket is dead), clean\n // it up so the exclusive PID-file write below has a chance to succeed.\n await claimDaemonSlot(pidPath, socketPath, log);\n\n const pidFileContents = `${process.pid}\\n${startTime}\\n`;\n\n // Claim the slot atomically BEFORE opening the SQLite database or\n // constructing the Wallet. Two concurrent `daemon start` invocations can\n // both pass `claimDaemonSlot` (the gap between its preflight and the slot\n // write is racy); without this ordering, both would open `wallet.db` and\n // both would run first-run SRP import before one loses the wx race.\n try {\n await writeFile(pidPath, pidFileContents, { flag: 'wx' });\n } catch (error) {\n throw error instanceof Error\n ? Object.assign(error, {\n message: `Failed to claim daemon slot at ${pidPath}: ${error.message}`,\n })\n : /* istanbul ignore next -- node:fs/promises always rejects with an Error */\n new Error(\n `Failed to claim daemon slot at ${pidPath}: ${String(error)}`,\n );\n }\n\n let wallet: Wallet | undefined;\n let dispose: (() => Promise<void>) | undefined;\n let handle: RpcSocketServerHandle | undefined;\n\n try {\n ({ wallet, dispose } = await createWallet({\n databasePath: dbPath,\n password,\n srp,\n infuraProjectId,\n log,\n }));\n\n const constructedWallet = wallet;\n // Arbitrary messenger dispatch is intentional: the CLI exposes the full\n // messenger surface over a Unix socket inside the per-user oclif data\n // directory. The dataDir is chmodded to 0o700 above and the socket to\n // 0o600 by the RPC server on bind, so only the owning user can open them,\n // but there is no in-process auth check beyond that filesystem-permission\n // barrier. The messenger is strongly typed by action name; we narrow it\n // once here to the RpcDispatcher shape the `call` handler needs.\n const dispatch = constructedWallet.messenger.call.bind(\n constructedWallet.messenger,\n ) as unknown as RpcDispatcher;\n\n const handlers: RpcHandlerMap = {\n getStatus: defineHandler(\n literal(null),\n async (): Promise<DaemonStatusInfo> => ({\n pid: process.pid,\n uptime: Math.floor((Date.now() - startTime) / 1000),\n }),\n ),\n call: defineHandler(callParamsStruct, async (params) => {\n const [action, ...args] = params;\n return await dispatch(action, ...(args as Json[]));\n }),\n // Exposes the callable surface for discovery: it grows silently as\n // controllers are wired, so consumers need a way to see it without a\n // hand-kept catalog that would rot.\n listActions: defineHandler(\n literal(null),\n async (): Promise<Json> =>\n constructedWallet.messenger.getRegisteredActionTypes(),\n ),\n // Dedicated send handler; see `runSendTransaction` for why the generic\n // `call` dispatch cannot carry a broadcast result.\n sendTransaction: defineHandler(\n SendTransactionParamsStruct,\n async (params) =>\n runSendTransaction(constructedWallet.messenger, params),\n ),\n };\n\n // `startRpcSocketServer` restricts the socket to the owner (chmod 0o600)\n // on bind and never leaves a live server/socket behind if it rejects, so\n // the catch below has nothing of its own to close.\n handle = await startRpcSocketServer({\n socketPath,\n handlers,\n onShutdown: async () => shutdown('RPC shutdown'),\n log,\n });\n } catch (error) {\n // `dispose` is undefined only when `createWallet` itself threw — it has\n // already torn down its own store in that case.\n if (dispose) {\n await dispose();\n }\n // Only remove the PID file if it's still ours (we may have lost the race\n // and the file now belongs to another daemon).\n await removeOwnedPidFile(pidPath, pidFileContents).catch(\n (rmError: unknown) => {\n log(`Failed to remove PID file during cleanup: ${String(rmError)}`);\n },\n );\n throw error;\n }\n\n // Stable non-undefined refs for the shutdown closures (TS won't narrow the\n // outer `let`s across closure escape).\n const activeHandle = handle;\n const activeDispose = dispose;\n\n log(`Daemon started. Socket: ${socketPath}`);\n\n let shutdownPromise: Promise<void> | undefined;\n\n /**\n * Shut down the daemon idempotently. Concurrent calls coalesce.\n *\n * @param reason - A label describing why shutdown was triggered.\n * @returns A promise that resolves when shutdown completes.\n */\n async function shutdown(reason: string): Promise<void> {\n if (shutdownPromise === undefined) {\n log(`Shutting down (${reason})...`);\n shutdownPromise = (async (): Promise<void> => {\n try {\n await activeHandle.close();\n } catch (closeError) {\n log(`handle.close() failed: ${String(closeError)}`);\n }\n try {\n await activeDispose();\n } catch (disposeError) {\n log(`dispose() failed during shutdown: ${String(disposeError)}`);\n }\n await Promise.all([\n removeOwnedPidFile(pidPath, pidFileContents).catch(\n (rmError: unknown) => {\n log(`Failed to remove PID file: ${String(rmError)}`);\n },\n ),\n rm(socketPath, { force: true }).catch((rmError: unknown) => {\n log(`Failed to remove socket file: ${String(rmError)}`);\n }),\n ]);\n })();\n }\n return shutdownPromise;\n }\n\n process.on('SIGTERM', () => {\n /* istanbul ignore next -- `shutdown` logs each step internally; the catch only guards against an unhandled rejection from the signal handler. */\n shutdown('SIGTERM').catch(() => undefined);\n });\n process.on('SIGINT', () => {\n /* istanbul ignore next -- `shutdown` logs each step internally; the catch only guards against an unhandled rejection from the signal handler. */\n shutdown('SIGINT').catch(() => undefined);\n });\n}\n\n/**\n * Refuse to start if a responsive daemon already owns the socket. Otherwise\n * clear any stale PID/socket files so the exclusive PID-file write can\n * proceed.\n *\n * @param pidPath - The PID file path.\n * @param socketPath - The socket path.\n * @param log - Logger for diagnostic messages.\n */\nasync function claimDaemonSlot(\n pidPath: string,\n socketPath: string,\n log: Logger,\n): Promise<void> {\n const existingPid = await readPidFile(pidPath);\n const ping = await pingDaemon(socketPath);\n\n if (ping.status === 'responsive') {\n const pidPart =\n existingPid === undefined\n ? '(no PID file present)'\n : `(pid ${existingPid})`;\n throw new Error(`A daemon is already running on ${socketPath} ${pidPart}`);\n }\n\n // Refuse to clobber when the recorded PID is still alive, regardless of\n // whether the socket exists. Possible scenarios:\n // - `unreachable`: wedged or mid-startup sibling daemon (socket present\n // but not responding to JSON-RPC).\n // - `absent`: a sibling daemon that hasn't yet bound its socket, or one\n // whose socket was manually removed. In either case, removing its PID\n // file would orphan it from `daemon stop`.\n if (existingPid !== undefined && isProcessAlive(existingPid)) {\n const detail =\n ping.status === 'unreachable'\n ? `socket at ${socketPath} is unresponsive (${ping.error.message})`\n : `no socket at ${socketPath}, but pid is still alive`;\n throw new Error(\n `A daemon is already running (pid ${existingPid}): ${detail}. ` +\n `Run \\`mm daemon stop\\` (or \\`mm daemon purge\\`) before starting a new daemon.`,\n );\n }\n\n if (ping.status === 'unreachable') {\n log(`Removing stale socket at ${socketPath} (${ping.error.message}).`);\n }\n // Always clear both files before claiming the slot. The PID file may be\n // corrupt (truncated, partial write from a crashed run); without this, the\n // exclusive `wx` write below would fail with EEXIST and the daemon could\n // not start until a human manually deleted the file.\n await Promise.all([\n rm(pidPath, { force: true }),\n rm(socketPath, { force: true }),\n ]);\n}\n\n/**\n * Remove the PID file only if it still contains our exact contents. Guards\n * against a racing daemon's PID file being removed by this daemon during\n * cleanup.\n *\n * @param pidPath - Path to the PID file.\n * @param expectedContents - The contents we wrote when claiming the slot.\n */\nasync function removeOwnedPidFile(\n pidPath: string,\n expectedContents: string,\n): Promise<void> {\n let actual: string;\n try {\n actual = await readFile(pidPath, 'utf-8');\n } catch (error: unknown) {\n if (isErrorWithCode(error, 'ENOENT')) {\n return;\n }\n throw error;\n }\n if (actual === expectedContents) {\n await rm(pidPath, { force: true });\n }\n}\n\n/**\n * Create a file logger that appends timestamped lines to `logPath`, falling\n * back to stderr if the append fails.\n *\n * @param logPath - The log file path.\n * @returns A logging function.\n */\nfunction makeLogger(logPath: string): Logger {\n return (message: string): void => {\n const line = `[${new Date().toISOString()}] ${message}\\n`;\n appendFile(logPath, line).catch((error: unknown) => {\n process.stderr.write(`[log write failed: ${String(error)}] ${message}\\n`);\n });\n };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=daemon-entry.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-entry.d.cts","sourceRoot":"","sources":["../../src/daemon/daemon-entry.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=daemon-entry.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-entry.d.mts","sourceRoot":"","sources":["../../src/daemon/daemon-entry.ts"],"names":[],"mappings":""}
@@ -1,14 +1,14 @@
1
- import { define, literal } from '@metamask/superstruct';
2
- import { appendFile, readFile, rm, writeFile } from 'node:fs/promises';
3
- import { pingDaemon } from './daemon-client.js';
4
- import { ensureOwnerOnlyDirectory } from './data-dir.js';
5
- import { getDaemonPaths } from './paths.js';
6
- import { startRpcSocketServer } from './rpc-socket-server.js';
7
- import { Password, Srp } from './secrets.js';
8
- import { runSendTransaction, SendTransactionParamsStruct, } from './send-transaction.js';
9
- import { defineHandler } from './types.js';
10
- import { isErrorWithCode, isProcessAlive, readPidFile } from './utils.js';
11
- import { createWallet } from './wallet-factory.js';
1
+ import { define, literal } from "@metamask/superstruct";
2
+ import { appendFile, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { pingDaemon } from "./daemon-client.mjs";
4
+ import { ensureOwnerOnlyDirectory } from "./data-dir.mjs";
5
+ import { getDaemonPaths } from "./paths.mjs";
6
+ import { startRpcSocketServer } from "./rpc-socket-server.mjs";
7
+ import { Password, Srp } from "./secrets.mjs";
8
+ import { runSendTransaction, SendTransactionParamsStruct } from "./send-transaction.mjs";
9
+ import { defineHandler } from "./types.mjs";
10
+ import { isErrorWithCode, isProcessAlive, readPidFile } from "./utils.mjs";
11
+ import { createWallet } from "./wallet-factory.mjs";
12
12
  /**
13
13
  * Params struct for the `call` RPC method. `params` must be a non-empty array
14
14
  * whose first element is the messenger action name; remaining elements are
@@ -273,4 +273,4 @@ function makeLogger(logPath) {
273
273
  });
274
274
  };
275
275
  }
276
- //# sourceMappingURL=daemon-entry.js.map
276
+ //# sourceMappingURL=daemon-entry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-entry.mjs","sourceRoot":"","sources":["../../src/daemon/daemon-entry.ts"],"names":[],"mappings":"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","sourcesContent":["import { define, literal } from '@metamask/superstruct';\nimport type { Json } from '@metamask/utils';\nimport type { Wallet } from '@metamask/wallet';\nimport { appendFile, readFile, rm, writeFile } from 'node:fs/promises';\n\nimport { pingDaemon } from './daemon-client.js';\nimport { ensureOwnerOnlyDirectory } from './data-dir.js';\nimport { getDaemonPaths } from './paths.js';\nimport { startRpcSocketServer } from './rpc-socket-server.js';\nimport type { RpcSocketServerHandle } from './rpc-socket-server.js';\nimport { Password, Srp } from './secrets.js';\nimport {\n runSendTransaction,\n SendTransactionParamsStruct,\n} from './send-transaction.js';\nimport { defineHandler } from './types.js';\nimport type {\n DaemonStatusInfo,\n Logger,\n RpcDispatcher,\n RpcHandlerMap,\n} from './types.js';\nimport { isErrorWithCode, isProcessAlive, readPidFile } from './utils.js';\nimport { createWallet } from './wallet-factory.js';\n\n/**\n * Params struct for the `call` RPC method. `params` must be a non-empty array\n * whose first element is the messenger action name; remaining elements are\n * positional action arguments forwarded as-is to `messenger.call`.\n */\nconst callParamsStruct = define<[string, ...unknown[]]>(\n 'CallParams',\n (value) => {\n if (!Array.isArray(value)) {\n return 'Expected an array';\n }\n if (value.length === 0) {\n return 'Expected a non-empty array';\n }\n if (typeof value[0] !== 'string') {\n return 'Expected the first element to be a string action name';\n }\n return true;\n },\n);\n\nconst startTime = Date.now();\n\nmain().catch((error: unknown) => {\n process.stderr.write(`Daemon fatal: ${String(error)}\\n`);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n const dataDir = process.env.MM_DAEMON_DATA_DIR;\n if (!dataDir) {\n throw new Error('MM_DAEMON_DATA_DIR environment variable is required');\n }\n\n const infuraProjectId = process.env.INFURA_PROJECT_ID;\n if (!infuraProjectId) {\n throw new Error('INFURA_PROJECT_ID environment variable is required');\n }\n\n // Password is optional: when absent, the daemon starts without unlocking\n // the keyring (e.g. when the user prefers to call `mm wallet unlock`\n // interactively rather than embed the password in their environment).\n // First-run startup still requires a password; wallet-factory enforces\n // that and surfaces a clear error.\n const passwordRaw = process.env.MM_WALLET_PASSWORD;\n\n const srpRaw = process.env.MM_WALLET_SRP;\n if (!srpRaw) {\n throw new Error('MM_WALLET_SRP environment variable is required');\n }\n\n // Scrub before validation so a throw from Password.from / Srp.from (bad\n // value) does not leave the raw secrets in the long-lived daemon's env.\n delete process.env.MM_WALLET_PASSWORD;\n delete process.env.MM_WALLET_SRP;\n\n const password = passwordRaw ? Password.from(passwordRaw) : undefined;\n const srp = Srp.from(srpRaw);\n\n await ensureOwnerOnlyDirectory(dataDir);\n\n const {\n socketPath: defaultSocketPath,\n pidPath,\n logPath,\n dbPath,\n } = getDaemonPaths(dataDir);\n const socketPath = process.env.MM_DAEMON_SOCKET_PATH ?? defaultSocketPath;\n\n const log = makeLogger(logPath);\n log('Starting daemon...');\n\n // Pre-flight: refuse to take over if a responsive daemon already owns this\n // socket. If the existing PID file is stale (or the socket is dead), clean\n // it up so the exclusive PID-file write below has a chance to succeed.\n await claimDaemonSlot(pidPath, socketPath, log);\n\n const pidFileContents = `${process.pid}\\n${startTime}\\n`;\n\n // Claim the slot atomically BEFORE opening the SQLite database or\n // constructing the Wallet. Two concurrent `daemon start` invocations can\n // both pass `claimDaemonSlot` (the gap between its preflight and the slot\n // write is racy); without this ordering, both would open `wallet.db` and\n // both would run first-run SRP import before one loses the wx race.\n try {\n await writeFile(pidPath, pidFileContents, { flag: 'wx' });\n } catch (error) {\n throw error instanceof Error\n ? Object.assign(error, {\n message: `Failed to claim daemon slot at ${pidPath}: ${error.message}`,\n })\n : /* istanbul ignore next -- node:fs/promises always rejects with an Error */\n new Error(\n `Failed to claim daemon slot at ${pidPath}: ${String(error)}`,\n );\n }\n\n let wallet: Wallet | undefined;\n let dispose: (() => Promise<void>) | undefined;\n let handle: RpcSocketServerHandle | undefined;\n\n try {\n ({ wallet, dispose } = await createWallet({\n databasePath: dbPath,\n password,\n srp,\n infuraProjectId,\n log,\n }));\n\n const constructedWallet = wallet;\n // Arbitrary messenger dispatch is intentional: the CLI exposes the full\n // messenger surface over a Unix socket inside the per-user oclif data\n // directory. The dataDir is chmodded to 0o700 above and the socket to\n // 0o600 by the RPC server on bind, so only the owning user can open them,\n // but there is no in-process auth check beyond that filesystem-permission\n // barrier. The messenger is strongly typed by action name; we narrow it\n // once here to the RpcDispatcher shape the `call` handler needs.\n const dispatch = constructedWallet.messenger.call.bind(\n constructedWallet.messenger,\n ) as unknown as RpcDispatcher;\n\n const handlers: RpcHandlerMap = {\n getStatus: defineHandler(\n literal(null),\n async (): Promise<DaemonStatusInfo> => ({\n pid: process.pid,\n uptime: Math.floor((Date.now() - startTime) / 1000),\n }),\n ),\n call: defineHandler(callParamsStruct, async (params) => {\n const [action, ...args] = params;\n return await dispatch(action, ...(args as Json[]));\n }),\n // Exposes the callable surface for discovery: it grows silently as\n // controllers are wired, so consumers need a way to see it without a\n // hand-kept catalog that would rot.\n listActions: defineHandler(\n literal(null),\n async (): Promise<Json> =>\n constructedWallet.messenger.getRegisteredActionTypes(),\n ),\n // Dedicated send handler; see `runSendTransaction` for why the generic\n // `call` dispatch cannot carry a broadcast result.\n sendTransaction: defineHandler(\n SendTransactionParamsStruct,\n async (params) =>\n runSendTransaction(constructedWallet.messenger, params),\n ),\n };\n\n // `startRpcSocketServer` restricts the socket to the owner (chmod 0o600)\n // on bind and never leaves a live server/socket behind if it rejects, so\n // the catch below has nothing of its own to close.\n handle = await startRpcSocketServer({\n socketPath,\n handlers,\n onShutdown: async () => shutdown('RPC shutdown'),\n log,\n });\n } catch (error) {\n // `dispose` is undefined only when `createWallet` itself threw — it has\n // already torn down its own store in that case.\n if (dispose) {\n await dispose();\n }\n // Only remove the PID file if it's still ours (we may have lost the race\n // and the file now belongs to another daemon).\n await removeOwnedPidFile(pidPath, pidFileContents).catch(\n (rmError: unknown) => {\n log(`Failed to remove PID file during cleanup: ${String(rmError)}`);\n },\n );\n throw error;\n }\n\n // Stable non-undefined refs for the shutdown closures (TS won't narrow the\n // outer `let`s across closure escape).\n const activeHandle = handle;\n const activeDispose = dispose;\n\n log(`Daemon started. Socket: ${socketPath}`);\n\n let shutdownPromise: Promise<void> | undefined;\n\n /**\n * Shut down the daemon idempotently. Concurrent calls coalesce.\n *\n * @param reason - A label describing why shutdown was triggered.\n * @returns A promise that resolves when shutdown completes.\n */\n async function shutdown(reason: string): Promise<void> {\n if (shutdownPromise === undefined) {\n log(`Shutting down (${reason})...`);\n shutdownPromise = (async (): Promise<void> => {\n try {\n await activeHandle.close();\n } catch (closeError) {\n log(`handle.close() failed: ${String(closeError)}`);\n }\n try {\n await activeDispose();\n } catch (disposeError) {\n log(`dispose() failed during shutdown: ${String(disposeError)}`);\n }\n await Promise.all([\n removeOwnedPidFile(pidPath, pidFileContents).catch(\n (rmError: unknown) => {\n log(`Failed to remove PID file: ${String(rmError)}`);\n },\n ),\n rm(socketPath, { force: true }).catch((rmError: unknown) => {\n log(`Failed to remove socket file: ${String(rmError)}`);\n }),\n ]);\n })();\n }\n return shutdownPromise;\n }\n\n process.on('SIGTERM', () => {\n /* istanbul ignore next -- `shutdown` logs each step internally; the catch only guards against an unhandled rejection from the signal handler. */\n shutdown('SIGTERM').catch(() => undefined);\n });\n process.on('SIGINT', () => {\n /* istanbul ignore next -- `shutdown` logs each step internally; the catch only guards against an unhandled rejection from the signal handler. */\n shutdown('SIGINT').catch(() => undefined);\n });\n}\n\n/**\n * Refuse to start if a responsive daemon already owns the socket. Otherwise\n * clear any stale PID/socket files so the exclusive PID-file write can\n * proceed.\n *\n * @param pidPath - The PID file path.\n * @param socketPath - The socket path.\n * @param log - Logger for diagnostic messages.\n */\nasync function claimDaemonSlot(\n pidPath: string,\n socketPath: string,\n log: Logger,\n): Promise<void> {\n const existingPid = await readPidFile(pidPath);\n const ping = await pingDaemon(socketPath);\n\n if (ping.status === 'responsive') {\n const pidPart =\n existingPid === undefined\n ? '(no PID file present)'\n : `(pid ${existingPid})`;\n throw new Error(`A daemon is already running on ${socketPath} ${pidPart}`);\n }\n\n // Refuse to clobber when the recorded PID is still alive, regardless of\n // whether the socket exists. Possible scenarios:\n // - `unreachable`: wedged or mid-startup sibling daemon (socket present\n // but not responding to JSON-RPC).\n // - `absent`: a sibling daemon that hasn't yet bound its socket, or one\n // whose socket was manually removed. In either case, removing its PID\n // file would orphan it from `daemon stop`.\n if (existingPid !== undefined && isProcessAlive(existingPid)) {\n const detail =\n ping.status === 'unreachable'\n ? `socket at ${socketPath} is unresponsive (${ping.error.message})`\n : `no socket at ${socketPath}, but pid is still alive`;\n throw new Error(\n `A daemon is already running (pid ${existingPid}): ${detail}. ` +\n `Run \\`mm daemon stop\\` (or \\`mm daemon purge\\`) before starting a new daemon.`,\n );\n }\n\n if (ping.status === 'unreachable') {\n log(`Removing stale socket at ${socketPath} (${ping.error.message}).`);\n }\n // Always clear both files before claiming the slot. The PID file may be\n // corrupt (truncated, partial write from a crashed run); without this, the\n // exclusive `wx` write below would fail with EEXIST and the daemon could\n // not start until a human manually deleted the file.\n await Promise.all([\n rm(pidPath, { force: true }),\n rm(socketPath, { force: true }),\n ]);\n}\n\n/**\n * Remove the PID file only if it still contains our exact contents. Guards\n * against a racing daemon's PID file being removed by this daemon during\n * cleanup.\n *\n * @param pidPath - Path to the PID file.\n * @param expectedContents - The contents we wrote when claiming the slot.\n */\nasync function removeOwnedPidFile(\n pidPath: string,\n expectedContents: string,\n): Promise<void> {\n let actual: string;\n try {\n actual = await readFile(pidPath, 'utf-8');\n } catch (error: unknown) {\n if (isErrorWithCode(error, 'ENOENT')) {\n return;\n }\n throw error;\n }\n if (actual === expectedContents) {\n await rm(pidPath, { force: true });\n }\n}\n\n/**\n * Create a file logger that appends timestamped lines to `logPath`, falling\n * back to stderr if the append fails.\n *\n * @param logPath - The log file path.\n * @returns A logging function.\n */\nfunction makeLogger(logPath: string): Logger {\n return (message: string): void => {\n const line = `[${new Date().toISOString()}] ${message}\\n`;\n appendFile(logPath, line).catch((error: unknown) => {\n process.stderr.write(`[log write failed: ${String(error)}] ${message}\\n`);\n });\n };\n}\n"]}