@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.
- package/CHANGELOG.md +5 -0
- package/README.md +13 -1
- package/dist/commands/daemon/start.cjs +7 -3
- package/dist/commands/daemon/start.cjs.map +1 -1
- package/dist/commands/daemon/start.d.cts +1 -1
- package/dist/commands/daemon/start.d.cts.map +1 -1
- package/dist/commands/daemon/start.d.mts +1 -1
- package/dist/commands/daemon/start.d.mts.map +1 -1
- package/dist/commands/daemon/start.mjs +7 -3
- package/dist/commands/daemon/start.mjs.map +1 -1
- package/dist/commands/wallet/unlock.cjs +75 -0
- package/dist/commands/wallet/unlock.cjs.map +1 -0
- package/dist/commands/wallet/unlock.d.cts +11 -0
- package/dist/commands/wallet/unlock.d.cts.map +1 -0
- package/dist/commands/wallet/unlock.d.mts +11 -0
- package/dist/commands/wallet/unlock.d.mts.map +1 -0
- package/dist/commands/wallet/unlock.mjs +73 -0
- package/dist/commands/wallet/unlock.mjs.map +1 -0
- package/dist/daemon/daemon-entry.cjs +52 -27
- package/dist/daemon/daemon-entry.cjs.map +1 -1
- package/dist/daemon/daemon-entry.mjs +52 -27
- package/dist/daemon/daemon-entry.mjs.map +1 -1
- package/dist/daemon/daemon-spawn.cjs +19 -8
- package/dist/daemon/daemon-spawn.cjs.map +1 -1
- package/dist/daemon/daemon-spawn.d.cts.map +1 -1
- package/dist/daemon/daemon-spawn.d.mts.map +1 -1
- package/dist/daemon/daemon-spawn.mjs +19 -8
- package/dist/daemon/daemon-spawn.mjs.map +1 -1
- package/dist/daemon/prompts.cjs +17 -1
- package/dist/daemon/prompts.cjs.map +1 -1
- package/dist/daemon/prompts.d.cts +9 -0
- package/dist/daemon/prompts.d.cts.map +1 -1
- package/dist/daemon/prompts.d.mts +9 -0
- package/dist/daemon/prompts.d.mts.map +1 -1
- package/dist/daemon/prompts.mjs +15 -0
- package/dist/daemon/prompts.mjs.map +1 -1
- package/dist/daemon/rpc-socket-server.cjs +14 -1
- package/dist/daemon/rpc-socket-server.cjs.map +1 -1
- package/dist/daemon/rpc-socket-server.d.cts.map +1 -1
- package/dist/daemon/rpc-socket-server.d.mts.map +1 -1
- package/dist/daemon/rpc-socket-server.mjs +14 -1
- package/dist/daemon/rpc-socket-server.mjs.map +1 -1
- package/dist/daemon/secrets.cjs +137 -0
- package/dist/daemon/secrets.cjs.map +1 -0
- package/dist/daemon/secrets.d.cts +72 -0
- package/dist/daemon/secrets.d.cts.map +1 -0
- package/dist/daemon/secrets.d.mts +72 -0
- package/dist/daemon/secrets.d.mts.map +1 -0
- package/dist/daemon/secrets.mjs +132 -0
- package/dist/daemon/secrets.mjs.map +1 -0
- package/dist/daemon/types.cjs +17 -0
- package/dist/daemon/types.cjs.map +1 -1
- package/dist/daemon/types.d.cts +58 -7
- package/dist/daemon/types.d.cts.map +1 -1
- package/dist/daemon/types.d.mts +58 -7
- package/dist/daemon/types.d.mts.map +1 -1
- package/dist/daemon/types.mjs +15 -1
- package/dist/daemon/types.mjs.map +1 -1
- package/dist/daemon/utils.cjs +14 -2
- package/dist/daemon/utils.cjs.map +1 -1
- package/dist/daemon/utils.d.cts +9 -0
- package/dist/daemon/utils.d.cts.map +1 -1
- package/dist/daemon/utils.d.mts +9 -0
- package/dist/daemon/utils.d.mts.map +1 -1
- package/dist/daemon/utils.mjs +12 -1
- package/dist/daemon/utils.mjs.map +1 -1
- package/dist/daemon/wallet-factory.cjs +50 -8
- package/dist/daemon/wallet-factory.cjs.map +1 -1
- package/dist/daemon/wallet-factory.d.cts +17 -8
- package/dist/daemon/wallet-factory.d.cts.map +1 -1
- package/dist/daemon/wallet-factory.d.mts +17 -8
- package/dist/daemon/wallet-factory.d.mts.map +1 -1
- package/dist/daemon/wallet-factory.mjs +50 -8
- package/dist/daemon/wallet-factory.mjs.map +1 -1
- 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
|
|
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
|
|
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:
|
|
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;
|
|
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;
|
|
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;
|
|
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
|
|
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:
|
|
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;
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
33
|
-
//
|
|
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
|
-
|
|
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)}`);
|