Skip to content

feat(wallet-cli): add daemon commands, dev-mode launchers, and e2e smoke test#9255

Merged
sirtimid merged 11 commits into
mainfrom
sirtimid/wallet-cli-commands
Jun 30, 2026
Merged

feat(wallet-cli): add daemon commands, dev-mode launchers, and e2e smoke test#9255
sirtimid merged 11 commits into
mainfrom
sirtimid/wallet-cli-commands

Conversation

@sirtimid

@sirtimid sirtimid commented Jun 24, 2026

Copy link
Copy Markdown
Member

Explanation

The final landing slice for @metamask/wallet-cli, after the scaffold (#9065), persistence (#9067), transport (#9108), and factory (#9226) slices. It adds the user-facing command surface so the package is runnable:

  • src/commands/daemon/{start,stop,status,purge,call}.ts (+ tests) — the mm daemon command suite over the daemon layers already on main: start spawns the daemon and reports its socket, status/stop ping and tear it down, purge deletes the daemon state files, and call dispatches an arbitrary Controller:method messenger action.
  • bin/dev.mjs + bin/dev.cmd — the dev-mode launchers deferred from the scaffold slice, run via the tsx loader (knip ignoreDependencies: ['tsx']).
  • wallet-factory.e2e.test.ts — a smoke test that feeds the wired instanceOptions into a real Wallet (no mock) against :memory:, imports the SRP, and dispatches a messenger action — closing the gap flagged in feat(wallet-cli): add wallet factory and daemon entry point #9226 review. Offline/CI-safe (neither construction nor wallet.init() reaches the network).
  • src/test/run-command.ts + a README Usage section.

Checklist

  • build, test (100% coverage), lint, and changelog:validate pass.

Note

Medium Risk
New CLI paths accept wallet secrets and can invoke arbitrary messenger actions over a local socket; purge deletes persisted wallet state, though safeguards limit deletion when the daemon is still responsive.

Overview
Adds the runnable mm daemon command surface on top of the existing spawn/client/stop layers: start (Infura/password/SRP via flags or env), stop, status (orphan-PID and stale-PID warnings), purge (confirm/--force, whitelist-only file deletion, refuses purge while the daemon still responds), and call (JSON-RPC call with optional JSON-array args and timeout, TTY vs piped JSON output).

Also ships bin/dev.mjs/dev.cmd (oclif dev mode via tsx), a runCommand oclif test harness, a real Wallet :memory: e2e for createWallet, Jest Web Crypto polyfill environment for KeyringController tests, README Usage, and knip/tsconfig tweaks for wallet-cli.

Reviewed by Cursor Bugbot for commit 15fbe92. Bugbot is set up for automated code reviews on this repo. Configure here.

sirtimid added a commit that referenced this pull request Jun 24, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sirtimid sirtimid force-pushed the sirtimid/wallet-cli-wallet-factory branch from 7854c96 to 340a7b4 Compare June 25, 2026 13:18
Base automatically changed from sirtimid/wallet-cli-wallet-factory to main June 25, 2026 16:24
sirtimid added a commit that referenced this pull request Jun 25, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sirtimid sirtimid force-pushed the sirtimid/wallet-cli-commands branch 2 times, most recently from 0eb5147 to c29e73e Compare June 25, 2026 18:52
@sirtimid sirtimid marked this pull request as ready for review June 25, 2026 18:57
@sirtimid sirtimid requested review from a team as code owners June 25, 2026 18:57
@sirtimid sirtimid temporarily deployed to default-branch June 25, 2026 18:57 — with GitHub Actions Inactive
Comment thread packages/wallet-cli/bin/dev.cmd Outdated
sirtimid and others added 10 commits June 29, 2026 13:16
…oke test

Add the `mm daemon` command suite — `start`, `stop`, `status`, `purge`, and
`call` — completing the CLI's user-facing surface, plus the oclif test harness
(`src/test/run-command.ts`) and the dev-mode launchers (`bin/dev.mjs`,
`bin/dev.cmd`) deferred from the scaffold slice.

The commands are Erik's verbatim; their imported daemon interfaces all match
the modules already on `main` from the persistence/transport/factory slices.

Also add `tsx` as the dev-mode `--import` loader (with a knip
`ignoreDependencies` entry, since it's referenced only as an argument string),
exclude the test harness from coverage, and add a real-construction e2e smoke
test that feeds `buildInstanceOptions` into a real `Wallet` against `:memory:`
— closing the gap that the mocked unit test cannot reach. Documents the
`mm daemon` usage in the README and adds an ARCHITECTURE.md describing the
daemon → factory → persistence → transport layering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion

README documented MM_DAEMON_DATA_DIR as the data-dir override, but that's only
the internal start->daemon spawn-contract var (overwritten with config.dataDir
and ignored by the other commands). The real user override is MM_DATA_DIR, the
oclif scopedEnvVar('DATA_DIR'). Also assert the e2e's first account is a real
SRP-derived EVM address, so it fails loudly on an empty/placeholder account.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#9001 (NetworkController) landed and wired on main, making
`infuraProjectId` a required field of `CreateWalletConfig` and adding
`wallet.init()` to the factory's startup path. Pass a dummy project ID so
the e2e type-checks against the merged factory, and note in the comment
that `init()` (NetworkController) is offline-safe too — it is synchronous
and never calls `lookupNetwork`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove narration that restates what the code or test name already says
(the stop-vs-not-running branch, the "simulate a non-Error throw" notes,
the purge whitelist composition) and tighten the purge refuse-when-
responsive comment to its non-obvious rationale. Keeps the genuine "why"
comments (the call.ts Json cast safety, the purge whitelist rationale).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The e2e constructs a real KeyringController, whose default
browser-passworder encryptor uses the Web Crypto API — `crypto`
(`getRandomValues`/`subtle`) and the `CryptoKey` constructor (an
`instanceof` check in `exportKey`). Under `--experimental-vm-modules`
the test realm has neither global on Node < 21 (notably the CI
`test-18` job), so the e2e failed first with "reading 'getRandomValues'"
and then "CryptoKey is not defined".

Add a custom jest environment that polyfills both from `node:crypto`
when absent — the same two globals `@metamask/wallet`'s own
`Wallet.test.ts` sets for this flow. Verified on Node 18.20.8 by driving
the real browser-passworder `generateSalt -> keyFromPassword ->
exportKey` sequence: it throws under the bare `node` environment and
passes under this one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… e2e

`AccountsController:listAccounts` (and the rest of the AccountsController
read surface) is `@deprecated` in favor of AccountTreeController / Keyring
API v2 — neither wired in the CLI — so a CLI caller pointed at a
deprecated action with no way to know. Switch the `call` examples and the
e2e to `KeyringController:getState`, which is wired, non-deprecated, and
exposes the SRP-derived account address directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`bin/dev.cmd` invoked `node ... "%~dp0\dev"`, but the package only ships
`dev.mjs` (run.cmd uses `run.mjs`), so the Windows dev launcher pointed
at a nonexistent file. It also registered tsx via `--loader tsx`, which
is removed in tsx 4.x; switch to `--import tsx`, matching `daemon-spawn`
and the knip note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dev launchers (`bin/dev.mjs` / `bin/dev.cmd`, run with `--import
tsx`) put oclif in development mode, but oclif still loaded the compiled
`dist/commands` — so dev mode needed a prior build and never picked up
source edits. oclif's `tsPath` only rewrites `dist/<x>` to `src/<x>`
when the package's `tsconfig.json` declares both `outDir` and `rootDir`;
ours had only `baseUrl` (they live in `tsconfig.build.json`, which oclif
doesn't read). Add `outDir`/`rootDir` to `tsconfig.json` so dev mode
loads `src/**/*.ts` via tsx, matching `@metamask/core-backend`.

Verified: with `dist` removed, `node --import tsx bin/dev.mjs daemon
call --help` loads the command from source. Build (tsconfig.build.json),
eslint, constraints, and tests are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sirtimid sirtimid force-pushed the sirtimid/wallet-cli-commands branch from 1fe01e7 to 822000a Compare June 29, 2026 11:19

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 822000a. Configure here.

Comment thread packages/wallet-cli/src/commands/daemon/status.ts
When the Unix socket is missing, `mm daemon status` reported the daemon
as not running even when the recorded PID was still alive. That
contradicted `stopDaemon`, which treats an absent socket plus an alive
recorded PID as a daemon that outlived its socket. Consult the PID in the
absent branch and surface the orphan, pointing at `mm daemon stop` /
`mm daemon purge` for recovery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sirtimid sirtimid added this pull request to the merge queue Jun 30, 2026
Merged via the queue into main with commit f12df04 Jun 30, 2026
400 checks passed
@sirtimid sirtimid deleted the sirtimid/wallet-cli-commands branch June 30, 2026 05:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants