@deeeed/metamask-harness 0.5.0 → 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 (102) hide show
  1. package/CHANGELOG.md +51 -20
  2. package/adapters/core/inject.sh +1 -0
  3. package/adapters/extension/cleanup.mjs +1 -1
  4. package/adapters/extension/inject.mjs +4 -1
  5. package/adapters/extension/launch-browser.cjs +18 -4
  6. package/adapters/extension/live.sh +14 -2
  7. package/adapters/extension/refresh-build.sh +2 -2
  8. package/adapters/extension/sidepanel-toggle.sh +33 -12
  9. package/adapters/extension/start-watch.sh +2 -2
  10. package/adapters/extension/wallet-fixture-state.cjs +38 -24
  11. package/adapters/manifest.json +32 -3
  12. package/adapters/mobile/inject.sh +1 -0
  13. package/adapters/mobile/start-metro.sh +1 -1
  14. package/adapters/mobile/stop-metro.sh +20 -0
  15. package/adapters/mobile/verify.sh +1 -1
  16. package/adapters/shared/activate-repo-node.sh +1 -1
  17. package/adapters/shared/cli-ux.sh +44 -25
  18. package/adapters/shared/ensure-runner-deps.sh +30 -0
  19. package/adapters/shared/log-tui.mjs +4 -4
  20. package/adapters/shared/open-debug.mjs +1 -1
  21. package/adapters/shared/reap-checkout-metros.sh +53 -0
  22. package/adapters/shared/recipe-harness-root.mjs +23 -0
  23. package/adapters/shared/resolve-farmslot-ports-core.mjs +205 -0
  24. package/adapters/shared/resolve-farmslot-ports.mjs +20 -0
  25. package/adapters/shared/resolve-farmslot-ports.sh +19 -126
  26. package/bin/mm-harness +45 -4
  27. package/dist/adapters/extension/runtime.js +3 -1
  28. package/dist/adapters/mobile/provision.js +34 -1
  29. package/dist/adapters/mobile/runtime-decision.js +3 -1
  30. package/dist/adapters/resolve-farmslot-ports.js +22 -0
  31. package/dist/adapters/slot-ports.js +18 -29
  32. package/dist/cli.js +32 -1463
  33. package/dist/commands/call.js +183 -0
  34. package/dist/commands/completion-candidates.js +58 -0
  35. package/dist/commands/doctor.js +101 -0
  36. package/dist/commands/ensure-ready.js +24 -0
  37. package/dist/commands/flows.js +62 -0
  38. package/dist/commands/launch/extension.js +40 -0
  39. package/dist/commands/{launch.js → launch/index.js} +15 -47
  40. package/dist/commands/launch/mobile.js +10 -0
  41. package/dist/commands/manifest.js +72 -0
  42. package/dist/commands/parse-args.js +189 -0
  43. package/dist/commands/provision.js +136 -0
  44. package/dist/commands/resolve-extension.js +23 -0
  45. package/dist/commands/run-engine.js +341 -0
  46. package/dist/commands/run.js +217 -0
  47. package/dist/commands/runtime-decision.js +58 -0
  48. package/dist/commands/runtime-health.js +25 -0
  49. package/dist/commands/runtime-launch.js +139 -0
  50. package/dist/commands/self-test.js +52 -0
  51. package/dist/commands/stop.js +52 -0
  52. package/dist/harness.js +8 -48
  53. package/dist/mm-harness-cli.js +13 -8
  54. package/docs/CLI-SPEC.md +1 -1
  55. package/docs/CODE-MAP.md +62 -0
  56. package/library/README.md +14 -0
  57. package/library/actions/core/perps/_controller.mjs +1 -1
  58. package/library/actions/extension/platform/cdp.mjs +2 -2
  59. package/library/actions/extension/wallet/ensure_unlocked.mjs +7 -1
  60. package/library/actions/harness-exports.mjs +27 -0
  61. package/library/actions/mobile/wallet/ensure_unlocked.mjs +14 -2
  62. package/library/actions/mobile/wallet/setup.mjs +1 -1
  63. package/package.json +6 -7
  64. package/src/adapters/core/surface.ts +0 -71
  65. package/src/adapters/extension/ensure-ready.ts +0 -185
  66. package/src/adapters/extension/extension-id.ts +0 -107
  67. package/src/adapters/extension/runtime-decision.ts +0 -445
  68. package/src/adapters/extension/runtime.ts +0 -407
  69. package/src/adapters/extension/surface.ts +0 -88
  70. package/src/adapters/mobile/deps-markers.ts +0 -21
  71. package/src/adapters/mobile/prepare.ts +0 -246
  72. package/src/adapters/mobile/provision.ts +0 -594
  73. package/src/adapters/mobile/runtime-decision.ts +0 -459
  74. package/src/adapters/mobile/surface.ts +0 -71
  75. package/src/adapters/slot-ports.ts +0 -165
  76. package/src/adapters/surface.ts +0 -117
  77. package/src/adapters.ts +0 -601
  78. package/src/cli-color.ts +0 -92
  79. package/src/cli-commands.ts +0 -250
  80. package/src/cli-version.ts +0 -141
  81. package/src/cli.ts +0 -2091
  82. package/src/commands/debug.ts +0 -65
  83. package/src/commands/fixtures.ts +0 -198
  84. package/src/commands/launch.ts +0 -470
  85. package/src/commands/logs.ts +0 -99
  86. package/src/commands/shared.ts +0 -235
  87. package/src/commands/update.ts +0 -316
  88. package/src/completions-cache.ts +0 -86
  89. package/src/doctor.ts +0 -215
  90. package/src/harness.ts +0 -797
  91. package/src/heal-bounds.ts +0 -198
  92. package/src/index.ts +0 -15
  93. package/src/leaf-invoke.ts +0 -28
  94. package/src/live-adapter-contract.ts +0 -274
  95. package/src/manifest.ts +0 -47
  96. package/src/mm-harness-cli.ts +0 -655
  97. package/src/paths.ts +0 -198
  98. package/src/progress.ts +0 -117
  99. package/src/recording-target.ts +0 -147
  100. package/src/run-recording.ts +0 -329
  101. package/src/runner.ts +0 -108
  102. 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
- }