@deeeed/metamask-harness 0.5.1 → 0.6.1

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 (90) hide show
  1. package/CHANGELOG.md +47 -20
  2. package/adapters/core/inject.sh +1 -0
  3. package/adapters/extension/cleanup.mjs +1 -1
  4. package/adapters/extension/inject.mjs +2 -1
  5. package/adapters/extension/launch-browser.cjs +18 -4
  6. package/adapters/extension/live.sh +14 -2
  7. package/adapters/extension/sidepanel-toggle.sh +33 -12
  8. package/adapters/extension/wallet-fixture-state.cjs +38 -24
  9. package/adapters/manifest.json +13 -0
  10. package/adapters/mobile/inject.sh +1 -0
  11. package/adapters/mobile/start-metro.sh +6 -0
  12. package/adapters/mobile/stop-metro.sh +20 -0
  13. package/adapters/shared/cli-ux.sh +20 -2
  14. package/adapters/shared/ensure-runner-deps.sh +30 -0
  15. package/adapters/shared/reap-checkout-metros.sh +58 -0
  16. package/adapters/shared/recipe-harness-root.mjs +23 -0
  17. package/adapters/shared/resolve-farmslot-ports-core.mjs +15 -6
  18. package/bin/mm-harness +45 -4
  19. package/dist/adapters/extension/runtime.js +3 -1
  20. package/dist/adapters/mobile/provision.js +34 -1
  21. package/dist/adapters/slot-ports.js +3 -8
  22. package/dist/cli.js +32 -1463
  23. package/dist/commands/call.js +183 -0
  24. package/dist/commands/completion-candidates.js +58 -0
  25. package/dist/commands/doctor.js +101 -0
  26. package/dist/commands/ensure-ready.js +24 -0
  27. package/dist/commands/flows.js +62 -0
  28. package/dist/commands/launch/extension.js +40 -0
  29. package/dist/commands/{launch.js → launch/index.js} +15 -47
  30. package/dist/commands/launch/mobile.js +10 -0
  31. package/dist/commands/manifest.js +72 -0
  32. package/dist/commands/parse-args.js +189 -0
  33. package/dist/commands/provision.js +136 -0
  34. package/dist/commands/resolve-extension.js +23 -0
  35. package/dist/commands/run-engine.js +341 -0
  36. package/dist/commands/run.js +217 -0
  37. package/dist/commands/runtime-decision.js +58 -0
  38. package/dist/commands/runtime-health.js +25 -0
  39. package/dist/commands/runtime-launch.js +139 -0
  40. package/dist/commands/self-test.js +52 -0
  41. package/dist/commands/stop.js +52 -0
  42. package/dist/harness.js +8 -48
  43. package/dist/mm-harness-cli.js +13 -8
  44. package/docs/CLI-SPEC.md +1 -1
  45. package/docs/CODE-MAP.md +62 -0
  46. package/library/README.md +14 -0
  47. package/library/actions/extension/platform/cdp.mjs +1 -1
  48. package/library/actions/extension/wallet/ensure_unlocked.mjs +6 -0
  49. package/library/actions/mobile/wallet/ensure_unlocked.mjs +13 -1
  50. package/package.json +6 -7
  51. package/src/adapters/core/surface.ts +0 -71
  52. package/src/adapters/extension/ensure-ready.ts +0 -185
  53. package/src/adapters/extension/extension-id.ts +0 -107
  54. package/src/adapters/extension/runtime-decision.ts +0 -445
  55. package/src/adapters/extension/runtime.ts +0 -407
  56. package/src/adapters/extension/surface.ts +0 -88
  57. package/src/adapters/mobile/deps-markers.ts +0 -21
  58. package/src/adapters/mobile/prepare.ts +0 -246
  59. package/src/adapters/mobile/provision.ts +0 -594
  60. package/src/adapters/mobile/runtime-decision.ts +0 -466
  61. package/src/adapters/mobile/surface.ts +0 -71
  62. package/src/adapters/resolve-farmslot-ports.ts +0 -13
  63. package/src/adapters/slot-ports.ts +0 -158
  64. package/src/adapters/surface.ts +0 -117
  65. package/src/adapters.ts +0 -601
  66. package/src/cli-color.ts +0 -92
  67. package/src/cli-commands.ts +0 -250
  68. package/src/cli-version.ts +0 -141
  69. package/src/cli.ts +0 -2091
  70. package/src/commands/debug.ts +0 -65
  71. package/src/commands/fixtures.ts +0 -198
  72. package/src/commands/launch.ts +0 -470
  73. package/src/commands/logs.ts +0 -99
  74. package/src/commands/shared.ts +0 -235
  75. package/src/commands/update.ts +0 -316
  76. package/src/completions-cache.ts +0 -86
  77. package/src/doctor.ts +0 -215
  78. package/src/harness.ts +0 -797
  79. package/src/heal-bounds.ts +0 -198
  80. package/src/index.ts +0 -15
  81. package/src/leaf-invoke.ts +0 -28
  82. package/src/live-adapter-contract.ts +0 -274
  83. package/src/manifest.ts +0 -47
  84. package/src/mm-harness-cli.ts +0 -655
  85. package/src/paths.ts +0 -198
  86. package/src/progress.ts +0 -117
  87. package/src/recording-target.ts +0 -147
  88. package/src/run-recording.ts +0 -329
  89. package/src/runner.ts +0 -108
  90. package/src/types.ts +0 -57
@@ -1,65 +0,0 @@
1
- // debug — open the debug console by composing open-debug.mjs directly:
2
- // extension `debug` (--worker → service-worker target); mobile `debug`
3
- // (--dev-menu → developer menu). Cross-platform flag misuse teaches (exit 2).
4
-
5
- import path from 'node:path';
6
-
7
- import { runnerDir } from '../paths.ts';
8
- import { getAdapterSurface } from '../adapters/surface.ts';
9
- import { ADAPTER_DETECT_NEXT, EXIT, flag, parseFlags, resolveAdapter, spawnScript, targetOf, usageOut } from './shared.ts';
10
-
11
- const DEBUG_BOOLEANS = new Set(['worker', 'devMenu', 'json']);
12
-
13
- export async function handleDebug(argv: string[]): Promise<number> {
14
- const { options } = parseFlags(argv, DEBUG_BOOLEANS);
15
- const json = flag(options, 'json');
16
- const target = targetOf(options);
17
- const adapter = resolveAdapter(options, target);
18
-
19
- if (!adapter) return usageOut(json, 'debug', `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
20
- const surface = getAdapterSurface(adapter);
21
- if (surface.headless) return usageOut(json, 'debug', 'core is headless; there is no debug console.', surface.hints.relaunch);
22
-
23
- const worker = flag(options, 'worker');
24
- const devMenu = flag(options, 'devMenu');
25
- if (adapter === 'mobile' && worker) {
26
- return usageOut(
27
- json,
28
- 'debug',
29
- '--worker is extension-only (service-worker DevTools). Use --dev-menu on mobile.',
30
- 'mm-harness debug --dev-menu',
31
- );
32
- }
33
- if (adapter === 'extension' && devMenu) {
34
- return usageOut(
35
- json,
36
- 'debug',
37
- '--dev-menu is mobile-only (RN developer menu). Use --worker on the extension.',
38
- 'mm-harness debug --worker',
39
- );
40
- }
41
-
42
- const openDebug = path.join(runnerDir, 'adapters/shared/open-debug.mjs');
43
- const debugArgs: string[] = ['--adapter', adapter];
44
-
45
- if (adapter === 'extension') {
46
- if (process.env.CDP_PORT) debugArgs.push('--cdp-port', process.env.CDP_PORT);
47
- debugArgs.push('--target', worker ? 'worker' : 'page');
48
- } else {
49
- if (process.env.WATCHER_PORT) debugArgs.push('--port', process.env.WATCHER_PORT);
50
- debugArgs.push('--action', devMenu ? 'dev-menu' : 'debug');
51
- }
52
- if (json) debugArgs.push('--json');
53
-
54
- const result = spawnScript(process.execPath, [openDebug, ...debugArgs], target, json);
55
- if (json) {
56
- console.log(
57
- JSON.stringify(
58
- { schemaVersion: 1, command: 'debug', adapter, mode: worker ? 'worker' : devMenu ? 'dev-menu' : 'default', exitCode: result.status === 0 ? EXIT.ok : EXIT.runtime },
59
- null,
60
- 2,
61
- ),
62
- );
63
- }
64
- return result.status === 0 ? EXIT.ok : EXIT.runtime;
65
- }
@@ -1,198 +0,0 @@
1
- // fixtures <sync|set> — manage wallet DATA (never overlay/runtime state). `sync`
2
- // re-renders the wallet fixture files; `set` applies THE canonical fixture (SRP/
3
- // password read FROM the fixture, never typed) and always teaches the edit path.
4
-
5
- import fs from 'node:fs';
6
- import path from 'node:path';
7
-
8
- import { runnerDir, walletFixturePath } from '../paths.ts';
9
- import { getAdapterSurface } from '../adapters/surface.ts';
10
- import type { MetaMaskRecipeAdapter } from '../types.ts';
11
- import { ADAPTER_DETECT_NEXT, EXIT, flag, parseFlags, resolveAdapter, spawnScript, str, targetOf, usageOut } from './shared.ts';
12
-
13
- // One node injected from cli.ts so `fixtures set` can reuse the real engine path
14
- // (`call`'s one-node machinery) without a circular import.
15
- export interface CommandDeps {
16
- runOneNode: (
17
- adapter: MetaMaskRecipeAdapter,
18
- action: string,
19
- args: Record<string, string>,
20
- target: string,
21
- actionManifest: string | undefined,
22
- ) => Promise<{ status: 'pass' | 'fail' }>;
23
- }
24
-
25
- const FIXTURES_BOOLEANS = new Set(['json']);
26
-
27
- // Patterns indicating setup-wallet failed due to a Metro/bridge issue (not a bad
28
- // fixture). A clean Metro restart + relaunch is likely to resolve these.
29
- // Mirrors baseline mm-recipe:820-824 setup_wallet_failure_recoverable patterns.
30
- const RECOVERABLE_SETUP_WALLET_PATTERNS = [
31
- 'CDP not reachable',
32
- 'CDP bridge',
33
- 'is not installed',
34
- 'Engine.context.KeyringController not available',
35
- 'Engine does not exist',
36
- "Cannot read property 'transactions'",
37
- 'bridge-not-ready',
38
- 'debug-target-missing',
39
- 'CDP eval failed',
40
- 'CDP eval-async failed',
41
- ];
42
-
43
- function isRecoverableSetupWalletFailure(output: string): boolean {
44
- return RECOVERABLE_SETUP_WALLET_PATTERNS.some((p) => output.includes(p));
45
- }
46
-
47
- export async function handleFixtures(argv: string[], deps: CommandDeps): Promise<number> {
48
- const { positional, options } = parseFlags(argv, FIXTURES_BOOLEANS);
49
- const json = flag(options, 'json');
50
- const sub = positional[0];
51
- if (sub !== 'sync' && sub !== 'set') {
52
- return usageOut(json, 'fixtures', 'fixtures requires a subcommand: mm-harness fixtures <sync|set>', 'mm-harness fixtures sync or mm-harness fixtures set');
53
- }
54
- const target = targetOf(options);
55
- const adapter = resolveAdapter(options, target);
56
- if (!adapter) {
57
- return usageOut(json, 'fixtures', `could not detect the MetaMask repo type for ${target}`, ADAPTER_DETECT_NEXT);
58
- }
59
- const surface = getAdapterSurface(adapter);
60
- if (surface.headless) return usageOut(json, 'fixtures', 'core is headless; it has no wallet fixture.', surface.hints.launch);
61
-
62
- const canonicalFixture = walletFixturePath(target);
63
- // Platform-phrased retry hint on a setup-wallet failure: the surface owns the
64
- // (re)build/relaunch command; this command never spells out another platform's.
65
- const retryHint = `${surface.hints.relaunch} # relaunch, then retry: mm-harness fixtures set`;
66
-
67
- if (sub === 'sync') {
68
- const exitCode = fixturesSync(adapter, target, json);
69
- if (json) {
70
- console.log(
71
- JSON.stringify(
72
- { schemaVersion: 1, command: 'fixtures', action: 'sync', adapter, fixture: canonicalFixture, exitCode },
73
- null,
74
- 2,
75
- ),
76
- );
77
- }
78
- return exitCode;
79
- }
80
-
81
- // set — apply THE canonical wallet fixture (password read FROM the fixture).
82
- // Env-gap resolution (docs/CLI-SPEC.md Part 4): flag > env RECIPE_WALLET_FIXTURE
83
- // > the slot's canonical fixture.
84
- const fixturePath = path.resolve(str(options, 'fixture') ?? process.env.RECIPE_WALLET_FIXTURE ?? canonicalFixture);
85
- let status: 'pass' | 'fail';
86
- if (adapter === 'mobile') {
87
- // Compose setup-wallet.sh directly (requires bridge to be live).
88
- const setupWalletSh = path.join(runnerDir, 'adapters/mobile/bridge-runtime/setup-wallet.sh');
89
- const previousAppRoot = process.env.APP_ROOT;
90
- process.env.APP_ROOT = target;
91
- try {
92
- let result = spawnScript(setupWalletSh, ['--fixture', fixturePath], target, json);
93
- // One clean-Metro auto-retry on recoverable failures (CDP unreachable, bridge
94
- // not installed). RECIPE_SETUP_WALLET_RETRIED guards against infinite loops.
95
- if (result.status !== 0 && isRecoverableSetupWalletFailure(result.output) && !process.env['RECIPE_SETUP_WALLET_RETRIED']) {
96
- process.env['RECIPE_SETUP_WALLET_RETRIED'] = '1';
97
- if (!json) process.stderr.write(' setup-wallet: recoverable failure — restarting Metro and retrying\n Next: wait for relaunch, then wallet setup will retry automatically\n');
98
- const { prepareMobile } = await import('../adapters/mobile/prepare.ts');
99
- const platform = str(options, 'platform') ?? process.env['MOBILE_PLATFORM'] ?? 'ios';
100
- const relaunchResult = await prepareMobile(target, { platform, json, preflightMode: 'auto', clearMetro: true });
101
- if (relaunchResult.status === 0) {
102
- result = spawnScript(setupWalletSh, ['--fixture', fixturePath], target, json);
103
- }
104
- }
105
- status = result.status === 0 ? 'pass' : 'fail';
106
- } finally {
107
- delete process.env['RECIPE_SETUP_WALLET_RETRIED'];
108
- if (previousAppRoot === undefined) delete process.env.APP_ROOT;
109
- else process.env.APP_ROOT = previousAppRoot;
110
- }
111
- if (!json && status === 'fail') {
112
- console.error(` Next: ${retryHint}`);
113
- }
114
- } else {
115
- // Extension has no standalone set arm — reuse call's one-node machinery via
116
- // the real engine path (metamask.wallet.setup in the manifest).
117
- if (!fs.existsSync(fixturePath)) {
118
- return usageOut(
119
- json,
120
- 'fixtures',
121
- `no wallet fixture at ${fixturePath}.`,
122
- 'create it (or pass --fixture <path>), then re-run: mm-harness fixtures set',
123
- );
124
- }
125
- const previousFixtureEnv = process.env.RECIPE_WALLET_FIXTURE;
126
- process.env.RECIPE_WALLET_FIXTURE = fixturePath;
127
- try {
128
- const result = await deps.runOneNode('extension', 'metamask.wallet.setup', {}, target, str(options, 'actionManifest'));
129
- status = result.status;
130
- } finally {
131
- if (previousFixtureEnv === undefined) delete process.env.RECIPE_WALLET_FIXTURE;
132
- else process.env.RECIPE_WALLET_FIXTURE = previousFixtureEnv;
133
- }
134
- if (!json && status === 'fail') {
135
- console.error(` Next: ${retryHint}`);
136
- }
137
- }
138
-
139
- // Always teach where to customize the wallet.
140
- const teaching = `Wallet fixture applied. Want different accounts? Edit: ${canonicalFixture}`;
141
- if (json) {
142
- console.log(
143
- JSON.stringify(
144
- {
145
- schemaVersion: 1,
146
- command: 'fixtures',
147
- action: 'set',
148
- adapter,
149
- fixture: fixturePath,
150
- canonicalFixture,
151
- status,
152
- exitCode: status === 'pass' ? EXIT.ok : EXIT.runtime,
153
- message: teaching,
154
- error: status === 'fail' ? {
155
- code: 'SETUP_WALLET_FAILED',
156
- message: 'wallet fixture setup failed',
157
- userAction: retryHint,
158
- } : null,
159
- },
160
- null,
161
- 2,
162
- ),
163
- );
164
- } else {
165
- console.error(teaching);
166
- }
167
- return status === 'pass' ? EXIT.ok : EXIT.runtime;
168
- }
169
-
170
- // Sync the overlay installation + wallet fixture for the checkout.
171
- // Re-runs the adapter inject to refresh patched files, then copies the wallet
172
- // fixture to the canonical runtime location via sync-wallet-fixture.sh. Reached
173
- // only for device adapters (headless core returns earlier); the adapter is
174
- // forwarded verbatim to `install`.
175
- function fixturesSync(adapter: MetaMaskRecipeAdapter, target: string, json: boolean): number {
176
- const mmHarnessBin = path.join(runnerDir, 'bin/mm-harness');
177
- const installResult = spawnScript(
178
- mmHarnessBin,
179
- ['install', '--adapter', adapter, '--target', target],
180
- target,
181
- json,
182
- );
183
- if (installResult.status !== 0) {
184
- if (!json) {
185
- console.error(`✗ mm-harness fixtures sync: overlay reinstall failed.\n Next: mm-harness install --target ${target} # diagnose the install failure`);
186
- }
187
- return EXIT.runtime;
188
- }
189
-
190
- const syncFixtureSh = path.join(runnerDir, 'adapters/shared/sync-wallet-fixture.sh');
191
- const syncArgs = ['--target', target];
192
- if (process.env.CDP_PORT) syncArgs.push('--cdp-port', process.env.CDP_PORT);
193
- if (process.env.RECIPE_SLOT_ID) syncArgs.push('--slot-id', process.env.RECIPE_SLOT_ID);
194
- spawnScript(syncFixtureSh, syncArgs, target, json);
195
- // Non-zero from sync-wallet-fixture.sh is informational (no fixture source);
196
- // the install succeeded so the overlay is fresh.
197
- return EXIT.ok;
198
- }