@metamask-previews/wallet-cli 0.0.0-preview-b8d04f274 → 0.0.0-preview-1ccf11bcb

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 (75) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +13 -1
  3. package/dist/commands/daemon/start.cjs +7 -3
  4. package/dist/commands/daemon/start.cjs.map +1 -1
  5. package/dist/commands/daemon/start.d.cts +1 -1
  6. package/dist/commands/daemon/start.d.cts.map +1 -1
  7. package/dist/commands/daemon/start.d.mts +1 -1
  8. package/dist/commands/daemon/start.d.mts.map +1 -1
  9. package/dist/commands/daemon/start.mjs +7 -3
  10. package/dist/commands/daemon/start.mjs.map +1 -1
  11. package/dist/commands/wallet/unlock.cjs +75 -0
  12. package/dist/commands/wallet/unlock.cjs.map +1 -0
  13. package/dist/commands/wallet/unlock.d.cts +11 -0
  14. package/dist/commands/wallet/unlock.d.cts.map +1 -0
  15. package/dist/commands/wallet/unlock.d.mts +11 -0
  16. package/dist/commands/wallet/unlock.d.mts.map +1 -0
  17. package/dist/commands/wallet/unlock.mjs +73 -0
  18. package/dist/commands/wallet/unlock.mjs.map +1 -0
  19. package/dist/daemon/daemon-entry.cjs +52 -27
  20. package/dist/daemon/daemon-entry.cjs.map +1 -1
  21. package/dist/daemon/daemon-entry.mjs +52 -27
  22. package/dist/daemon/daemon-entry.mjs.map +1 -1
  23. package/dist/daemon/daemon-spawn.cjs +19 -8
  24. package/dist/daemon/daemon-spawn.cjs.map +1 -1
  25. package/dist/daemon/daemon-spawn.d.cts.map +1 -1
  26. package/dist/daemon/daemon-spawn.d.mts.map +1 -1
  27. package/dist/daemon/daemon-spawn.mjs +19 -8
  28. package/dist/daemon/daemon-spawn.mjs.map +1 -1
  29. package/dist/daemon/prompts.cjs +17 -1
  30. package/dist/daemon/prompts.cjs.map +1 -1
  31. package/dist/daemon/prompts.d.cts +9 -0
  32. package/dist/daemon/prompts.d.cts.map +1 -1
  33. package/dist/daemon/prompts.d.mts +9 -0
  34. package/dist/daemon/prompts.d.mts.map +1 -1
  35. package/dist/daemon/prompts.mjs +15 -0
  36. package/dist/daemon/prompts.mjs.map +1 -1
  37. package/dist/daemon/rpc-socket-server.cjs +14 -1
  38. package/dist/daemon/rpc-socket-server.cjs.map +1 -1
  39. package/dist/daemon/rpc-socket-server.d.cts.map +1 -1
  40. package/dist/daemon/rpc-socket-server.d.mts.map +1 -1
  41. package/dist/daemon/rpc-socket-server.mjs +14 -1
  42. package/dist/daemon/rpc-socket-server.mjs.map +1 -1
  43. package/dist/daemon/secrets.cjs +137 -0
  44. package/dist/daemon/secrets.cjs.map +1 -0
  45. package/dist/daemon/secrets.d.cts +72 -0
  46. package/dist/daemon/secrets.d.cts.map +1 -0
  47. package/dist/daemon/secrets.d.mts +72 -0
  48. package/dist/daemon/secrets.d.mts.map +1 -0
  49. package/dist/daemon/secrets.mjs +132 -0
  50. package/dist/daemon/secrets.mjs.map +1 -0
  51. package/dist/daemon/types.cjs +17 -0
  52. package/dist/daemon/types.cjs.map +1 -1
  53. package/dist/daemon/types.d.cts +58 -7
  54. package/dist/daemon/types.d.cts.map +1 -1
  55. package/dist/daemon/types.d.mts +58 -7
  56. package/dist/daemon/types.d.mts.map +1 -1
  57. package/dist/daemon/types.mjs +15 -1
  58. package/dist/daemon/types.mjs.map +1 -1
  59. package/dist/daemon/utils.cjs +14 -2
  60. package/dist/daemon/utils.cjs.map +1 -1
  61. package/dist/daemon/utils.d.cts +9 -0
  62. package/dist/daemon/utils.d.cts.map +1 -1
  63. package/dist/daemon/utils.d.mts +9 -0
  64. package/dist/daemon/utils.d.mts.map +1 -1
  65. package/dist/daemon/utils.mjs +12 -1
  66. package/dist/daemon/utils.mjs.map +1 -1
  67. package/dist/daemon/wallet-factory.cjs +50 -8
  68. package/dist/daemon/wallet-factory.cjs.map +1 -1
  69. package/dist/daemon/wallet-factory.d.cts +17 -8
  70. package/dist/daemon/wallet-factory.d.cts.map +1 -1
  71. package/dist/daemon/wallet-factory.d.mts +17 -8
  72. package/dist/daemon/wallet-factory.d.mts.map +1 -1
  73. package/dist/daemon/wallet-factory.mjs +50 -8
  74. package/dist/daemon/wallet-factory.mjs.map +1 -1
  75. package/package.json +7 -2
package/CHANGELOG.md CHANGED
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
+ - Wire the `transactionController` slot in the daemon wallet's instance options, so the daemon runs the `TransactionController` with an explicit CLI-appropriate configuration (swaps processing disabled, no client hooks) rather than relying on the controller's implicit defaults ([#9509](https://github.com/MetaMask/core/pull/9509))
13
+ - Add the `mm wallet unlock` command, which dispatches `KeyringController:submitPassword` over the daemon socket, allowing the keyring to be unlocked after a daemon start with no password or after a `mm daemon call KeyringController:setLocked` ([#8821](https://github.com/MetaMask/core/pull/8821))
12
14
  - Add the `mm daemon list` command, which prints the messenger actions the running daemon can dispatch via `daemon call`, enumerated from the live messenger so the list cannot drift from what `call` accepts ([#9339](https://github.com/MetaMask/core/pull/9339))
13
15
  - Add the `mm daemon` command suite (`start`, `stop`, `status`, `purge`, and `call`) for running the wallet daemon and dispatching messenger actions over its socket ([#9255](https://github.com/MetaMask/core/pull/9255))
14
16
  - Add a wallet factory and daemon entry point that construct a `@metamask/wallet` `Wallet` backed by the SQLite key-value store, hydrate it from persisted state, run controller initialization (aborting startup if any step fails), import the secret recovery phrase on first run, and expose a `dispose` teardown handle ([#9226](https://github.com/MetaMask/core/pull/9226))
@@ -18,7 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
20
 
19
21
  ### Changed
20
22
 
23
+ - `--password` / `MM_WALLET_PASSWORD` is now optional on `mm daemon start`; on subsequent runs, omitting it starts the daemon with a locked keyring, and the persisted vault is auto-unlocked when a password is supplied ([#8821](https://github.com/MetaMask/core/pull/8821))
24
+ - The daemon RPC server now validates `params` against each handler's superstruct before dispatch, returning a `-32602 invalidParams` error on mismatch instead of passing raw params to the handler ([#8846](https://github.com/MetaMask/core/pull/8846))
21
25
  - Report daemon socket connection errors consistently across `mm daemon call` and `mm daemon list` ([#9339](https://github.com/MetaMask/core/pull/9339))
22
26
  - Bump `@metamask/wallet` from `^3.0.0` to `^7.0.1` ([#9218](https://github.com/MetaMask/core/pull/9218), [#9263](https://github.com/MetaMask/core/pull/9263), [#9349](https://github.com/MetaMask/core/pull/9349), [#9396](https://github.com/MetaMask/core/pull/9396), [#9470](https://github.com/MetaMask/core/pull/9470))
27
+ - Wrap daemon password and SRP in opaque `Password` and `Srp` types that redact on logging; validated and unwrapped only at trust boundaries ([#8863](https://github.com/MetaMask/core/pull/8863))
23
28
 
24
29
  [Unreleased]: https://github.com/MetaMask/core/
package/README.md CHANGED
@@ -12,12 +12,24 @@ or
12
12
 
13
13
  ## Usage
14
14
 
15
- The CLI drives a long-lived background **daemon** that holds an unlocked `@metamask/wallet` in memory and exposes its messenger over a per-user Unix socket. All commands live under the `mm daemon` topic; run `mm --help` (or `mm daemon <command> --help`) for the full reference.
15
+ The CLI drives a long-lived background daemon that holds a `@metamask/wallet` in memory and exposes its messenger over a per-user Unix socket. All commands live under the `mm daemon` and `mm wallet` topics; run `mm --help` (or `mm <topic> <command> --help`) for the full reference.
16
16
 
17
17
  Start the daemon (flags may also be supplied as the `INFURA_PROJECT_ID`, `MM_WALLET_PASSWORD`, and `MM_WALLET_SRP` environment variables — preferred for secrets):
18
18
 
19
19
  ```sh
20
+ # First run — password required to import the secret recovery phrase:
20
21
  mm daemon start --infura-project-id <key> --password <pw> --srp "<phrase>"
22
+
23
+ # Subsequent runs — password optional. Omit to start with a locked keyring
24
+ # and unlock later with `mm wallet unlock`:
25
+ mm daemon start --infura-project-id <key> --srp "<phrase>"
26
+ ```
27
+
28
+ Unlock the keyring after a password-less start (or after `KeyringController:setLocked`):
29
+
30
+ ```sh
31
+ mm wallet unlock --password <pw> # or: MM_WALLET_PASSWORD=<pw> mm wallet unlock
32
+ mm wallet unlock # prompts interactively (input masked)
21
33
  ```
22
34
 
23
35
  Discover what the running wallet can do — `list` prints every messenger action currently dispatchable via `call`. This surface grows as more controllers are wired, so treat it as evolving rather than a stability contract:
@@ -2,11 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const daemon_spawn_1 = require("../../daemon/daemon-spawn.cjs");
5
+ const secrets_1 = require("../../daemon/secrets.cjs");
5
6
  class DaemonStart extends core_1.Command {
6
7
  async run() {
7
8
  const { flags } = await this.parse(DaemonStart);
8
9
  const infuraProjectId = flags['infura-project-id'];
9
- const { password, srp } = flags;
10
+ const password = flags.password ? secrets_1.Password.from(flags.password) : undefined;
11
+ const srp = secrets_1.Srp.from(flags.srp);
10
12
  const { state, socketPath } = await (0, daemon_spawn_1.ensureDaemon)({
11
13
  dataDir: this.config.dataDir,
12
14
  infuraProjectId,
@@ -26,6 +28,7 @@ DaemonStart.description = 'Start the wallet daemon';
26
28
  DaemonStart.examples = [
27
29
  '<%= config.bin %> daemon start --infura-project-id <key> --password <pw> --srp <phrase>',
28
30
  'INFURA_PROJECT_ID=<key> MM_WALLET_PASSWORD=<pw> MM_WALLET_SRP=<phrase> <%= config.bin %> daemon start',
31
+ '<%= config.bin %> daemon start --infura-project-id <key> --srp <phrase> # then `mm wallet unlock` later',
29
32
  ];
30
33
  DaemonStart.flags = {
31
34
  'infura-project-id': core_1.Flags.string({
@@ -34,9 +37,10 @@ DaemonStart.flags = {
34
37
  required: true,
35
38
  }),
36
39
  password: core_1.Flags.string({
37
- description: 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production)',
40
+ description: 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production). ' +
41
+ 'Required on first run; on subsequent runs, omit (and leave MM_WALLET_PASSWORD unset) to start with a locked keyring and use `mm wallet unlock`.',
38
42
  env: 'MM_WALLET_PASSWORD',
39
- required: true,
43
+ required: false,
40
44
  }),
41
45
  srp: core_1.Flags.string({
42
46
  description: 'Secret recovery phrase (testing only — use MM_WALLET_SRP env var in production)',
@@ -1 +1 @@
1
- {"version":3,"file":"start.cjs","sourceRoot":"","sources":["../../../src/commands/daemon/start.ts"],"names":[],"mappings":";;AAAA,sCAA6C;AAE7C,gEAAyD;AAEzD,MAAqB,WAAY,SAAQ,cAAO;IA4BvC,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChD,MAAM,eAAe,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;QAEhC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,IAAA,2BAAY,EAAC;YAC/C,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,eAAe;YACf,QAAQ;YACR,GAAG;YACH,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;SAC9B,CAAC,CAAC;QAEH,IAAI,KAAK,KAAK,iBAAiB,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,CACN,mCAAmC,UAAU,IAAI;gBAC/C,6FAA6F,CAChG,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;IACpD,CAAC;;AAjDe,uBAAW,GAAG,yBAAyB,CAAC;AAExC,oBAAQ,GAAG;IACzB,yFAAyF;IACzF,uGAAuG;CACxG,CAAC;AAEc,iBAAK,GAAG;IACtB,mBAAmB,EAAE,YAAK,CAAC,MAAM,CAAC;QAChC,WAAW,EAAE,sCAAsC;QACnD,GAAG,EAAE,mBAAmB;QACxB,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,QAAQ,EAAE,YAAK,CAAC,MAAM,CAAC;QACrB,WAAW,EACT,+EAA+E;QACjF,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,GAAG,EAAE,YAAK,CAAC,MAAM,CAAC;QAChB,WAAW,EACT,iFAAiF;QACnF,GAAG,EAAE,eAAe;QACpB,QAAQ,EAAE,IAAI;KACf,CAAC;CACH,CAAC;kBA1BiB,WAAW","sourcesContent":["import { Command, Flags } from '@oclif/core';\n\nimport { ensureDaemon } from '../../daemon/daemon-spawn';\n\nexport default class DaemonStart extends Command {\n static override description = 'Start the wallet daemon';\n\n static override examples = [\n '<%= config.bin %> daemon start --infura-project-id <key> --password <pw> --srp <phrase>',\n 'INFURA_PROJECT_ID=<key> MM_WALLET_PASSWORD=<pw> MM_WALLET_SRP=<phrase> <%= config.bin %> daemon start',\n ];\n\n static override flags = {\n 'infura-project-id': Flags.string({\n description: 'Infura project ID for network access',\n env: 'INFURA_PROJECT_ID',\n required: true,\n }),\n password: Flags.string({\n description:\n 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production)',\n env: 'MM_WALLET_PASSWORD',\n required: true,\n }),\n srp: Flags.string({\n description:\n 'Secret recovery phrase (testing only — use MM_WALLET_SRP env var in production)',\n env: 'MM_WALLET_SRP',\n required: true,\n }),\n };\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(DaemonStart);\n const infuraProjectId = flags['infura-project-id'];\n const { password, srp } = flags;\n\n const { state, socketPath } = await ensureDaemon({\n dataDir: this.config.dataDir,\n infuraProjectId,\n password,\n srp,\n packageRoot: this.config.root,\n });\n\n if (state === 'already-running') {\n this.log(\n `Daemon already running. Socket: ${socketPath}. ` +\n `The provided flags were not applied; run \\`mm daemon stop\\` and start again to change them.`,\n );\n return;\n }\n\n this.log(`Daemon running. Socket: ${socketPath}`);\n }\n}\n"]}
1
+ {"version":3,"file":"start.cjs","sourceRoot":"","sources":["../../../src/commands/daemon/start.ts"],"names":[],"mappings":";;AAAA,sCAA6C;AAE7C,gEAAyD;AACzD,sDAAqD;AAErD,MAAqB,WAAY,SAAQ,cAAO;IA8BvC,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChD,MAAM,eAAe,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,kBAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,MAAM,GAAG,GAAG,aAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEhC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,IAAA,2BAAY,EAAC;YAC/C,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,eAAe;YACf,QAAQ;YACR,GAAG;YACH,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;SAC9B,CAAC,CAAC;QAEH,IAAI,KAAK,KAAK,iBAAiB,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,CACN,mCAAmC,UAAU,IAAI;gBAC/C,6FAA6F,CAChG,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;IACpD,CAAC;;AApDe,uBAAW,GAAG,yBAAyB,CAAC;AAExC,oBAAQ,GAAG;IACzB,yFAAyF;IACzF,uGAAuG;IACvG,2GAA2G;CAC5G,CAAC;AAEc,iBAAK,GAAG;IACtB,mBAAmB,EAAE,YAAK,CAAC,MAAM,CAAC;QAChC,WAAW,EAAE,sCAAsC;QACnD,GAAG,EAAE,mBAAmB;QACxB,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,QAAQ,EAAE,YAAK,CAAC,MAAM,CAAC;QACrB,WAAW,EACT,iFAAiF;YACjF,iJAAiJ;QACnJ,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,KAAK;KAChB,CAAC;IACF,GAAG,EAAE,YAAK,CAAC,MAAM,CAAC;QAChB,WAAW,EACT,iFAAiF;QACnF,GAAG,EAAE,eAAe;QACpB,QAAQ,EAAE,IAAI;KACf,CAAC;CACH,CAAC;kBA5BiB,WAAW","sourcesContent":["import { Command, Flags } from '@oclif/core';\n\nimport { ensureDaemon } from '../../daemon/daemon-spawn';\nimport { Password, Srp } from '../../daemon/secrets';\n\nexport default class DaemonStart extends Command {\n static override description = 'Start the wallet daemon';\n\n static override examples = [\n '<%= config.bin %> daemon start --infura-project-id <key> --password <pw> --srp <phrase>',\n 'INFURA_PROJECT_ID=<key> MM_WALLET_PASSWORD=<pw> MM_WALLET_SRP=<phrase> <%= config.bin %> daemon start',\n '<%= config.bin %> daemon start --infura-project-id <key> --srp <phrase> # then `mm wallet unlock` later',\n ];\n\n static override flags = {\n 'infura-project-id': Flags.string({\n description: 'Infura project ID for network access',\n env: 'INFURA_PROJECT_ID',\n required: true,\n }),\n password: Flags.string({\n description:\n 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production). ' +\n 'Required on first run; on subsequent runs, omit (and leave MM_WALLET_PASSWORD unset) to start with a locked keyring and use `mm wallet unlock`.',\n env: 'MM_WALLET_PASSWORD',\n required: false,\n }),\n srp: Flags.string({\n description:\n 'Secret recovery phrase (testing only — use MM_WALLET_SRP env var in production)',\n env: 'MM_WALLET_SRP',\n required: true,\n }),\n };\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(DaemonStart);\n const infuraProjectId = flags['infura-project-id'];\n const password = flags.password ? Password.from(flags.password) : undefined;\n const srp = Srp.from(flags.srp);\n\n const { state, socketPath } = await ensureDaemon({\n dataDir: this.config.dataDir,\n infuraProjectId,\n password,\n srp,\n packageRoot: this.config.root,\n });\n\n if (state === 'already-running') {\n this.log(\n `Daemon already running. Socket: ${socketPath}. ` +\n `The provided flags were not applied; run \\`mm daemon stop\\` and start again to change them.`,\n );\n return;\n }\n\n this.log(`Daemon running. Socket: ${socketPath}`);\n }\n}\n"]}
@@ -4,7 +4,7 @@ export default class DaemonStart extends Command {
4
4
  static examples: string[];
5
5
  static flags: {
6
6
  'infura-project-id': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
- password: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ password: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  srp: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
9
  };
10
10
  run(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.cts","sourceRoot":"","sources":["../../../src/commands/daemon/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,oBAAoB;AAI7C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,OAAgB,WAAW,SAA6B;IAExD,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,KAAK;;;;MAkBnB;IAEW,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAuBlC"}
1
+ {"version":3,"file":"start.d.cts","sourceRoot":"","sources":["../../../src/commands/daemon/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,oBAAoB;AAK7C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,OAAgB,WAAW,SAA6B;IAExD,OAAgB,QAAQ,WAItB;IAEF,OAAgB,KAAK;;;;MAmBnB;IAEW,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAwBlC"}
@@ -4,7 +4,7 @@ export default class DaemonStart extends Command {
4
4
  static examples: string[];
5
5
  static flags: {
6
6
  'infura-project-id': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
- password: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ password: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  srp: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
9
  };
10
10
  run(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.mts","sourceRoot":"","sources":["../../../src/commands/daemon/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,oBAAoB;AAI7C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,OAAgB,WAAW,SAA6B;IAExD,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,KAAK;;;;MAkBnB;IAEW,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAuBlC"}
1
+ {"version":3,"file":"start.d.mts","sourceRoot":"","sources":["../../../src/commands/daemon/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,oBAAoB;AAK7C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,OAAgB,WAAW,SAA6B;IAExD,OAAgB,QAAQ,WAItB;IAEF,OAAgB,KAAK;;;;MAmBnB;IAEW,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAwBlC"}
@@ -1,10 +1,12 @@
1
1
  import { Command, Flags } from "@oclif/core";
2
2
  import { ensureDaemon } from "../../daemon/daemon-spawn.mjs";
3
+ import { Password, Srp } from "../../daemon/secrets.mjs";
3
4
  class DaemonStart extends Command {
4
5
  async run() {
5
6
  const { flags } = await this.parse(DaemonStart);
6
7
  const infuraProjectId = flags['infura-project-id'];
7
- const { password, srp } = flags;
8
+ const password = flags.password ? Password.from(flags.password) : undefined;
9
+ const srp = Srp.from(flags.srp);
8
10
  const { state, socketPath } = await ensureDaemon({
9
11
  dataDir: this.config.dataDir,
10
12
  infuraProjectId,
@@ -24,6 +26,7 @@ DaemonStart.description = 'Start the wallet daemon';
24
26
  DaemonStart.examples = [
25
27
  '<%= config.bin %> daemon start --infura-project-id <key> --password <pw> --srp <phrase>',
26
28
  'INFURA_PROJECT_ID=<key> MM_WALLET_PASSWORD=<pw> MM_WALLET_SRP=<phrase> <%= config.bin %> daemon start',
29
+ '<%= config.bin %> daemon start --infura-project-id <key> --srp <phrase> # then `mm wallet unlock` later',
27
30
  ];
28
31
  DaemonStart.flags = {
29
32
  'infura-project-id': Flags.string({
@@ -32,9 +35,10 @@ DaemonStart.flags = {
32
35
  required: true,
33
36
  }),
34
37
  password: Flags.string({
35
- description: 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production)',
38
+ description: 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production). ' +
39
+ 'Required on first run; on subsequent runs, omit (and leave MM_WALLET_PASSWORD unset) to start with a locked keyring and use `mm wallet unlock`.',
36
40
  env: 'MM_WALLET_PASSWORD',
37
- required: true,
41
+ required: false,
38
42
  }),
39
43
  srp: Flags.string({
40
44
  description: 'Secret recovery phrase (testing only — use MM_WALLET_SRP env var in production)',
@@ -1 +1 @@
1
- {"version":3,"file":"start.mjs","sourceRoot":"","sources":["../../../src/commands/daemon/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB;AAE7C,OAAO,EAAE,YAAY,EAAE,sCAAkC;AAEzD,MAAqB,WAAY,SAAQ,OAAO;IA4BvC,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChD,MAAM,eAAe,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;QAEhC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,YAAY,CAAC;YAC/C,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,eAAe;YACf,QAAQ;YACR,GAAG;YACH,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;SAC9B,CAAC,CAAC;QAEH,IAAI,KAAK,KAAK,iBAAiB,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,CACN,mCAAmC,UAAU,IAAI;gBAC/C,6FAA6F,CAChG,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;IACpD,CAAC;;AAjDe,uBAAW,GAAG,yBAAyB,CAAC;AAExC,oBAAQ,GAAG;IACzB,yFAAyF;IACzF,uGAAuG;CACxG,CAAC;AAEc,iBAAK,GAAG;IACtB,mBAAmB,EAAE,KAAK,CAAC,MAAM,CAAC;QAChC,WAAW,EAAE,sCAAsC;QACnD,GAAG,EAAE,mBAAmB;QACxB,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC;QACrB,WAAW,EACT,+EAA+E;QACjF,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC;QAChB,WAAW,EACT,iFAAiF;QACnF,GAAG,EAAE,eAAe;QACpB,QAAQ,EAAE,IAAI;KACf,CAAC;CACH,CAAC;eA1BiB,WAAW","sourcesContent":["import { Command, Flags } from '@oclif/core';\n\nimport { ensureDaemon } from '../../daemon/daemon-spawn';\n\nexport default class DaemonStart extends Command {\n static override description = 'Start the wallet daemon';\n\n static override examples = [\n '<%= config.bin %> daemon start --infura-project-id <key> --password <pw> --srp <phrase>',\n 'INFURA_PROJECT_ID=<key> MM_WALLET_PASSWORD=<pw> MM_WALLET_SRP=<phrase> <%= config.bin %> daemon start',\n ];\n\n static override flags = {\n 'infura-project-id': Flags.string({\n description: 'Infura project ID for network access',\n env: 'INFURA_PROJECT_ID',\n required: true,\n }),\n password: Flags.string({\n description:\n 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production)',\n env: 'MM_WALLET_PASSWORD',\n required: true,\n }),\n srp: Flags.string({\n description:\n 'Secret recovery phrase (testing only — use MM_WALLET_SRP env var in production)',\n env: 'MM_WALLET_SRP',\n required: true,\n }),\n };\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(DaemonStart);\n const infuraProjectId = flags['infura-project-id'];\n const { password, srp } = flags;\n\n const { state, socketPath } = await ensureDaemon({\n dataDir: this.config.dataDir,\n infuraProjectId,\n password,\n srp,\n packageRoot: this.config.root,\n });\n\n if (state === 'already-running') {\n this.log(\n `Daemon already running. Socket: ${socketPath}. ` +\n `The provided flags were not applied; run \\`mm daemon stop\\` and start again to change them.`,\n );\n return;\n }\n\n this.log(`Daemon running. Socket: ${socketPath}`);\n }\n}\n"]}
1
+ {"version":3,"file":"start.mjs","sourceRoot":"","sources":["../../../src/commands/daemon/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB;AAE7C,OAAO,EAAE,YAAY,EAAE,sCAAkC;AACzD,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,iCAA6B;AAErD,MAAqB,WAAY,SAAQ,OAAO;IA8BvC,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChD,MAAM,eAAe,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEhC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,YAAY,CAAC;YAC/C,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,eAAe;YACf,QAAQ;YACR,GAAG;YACH,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;SAC9B,CAAC,CAAC;QAEH,IAAI,KAAK,KAAK,iBAAiB,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,CACN,mCAAmC,UAAU,IAAI;gBAC/C,6FAA6F,CAChG,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;IACpD,CAAC;;AApDe,uBAAW,GAAG,yBAAyB,CAAC;AAExC,oBAAQ,GAAG;IACzB,yFAAyF;IACzF,uGAAuG;IACvG,2GAA2G;CAC5G,CAAC;AAEc,iBAAK,GAAG;IACtB,mBAAmB,EAAE,KAAK,CAAC,MAAM,CAAC;QAChC,WAAW,EAAE,sCAAsC;QACnD,GAAG,EAAE,mBAAmB;QACxB,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC;QACrB,WAAW,EACT,iFAAiF;YACjF,iJAAiJ;QACnJ,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,KAAK;KAChB,CAAC;IACF,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC;QAChB,WAAW,EACT,iFAAiF;QACnF,GAAG,EAAE,eAAe;QACpB,QAAQ,EAAE,IAAI;KACf,CAAC;CACH,CAAC;eA5BiB,WAAW","sourcesContent":["import { Command, Flags } from '@oclif/core';\n\nimport { ensureDaemon } from '../../daemon/daemon-spawn';\nimport { Password, Srp } from '../../daemon/secrets';\n\nexport default class DaemonStart extends Command {\n static override description = 'Start the wallet daemon';\n\n static override examples = [\n '<%= config.bin %> daemon start --infura-project-id <key> --password <pw> --srp <phrase>',\n 'INFURA_PROJECT_ID=<key> MM_WALLET_PASSWORD=<pw> MM_WALLET_SRP=<phrase> <%= config.bin %> daemon start',\n '<%= config.bin %> daemon start --infura-project-id <key> --srp <phrase> # then `mm wallet unlock` later',\n ];\n\n static override flags = {\n 'infura-project-id': Flags.string({\n description: 'Infura project ID for network access',\n env: 'INFURA_PROJECT_ID',\n required: true,\n }),\n password: Flags.string({\n description:\n 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production). ' +\n 'Required on first run; on subsequent runs, omit (and leave MM_WALLET_PASSWORD unset) to start with a locked keyring and use `mm wallet unlock`.',\n env: 'MM_WALLET_PASSWORD',\n required: false,\n }),\n srp: Flags.string({\n description:\n 'Secret recovery phrase (testing only — use MM_WALLET_SRP env var in production)',\n env: 'MM_WALLET_SRP',\n required: true,\n }),\n };\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(DaemonStart);\n const infuraProjectId = flags['infura-project-id'];\n const password = flags.password ? Password.from(flags.password) : undefined;\n const srp = Srp.from(flags.srp);\n\n const { state, socketPath } = await ensureDaemon({\n dataDir: this.config.dataDir,\n infuraProjectId,\n password,\n srp,\n packageRoot: this.config.root,\n });\n\n if (state === 'already-running') {\n this.log(\n `Daemon already running. Socket: ${socketPath}. ` +\n `The provided flags were not applied; run \\`mm daemon stop\\` and start again to change them.`,\n );\n return;\n }\n\n this.log(`Daemon running. Socket: ${socketPath}`);\n }\n}\n"]}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@metamask/utils");
4
+ const core_1 = require("@oclif/core");
5
+ const daemon_client_1 = require("../../daemon/daemon-client.cjs");
6
+ const paths_1 = require("../../daemon/paths.cjs");
7
+ const prompts_1 = require("../../daemon/prompts.cjs");
8
+ const utils_2 = require("../../daemon/utils.cjs");
9
+ class WalletUnlock extends core_1.Command {
10
+ async run() {
11
+ const { flags } = await this.parse(WalletUnlock);
12
+ const { timeout: timeoutMs } = flags;
13
+ // `emptyToUndefined` collapses `--password ''` and `MM_WALLET_PASSWORD=''`
14
+ // to undefined so the prompt fires instead of sending an empty string.
15
+ const flagPassword = (0, utils_2.emptyToUndefined)(flags.password);
16
+ let password;
17
+ try {
18
+ password = flagPassword ?? (await (0, prompts_1.promptPassword)());
19
+ }
20
+ catch (error) {
21
+ // Only swallow ExitPromptError (@inquirer/core's Ctrl+C signal); any
22
+ // other rejection (import failure, internal prompt crash) should surface.
23
+ if (error instanceof Error && error.name === 'ExitPromptError') {
24
+ return;
25
+ }
26
+ throw error;
27
+ }
28
+ const { socketPath } = (0, paths_1.getDaemonPaths)(this.config.dataDir);
29
+ let response;
30
+ try {
31
+ response = await (0, daemon_client_1.sendCommand)({
32
+ socketPath,
33
+ method: 'call',
34
+ params: ['KeyringController:submitPassword', password],
35
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
36
+ });
37
+ }
38
+ catch (error) {
39
+ this.error((0, utils_2.makeDaemonConnectionError)(error));
40
+ }
41
+ if ((0, utils_1.isJsonRpcFailure)(response)) {
42
+ const { code, message, data } = response.error;
43
+ // `isJsonRpcFailure` already validates that `data` is JSON, so
44
+ // `JSON.stringify` cannot throw here.
45
+ const dataSuffix = data === undefined || data === null
46
+ ? ''
47
+ : ` data=${JSON.stringify(data)}`;
48
+ this.error(`Failed to unlock: ${message} (code ${String(code)})${dataSuffix}`);
49
+ }
50
+ this.log('Wallet unlocked.');
51
+ }
52
+ }
53
+ WalletUnlock.description = 'Unlock the wallet (submits the password to KeyringController). ' +
54
+ 'Use this after `mm daemon start` was run without `--password`, or ' +
55
+ 'after the keyring was locked via `KeyringController:setLocked`.';
56
+ WalletUnlock.examples = [
57
+ '<%= config.bin %> wallet unlock --password <pw>',
58
+ 'MM_WALLET_PASSWORD=<pw> <%= config.bin %> wallet unlock',
59
+ '<%= config.bin %> wallet unlock # prompts interactively',
60
+ ];
61
+ WalletUnlock.flags = {
62
+ password: core_1.Flags.string({
63
+ description: 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production). ' +
64
+ 'When omitted, the command prompts interactively.',
65
+ env: 'MM_WALLET_PASSWORD',
66
+ required: false,
67
+ }),
68
+ timeout: core_1.Flags.integer({
69
+ char: 't',
70
+ description: 'Response timeout in milliseconds',
71
+ required: false,
72
+ }),
73
+ };
74
+ exports.default = WalletUnlock;
75
+ //# sourceMappingURL=unlock.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlock.cjs","sourceRoot":"","sources":["../../../src/commands/wallet/unlock.ts"],"names":[],"mappings":";;AAAA,2CAAmD;AACnD,sCAA6C;AAE7C,kEAAyD;AACzD,kDAAoD;AACpD,sDAAsD;AACtD,kDAG4B;AAE5B,MAAqB,YAAa,SAAQ,cAAO;IA2BxC,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAErC,2EAA2E;QAC3E,uEAAuE;QACvE,MAAM,YAAY,GAAG,IAAA,wBAAgB,EAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtD,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,YAAY,IAAI,CAAC,MAAM,IAAA,wBAAc,GAAE,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,qEAAqE;YACrE,0EAA0E;YAC1E,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBAC/D,OAAO;YACT,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,GAAG,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAE3D,IAAI,QAAQ,CAAC;QACb,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,IAAA,2BAAW,EAAC;gBAC3B,UAAU;gBACV,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,CAAC,kCAAkC,EAAE,QAAQ,CAAC;gBACtD,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;aAClD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,IAAA,iCAAyB,EAAC,KAAK,CAAC,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,IAAA,wBAAgB,EAAC,QAAQ,CAAC,EAAE,CAAC;YAC/B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC;YAC/C,+DAA+D;YAC/D,sCAAsC;YACtC,MAAM,UAAU,GACd,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;gBACjC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,CACR,qBAAqB,OAAO,UAAU,MAAM,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,CACnE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC/B,CAAC;;AAzEe,wBAAW,GACzB,iEAAiE;IACjE,oEAAoE;IACpE,iEAAiE,CAAC;AAEpD,qBAAQ,GAAG;IACzB,iDAAiD;IACjD,yDAAyD;IACzD,2DAA2D;CAC5D,CAAC;AAEc,kBAAK,GAAG;IACtB,QAAQ,EAAE,YAAK,CAAC,MAAM,CAAC;QACrB,WAAW,EACT,iFAAiF;YACjF,kDAAkD;QACpD,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,KAAK;KAChB,CAAC;IACF,OAAO,EAAE,YAAK,CAAC,OAAO,CAAC;QACrB,IAAI,EAAE,GAAG;QACT,WAAW,EAAE,kCAAkC;QAC/C,QAAQ,EAAE,KAAK;KAChB,CAAC;CACH,CAAC;kBAzBiB,YAAY","sourcesContent":["import { isJsonRpcFailure } from '@metamask/utils';\nimport { Command, Flags } from '@oclif/core';\n\nimport { sendCommand } from '../../daemon/daemon-client';\nimport { getDaemonPaths } from '../../daemon/paths';\nimport { promptPassword } from '../../daemon/prompts';\nimport {\n emptyToUndefined,\n makeDaemonConnectionError,\n} from '../../daemon/utils';\n\nexport default class WalletUnlock extends Command {\n static override description =\n 'Unlock the wallet (submits the password to KeyringController). ' +\n 'Use this after `mm daemon start` was run without `--password`, or ' +\n 'after the keyring was locked via `KeyringController:setLocked`.';\n\n static override examples = [\n '<%= config.bin %> wallet unlock --password <pw>',\n 'MM_WALLET_PASSWORD=<pw> <%= config.bin %> wallet unlock',\n '<%= config.bin %> wallet unlock # prompts interactively',\n ];\n\n static override flags = {\n password: Flags.string({\n description:\n 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production). ' +\n 'When omitted, the command prompts interactively.',\n env: 'MM_WALLET_PASSWORD',\n required: false,\n }),\n timeout: Flags.integer({\n char: 't',\n description: 'Response timeout in milliseconds',\n required: false,\n }),\n };\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(WalletUnlock);\n const { timeout: timeoutMs } = flags;\n\n // `emptyToUndefined` collapses `--password ''` and `MM_WALLET_PASSWORD=''`\n // to undefined so the prompt fires instead of sending an empty string.\n const flagPassword = emptyToUndefined(flags.password);\n let password: string;\n try {\n password = flagPassword ?? (await promptPassword());\n } catch (error) {\n // Only swallow ExitPromptError (@inquirer/core's Ctrl+C signal); any\n // other rejection (import failure, internal prompt crash) should surface.\n if (error instanceof Error && error.name === 'ExitPromptError') {\n return;\n }\n throw error;\n }\n\n const { socketPath } = getDaemonPaths(this.config.dataDir);\n\n let response;\n try {\n response = await sendCommand({\n socketPath,\n method: 'call',\n params: ['KeyringController:submitPassword', password],\n ...(timeoutMs === undefined ? {} : { timeoutMs }),\n });\n } catch (error) {\n this.error(makeDaemonConnectionError(error));\n }\n\n if (isJsonRpcFailure(response)) {\n const { code, message, data } = response.error;\n // `isJsonRpcFailure` already validates that `data` is JSON, so\n // `JSON.stringify` cannot throw here.\n const dataSuffix =\n data === undefined || data === null\n ? ''\n : ` data=${JSON.stringify(data)}`;\n this.error(\n `Failed to unlock: ${message} (code ${String(code)})${dataSuffix}`,\n );\n }\n\n this.log('Wallet unlocked.');\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class WalletUnlock extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ password: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ timeout: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=unlock.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlock.d.cts","sourceRoot":"","sources":["../../../src/commands/wallet/unlock.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,oBAAoB;AAU7C,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,OAAO;IAC/C,OAAgB,WAAW,SAGyC;IAEpE,OAAgB,QAAQ,WAItB;IAEF,OAAgB,KAAK;;;MAanB;IAEW,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAgDlC"}
@@ -0,0 +1,11 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class WalletUnlock extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ password: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ timeout: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=unlock.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlock.d.mts","sourceRoot":"","sources":["../../../src/commands/wallet/unlock.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,oBAAoB;AAU7C,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,OAAO;IAC/C,OAAgB,WAAW,SAGyC;IAEpE,OAAgB,QAAQ,WAItB;IAEF,OAAgB,KAAK;;;MAanB;IAEW,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAgDlC"}
@@ -0,0 +1,73 @@
1
+ import { isJsonRpcFailure } from "@metamask/utils";
2
+ import { Command, Flags } from "@oclif/core";
3
+ import { sendCommand } from "../../daemon/daemon-client.mjs";
4
+ import { getDaemonPaths } from "../../daemon/paths.mjs";
5
+ import { promptPassword } from "../../daemon/prompts.mjs";
6
+ import { emptyToUndefined, makeDaemonConnectionError } from "../../daemon/utils.mjs";
7
+ class WalletUnlock extends Command {
8
+ async run() {
9
+ const { flags } = await this.parse(WalletUnlock);
10
+ const { timeout: timeoutMs } = flags;
11
+ // `emptyToUndefined` collapses `--password ''` and `MM_WALLET_PASSWORD=''`
12
+ // to undefined so the prompt fires instead of sending an empty string.
13
+ const flagPassword = emptyToUndefined(flags.password);
14
+ let password;
15
+ try {
16
+ password = flagPassword ?? (await promptPassword());
17
+ }
18
+ catch (error) {
19
+ // Only swallow ExitPromptError (@inquirer/core's Ctrl+C signal); any
20
+ // other rejection (import failure, internal prompt crash) should surface.
21
+ if (error instanceof Error && error.name === 'ExitPromptError') {
22
+ return;
23
+ }
24
+ throw error;
25
+ }
26
+ const { socketPath } = getDaemonPaths(this.config.dataDir);
27
+ let response;
28
+ try {
29
+ response = await sendCommand({
30
+ socketPath,
31
+ method: 'call',
32
+ params: ['KeyringController:submitPassword', password],
33
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
34
+ });
35
+ }
36
+ catch (error) {
37
+ this.error(makeDaemonConnectionError(error));
38
+ }
39
+ if (isJsonRpcFailure(response)) {
40
+ const { code, message, data } = response.error;
41
+ // `isJsonRpcFailure` already validates that `data` is JSON, so
42
+ // `JSON.stringify` cannot throw here.
43
+ const dataSuffix = data === undefined || data === null
44
+ ? ''
45
+ : ` data=${JSON.stringify(data)}`;
46
+ this.error(`Failed to unlock: ${message} (code ${String(code)})${dataSuffix}`);
47
+ }
48
+ this.log('Wallet unlocked.');
49
+ }
50
+ }
51
+ WalletUnlock.description = 'Unlock the wallet (submits the password to KeyringController). ' +
52
+ 'Use this after `mm daemon start` was run without `--password`, or ' +
53
+ 'after the keyring was locked via `KeyringController:setLocked`.';
54
+ WalletUnlock.examples = [
55
+ '<%= config.bin %> wallet unlock --password <pw>',
56
+ 'MM_WALLET_PASSWORD=<pw> <%= config.bin %> wallet unlock',
57
+ '<%= config.bin %> wallet unlock # prompts interactively',
58
+ ];
59
+ WalletUnlock.flags = {
60
+ password: Flags.string({
61
+ description: 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production). ' +
62
+ 'When omitted, the command prompts interactively.',
63
+ env: 'MM_WALLET_PASSWORD',
64
+ required: false,
65
+ }),
66
+ timeout: Flags.integer({
67
+ char: 't',
68
+ description: 'Response timeout in milliseconds',
69
+ required: false,
70
+ }),
71
+ };
72
+ export default WalletUnlock;
73
+ //# sourceMappingURL=unlock.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlock.mjs","sourceRoot":"","sources":["../../../src/commands/wallet/unlock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,wBAAwB;AACnD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB;AAE7C,OAAO,EAAE,WAAW,EAAE,uCAAmC;AACzD,OAAO,EAAE,cAAc,EAAE,+BAA2B;AACpD,OAAO,EAAE,cAAc,EAAE,iCAA6B;AACtD,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EAC1B,+BAA2B;AAE5B,MAAqB,YAAa,SAAQ,OAAO;IA2BxC,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;QAErC,2EAA2E;QAC3E,uEAAuE;QACvE,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtD,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,YAAY,IAAI,CAAC,MAAM,cAAc,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,qEAAqE;YACrE,0EAA0E;YAC1E,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBAC/D,OAAO;YACT,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAE3D,IAAI,QAAQ,CAAC;QACb,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,WAAW,CAAC;gBAC3B,UAAU;gBACV,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,CAAC,kCAAkC,EAAE,QAAQ,CAAC;gBACtD,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;aAClD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC;YAC/C,+DAA+D;YAC/D,sCAAsC;YACtC,MAAM,UAAU,GACd,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;gBACjC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,CACR,qBAAqB,OAAO,UAAU,MAAM,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,CACnE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC/B,CAAC;;AAzEe,wBAAW,GACzB,iEAAiE;IACjE,oEAAoE;IACpE,iEAAiE,CAAC;AAEpD,qBAAQ,GAAG;IACzB,iDAAiD;IACjD,yDAAyD;IACzD,2DAA2D;CAC5D,CAAC;AAEc,kBAAK,GAAG;IACtB,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC;QACrB,WAAW,EACT,iFAAiF;YACjF,kDAAkD;QACpD,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,KAAK;KAChB,CAAC;IACF,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC;QACrB,IAAI,EAAE,GAAG;QACT,WAAW,EAAE,kCAAkC;QAC/C,QAAQ,EAAE,KAAK;KAChB,CAAC;CACH,CAAC;eAzBiB,YAAY","sourcesContent":["import { isJsonRpcFailure } from '@metamask/utils';\nimport { Command, Flags } from '@oclif/core';\n\nimport { sendCommand } from '../../daemon/daemon-client';\nimport { getDaemonPaths } from '../../daemon/paths';\nimport { promptPassword } from '../../daemon/prompts';\nimport {\n emptyToUndefined,\n makeDaemonConnectionError,\n} from '../../daemon/utils';\n\nexport default class WalletUnlock extends Command {\n static override description =\n 'Unlock the wallet (submits the password to KeyringController). ' +\n 'Use this after `mm daemon start` was run without `--password`, or ' +\n 'after the keyring was locked via `KeyringController:setLocked`.';\n\n static override examples = [\n '<%= config.bin %> wallet unlock --password <pw>',\n 'MM_WALLET_PASSWORD=<pw> <%= config.bin %> wallet unlock',\n '<%= config.bin %> wallet unlock # prompts interactively',\n ];\n\n static override flags = {\n password: Flags.string({\n description:\n 'Wallet password (testing only — use MM_WALLET_PASSWORD env var in production). ' +\n 'When omitted, the command prompts interactively.',\n env: 'MM_WALLET_PASSWORD',\n required: false,\n }),\n timeout: Flags.integer({\n char: 't',\n description: 'Response timeout in milliseconds',\n required: false,\n }),\n };\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(WalletUnlock);\n const { timeout: timeoutMs } = flags;\n\n // `emptyToUndefined` collapses `--password ''` and `MM_WALLET_PASSWORD=''`\n // to undefined so the prompt fires instead of sending an empty string.\n const flagPassword = emptyToUndefined(flags.password);\n let password: string;\n try {\n password = flagPassword ?? (await promptPassword());\n } catch (error) {\n // Only swallow ExitPromptError (@inquirer/core's Ctrl+C signal); any\n // other rejection (import failure, internal prompt crash) should surface.\n if (error instanceof Error && error.name === 'ExitPromptError') {\n return;\n }\n throw error;\n }\n\n const { socketPath } = getDaemonPaths(this.config.dataDir);\n\n let response;\n try {\n response = await sendCommand({\n socketPath,\n method: 'call',\n params: ['KeyringController:submitPassword', password],\n ...(timeoutMs === undefined ? {} : { timeoutMs }),\n });\n } catch (error) {\n this.error(makeDaemonConnectionError(error));\n }\n\n if (isJsonRpcFailure(response)) {\n const { code, message, data } = response.error;\n // `isJsonRpcFailure` already validates that `data` is JSON, so\n // `JSON.stringify` cannot throw here.\n const dataSuffix =\n data === undefined || data === null\n ? ''\n : ` data=${JSON.stringify(data)}`;\n this.error(\n `Failed to unlock: ${message} (code ${String(code)})${dataSuffix}`,\n );\n }\n\n this.log('Wallet unlocked.');\n }\n}\n"]}
@@ -1,12 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const superstruct_1 = require("@metamask/superstruct");
3
4
  const promises_1 = require("node:fs/promises");
4
5
  const daemon_client_1 = require("./daemon-client.cjs");
5
6
  const data_dir_1 = require("./data-dir.cjs");
6
7
  const paths_1 = require("./paths.cjs");
7
8
  const rpc_socket_server_1 = require("./rpc-socket-server.cjs");
9
+ const secrets_1 = require("./secrets.cjs");
10
+ const types_1 = require("./types.cjs");
8
11
  const utils_1 = require("./utils.cjs");
9
12
  const wallet_factory_1 = require("./wallet-factory.cjs");
13
+ /**
14
+ * Params struct for the `call` RPC method. `params` must be a non-empty array
15
+ * whose first element is the messenger action name; remaining elements are
16
+ * positional action arguments forwarded as-is to `messenger.call`.
17
+ */
18
+ const callParamsStruct = (0, superstruct_1.define)('CallParams', (value) => {
19
+ if (!Array.isArray(value)) {
20
+ return 'Expected an array';
21
+ }
22
+ if (value.length === 0) {
23
+ return 'Expected a non-empty array';
24
+ }
25
+ if (typeof value[0] !== 'string') {
26
+ return 'Expected the first element to be a string action name';
27
+ }
28
+ return true;
29
+ });
10
30
  const startTime = Date.now();
11
31
  main().catch((error) => {
12
32
  process.stderr.write(`Daemon fatal: ${String(error)}\n`);
@@ -21,20 +41,22 @@ async function main() {
21
41
  if (!infuraProjectId) {
22
42
  throw new Error('INFURA_PROJECT_ID environment variable is required');
23
43
  }
24
- const password = process.env.MM_WALLET_PASSWORD;
25
- if (!password) {
26
- throw new Error('MM_WALLET_PASSWORD environment variable is required');
27
- }
28
- const srp = process.env.MM_WALLET_SRP;
29
- if (!srp) {
44
+ // Password is optional: when absent, the daemon starts without unlocking
45
+ // the keyring (e.g. when the user prefers to call `mm wallet unlock`
46
+ // interactively rather than embed the password in their environment).
47
+ // First-run startup still requires a password; wallet-factory enforces
48
+ // that and surfaces a clear error.
49
+ const passwordRaw = process.env.MM_WALLET_PASSWORD;
50
+ const srpRaw = process.env.MM_WALLET_SRP;
51
+ if (!srpRaw) {
30
52
  throw new Error('MM_WALLET_SRP environment variable is required');
31
53
  }
32
- // Scrub the wallet secrets from the environment now they are captured. The
33
- // daemon is long-lived and dispatches arbitrary messenger actions over its
34
- // socket, so leaving the SRP/password in `process.env` for its whole lifetime
35
- // needlessly widens their exposure to any in-process code.
54
+ // Scrub before validation so a throw from Password.from / Srp.from (bad
55
+ // value) does not leave the raw secrets in the long-lived daemon's env.
36
56
  delete process.env.MM_WALLET_PASSWORD;
37
57
  delete process.env.MM_WALLET_SRP;
58
+ const password = passwordRaw ? secrets_1.Password.from(passwordRaw) : undefined;
59
+ const srp = secrets_1.Srp.from(srpRaw);
38
60
  await (0, data_dir_1.ensureOwnerOnlyDirectory)(dataDir);
39
61
  const { socketPath: defaultSocketPath, pidPath, logPath, dbPath, } = (0, paths_1.getDaemonPaths)(dataDir);
40
62
  const socketPath = process.env.MM_DAEMON_SOCKET_PATH ?? defaultSocketPath;
@@ -73,29 +95,27 @@ async function main() {
73
95
  log,
74
96
  }));
75
97
  const constructedWallet = wallet;
98
+ // Arbitrary messenger dispatch is intentional: the CLI exposes the full
99
+ // messenger surface over a Unix socket inside the per-user oclif data
100
+ // directory. The dataDir is chmodded to 0o700 above and the socket to
101
+ // 0o600 by the RPC server on bind, so only the owning user can open them,
102
+ // but there is no in-process auth check beyond that filesystem-permission
103
+ // barrier. The messenger is strongly typed by action name; we narrow it
104
+ // once here to the RpcDispatcher shape the `call` handler needs.
105
+ const dispatch = constructedWallet.messenger.call.bind(constructedWallet.messenger);
76
106
  const handlers = {
77
- getStatus: async () => ({
107
+ getStatus: (0, types_1.defineHandler)((0, superstruct_1.literal)(null), async () => ({
78
108
  pid: process.pid,
79
109
  uptime: Math.floor((Date.now() - startTime) / 1000),
110
+ })),
111
+ call: (0, types_1.defineHandler)(callParamsStruct, async (params) => {
112
+ const [action, ...args] = params;
113
+ return await dispatch(action, ...args);
80
114
  }),
81
115
  // Exposes the callable surface for discovery: it grows silently as
82
116
  // controllers are wired, so consumers need a way to see it without a
83
117
  // hand-kept catalog that would rot.
84
- listActions: async () => constructedWallet.messenger.getRegisteredActionTypes(),
85
- // Arbitrary messenger dispatch is intentional: the CLI exposes the full
86
- // messenger surface over a Unix socket inside the per-user oclif data
87
- // directory. The dataDir is chmodded to 0o700 above and the socket to
88
- // 0o600 by the RPC server on bind, so only the owning user can open
89
- // them, but there is no in-process auth check beyond that
90
- // filesystem-permission barrier.
91
- call: async (params) => {
92
- if (!Array.isArray(params) || typeof params[0] !== 'string') {
93
- throw new Error('Expected params to be an array with an action name');
94
- }
95
- const [action, ...args] = params;
96
- const result = constructedWallet.messenger.call(action, ...args);
97
- return (result instanceof Promise ? await result : result);
98
- },
118
+ listActions: (0, types_1.defineHandler)((0, superstruct_1.literal)(null), async () => constructedWallet.messenger.getRegisteredActionTypes()),
99
119
  };
100
120
  // `startRpcSocketServer` restricts the socket to the owner (chmod 0o600)
101
121
  // on bind and never leaves a live server/socket behind if it rejects, so
@@ -142,7 +162,12 @@ async function main() {
142
162
  catch (closeError) {
143
163
  log(`handle.close() failed: ${String(closeError)}`);
144
164
  }
145
- await activeDispose();
165
+ try {
166
+ await activeDispose();
167
+ }
168
+ catch (disposeError) {
169
+ log(`dispose() failed during shutdown: ${String(disposeError)}`);
170
+ }
146
171
  await Promise.all([
147
172
  removeOwnedPidFile(pidPath, pidFileContents).catch((rmError) => {
148
173
  log(`Failed to remove PID file: ${String(rmError)}`);