@metamask-previews/wallet-cli 0.0.0-preview-9de9aa4 → 0.0.0-preview-153490ebd

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 (64) hide show
  1. package/CHANGELOG.md +2 -5
  2. package/dist/daemon/types.cjs.map +1 -1
  3. package/dist/daemon/types.d.cts +0 -28
  4. package/dist/daemon/types.d.cts.map +1 -1
  5. package/dist/daemon/types.d.mts +0 -28
  6. package/dist/daemon/types.d.mts.map +1 -1
  7. package/dist/daemon/types.mjs.map +1 -1
  8. package/package.json +2 -4
  9. package/dist/daemon/daemon-client.cjs +0 -140
  10. package/dist/daemon/daemon-client.cjs.map +0 -1
  11. package/dist/daemon/daemon-client.d.cts +0 -79
  12. package/dist/daemon/daemon-client.d.cts.map +0 -1
  13. package/dist/daemon/daemon-client.d.mts +0 -79
  14. package/dist/daemon/daemon-client.d.mts.map +0 -1
  15. package/dist/daemon/daemon-client.mjs +0 -135
  16. package/dist/daemon/daemon-client.mjs.map +0 -1
  17. package/dist/daemon/daemon-spawn.cjs +0 -106
  18. package/dist/daemon/daemon-spawn.cjs.map +0 -1
  19. package/dist/daemon/daemon-spawn.d.cts +0 -28
  20. package/dist/daemon/daemon-spawn.d.cts.map +0 -1
  21. package/dist/daemon/daemon-spawn.d.mts +0 -28
  22. package/dist/daemon/daemon-spawn.d.mts.map +0 -1
  23. package/dist/daemon/daemon-spawn.mjs +0 -102
  24. package/dist/daemon/daemon-spawn.mjs.map +0 -1
  25. package/dist/daemon/prompts.cjs +0 -21
  26. package/dist/daemon/prompts.cjs.map +0 -1
  27. package/dist/daemon/prompts.d.cts +0 -11
  28. package/dist/daemon/prompts.d.cts.map +0 -1
  29. package/dist/daemon/prompts.d.mts +0 -11
  30. package/dist/daemon/prompts.d.mts.map +0 -1
  31. package/dist/daemon/prompts.mjs +0 -17
  32. package/dist/daemon/prompts.mjs.map +0 -1
  33. package/dist/daemon/rpc-socket-server.cjs +0 -271
  34. package/dist/daemon/rpc-socket-server.cjs.map +0 -1
  35. package/dist/daemon/rpc-socket-server.d.cts +0 -44
  36. package/dist/daemon/rpc-socket-server.d.cts.map +0 -1
  37. package/dist/daemon/rpc-socket-server.d.mts +0 -44
  38. package/dist/daemon/rpc-socket-server.d.mts.map +0 -1
  39. package/dist/daemon/rpc-socket-server.mjs +0 -267
  40. package/dist/daemon/rpc-socket-server.mjs.map +0 -1
  41. package/dist/daemon/socket-line.cjs +0 -89
  42. package/dist/daemon/socket-line.cjs.map +0 -1
  43. package/dist/daemon/socket-line.d.cts +0 -19
  44. package/dist/daemon/socket-line.d.cts.map +0 -1
  45. package/dist/daemon/socket-line.d.mts +0 -19
  46. package/dist/daemon/socket-line.d.mts.map +0 -1
  47. package/dist/daemon/socket-line.mjs +0 -84
  48. package/dist/daemon/socket-line.mjs.map +0 -1
  49. package/dist/daemon/stop-daemon.cjs +0 -103
  50. package/dist/daemon/stop-daemon.cjs.map +0 -1
  51. package/dist/daemon/stop-daemon.d.cts +0 -18
  52. package/dist/daemon/stop-daemon.d.cts.map +0 -1
  53. package/dist/daemon/stop-daemon.d.mts +0 -18
  54. package/dist/daemon/stop-daemon.d.mts.map +0 -1
  55. package/dist/daemon/stop-daemon.mjs +0 -99
  56. package/dist/daemon/stop-daemon.mjs.map +0 -1
  57. package/dist/daemon/utils.cjs +0 -109
  58. package/dist/daemon/utils.cjs.map +0 -1
  59. package/dist/daemon/utils.d.cts +0 -50
  60. package/dist/daemon/utils.d.cts.map +0 -1
  61. package/dist/daemon/utils.d.mts +0 -50
  62. package/dist/daemon/utils.d.mts.map +0 -1
  63. package/dist/daemon/utils.mjs +0 -101
  64. package/dist/daemon/utils.mjs.map +0 -1
@@ -1,271 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.startRpcSocketServer = void 0;
4
- const rpc_errors_1 = require("@metamask/rpc-errors");
5
- const utils_1 = require("@metamask/utils");
6
- const promises_1 = require("node:fs/promises");
7
- const node_net_1 = require("node:net");
8
- const utils_2 = require("./utils.cjs");
9
- const CONNECTION_TIMEOUT_MS = 30000;
10
- /**
11
- * Start a Unix socket server that processes JSON-RPC requests.
12
- *
13
- * Each connection reads one newline-delimited JSON-RPC request, processes it
14
- * via the provided handler map, writes a JSON-RPC response, and closes.
15
- *
16
- * The special `shutdown` method is intercepted before handler dispatch and
17
- * triggers the provided {@link StartRpcSocketServerOptions.onShutdown} callback
18
- * after responding.
19
- *
20
- * @param options - Server options.
21
- * @param options.socketPath - The Unix socket path to listen on.
22
- * @param options.handlers - Map of RPC method names to handler functions.
23
- * @param options.onShutdown - Optional callback invoked when a `shutdown` RPC is received.
24
- * @param options.log - Optional logger for server-side diagnostics.
25
- * @returns A handle with a `close()` function for cleanup.
26
- */
27
- async function startRpcSocketServer({ socketPath, handlers, onShutdown, log, }) {
28
- const logFn = log ?? defaultLog;
29
- const server = (0, node_net_1.createServer)((socket) => {
30
- let buffer = '';
31
- // Destroy connections that never send a complete request line. `unref` so
32
- // the timer alone cannot keep the event loop alive at shutdown.
33
- const timer = setTimeout(() => {
34
- socket.destroy();
35
- }, CONNECTION_TIMEOUT_MS);
36
- timer.unref();
37
- /**
38
- * Clear the idle-connection timer. Called from data, close, and error
39
- * paths so the timer never outlives the connection itself.
40
- */
41
- const clearIdleTimer = () => {
42
- clearTimeout(timer);
43
- };
44
- const onData = (data) => {
45
- buffer += data.toString();
46
- const idx = buffer.indexOf('\n');
47
- if (idx === -1) {
48
- return;
49
- }
50
- clearIdleTimer();
51
- // One request per connection.
52
- socket.removeListener('data', onData);
53
- const line = buffer.slice(0, idx);
54
- const remaining = buffer.slice(idx + 1);
55
- buffer = '';
56
- if (remaining.length > 0) {
57
- socket.end(`${JSON.stringify({
58
- jsonrpc: '2.0',
59
- error: rpc_errors_1.rpcErrors
60
- .invalidRequest({
61
- message: 'Only one request per connection is allowed',
62
- })
63
- .serialize(),
64
- })}\n`);
65
- return;
66
- }
67
- handleRequest(handlers, line, onShutdown, logFn)
68
- .then((response) => {
69
- socket.end(`${JSON.stringify(response)}\n`);
70
- return undefined;
71
- })
72
- .catch((dispatchError) => {
73
- logFn(`Unhandled RPC dispatch error: ${String(dispatchError)}`);
74
- socket.end(`${JSON.stringify({
75
- jsonrpc: '2.0',
76
- error: rpc_errors_1.rpcErrors
77
- .internal({ message: 'Internal error' })
78
- .serialize(),
79
- })}\n`);
80
- });
81
- };
82
- socket.on('data', onData);
83
- socket.once('close', clearIdleTimer);
84
- socket.on('error', (socketError) => {
85
- clearIdleTimer();
86
- const { code } = socketError;
87
- if (code === 'EPIPE' || code === 'ECONNRESET') {
88
- return; // Expected during probe/disconnect.
89
- }
90
- logFn(`Unexpected socket error: ${String(socketError)}`);
91
- });
92
- });
93
- await listen(server, socketPath);
94
- return {
95
- close: async () => closeServer(server),
96
- };
97
- }
98
- exports.startRpcSocketServer = startRpcSocketServer;
99
- /**
100
- * Default fallback logger: writes to stderr. Daemons spawned with
101
- * `stdio: 'ignore'` should always pass an explicit `log`.
102
- *
103
- * @param message - The message to log.
104
- */
105
- function defaultLog(message) {
106
- process.stderr.write(`${message}\n`);
107
- }
108
- /**
109
- * Handle a single JSON-RPC request line, intercepting the `shutdown` method.
110
- *
111
- * @param handlers - The RPC handler map.
112
- * @param line - The raw JSON line from the socket.
113
- * @param onShutdown - Optional shutdown callback.
114
- * @param log - Logger for diagnostic messages.
115
- * @returns A JSON-RPC response object.
116
- */
117
- async function handleRequest(handlers, line, onShutdown, log) {
118
- let parsed;
119
- try {
120
- parsed = JSON.parse(line);
121
- }
122
- catch {
123
- return {
124
- jsonrpc: '2.0',
125
- id: null,
126
- error: rpc_errors_1.rpcErrors.parse({ message: 'Parse error' }).serialize(),
127
- };
128
- }
129
- if (!(0, utils_1.isJsonRpcRequest)(parsed)) {
130
- const id = typeof parsed === 'object' &&
131
- parsed !== null &&
132
- (0, utils_1.hasProperty)(parsed, 'id') &&
133
- isValidJsonRpcId(parsed.id)
134
- ? parsed.id
135
- : null;
136
- return {
137
- jsonrpc: '2.0',
138
- id,
139
- error: rpc_errors_1.rpcErrors
140
- .invalidRequest({ message: 'Invalid JSON-RPC request' })
141
- .serialize(),
142
- };
143
- }
144
- const { id, method, params } = parsed;
145
- try {
146
- if (method === 'shutdown') {
147
- if (onShutdown) {
148
- setTimeout(() => {
149
- onShutdown().catch((error) => {
150
- log(`onShutdown callback failed: ${String(error)}`);
151
- });
152
- }, 0);
153
- }
154
- return { jsonrpc: '2.0', id, result: { status: 'shutting down' } };
155
- }
156
- const handler = Object.prototype.hasOwnProperty.call(handlers, method)
157
- ? handlers[method]
158
- : undefined;
159
- if (!handler) {
160
- return {
161
- jsonrpc: '2.0',
162
- id,
163
- error: rpc_errors_1.rpcErrors
164
- .methodNotFound({ message: `Method not found: ${method}` })
165
- .serialize(),
166
- };
167
- }
168
- const result = await handler(coerceHandlerParams(params));
169
- return { jsonrpc: '2.0', id, result: result ?? null };
170
- }
171
- catch (error) {
172
- log(`RPC handler "${method}" failed: ${String(error)}`);
173
- if (isRpcError(error)) {
174
- return { jsonrpc: '2.0', id, error };
175
- }
176
- const message = error instanceof Error ? error.message : 'Internal error';
177
- return {
178
- jsonrpc: '2.0',
179
- id,
180
- error: rpc_errors_1.rpcErrors.internal({ message }).serialize(),
181
- };
182
- }
183
- }
184
- /**
185
- * Narrow `params` to the shape handlers expect. JSON-RPC 2.0 requires
186
- * `params`, when present, to be an array or object; both are valid `Json`.
187
- *
188
- * @param params - The validated `params` field from a JSON-RPC request.
189
- * @returns The same value, or `null` when absent.
190
- */
191
- function coerceHandlerParams(params) {
192
- return params ?? null;
193
- }
194
- /**
195
- * Per JSON-RPC 2.0, `id` must be a string, number, or null. Used when
196
- * salvaging an `id` from a parse-success-but-not-valid-request payload.
197
- *
198
- * @param value - The candidate id.
199
- * @returns True if the value is an acceptable JSON-RPC id.
200
- */
201
- function isValidJsonRpcId(value) {
202
- return (value === null || typeof value === 'string' || typeof value === 'number');
203
- }
204
- /**
205
- * Check if an error is an RPC error with a numeric code.
206
- *
207
- * @param error - The error to check.
208
- * @returns True if the error has a numeric code property.
209
- */
210
- function isRpcError(error) {
211
- return (typeof error === 'object' &&
212
- error !== null &&
213
- (0, utils_1.hasProperty)(error, 'code') &&
214
- typeof error.code === 'number' &&
215
- (0, utils_1.hasProperty)(error, 'message') &&
216
- typeof error.message === 'string');
217
- }
218
- /**
219
- * Start listening on a Unix socket path, removing any stale socket file.
220
- *
221
- * @param server - The net.Server instance.
222
- * @param socketPath - The Unix socket path.
223
- */
224
- async function listen(server, socketPath) {
225
- try {
226
- await (0, promises_1.unlink)(socketPath);
227
- }
228
- catch (error) {
229
- if (!(0, utils_2.isErrorWithCode)(error, 'ENOENT')) {
230
- throw error;
231
- }
232
- }
233
- await new Promise((resolve, reject) => {
234
- server.on('error', reject);
235
- server.listen(socketPath, () => {
236
- server.removeListener('error', reject);
237
- resolve();
238
- });
239
- });
240
- // Restrict the socket to its owner. The daemon hosts an unlocked wallet, so
241
- // a world-connectable socket would let any local user drive it. listen()
242
- // creates the socket with umask-derived (typically world-accessible) perms.
243
- try {
244
- await (0, promises_1.chmod)(socketPath, 0o600);
245
- }
246
- catch (error) {
247
- // Never leave a possibly world-accessible socket listening with no handle
248
- // to close it. Cleanup is best-effort so the chmod failure still surfaces.
249
- await closeServer(server).catch(() => undefined);
250
- await (0, promises_1.unlink)(socketPath).catch(() => undefined);
251
- throw error;
252
- }
253
- }
254
- /**
255
- * Close a server, resolving once it stops accepting connections.
256
- *
257
- * @param server - The net.Server instance.
258
- */
259
- async function closeServer(server) {
260
- await new Promise((resolve, reject) => {
261
- server.close((error) => {
262
- if (error) {
263
- reject(error);
264
- }
265
- else {
266
- resolve();
267
- }
268
- });
269
- });
270
- }
271
- //# sourceMappingURL=rpc-socket-server.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rpc-socket-server.cjs","sourceRoot":"","sources":["../../src/daemon/rpc-socket-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n JsonRpcId,\n JsonRpcParams,\n JsonRpcResponse,\n} from '@metamask/utils';\nimport { hasProperty, isJsonRpcRequest } from '@metamask/utils';\nimport { chmod, unlink } from 'node:fs/promises';\nimport { createServer } from 'node:net';\nimport type { Server } from 'node:net';\n\nimport type { RpcHandlerMap } from './types';\nimport { isErrorWithCode } from './utils';\n\nconst CONNECTION_TIMEOUT_MS = 30_000;\n\n/**\n * Handle returned by {@link startRpcSocketServer}.\n */\nexport type RpcSocketServerHandle = {\n close: () => Promise<void>;\n};\n\n/**\n * Options for {@link startRpcSocketServer}.\n */\nexport type StartRpcSocketServerOptions = {\n /** The Unix socket path to listen on. */\n socketPath: string;\n /** Map of RPC method names to handler functions. */\n handlers: RpcHandlerMap;\n /** Callback invoked when a `shutdown` RPC is received. */\n onShutdown?: (() => Promise<void>) | undefined;\n /**\n * Optional logger for server-side diagnostics (unexpected socket errors,\n * unhandled handler rejections, `onShutdown` callback failures). Without\n * this, failures fall back to `process.stderr.write`, which is discarded\n * when the daemon is spawned with `stdio: 'ignore'`.\n */\n log?: ((message: string) => void) | undefined;\n};\n\n/**\n * Start a Unix socket server that processes JSON-RPC requests.\n *\n * Each connection reads one newline-delimited JSON-RPC request, processes it\n * via the provided handler map, writes a JSON-RPC response, and closes.\n *\n * The special `shutdown` method is intercepted before handler dispatch and\n * triggers the provided {@link StartRpcSocketServerOptions.onShutdown} callback\n * after responding.\n *\n * @param options - Server options.\n * @param options.socketPath - The Unix socket path to listen on.\n * @param options.handlers - Map of RPC method names to handler functions.\n * @param options.onShutdown - Optional callback invoked when a `shutdown` RPC is received.\n * @param options.log - Optional logger for server-side diagnostics.\n * @returns A handle with a `close()` function for cleanup.\n */\nexport async function startRpcSocketServer({\n socketPath,\n handlers,\n onShutdown,\n log,\n}: StartRpcSocketServerOptions): Promise<RpcSocketServerHandle> {\n const logFn = log ?? defaultLog;\n\n const server = createServer((socket) => {\n let buffer = '';\n\n // Destroy connections that never send a complete request line. `unref` so\n // the timer alone cannot keep the event loop alive at shutdown.\n const timer = setTimeout(() => {\n socket.destroy();\n }, CONNECTION_TIMEOUT_MS);\n timer.unref();\n\n /**\n * Clear the idle-connection timer. Called from data, close, and error\n * paths so the timer never outlives the connection itself.\n */\n const clearIdleTimer = (): void => {\n clearTimeout(timer);\n };\n\n const onData = (data: Buffer): void => {\n buffer += data.toString();\n const idx = buffer.indexOf('\\n');\n if (idx === -1) {\n return;\n }\n\n clearIdleTimer();\n\n // One request per connection.\n socket.removeListener('data', onData);\n\n const line = buffer.slice(0, idx);\n const remaining = buffer.slice(idx + 1);\n buffer = '';\n\n if (remaining.length > 0) {\n socket.end(\n `${JSON.stringify({\n jsonrpc: '2.0',\n error: rpcErrors\n .invalidRequest({\n message: 'Only one request per connection is allowed',\n })\n .serialize(),\n })}\\n`,\n );\n return;\n }\n\n handleRequest(handlers, line, onShutdown, logFn)\n .then((response) => {\n socket.end(`${JSON.stringify(response)}\\n`);\n return undefined;\n })\n .catch((dispatchError: unknown) => {\n logFn(`Unhandled RPC dispatch error: ${String(dispatchError)}`);\n socket.end(\n `${JSON.stringify({\n jsonrpc: '2.0',\n error: rpcErrors\n .internal({ message: 'Internal error' })\n .serialize(),\n })}\\n`,\n );\n });\n };\n socket.on('data', onData);\n socket.once('close', clearIdleTimer);\n socket.on('error', (socketError: NodeJS.ErrnoException) => {\n clearIdleTimer();\n const { code } = socketError;\n if (code === 'EPIPE' || code === 'ECONNRESET') {\n return; // Expected during probe/disconnect.\n }\n logFn(`Unexpected socket error: ${String(socketError)}`);\n });\n });\n\n await listen(server, socketPath);\n\n return {\n close: async (): Promise<void> => closeServer(server),\n };\n}\n\n/**\n * Default fallback logger: writes to stderr. Daemons spawned with\n * `stdio: 'ignore'` should always pass an explicit `log`.\n *\n * @param message - The message to log.\n */\nfunction defaultLog(message: string): void {\n process.stderr.write(`${message}\\n`);\n}\n\n/**\n * Handle a single JSON-RPC request line, intercepting the `shutdown` method.\n *\n * @param handlers - The RPC handler map.\n * @param line - The raw JSON line from the socket.\n * @param onShutdown - Optional shutdown callback.\n * @param log - Logger for diagnostic messages.\n * @returns A JSON-RPC response object.\n */\nasync function handleRequest(\n handlers: RpcHandlerMap,\n line: string,\n onShutdown: (() => Promise<void>) | undefined,\n log: (message: string) => void,\n): Promise<JsonRpcResponse> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return {\n jsonrpc: '2.0',\n id: null,\n error: rpcErrors.parse({ message: 'Parse error' }).serialize(),\n };\n }\n\n if (!isJsonRpcRequest(parsed)) {\n const id: JsonRpcId =\n typeof parsed === 'object' &&\n parsed !== null &&\n hasProperty(parsed, 'id') &&\n isValidJsonRpcId(parsed.id)\n ? parsed.id\n : null;\n return {\n jsonrpc: '2.0',\n id,\n error: rpcErrors\n .invalidRequest({ message: 'Invalid JSON-RPC request' })\n .serialize(),\n };\n }\n\n const { id, method, params } = parsed;\n\n try {\n if (method === 'shutdown') {\n if (onShutdown) {\n setTimeout(() => {\n onShutdown().catch((error: unknown) => {\n log(`onShutdown callback failed: ${String(error)}`);\n });\n }, 0);\n }\n return { jsonrpc: '2.0', id, result: { status: 'shutting down' } };\n }\n\n const handler = Object.prototype.hasOwnProperty.call(handlers, method)\n ? handlers[method]\n : undefined;\n if (!handler) {\n return {\n jsonrpc: '2.0',\n id,\n error: rpcErrors\n .methodNotFound({ message: `Method not found: ${method}` })\n .serialize(),\n };\n }\n\n const result = await handler(coerceHandlerParams(params));\n return { jsonrpc: '2.0', id, result: result ?? null };\n } catch (error) {\n log(`RPC handler \"${method}\" failed: ${String(error)}`);\n if (isRpcError(error)) {\n return { jsonrpc: '2.0', id, error };\n }\n const message = error instanceof Error ? error.message : 'Internal error';\n return {\n jsonrpc: '2.0',\n id,\n error: rpcErrors.internal({ message }).serialize(),\n };\n }\n}\n\n/**\n * Narrow `params` to the shape handlers expect. JSON-RPC 2.0 requires\n * `params`, when present, to be an array or object; both are valid `Json`.\n *\n * @param params - The validated `params` field from a JSON-RPC request.\n * @returns The same value, or `null` when absent.\n */\nfunction coerceHandlerParams(\n params: JsonRpcParams | undefined,\n): JsonRpcParams | null {\n return params ?? null;\n}\n\n/**\n * Per JSON-RPC 2.0, `id` must be a string, number, or null. Used when\n * salvaging an `id` from a parse-success-but-not-valid-request payload.\n *\n * @param value - The candidate id.\n * @returns True if the value is an acceptable JSON-RPC id.\n */\nfunction isValidJsonRpcId(value: unknown): value is JsonRpcId {\n return (\n value === null || typeof value === 'string' || typeof value === 'number'\n );\n}\n\n/**\n * Check if an error is an RPC error with a numeric code.\n *\n * @param error - The error to check.\n * @returns True if the error has a numeric code property.\n */\nfunction isRpcError(\n error: unknown,\n): error is { code: number; message: string } {\n return (\n typeof error === 'object' &&\n error !== null &&\n hasProperty(error, 'code') &&\n typeof error.code === 'number' &&\n hasProperty(error, 'message') &&\n typeof error.message === 'string'\n );\n}\n\n/**\n * Start listening on a Unix socket path, removing any stale socket file.\n *\n * @param server - The net.Server instance.\n * @param socketPath - The Unix socket path.\n */\nasync function listen(server: Server, socketPath: string): Promise<void> {\n try {\n await unlink(socketPath);\n } catch (error) {\n if (!isErrorWithCode(error, 'ENOENT')) {\n throw error;\n }\n }\n\n await new Promise<void>((resolve, reject) => {\n server.on('error', reject);\n server.listen(socketPath, () => {\n server.removeListener('error', reject);\n resolve();\n });\n });\n\n // Restrict the socket to its owner. The daemon hosts an unlocked wallet, so\n // a world-connectable socket would let any local user drive it. listen()\n // creates the socket with umask-derived (typically world-accessible) perms.\n try {\n await chmod(socketPath, 0o600);\n } catch (error) {\n // Never leave a possibly world-accessible socket listening with no handle\n // to close it. Cleanup is best-effort so the chmod failure still surfaces.\n await closeServer(server).catch(() => undefined);\n await unlink(socketPath).catch(() => undefined);\n throw error;\n }\n}\n\n/**\n * Close a server, resolving once it stops accepting connections.\n *\n * @param server - The net.Server instance.\n */\nasync function closeServer(server: Server): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n}\n"]}
@@ -1,44 +0,0 @@
1
- import type { RpcHandlerMap } from "./types.cjs";
2
- /**
3
- * Handle returned by {@link startRpcSocketServer}.
4
- */
5
- export type RpcSocketServerHandle = {
6
- close: () => Promise<void>;
7
- };
8
- /**
9
- * Options for {@link startRpcSocketServer}.
10
- */
11
- export type StartRpcSocketServerOptions = {
12
- /** The Unix socket path to listen on. */
13
- socketPath: string;
14
- /** Map of RPC method names to handler functions. */
15
- handlers: RpcHandlerMap;
16
- /** Callback invoked when a `shutdown` RPC is received. */
17
- onShutdown?: (() => Promise<void>) | undefined;
18
- /**
19
- * Optional logger for server-side diagnostics (unexpected socket errors,
20
- * unhandled handler rejections, `onShutdown` callback failures). Without
21
- * this, failures fall back to `process.stderr.write`, which is discarded
22
- * when the daemon is spawned with `stdio: 'ignore'`.
23
- */
24
- log?: ((message: string) => void) | undefined;
25
- };
26
- /**
27
- * Start a Unix socket server that processes JSON-RPC requests.
28
- *
29
- * Each connection reads one newline-delimited JSON-RPC request, processes it
30
- * via the provided handler map, writes a JSON-RPC response, and closes.
31
- *
32
- * The special `shutdown` method is intercepted before handler dispatch and
33
- * triggers the provided {@link StartRpcSocketServerOptions.onShutdown} callback
34
- * after responding.
35
- *
36
- * @param options - Server options.
37
- * @param options.socketPath - The Unix socket path to listen on.
38
- * @param options.handlers - Map of RPC method names to handler functions.
39
- * @param options.onShutdown - Optional callback invoked when a `shutdown` RPC is received.
40
- * @param options.log - Optional logger for server-side diagnostics.
41
- * @returns A handle with a `close()` function for cleanup.
42
- */
43
- export declare function startRpcSocketServer({ socketPath, handlers, onShutdown, log, }: StartRpcSocketServerOptions): Promise<RpcSocketServerHandle>;
44
- //# sourceMappingURL=rpc-socket-server.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rpc-socket-server.d.cts","sourceRoot":"","sources":["../../src/daemon/rpc-socket-server.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAgB;AAK7C;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,QAAQ,EAAE,aAAa,CAAC;IACxB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/C;;;;;OAKG;IACH,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC/C,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,UAAU,EACV,QAAQ,EACR,UAAU,EACV,GAAG,GACJ,EAAE,2BAA2B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAqF9D"}
@@ -1,44 +0,0 @@
1
- import type { RpcHandlerMap } from "./types.mjs";
2
- /**
3
- * Handle returned by {@link startRpcSocketServer}.
4
- */
5
- export type RpcSocketServerHandle = {
6
- close: () => Promise<void>;
7
- };
8
- /**
9
- * Options for {@link startRpcSocketServer}.
10
- */
11
- export type StartRpcSocketServerOptions = {
12
- /** The Unix socket path to listen on. */
13
- socketPath: string;
14
- /** Map of RPC method names to handler functions. */
15
- handlers: RpcHandlerMap;
16
- /** Callback invoked when a `shutdown` RPC is received. */
17
- onShutdown?: (() => Promise<void>) | undefined;
18
- /**
19
- * Optional logger for server-side diagnostics (unexpected socket errors,
20
- * unhandled handler rejections, `onShutdown` callback failures). Without
21
- * this, failures fall back to `process.stderr.write`, which is discarded
22
- * when the daemon is spawned with `stdio: 'ignore'`.
23
- */
24
- log?: ((message: string) => void) | undefined;
25
- };
26
- /**
27
- * Start a Unix socket server that processes JSON-RPC requests.
28
- *
29
- * Each connection reads one newline-delimited JSON-RPC request, processes it
30
- * via the provided handler map, writes a JSON-RPC response, and closes.
31
- *
32
- * The special `shutdown` method is intercepted before handler dispatch and
33
- * triggers the provided {@link StartRpcSocketServerOptions.onShutdown} callback
34
- * after responding.
35
- *
36
- * @param options - Server options.
37
- * @param options.socketPath - The Unix socket path to listen on.
38
- * @param options.handlers - Map of RPC method names to handler functions.
39
- * @param options.onShutdown - Optional callback invoked when a `shutdown` RPC is received.
40
- * @param options.log - Optional logger for server-side diagnostics.
41
- * @returns A handle with a `close()` function for cleanup.
42
- */
43
- export declare function startRpcSocketServer({ socketPath, handlers, onShutdown, log, }: StartRpcSocketServerOptions): Promise<RpcSocketServerHandle>;
44
- //# sourceMappingURL=rpc-socket-server.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rpc-socket-server.d.mts","sourceRoot":"","sources":["../../src/daemon/rpc-socket-server.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAgB;AAK7C;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,QAAQ,EAAE,aAAa,CAAC;IACxB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/C;;;;;OAKG;IACH,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC/C,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,UAAU,EACV,QAAQ,EACR,UAAU,EACV,GAAG,GACJ,EAAE,2BAA2B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAqF9D"}
@@ -1,267 +0,0 @@
1
- import { rpcErrors } from "@metamask/rpc-errors";
2
- import { hasProperty, isJsonRpcRequest } from "@metamask/utils";
3
- import { chmod, unlink } from "node:fs/promises";
4
- import { createServer } from "node:net";
5
- import { isErrorWithCode } from "./utils.mjs";
6
- const CONNECTION_TIMEOUT_MS = 30000;
7
- /**
8
- * Start a Unix socket server that processes JSON-RPC requests.
9
- *
10
- * Each connection reads one newline-delimited JSON-RPC request, processes it
11
- * via the provided handler map, writes a JSON-RPC response, and closes.
12
- *
13
- * The special `shutdown` method is intercepted before handler dispatch and
14
- * triggers the provided {@link StartRpcSocketServerOptions.onShutdown} callback
15
- * after responding.
16
- *
17
- * @param options - Server options.
18
- * @param options.socketPath - The Unix socket path to listen on.
19
- * @param options.handlers - Map of RPC method names to handler functions.
20
- * @param options.onShutdown - Optional callback invoked when a `shutdown` RPC is received.
21
- * @param options.log - Optional logger for server-side diagnostics.
22
- * @returns A handle with a `close()` function for cleanup.
23
- */
24
- export async function startRpcSocketServer({ socketPath, handlers, onShutdown, log, }) {
25
- const logFn = log ?? defaultLog;
26
- const server = createServer((socket) => {
27
- let buffer = '';
28
- // Destroy connections that never send a complete request line. `unref` so
29
- // the timer alone cannot keep the event loop alive at shutdown.
30
- const timer = setTimeout(() => {
31
- socket.destroy();
32
- }, CONNECTION_TIMEOUT_MS);
33
- timer.unref();
34
- /**
35
- * Clear the idle-connection timer. Called from data, close, and error
36
- * paths so the timer never outlives the connection itself.
37
- */
38
- const clearIdleTimer = () => {
39
- clearTimeout(timer);
40
- };
41
- const onData = (data) => {
42
- buffer += data.toString();
43
- const idx = buffer.indexOf('\n');
44
- if (idx === -1) {
45
- return;
46
- }
47
- clearIdleTimer();
48
- // One request per connection.
49
- socket.removeListener('data', onData);
50
- const line = buffer.slice(0, idx);
51
- const remaining = buffer.slice(idx + 1);
52
- buffer = '';
53
- if (remaining.length > 0) {
54
- socket.end(`${JSON.stringify({
55
- jsonrpc: '2.0',
56
- error: rpcErrors
57
- .invalidRequest({
58
- message: 'Only one request per connection is allowed',
59
- })
60
- .serialize(),
61
- })}\n`);
62
- return;
63
- }
64
- handleRequest(handlers, line, onShutdown, logFn)
65
- .then((response) => {
66
- socket.end(`${JSON.stringify(response)}\n`);
67
- return undefined;
68
- })
69
- .catch((dispatchError) => {
70
- logFn(`Unhandled RPC dispatch error: ${String(dispatchError)}`);
71
- socket.end(`${JSON.stringify({
72
- jsonrpc: '2.0',
73
- error: rpcErrors
74
- .internal({ message: 'Internal error' })
75
- .serialize(),
76
- })}\n`);
77
- });
78
- };
79
- socket.on('data', onData);
80
- socket.once('close', clearIdleTimer);
81
- socket.on('error', (socketError) => {
82
- clearIdleTimer();
83
- const { code } = socketError;
84
- if (code === 'EPIPE' || code === 'ECONNRESET') {
85
- return; // Expected during probe/disconnect.
86
- }
87
- logFn(`Unexpected socket error: ${String(socketError)}`);
88
- });
89
- });
90
- await listen(server, socketPath);
91
- return {
92
- close: async () => closeServer(server),
93
- };
94
- }
95
- /**
96
- * Default fallback logger: writes to stderr. Daemons spawned with
97
- * `stdio: 'ignore'` should always pass an explicit `log`.
98
- *
99
- * @param message - The message to log.
100
- */
101
- function defaultLog(message) {
102
- process.stderr.write(`${message}\n`);
103
- }
104
- /**
105
- * Handle a single JSON-RPC request line, intercepting the `shutdown` method.
106
- *
107
- * @param handlers - The RPC handler map.
108
- * @param line - The raw JSON line from the socket.
109
- * @param onShutdown - Optional shutdown callback.
110
- * @param log - Logger for diagnostic messages.
111
- * @returns A JSON-RPC response object.
112
- */
113
- async function handleRequest(handlers, line, onShutdown, log) {
114
- let parsed;
115
- try {
116
- parsed = JSON.parse(line);
117
- }
118
- catch {
119
- return {
120
- jsonrpc: '2.0',
121
- id: null,
122
- error: rpcErrors.parse({ message: 'Parse error' }).serialize(),
123
- };
124
- }
125
- if (!isJsonRpcRequest(parsed)) {
126
- const id = typeof parsed === 'object' &&
127
- parsed !== null &&
128
- hasProperty(parsed, 'id') &&
129
- isValidJsonRpcId(parsed.id)
130
- ? parsed.id
131
- : null;
132
- return {
133
- jsonrpc: '2.0',
134
- id,
135
- error: rpcErrors
136
- .invalidRequest({ message: 'Invalid JSON-RPC request' })
137
- .serialize(),
138
- };
139
- }
140
- const { id, method, params } = parsed;
141
- try {
142
- if (method === 'shutdown') {
143
- if (onShutdown) {
144
- setTimeout(() => {
145
- onShutdown().catch((error) => {
146
- log(`onShutdown callback failed: ${String(error)}`);
147
- });
148
- }, 0);
149
- }
150
- return { jsonrpc: '2.0', id, result: { status: 'shutting down' } };
151
- }
152
- const handler = Object.prototype.hasOwnProperty.call(handlers, method)
153
- ? handlers[method]
154
- : undefined;
155
- if (!handler) {
156
- return {
157
- jsonrpc: '2.0',
158
- id,
159
- error: rpcErrors
160
- .methodNotFound({ message: `Method not found: ${method}` })
161
- .serialize(),
162
- };
163
- }
164
- const result = await handler(coerceHandlerParams(params));
165
- return { jsonrpc: '2.0', id, result: result ?? null };
166
- }
167
- catch (error) {
168
- log(`RPC handler "${method}" failed: ${String(error)}`);
169
- if (isRpcError(error)) {
170
- return { jsonrpc: '2.0', id, error };
171
- }
172
- const message = error instanceof Error ? error.message : 'Internal error';
173
- return {
174
- jsonrpc: '2.0',
175
- id,
176
- error: rpcErrors.internal({ message }).serialize(),
177
- };
178
- }
179
- }
180
- /**
181
- * Narrow `params` to the shape handlers expect. JSON-RPC 2.0 requires
182
- * `params`, when present, to be an array or object; both are valid `Json`.
183
- *
184
- * @param params - The validated `params` field from a JSON-RPC request.
185
- * @returns The same value, or `null` when absent.
186
- */
187
- function coerceHandlerParams(params) {
188
- return params ?? null;
189
- }
190
- /**
191
- * Per JSON-RPC 2.0, `id` must be a string, number, or null. Used when
192
- * salvaging an `id` from a parse-success-but-not-valid-request payload.
193
- *
194
- * @param value - The candidate id.
195
- * @returns True if the value is an acceptable JSON-RPC id.
196
- */
197
- function isValidJsonRpcId(value) {
198
- return (value === null || typeof value === 'string' || typeof value === 'number');
199
- }
200
- /**
201
- * Check if an error is an RPC error with a numeric code.
202
- *
203
- * @param error - The error to check.
204
- * @returns True if the error has a numeric code property.
205
- */
206
- function isRpcError(error) {
207
- return (typeof error === 'object' &&
208
- error !== null &&
209
- hasProperty(error, 'code') &&
210
- typeof error.code === 'number' &&
211
- hasProperty(error, 'message') &&
212
- typeof error.message === 'string');
213
- }
214
- /**
215
- * Start listening on a Unix socket path, removing any stale socket file.
216
- *
217
- * @param server - The net.Server instance.
218
- * @param socketPath - The Unix socket path.
219
- */
220
- async function listen(server, socketPath) {
221
- try {
222
- await unlink(socketPath);
223
- }
224
- catch (error) {
225
- if (!isErrorWithCode(error, 'ENOENT')) {
226
- throw error;
227
- }
228
- }
229
- await new Promise((resolve, reject) => {
230
- server.on('error', reject);
231
- server.listen(socketPath, () => {
232
- server.removeListener('error', reject);
233
- resolve();
234
- });
235
- });
236
- // Restrict the socket to its owner. The daemon hosts an unlocked wallet, so
237
- // a world-connectable socket would let any local user drive it. listen()
238
- // creates the socket with umask-derived (typically world-accessible) perms.
239
- try {
240
- await chmod(socketPath, 0o600);
241
- }
242
- catch (error) {
243
- // Never leave a possibly world-accessible socket listening with no handle
244
- // to close it. Cleanup is best-effort so the chmod failure still surfaces.
245
- await closeServer(server).catch(() => undefined);
246
- await unlink(socketPath).catch(() => undefined);
247
- throw error;
248
- }
249
- }
250
- /**
251
- * Close a server, resolving once it stops accepting connections.
252
- *
253
- * @param server - The net.Server instance.
254
- */
255
- async function closeServer(server) {
256
- await new Promise((resolve, reject) => {
257
- server.close((error) => {
258
- if (error) {
259
- reject(error);
260
- }
261
- else {
262
- resolve();
263
- }
264
- });
265
- });
266
- }
267
- //# sourceMappingURL=rpc-socket-server.mjs.map