feat(wallet-cli): add daemon commands, dev-mode launchers, and e2e smoke test#9255
Open
sirtimid wants to merge 10 commits into
Open
feat(wallet-cli): add daemon commands, dev-mode launchers, and e2e smoke test#9255sirtimid wants to merge 10 commits into
sirtimid wants to merge 10 commits into
Conversation
sirtimid
added a commit
that referenced
this pull request
Jun 24, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7854c96 to
340a7b4
Compare
…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>
4736dda to
12029ec
Compare
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>
0eb5147 to
c29e73e
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 c29e73e. Configure here.
… 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

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) — themm daemoncommand suite over the daemon layers already onmain:startspawns the daemon and reports its socket,status/stopping and tear it down,purgedeletes the daemon state files, andcalldispatches an arbitraryController:methodmessenger action.bin/dev.mjs+bin/dev.cmd— the dev-mode launchers deferred from the scaffold slice, run via thetsxloader (knipignoreDependencies: ['tsx']).wallet-factory.e2e.test.ts— a smoke test that feeds the wiredinstanceOptionsinto a realWallet(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 norwallet.init()reaches the network).src/test/run-command.ts+ a README Usage section.Checklist
build,test(100% coverage),lint, andchangelog:validatepass.Note
Medium Risk
Commands start an unlocked wallet with secrets and
daemon callcan invoke arbitrary messenger actions;purgedeletes SQLite wallet state but refuses when the daemon is still responsive.Overview
Adds the user-facing
mm daemoncommand suite so@metamask/wallet-cliis runnable on top of the existing spawn/transport/factory layers:start(credentials via flags orINFURA_PROJECT_ID/MM_WALLET_PASSWORD/MM_WALLET_SRP),status,stop,purge(confirm or--force, whitelist-only file deletion with a guard if the daemon is still responsive), andcall(JSON-RPCcallto anyController:methodwith optional JSON-array args and--timeout).Also ships
bin/dev.mjs/bin/dev.cmdfor oclif dev mode viatsx, arun-commandtest harness for oclif commands,wallet-factory.e2e.test.tsagainst a real in-memoryWallet, Jest Web Crypto polyfill for KeyringController tests, README Usage, changelog entry, knip ignore fortsx, andtsconfigoutDir/rootDirso commands compile todist.Reviewed by Cursor Bugbot for commit 1fe01e7. Bugbot is set up for automated code reviews on this repo. Configure here.