@deeeed/metamask-harness 0.5.1 → 0.6.0

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 (89) hide show
  1. package/CHANGELOG.md +37 -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/stop-metro.sh +20 -0
  12. package/adapters/shared/cli-ux.sh +20 -2
  13. package/adapters/shared/ensure-runner-deps.sh +30 -0
  14. package/adapters/shared/reap-checkout-metros.sh +53 -0
  15. package/adapters/shared/recipe-harness-root.mjs +23 -0
  16. package/adapters/shared/resolve-farmslot-ports-core.mjs +15 -6
  17. package/bin/mm-harness +45 -4
  18. package/dist/adapters/extension/runtime.js +3 -1
  19. package/dist/adapters/mobile/provision.js +34 -1
  20. package/dist/adapters/slot-ports.js +3 -8
  21. package/dist/cli.js +32 -1463
  22. package/dist/commands/call.js +183 -0
  23. package/dist/commands/completion-candidates.js +58 -0
  24. package/dist/commands/doctor.js +101 -0
  25. package/dist/commands/ensure-ready.js +24 -0
  26. package/dist/commands/flows.js +62 -0
  27. package/dist/commands/launch/extension.js +40 -0
  28. package/dist/commands/{launch.js → launch/index.js} +15 -47
  29. package/dist/commands/launch/mobile.js +10 -0
  30. package/dist/commands/manifest.js +72 -0
  31. package/dist/commands/parse-args.js +189 -0
  32. package/dist/commands/provision.js +136 -0
  33. package/dist/commands/resolve-extension.js +23 -0
  34. package/dist/commands/run-engine.js +341 -0
  35. package/dist/commands/run.js +217 -0
  36. package/dist/commands/runtime-decision.js +58 -0
  37. package/dist/commands/runtime-health.js +25 -0
  38. package/dist/commands/runtime-launch.js +139 -0
  39. package/dist/commands/self-test.js +52 -0
  40. package/dist/commands/stop.js +52 -0
  41. package/dist/harness.js +8 -48
  42. package/dist/mm-harness-cli.js +13 -8
  43. package/docs/CLI-SPEC.md +1 -1
  44. package/docs/CODE-MAP.md +62 -0
  45. package/library/README.md +14 -0
  46. package/library/actions/extension/platform/cdp.mjs +1 -1
  47. package/library/actions/extension/wallet/ensure_unlocked.mjs +6 -0
  48. package/library/actions/mobile/wallet/ensure_unlocked.mjs +13 -1
  49. package/package.json +6 -7
  50. package/src/adapters/core/surface.ts +0 -71
  51. package/src/adapters/extension/ensure-ready.ts +0 -185
  52. package/src/adapters/extension/extension-id.ts +0 -107
  53. package/src/adapters/extension/runtime-decision.ts +0 -445
  54. package/src/adapters/extension/runtime.ts +0 -407
  55. package/src/adapters/extension/surface.ts +0 -88
  56. package/src/adapters/mobile/deps-markers.ts +0 -21
  57. package/src/adapters/mobile/prepare.ts +0 -246
  58. package/src/adapters/mobile/provision.ts +0 -594
  59. package/src/adapters/mobile/runtime-decision.ts +0 -466
  60. package/src/adapters/mobile/surface.ts +0 -71
  61. package/src/adapters/resolve-farmslot-ports.ts +0 -13
  62. package/src/adapters/slot-ports.ts +0 -158
  63. package/src/adapters/surface.ts +0 -117
  64. package/src/adapters.ts +0 -601
  65. package/src/cli-color.ts +0 -92
  66. package/src/cli-commands.ts +0 -250
  67. package/src/cli-version.ts +0 -141
  68. package/src/cli.ts +0 -2091
  69. package/src/commands/debug.ts +0 -65
  70. package/src/commands/fixtures.ts +0 -198
  71. package/src/commands/launch.ts +0 -470
  72. package/src/commands/logs.ts +0 -99
  73. package/src/commands/shared.ts +0 -235
  74. package/src/commands/update.ts +0 -316
  75. package/src/completions-cache.ts +0 -86
  76. package/src/doctor.ts +0 -215
  77. package/src/harness.ts +0 -797
  78. package/src/heal-bounds.ts +0 -198
  79. package/src/index.ts +0 -15
  80. package/src/leaf-invoke.ts +0 -28
  81. package/src/live-adapter-contract.ts +0 -274
  82. package/src/manifest.ts +0 -47
  83. package/src/mm-harness-cli.ts +0 -655
  84. package/src/paths.ts +0 -198
  85. package/src/progress.ts +0 -117
  86. package/src/recording-target.ts +0 -147
  87. package/src/run-recording.ts +0 -329
  88. package/src/runner.ts +0 -108
  89. package/src/types.ts +0 -57
@@ -1,250 +0,0 @@
1
- /**
2
- * cli-commands.ts — single source of truth for recipe CLI tab completion.
3
- */
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
-
8
- interface CommandSpec {
9
- name: string;
10
- aliases?: string[];
11
- desc?: string;
12
- flags?: string[];
13
- args?: string[];
14
- }
15
-
16
- interface CommandSpecMap {
17
- shared: CommandSpec[];
18
- extension: CommandSpec[];
19
- mobile: CommandSpec[];
20
- shim: CommandSpec[];
21
- }
22
-
23
- export const SPEC: CommandSpecMap = {
24
- shim: [
25
- { name: 'sync', desc: 'Copy fixture, write slot context, install harness', flags: ['--dry-run', '--no-install'] },
26
- { name: 'help', aliases: ['-h', '--help'], desc: 'Show usage' },
27
- ],
28
- shared: [
29
- { name: 'status', aliases: ['health', 'home'], desc: 'Home status + next commands', flags: ['--json'] },
30
- { name: 'ports', desc: 'Slot ports and runtime paths', flags: ['--json'] },
31
- { name: 'up', desc: 'Decide + run minimum work to reach ready', flags: ['--json', '--dry-run'] },
32
- { name: 'sync', desc: 'Refresh harness + canonicalize wallet fixture', flags: ['--json'] },
33
- { name: 'logs', aliases: ['tail'], desc: 'Compact build events or full log', flags: ['--full', '-f'] },
34
- { name: 'debug', aliases: ['devtools', 'inspect'], desc: 'Open DevTools UI', flags: ['--json', '--no-open'] },
35
- { name: 'actions', desc: 'List runnable recipe actions', flags: ['--json'] },
36
- { name: 'doctor', desc: 'Check harness/orchestration health', flags: ['--json', '--target', '--adapter', '--runtime-dir'] },
37
- { name: 'run', desc: 'Execute a proof recipe', args: ['recipe.json'] },
38
- { name: 'interactive', aliases: ['menu'], desc: 'Interactive command menu' },
39
- { name: 'prepare', desc: 'Install harness (+ optional validate)', flags: ['--target', '--runtime-dir', '--json'] },
40
- { name: 'runtime-status', desc: 'Structured runtime status JSON', flags: ['--json', '--target', '--cdp-port', '--runtime-dir'] },
41
- ],
42
- extension: [
43
- { name: 'decision', aliases: ['decide'], desc: 'Runtime decision (what next?)' },
44
- { name: 'update', aliases: ['sync-runtime'], desc: 'Perform recommended runtime action' },
45
- { name: 'ready', aliases: ['ensure-ready'], desc: 'Ensure one healthy extension home tab' },
46
- { name: 'watch', aliases: ['start-watch', 'watcher'], desc: 'Start/reuse webpack watcher', flags: ['--full', '-f'] },
47
- { name: 'stop', aliases: ['stop-watch', 'stop-watcher'], desc: 'Stop this checkout watcher' },
48
- { name: 'rebuild', aliases: ['reset', 'reopen', 'reload', 'browser', 'launch', 'full-launch', 'runtime-launch', 'build', 'build:once', 'build-once', 'refresh-once'], desc: 'Clean webpack + browser launch', flags: ['--json', '--dry-run', '--full'] },
49
- { name: 'sidepanel', desc: 'Side panel helper', args: ['cycle', 'open', 'close', 'toggle', 'status'] },
50
- { name: 'prepare', flags: ['--target', '--cdp-port', '--runtime-dir', '--validate'] },
51
- { name: 'debug', args: ['page', 'worker'], flags: ['--json', '--no-open'] },
52
- ],
53
- mobile: [
54
- { name: 'ios', aliases: ['start'], desc: 'Start Metro + launch iOS dev client' },
55
- { name: 'android', desc: 'Start Metro + launch Android dev client' },
56
- { name: 'setup:ios', desc: 'iOS launch + wallet fixture setup' },
57
- { name: 'setup:android', desc: 'Android launch + wallet fixture setup' },
58
- { name: 'stop', desc: 'Stop Metro for this slot' },
59
- { name: 'app-status', aliases: ['bridge-status'], desc: 'Bridge/runtime status' },
60
- { name: 'route', aliases: ['get-route'], desc: 'Current navigation route' },
61
- { name: 'navigate', desc: 'Navigate by route name', args: ['RouteName'] },
62
- { name: 'back', aliases: ['go-back'], desc: 'Navigate back' },
63
- { name: 'unlock', desc: 'Unlock with fixture password', args: ['password'] },
64
- { name: 'setup-wallet', aliases: ['wallet-setup'], desc: 'Apply wallet fixture', args: ['fixture.json'] },
65
- { name: 'accounts', aliases: ['list-accounts'], desc: 'List accounts' },
66
- { name: 'select-account', aliases: ['switch-account'], desc: 'Switch account', args: ['address'] },
67
- { name: 'screenshot', desc: 'Capture simulator/device screenshot', args: ['path'] },
68
- { name: 'dev-menu', aliases: ['devmenu'], desc: 'Open RN developer menu', flags: ['--json', '--no-open'] },
69
- { name: 'prepare', flags: ['--target', '--platform', '--preflight-mode', '--port', '--simulator', '--adb-serial', '--runtime-dir', '--wallet-setup', '--wallet-fixture'] },
70
- { name: 'debug', flags: ['--json', '--no-open', '--action'] },
71
- ],
72
- };
73
-
74
- const GLOBAL_FLAGS = ['--json', '--dry-run', '--full', '-f', '--help', '-h', '--no-open', '--no-color'];
75
-
76
- function mergeCommands(adapter: string): CommandSpec[] {
77
- const byName = new Map<string, CommandSpec & { aliases: string[]; flags: string[]; args: string[] }>();
78
- const add = (cmd: CommandSpec) => {
79
- const existing = byName.get(cmd.name);
80
- if (!existing) {
81
- byName.set(cmd.name, {
82
- ...cmd,
83
- aliases: [...(cmd.aliases || [])],
84
- flags: [...(cmd.flags || [])],
85
- args: [...(cmd.args || [])],
86
- });
87
- return;
88
- }
89
- existing.aliases = [...new Set([...(existing.aliases || []), ...(cmd.aliases || [])])];
90
- existing.flags = [...new Set([...(existing.flags || []), ...(cmd.flags || [])])];
91
- existing.args = [...new Set([...(existing.args || []), ...(cmd.args || [])])];
92
- if (!existing.desc && cmd.desc) existing.desc = cmd.desc;
93
- };
94
- for (const cmd of SPEC.shared) add(cmd);
95
- for (const cmd of SPEC[adapter as keyof CommandSpecMap] || []) add(cmd);
96
- return [...byName.values()];
97
- }
98
-
99
- export function commandTokens(adapter: string, { includeAliases = true }: { includeAliases?: boolean } = {}): string[] {
100
- const tokens = new Set<string>();
101
- for (const cmd of mergeCommands(adapter)) {
102
- tokens.add(cmd.name);
103
- if (includeAliases) for (const alias of cmd.aliases || []) tokens.add(alias);
104
- }
105
- return [...tokens].sort();
106
- }
107
-
108
- export function shimCommandTokens(): string[] {
109
- const tokens = new Set<string>();
110
- for (const cmd of SPEC.shim) {
111
- tokens.add(cmd.name);
112
- for (const alias of cmd.aliases || []) tokens.add(alias);
113
- }
114
- return [...tokens].sort();
115
- }
116
-
117
- export function lookupCommand(adapter: string, token: string): CommandSpec | null {
118
- for (const cmd of mergeCommands(adapter)) {
119
- if (cmd.name === token) return cmd;
120
- if ((cmd.aliases || []).includes(token)) return cmd;
121
- }
122
- for (const cmd of SPEC.shim) {
123
- if (cmd.name === token) return cmd;
124
- if ((cmd.aliases || []).includes(token)) return cmd;
125
- }
126
- return null;
127
- }
128
-
129
- export function flagsForPosition(adapter: string, commandToken: string): string[] {
130
- const cmd = lookupCommand(adapter, commandToken);
131
- const flags = new Set(GLOBAL_FLAGS);
132
- if (cmd) for (const flag of cmd.flags || []) flags.add(flag);
133
- return [...flags].sort();
134
- }
135
-
136
- export function argsForPosition(adapter: string, commandToken: string, argIndex: number): string[] {
137
- const cmd = lookupCommand(adapter, commandToken);
138
- if (!cmd?.args?.length) return [];
139
- const value = cmd.args[Math.max(0, argIndex)];
140
- return value ? [value] : cmd.args;
141
- }
142
-
143
- const RUN_RECIPE_DIRS: ((adapter: string, root: string) => string)[] = [
144
- (adapter, root) => path.join(root, 'temp/recipe/harness', adapter, 'runner/recipes'),
145
- (adapter, root) => path.join(root, 'temp/agentic/recipe-harness', adapter, 'runner/recipes'),
146
- ];
147
-
148
- export function listRunRecipes(adapter: string, root: string, cwd = process.cwd()): string[] {
149
- const byBase = new Map<string, string>();
150
- for (const resolveDir of RUN_RECIPE_DIRS) {
151
- const dir = resolveDir(adapter, root);
152
- if (!fs.existsSync(dir)) continue;
153
- for (const name of fs.readdirSync(dir)) {
154
- if (!name.endsWith('.recipe.json') || byBase.has(name)) continue;
155
- byBase.set(name, path.join(dir, name));
156
- }
157
- }
158
- return [...byBase.values()]
159
- .map((abs) => path.relative(cwd, abs))
160
- .sort();
161
- }
162
-
163
- function usage(): void {
164
- process.stderr.write(`Usage:
165
- cli-commands dump --adapter extension|mobile|shim [--aliases]
166
- cli-commands describe --adapter extension|mobile|shim
167
- cli-commands flags --adapter <adapter> --command <name>
168
- cli-commands args --adapter <adapter> --command <name> [--index <n>]
169
- cli-commands run-recipes --adapter extension|mobile --root <repo> [--cwd <dir>]
170
- cli-commands global-flags
171
- `);
172
- }
173
-
174
- async function main(): Promise<void> {
175
- const { parseArgs: parseArgsUtil } = await import('node:util');
176
- const { values, positionals } = parseArgsUtil({
177
- allowPositionals: true,
178
- args: process.argv.slice(2),
179
- options: {
180
- adapter: { type: 'string' },
181
- command: { type: 'string' },
182
- aliases: { type: 'boolean', default: true },
183
- index: { type: 'string', default: '0' },
184
- root: { type: 'string' },
185
- cwd: { type: 'string' },
186
- },
187
- });
188
-
189
- const command = positionals[0];
190
- if (!command || command === '--help' || command === '-h') {
191
- usage();
192
- process.exit(command ? 0 : 2);
193
- }
194
-
195
- if (command === 'global-flags') {
196
- process.stdout.write(`${GLOBAL_FLAGS.join('\n')}\n`);
197
- return;
198
- }
199
-
200
- const adapter = values.adapter;
201
- if (!adapter && command !== 'global-flags') {
202
- usage();
203
- process.exit(2);
204
- }
205
-
206
- if (command === 'dump') {
207
- process.stdout.write(`${commandTokens(adapter as string, { includeAliases: values.aliases }).join('\n')}\n`);
208
- return;
209
- }
210
-
211
- if (command === 'describe') {
212
- const rows = adapter === 'shim' ? SPEC.shim : mergeCommands(adapter as string);
213
- for (const cmd of rows) {
214
- const aliases = (cmd.aliases || []).join('|');
215
- const label = aliases ? `${cmd.name}|${aliases}` : cmd.name;
216
- process.stdout.write(`${label}\t${cmd.desc || cmd.name}\n`);
217
- }
218
- return;
219
- }
220
-
221
- if (command === 'flags') {
222
- process.stdout.write(`${flagsForPosition(adapter as string, values.command || '').join('\n')}\n`);
223
- return;
224
- }
225
-
226
- if (command === 'args') {
227
- const index = Number.parseInt(values.index as string, 10) || 0;
228
- process.stdout.write(`${argsForPosition(adapter as string, values.command || '', index).join('\n')}\n`);
229
- return;
230
- }
231
-
232
- if (command === 'run-recipes') {
233
- const root = values.root ? path.resolve(values.root) : process.cwd();
234
- const cwd = values.cwd ? path.resolve(values.cwd) : process.cwd();
235
- process.stdout.write(`${listRunRecipes(adapter as string, root, cwd).join('\n')}\n`);
236
- return;
237
- }
238
-
239
- usage();
240
- process.exit(2);
241
- }
242
-
243
- const _filename = fileURLToPath(import.meta.url);
244
- const isMain = process.argv[1] && path.resolve(process.argv[1]) === _filename;
245
- if (isMain) {
246
- main().catch((error: unknown) => {
247
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
248
- process.exit(1);
249
- });
250
- }
@@ -1,141 +0,0 @@
1
- /**
2
- * cli-version.ts — detect stale installed harness scripts vs the active runner.
3
- */
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import { spawnSync } from 'node:child_process';
7
- import { parseArgs } from 'node:util';
8
-
9
- function readJson(file: string): unknown {
10
- try {
11
- return JSON.parse(fs.readFileSync(file, 'utf8'));
12
- } catch {
13
- return null;
14
- }
15
- }
16
-
17
- function readPackageVersion(dir: string): string {
18
- try {
19
- const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')) as { version?: string };
20
- return pkg.version || 'unknown';
21
- } catch {
22
- return 'unknown';
23
- }
24
- }
25
-
26
- function gitHead(dir: string): string {
27
- const result = spawnSync('git', ['-C', dir, 'rev-parse', 'HEAD'], { encoding: 'utf8' });
28
- return result.status === 0 ? result.stdout.trim() : 'unknown';
29
- }
30
-
31
- interface HarnessCheckInput {
32
- runnerRoot: string;
33
- manifestPath: string;
34
- installedShaPath?: string;
35
- }
36
-
37
- interface HarnessCheckResult {
38
- stale: boolean;
39
- reasons: string[];
40
- installedRevision?: string | null;
41
- currentRevision?: string;
42
- installedVersion?: string | null;
43
- currentVersion?: string;
44
- manifest?: string;
45
- }
46
-
47
- export function checkHarness({ runnerRoot, manifestPath }: HarnessCheckInput): HarnessCheckResult {
48
- const manifest = readJson(manifestPath);
49
- const reasons: string[] = [];
50
- if (!manifest) {
51
- return { stale: false, reasons: ['no harness manifest'] };
52
- }
53
-
54
- const currentRevision = gitHead(runnerRoot);
55
- const currentVersion = readPackageVersion(runnerRoot);
56
- const manifestRecord = manifest as Record<string, unknown>;
57
- const source = manifestRecord.source as Record<string, unknown> | undefined;
58
- const installedRevision = (source?.runnerRevision as string) || null;
59
- const installedVersion = (manifestRecord.runnerPackageVersion as string) || null;
60
-
61
- if (installedRevision && installedRevision !== 'unknown' && installedRevision !== currentRevision) {
62
- reasons.push(`runner revision ${installedRevision.slice(0, 8)} != ${currentRevision.slice(0, 8)}`);
63
- }
64
- if (installedVersion && installedVersion !== currentVersion) {
65
- reasons.push(`runner package ${installedVersion} != ${currentVersion}`);
66
- }
67
-
68
- return {
69
- stale: reasons.length > 0,
70
- reasons,
71
- installedRevision,
72
- currentRevision,
73
- installedVersion,
74
- currentVersion,
75
- };
76
- }
77
-
78
- function usage(): void {
79
- console.error(`Usage:
80
- cli-version check --runner-root <dir> --manifest <path> [--installed-sha <path>] [--json]`);
81
- }
82
-
83
- async function main(): Promise<void> {
84
- const [command, ...rest] = process.argv.slice(2);
85
- const { values } = parseArgs({
86
- args: rest,
87
- allowPositionals: false,
88
- options: {
89
- 'runner-root': { type: 'string' },
90
- manifest: { type: 'string' },
91
- 'installed-sha': { type: 'string' },
92
- json: { type: 'boolean', default: false },
93
- },
94
- });
95
-
96
- if (!command || command === '--help' || command === '-h') {
97
- usage();
98
- process.exit(command ? 0 : 2);
99
- }
100
-
101
- if (command !== 'check') {
102
- usage();
103
- process.exit(2);
104
- }
105
-
106
- const runnerRoot = values['runner-root'];
107
- const manifestPath = values.manifest;
108
- if (!runnerRoot || !manifestPath) {
109
- usage();
110
- process.exit(2);
111
- }
112
-
113
- const payload: HarnessCheckResult & { manifest: string } = {
114
- ...checkHarness({
115
- runnerRoot,
116
- manifestPath,
117
- installedShaPath: values['installed-sha'],
118
- }),
119
- manifest: manifestPath,
120
- };
121
-
122
- if (values.json) {
123
- process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
124
- return;
125
- }
126
-
127
- if (payload.stale) {
128
- process.stderr.write(`recipe: harness stale (${payload.reasons.join('; ')})\n`);
129
- process.stderr.write('recipe: run: recipe sync\n');
130
- process.exit(1);
131
- }
132
- process.stdout.write('harness up to date\n');
133
- }
134
-
135
- const isMain = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.meta.filename ?? '');
136
- if (isMain) {
137
- main().catch((error: unknown) => {
138
- console.error(error instanceof Error ? error.message : String(error));
139
- process.exit(1);
140
- });
141
- }