@deeeed/metamask-harness 0.83.0 → 0.84.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.
- package/CHANGELOG.md +28 -0
- package/README.md +8 -1
- package/adapters/manifest.json +14 -110
- package/adapters/shared/session-lock.mjs +43 -0
- package/adapters/web-dapp/metamask-signer.mjs +212 -0
- package/dist/adapters/extension/run.js +4 -2
- package/dist/adapters/extension/runtime.js +71 -18
- package/dist/adapters/mobile/devices.js +5 -1
- package/dist/adapters/mobile/run.js +1 -1
- package/dist/adapters/mobile/surface.js +1 -2
- package/dist/adapters/plugin-surface.js +14 -42
- package/dist/adapters/session-lock.js +23 -0
- package/dist/adapters/web-dapp/console-capture.js +2 -20
- package/dist/adapters/web-dapp/run.js +25 -24
- package/dist/adapters/web-dapp/surface.js +32 -108
- package/dist/command-contract.js +1342 -643
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/launch/device.js +1 -1
- package/dist/doctor.js +5 -2
- package/dist/mm-harness-cli.js +228 -1210
- package/docs/CONTRIBUTING.md +6 -4
- package/docs/cli-contract.md +1 -1
- package/docs/feature-guard.md +1 -1
- package/library/actions/shared/console-collector.mjs +1 -1
- package/library/actions/web-dapp/app/launch.mjs +1 -1
- package/library/actions/web-dapp/platform/adapter-web-lib.mjs +18 -0
- package/library/actions/web-dapp/platform/hud.mjs +1 -1
- package/library/actions/web-dapp/platform/page.mjs +2 -3
- package/library/actions/web-dapp/platform/runtime.mjs +29 -178
- package/package.json +7 -7
- package/scripts/completions.sh +1 -1
- package/adapters/web-dapp/cleanup.mjs +0 -69
- package/adapters/web-dapp/inject.mjs +0 -75
- package/adapters/web-dapp/launch.mjs +0 -631
- package/adapters/web-dapp/lib/blocked-hosts.mjs +0 -24
- package/adapters/web-dapp/lib/cdp-client.mjs +0 -84
- package/adapters/web-dapp/lib/display.mjs +0 -115
- package/adapters/web-dapp/lib/hud.mjs +0 -148
- package/adapters/web-dapp/lib/policy-fence.mjs +0 -119
- package/adapters/web-dapp/lib/policy.mjs +0 -84
- package/adapters/web-dapp/lib/processes.mjs +0 -187
- package/adapters/web-dapp/lib/readiness.mjs +0 -272
- package/adapters/web-dapp/stop.mjs +0 -38
- package/adapters/web-dapp/verify.mjs +0 -51
- package/adapters/web-dapp/wallet-host.mjs +0 -520
- package/dist/adapters/web-dapp/doctor-checks.js +0 -33
- package/dist/adapters/web-dapp/launch.js +0 -87
- package/dist/adapters/web-dapp/overlay.js +0 -21
- package/dist/adapters/web-dapp/readiness.js +0 -10
- package/dist/adapters/web-dapp/stop.js +0 -20
- package/dist/cli.js +0 -202
package/docs/CONTRIBUTING.md
CHANGED
|
@@ -67,9 +67,11 @@ in the registry, so a platform registered the same way is reached by the
|
|
|
67
67
|
commands that take `--adapter` with no edit to shared code
|
|
68
68
|
(`tests/fixtures/example-adapter.ts`); `status` detects the adapter from the
|
|
69
69
|
checkout.
|
|
70
|
-
The
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
The public command table (`src/command-contract.ts`) reads the `--adapter`
|
|
71
|
+
choices when a command is validated: the registered adapters, then the adapters
|
|
72
|
+
the libraries on `RECIPE_LIBRARY_PATH` and the command's `--library` entries
|
|
73
|
+
declare. A new platform or library plugin reaches `bin/mm-harness` with no edit
|
|
74
|
+
to the table.
|
|
73
75
|
|
|
74
76
|
Extend `AdapterSurface` when a platform owns the behavior; members a platform
|
|
75
77
|
does not need are optional, and Core reports headless/N/A explicitly. Put each
|
|
@@ -82,7 +84,7 @@ command-local platform ladders for surface-owned behavior:
|
|
|
82
84
|
- `run`/`call` come from `@farmslot/recipe-cli/harness` and read `run`,
|
|
83
85
|
`recording`, `diagnostics`, `observation` and `sourceFingerprint`;
|
|
84
86
|
mm-harness hands them its engine (`src/engine.ts`) and its device targeting
|
|
85
|
-
and plan steps (`src/cli.ts`);
|
|
87
|
+
and plan steps (`src/mm-harness-cli.ts`);
|
|
86
88
|
- the runner reads `actions` (manifest, bundled semantic actions, live adapter
|
|
87
89
|
scripts, `cdp.target`, the `ui.*` transport, adapter wrappers, input checks);
|
|
88
90
|
- `install`/`verify`/`cleanup` read `harness`, `agentic-runtime.json` reads
|
package/docs/cli-contract.md
CHANGED
|
@@ -94,7 +94,7 @@ These are real farm bugs. The goldens freeze the current rejections, so fixing e
|
|
|
94
94
|
|
|
95
95
|
`goldens-terminal` freezes the `va-mmcx-terminal-farm` hooks with their exact argv (`recipe_action_manifest`, `recipe_doctor`, `recipe_harness_{install,verify,cleanup}`, `recipe_run`, the browser `boot`/`shutdown` resources and `teardown`), against a fresh fixture slot: no slot dev server, no Extension dist, and the farm's wallet fixture outside the slot, so `install` copies it in. `launch` is frozen on its cold path: it refuses at the missing dist before any browser starts. The slot's CDP port is the one port no port holder reserves: a farm slot with no browser has it free, and the Terminal's port checks probe it.
|
|
96
96
|
|
|
97
|
-
`terminal` is the plugin the `experimental-metamask-recipe-terminal` library declares (`adapters.terminal`, extends the built-in `web-dapp`), so the family loads it: set `GOLDENS_TERMINAL_LIBRARY` to a checkout of that library
|
|
97
|
+
`terminal` is the plugin the `experimental-metamask-recipe-terminal` library declares (`adapters.terminal`, extends the built-in `web-dapp`), so the family loads it: set `GOLDENS_TERMINAL_LIBRARY` to a checkout of that library. The family copies one pinned commit of it (`GOLDENS_TERMINAL_LIBRARY_REV` in `goldens-terminal.test.sh`) into its sandbox with `git archive` and puts it on `RECIPE_LIBRARY_PATH`, so the goldens don't move when the checkout does. The checkout must contain that commit (`git fetch`); moving the pin and regenerating the goldens is one change. CI has no access to the private library and skips the family (`CI` set); local runs need the variable, and without it outside CI the family fails. A release gate must set it too (proposed for the release checklist). `goldens-discovery` covers the built-in `web-dapp` in its per-adapter loop.
|
|
98
98
|
|
|
99
99
|
Outside this boundary: a successful `launch` and the `temp/recipe/runtime/terminal/` layout (`browser.pid`), the library's recipes, `run terminal.perps.<name> --plan`, and the recipe-terminal `console-guard.mjs` import paths.
|
|
100
100
|
|
package/docs/feature-guard.md
CHANGED
|
@@ -74,7 +74,7 @@ Captures are normalized (paths, versions, pids, timestamps, durations, epoch num
|
|
|
74
74
|
|
|
75
75
|
What the normaliser deliberately masks, and so cannot catch: any semver under a key ending in `version` other than the harness's own, numbers under keys ending in `Ms`/`Seconds`/`duration`/`elapsed` (so a changed timeout value), epoch-millisecond numbers, and the `volatile` paths (a new product warning during a live run). Each varies run to run on real slots; the goldens freeze the static values.
|
|
76
76
|
|
|
77
|
-
Machine rules: on macwork, headless only and no focus stealing (`TERMINAL_HEADLESS=1` is in the matrix).
|
|
77
|
+
Machine rules: on macwork, headless only and no focus stealing (`TERMINAL_HEADLESS=1` is in the matrix). A headful `run`/`call` (Extension, including its wallet popup, or a headful web-dapp) can't proceed while macwork's screen is locked: it refuses with `SESSION_LOCKED` at its runtime check (it used to stall until a `Page.captureScreenshot` timeout). Mobile and the Extension run on free macpro or mini slots, never mini-mm-3. The differential stops and cleans the slot runtime it used; reinstall the farm's overlay afterwards if the slot is handed back to the farm.
|
|
78
78
|
|
|
79
79
|
## 3. Release smoke
|
|
80
80
|
|
|
@@ -15,7 +15,7 @@ export const COLLECTOR_SCRIPT_NAME = 'console-tail.mjs';
|
|
|
15
15
|
export const CAPTURE_CONTROL_MARK = '[mm-harness-capture-control]';
|
|
16
16
|
|
|
17
17
|
// Same derivation as ownerMarker('console-collector', …) in
|
|
18
|
-
//
|
|
18
|
+
// @farmslot/adapter-web/web-dapp, so the web-dapp stop path finds it.
|
|
19
19
|
export function collectorOwnerMarker(runtimeDir) {
|
|
20
20
|
const id = createHash('sha256').update(path.resolve(runtimeDir)).digest('hex').slice(0, 32);
|
|
21
21
|
return `--mm-harness-owner=console-collector-${id}`;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// start forgets the app's stored agent key and the extension's dapp permission.
|
|
5
5
|
|
|
6
6
|
import { appPort, cdpPort, isDirectRun, resolveSigner, runAdapter, webDappPolicy } from '../platform/runtime.mjs';
|
|
7
|
-
import { launchWebDappBrowser } from '
|
|
7
|
+
import { launchWebDappBrowser } from '@farmslot/adapter-web/web-dapp';
|
|
8
8
|
|
|
9
9
|
export async function launch(input) {
|
|
10
10
|
const node = input.node ?? {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Helpers from @farmslot/adapter-web's web-dapp lib that its public
|
|
2
|
+
// `./web-dapp` entry does not export, loaded from the installed package, never
|
|
3
|
+
// copied: the display and HUD helpers the actions share with the launcher and
|
|
4
|
+
// wallet host (one hud.json format), and the fixture and readiness helpers the
|
|
5
|
+
// MetaMask signer module uses. The signer module cannot import `./web-dapp`
|
|
6
|
+
// itself: the launch leaf loads it while that entry is still evaluating.
|
|
7
|
+
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { pathToFileURL } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const packageRoot = path.dirname(createRequire(import.meta.url).resolve('@farmslot/adapter-web/package.json'));
|
|
13
|
+
const lib = (name) => import(pathToFileURL(path.join(packageRoot, 'src/web-dapp/lib', name)).href);
|
|
14
|
+
|
|
15
|
+
export const { resolveHeadless, resolveSlowMo } = await lib('display.mjs');
|
|
16
|
+
export const { HUD_ELEMENT, clearHudState, hudRenderExpression, nextHudState, readHudState, writeHudState } = await lib('hud.mjs');
|
|
17
|
+
export const { fixtureEntry } = await lib('runtime.mjs');
|
|
18
|
+
export const { check: readinessCheck } = await lib('readiness.mjs');
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { isAppUrl } from '@farmslot/adapter-web/origin';
|
|
7
7
|
|
|
8
|
-
import { clearHudState, hudRenderExpression, nextHudState, readHudState, writeHudState } from '
|
|
8
|
+
import { clearHudState, hudRenderExpression, nextHudState, readHudState, writeHudState } from './adapter-web-lib.mjs';
|
|
9
9
|
import { appOrigin, cdpPort, readBrowserState, webDappRuntimeDir } from './runtime.mjs';
|
|
10
10
|
import { importRecipeHarnessRuntimeCdp } from '../../harness-exports.mjs';
|
|
11
11
|
|
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
// so it stays the window's active tab and keeps painting without it.
|
|
8
8
|
|
|
9
9
|
import { isAppUrl } from '@farmslot/adapter-web/origin';
|
|
10
|
+
import { cdpListenerPids } from '@farmslot/adapter-web/web-dapp';
|
|
10
11
|
|
|
11
|
-
import { resolveSlowMo } from '
|
|
12
|
-
import { HUD_ELEMENT } from '../../../../adapters/web-dapp/lib/hud.mjs';
|
|
13
|
-
import { cdpListenerPids } from '../../../../adapters/web-dapp/lib/processes.mjs';
|
|
12
|
+
import { HUD_ELEMENT, resolveSlowMo } from './adapter-web-lib.mjs';
|
|
14
13
|
import { appOrigin, cdpPort, readBrowserState } from './runtime.mjs';
|
|
15
14
|
import { importRecipeHarnessRuntimeCdp } from '../../harness-exports.mjs';
|
|
16
15
|
|
|
@@ -1,186 +1,37 @@
|
|
|
1
1
|
// Resolve the web-dapp slot runtime: the venue policy, ports, runtime paths,
|
|
2
|
-
// browser state and wallet-fixture accounts
|
|
3
|
-
//
|
|
2
|
+
// browser state and wallet-fixture accounts, from @farmslot/adapter-web; plus
|
|
3
|
+
// the live-action plumbing (input, direct run, output). Key material is only
|
|
4
|
+
// ever returned as a viem account object; callers must never log or persist it.
|
|
4
5
|
|
|
5
|
-
import { realpathSync } from 'node:fs';
|
|
6
6
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
7
|
-
import path from 'node:path';
|
|
8
7
|
import { pathToFileURL } from 'node:url';
|
|
9
8
|
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function webDappRuntimePath(projectRoot, ...segments) {
|
|
38
|
-
return path.join(webDappRuntimeDir(projectRoot), ...segments);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function portFrom(raw, label) {
|
|
42
|
-
if (raw === undefined || raw === null || raw === '') return undefined;
|
|
43
|
-
const port = Number(raw);
|
|
44
|
-
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
45
|
-
throw new Error(`${label} must be a TCP port, got ${JSON.stringify(raw)}.`);
|
|
46
|
-
}
|
|
47
|
-
return port;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// The slot's Next.js dev server. Farmslot passes it as --watcher-port {{port}};
|
|
51
|
-
// TERMINAL_APP_PORT is the explicit override.
|
|
52
|
-
export function appPort(input = {}, env = process.env) {
|
|
53
|
-
const port =
|
|
54
|
-
portFrom(input.node?.app_port, 'app_port') ??
|
|
55
|
-
portFrom(env.TERMINAL_APP_PORT, 'TERMINAL_APP_PORT') ??
|
|
56
|
-
portFrom(env.RECIPE_WATCHER_PORT, 'RECIPE_WATCHER_PORT') ??
|
|
57
|
-
portFrom(env.WATCHER_PORT, 'WATCHER_PORT');
|
|
58
|
-
if (!port) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
`${webDappPolicy().adapterId} adapter requires the slot dev-server port.\n` +
|
|
61
|
-
'Next: pass --watcher-port <port> (Farmslot: {{port}}) or set TERMINAL_APP_PORT.',
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
return port;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function appOrigin(input = {}, env = process.env) {
|
|
68
|
-
return `http://localhost:${appPort(input, env)}`;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function cdpPort(input = {}, env = process.env) {
|
|
72
|
-
const port =
|
|
73
|
-
portFrom(input.node?.cdp_port, 'cdp_port') ??
|
|
74
|
-
portFrom(env.RECIPE_CDP_PORT, 'RECIPE_CDP_PORT') ??
|
|
75
|
-
portFrom(env.CDP_PORT, 'CDP_PORT');
|
|
76
|
-
if (!port) {
|
|
77
|
-
throw new Error(
|
|
78
|
-
`${webDappPolicy().adapterId} adapter requires a per-slot browser CDP port.\n` +
|
|
79
|
-
'Next: pass --cdp-port <port> (Farmslot: {{cdp_port}}).',
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
return port;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export async function readJsonFile(file) {
|
|
86
|
-
try {
|
|
87
|
-
return JSON.parse(await readFile(file, 'utf8'));
|
|
88
|
-
} catch (error) {
|
|
89
|
-
if (error?.code === 'ENOENT') return null;
|
|
90
|
-
throw error;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export async function writePrivateJson(file, value) {
|
|
95
|
-
await writeFile(file, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export async function readBrowserState(projectRoot) {
|
|
99
|
-
return readJsonFile(webDappRuntimePath(projectRoot, 'browser.json'));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function resolveSigner(raw) {
|
|
103
|
-
const signer = raw == null || raw === '' ? 'extension' : String(raw);
|
|
104
|
-
if (!SIGNER_MODES.includes(signer)) {
|
|
105
|
-
throw new Error(`signer must be one of ${SIGNER_MODES.join(' | ')}, got ${JSON.stringify(raw)}.`);
|
|
106
|
-
}
|
|
107
|
-
return signer;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Account name precedence: node.account_name → node.account (when not an
|
|
111
|
-
// address) → the running browser's account → dev1.
|
|
112
|
-
export function accountName(input = {}, browserState = null) {
|
|
113
|
-
const explicit = input.node?.account_name ?? input.node?.account;
|
|
114
|
-
if (typeof explicit === 'string' && explicit.trim() && !/^0x[0-9a-fA-F]{40}$/u.test(explicit.trim())) {
|
|
115
|
-
return explicit.trim();
|
|
116
|
-
}
|
|
117
|
-
return browserState?.account?.name ?? DEFAULT_ACCOUNT;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function fixturePath(projectRoot, env = process.env) {
|
|
121
|
-
return env.RECIPE_WALLET_FIXTURE ? path.resolve(env.RECIPE_WALLET_FIXTURE) : walletFixturePath(projectRoot);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export async function loadWalletFixture(projectRoot, env = process.env) {
|
|
125
|
-
const file = fixturePath(projectRoot, env);
|
|
126
|
-
const fixture = await readJsonFile(file);
|
|
127
|
-
if (!fixture) {
|
|
128
|
-
throw new Error(
|
|
129
|
-
`wallet fixture not found at ${file}.\n` +
|
|
130
|
-
`Next: mm-harness install --adapter ${webDappPolicy().adapterId} --target <checkout> with RECIPE_WALLET_FIXTURE set, or copy the fixture there (0600).`,
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
if (!Array.isArray(fixture.accounts) || fixture.accounts.length === 0) {
|
|
134
|
-
throw new Error(`wallet fixture at ${file} has no accounts.`);
|
|
135
|
-
}
|
|
136
|
-
return { file, fixture };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function fixtureEntry(fixture, name) {
|
|
140
|
-
const entry = fixture.accounts.find((account) => account?.name === name);
|
|
141
|
-
if (!entry) {
|
|
142
|
-
const names = fixture.accounts.map((account) => account?.name).filter(Boolean).join(', ');
|
|
143
|
-
throw new Error(`wallet fixture has no account named "${name}". Available: ${names}.`);
|
|
144
|
-
}
|
|
145
|
-
if (entry.type !== 'mnemonic' && entry.type !== 'privateKey') {
|
|
146
|
-
throw new Error(`wallet fixture account "${name}" must be mnemonic or privateKey, got "${entry.type}".`);
|
|
147
|
-
}
|
|
148
|
-
if (typeof entry.value !== 'string' || !entry.value.trim()) {
|
|
149
|
-
throw new Error(`wallet fixture account "${name}" has no value.`);
|
|
150
|
-
}
|
|
151
|
-
return entry;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Mnemonics use BIP-44 index 0, the MetaMask default and the derivation the
|
|
155
|
-
// Extension fixture flow imports.
|
|
156
|
-
export function viemAccountFromEntry(entry) {
|
|
157
|
-
if (entry.type === 'mnemonic') return mnemonicToAccount(entry.value.trim(), { addressIndex: 0 });
|
|
158
|
-
const raw = entry.value.trim();
|
|
159
|
-
const key = raw.startsWith('0x') ? raw : `0x${raw}`;
|
|
160
|
-
if (!/^0x[0-9a-fA-F]{64}$/u.test(key)) {
|
|
161
|
-
throw new Error(`wallet fixture privateKey account "${entry.name}" is not a 32-byte hex key.`);
|
|
162
|
-
}
|
|
163
|
-
return privateKeyToAccount(key);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export async function fixtureAccount(projectRoot, name, env = process.env) {
|
|
167
|
-
const { fixture } = await loadWalletFixture(projectRoot, env);
|
|
168
|
-
return viemAccountFromEntry(fixtureEntry(fixture, name));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Address-only view of the account a node targets: an explicit address wins
|
|
172
|
-
// (read-only use), otherwise the fixture account is derived.
|
|
173
|
-
export async function resolveAccountAddress(input, env = process.env) {
|
|
174
|
-
const raw = input.node?.account;
|
|
175
|
-
if (typeof raw === 'string' && /^0x[0-9a-fA-F]{40}$/u.test(raw.trim())) {
|
|
176
|
-
return { name: null, address: raw.trim() };
|
|
177
|
-
}
|
|
178
|
-
const projectRoot = input.context?.projectRoot;
|
|
179
|
-
const browserState = projectRoot ? await readBrowserState(projectRoot) : null;
|
|
180
|
-
const name = accountName(input, browserState);
|
|
181
|
-
const account = await fixtureAccount(projectRoot, name, env);
|
|
182
|
-
return { name, address: account.address };
|
|
183
|
-
}
|
|
9
|
+
import { webDappAdapterId } from '@farmslot/adapter-web/web-dapp';
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
accountName,
|
|
13
|
+
appOrigin,
|
|
14
|
+
appPort,
|
|
15
|
+
canonicalPath,
|
|
16
|
+
cdpPort,
|
|
17
|
+
DEFAULT_ACCOUNT,
|
|
18
|
+
fixtureAccount,
|
|
19
|
+
fixtureEntry,
|
|
20
|
+
fixturePath,
|
|
21
|
+
loadWalletFixture,
|
|
22
|
+
POLICY_ENV,
|
|
23
|
+
readBrowserState,
|
|
24
|
+
readJsonFile,
|
|
25
|
+
resolveAccountAddress,
|
|
26
|
+
resolveSigner,
|
|
27
|
+
SIGNER_MODES,
|
|
28
|
+
viemAccountFromEntry,
|
|
29
|
+
webDappAdapterId,
|
|
30
|
+
webDappPolicy,
|
|
31
|
+
webDappRuntimeDir,
|
|
32
|
+
webDappRuntimePath,
|
|
33
|
+
writePrivateJson,
|
|
34
|
+
} from '@farmslot/adapter-web/web-dapp';
|
|
184
35
|
|
|
185
36
|
export async function loadInput() {
|
|
186
37
|
const inputPath = process.argv[2] || process.env.METAMASK_RECIPE_ADAPTER_INPUT;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.84.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@farmslot/adapter-node": "0.1.
|
|
25
|
-
"@farmslot/adapter-rn": "0.
|
|
26
|
-
"@farmslot/adapter-sdk": "0.
|
|
27
|
-
"@farmslot/adapter-web": "0.
|
|
24
|
+
"@farmslot/adapter-node": "0.1.4",
|
|
25
|
+
"@farmslot/adapter-rn": "0.19.1",
|
|
26
|
+
"@farmslot/adapter-sdk": "0.7.1",
|
|
27
|
+
"@farmslot/adapter-web": "0.6.2",
|
|
28
28
|
"@farmslot/agent-runtime": "0.17.0",
|
|
29
29
|
"@farmslot/handoff": "^0.3.1",
|
|
30
30
|
"@farmslot/protocol": "0.34.0",
|
|
31
|
-
"@farmslot/recipe-cli": "0.
|
|
32
|
-
"@farmslot/recipe-runner": "0.
|
|
31
|
+
"@farmslot/recipe-cli": "0.10.0",
|
|
32
|
+
"@farmslot/recipe-runner": "0.27.0",
|
|
33
33
|
"agent-device": "0.19.3",
|
|
34
34
|
"commander": "^12.0.0",
|
|
35
35
|
"esbuild": "0.28.1",
|
package/scripts/completions.sh
CHANGED
|
@@ -45,7 +45,7 @@ _mmh_flags_for() {
|
|
|
45
45
|
execution-template) printf '%s' "--dir --domain-dir --project-worker --project-name --package-templates --package-id --flow --run-mode --platform --domain --id --provenance --include-shadowed --no-include-shadowed --title --force --json" ;;
|
|
46
46
|
recipe-quality) printf '%s' "--input --output --recipe --task --evidence --params --adapter --library --advisor --advisor-model --advisor-stages --json" ;;
|
|
47
47
|
actions) printf '%s' "--adapter --action --kind --raw --action-manifest --target --json" ;;
|
|
48
|
-
call) printf '%s' "--list --domain --source --sort --
|
|
48
|
+
call) printf '%s' "--list --domain --source --sort --adapter --target --artifacts-dir --action-manifest --heal --json" ;;
|
|
49
49
|
run) printf '%s' "--list --domain --source --sort --describe --plan --adapter --artifacts-dir --target --library --cdp-port --heal --record-video --json" ;;
|
|
50
50
|
doctor) printf '%s' "--adapter --target --fix --json" ;;
|
|
51
51
|
install|verify|cleanup) printf '%s' "--adapter --target --json" ;;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// cleanup.mjs — `mm-harness cleanup --adapter <web-dapp adapter>`: stop the slot
|
|
3
|
-
// browser and wallet host, then remove the slot's runtime state and the installed
|
|
4
|
-
// wallet fixture. Browser profiles are kept unless --reset-profile.
|
|
5
|
-
//
|
|
6
|
-
// Inputs: --target <checkout> [--cdp-port <port>] [--reset-profile]
|
|
7
|
-
// Outputs: JSON summary on stdout. Exit 0 cleaned; 1 the CDP port stayed busy.
|
|
8
|
-
// Never touches: product source files, the Next.js dev server.
|
|
9
|
-
|
|
10
|
-
import { existsSync, readdirSync, rmSync } from 'node:fs';
|
|
11
|
-
import path from 'node:path';
|
|
12
|
-
import { fileURLToPath } from 'node:url';
|
|
13
|
-
|
|
14
|
-
import { recipeHarnessPath } from '@farmslot/recipe-cli/harness';
|
|
15
|
-
import { walletFixturePath } from '../../library/actions/harness-exports.mjs';
|
|
16
|
-
import { webDappPolicy, webDappRuntimeDir } from '../../library/actions/web-dapp/platform/runtime.mjs';
|
|
17
|
-
import { stopConsoleCollector, stopWebDappBrowser } from './lib/processes.mjs';
|
|
18
|
-
|
|
19
|
-
const usage = 'Usage: cleanup.mjs --target <checkout> [--cdp-port <port>] [--reset-profile]';
|
|
20
|
-
|
|
21
|
-
export async function cleanupWebDappRuntime({ target, cdpPort, resetProfile = false }) {
|
|
22
|
-
const root = path.resolve(target);
|
|
23
|
-
const stop = await stopWebDappBrowser(root, { cdpPort });
|
|
24
|
-
await stopConsoleCollector(root);
|
|
25
|
-
const runtime = webDappRuntimeDir(root);
|
|
26
|
-
const removed = [];
|
|
27
|
-
if (existsSync(runtime)) {
|
|
28
|
-
for (const entry of readdirSync(runtime)) {
|
|
29
|
-
if (entry.startsWith('profile-') && !resetProfile) continue;
|
|
30
|
-
rmSync(path.join(runtime, entry), { recursive: true, force: true });
|
|
31
|
-
removed.push(entry);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
const overlay = recipeHarnessPath(root, webDappPolicy().adapterId);
|
|
35
|
-
if (existsSync(overlay)) {
|
|
36
|
-
rmSync(overlay, { recursive: true, force: true });
|
|
37
|
-
removed.push(path.relative(root, overlay));
|
|
38
|
-
}
|
|
39
|
-
const fixture = walletFixturePath(root);
|
|
40
|
-
if (existsSync(fixture)) {
|
|
41
|
-
rmSync(fixture, { force: true });
|
|
42
|
-
removed.push(path.relative(root, fixture));
|
|
43
|
-
}
|
|
44
|
-
return { status: 'pass', target: root, stopped: stop.stopped, cdpPortFree: stop.cdpPortFree, removed };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
48
|
-
const argv = process.argv.slice(2);
|
|
49
|
-
if (argv.includes('--help') || argv.includes('-h')) {
|
|
50
|
-
process.stdout.write(`${usage}\n`);
|
|
51
|
-
process.exit(0);
|
|
52
|
-
}
|
|
53
|
-
const value = (flag) => {
|
|
54
|
-
const index = argv.indexOf(flag);
|
|
55
|
-
return index >= 0 ? argv[index + 1] : undefined;
|
|
56
|
-
};
|
|
57
|
-
const rawPort = value('--cdp-port') ?? process.env.RECIPE_CDP_PORT ?? process.env.CDP_PORT;
|
|
58
|
-
try {
|
|
59
|
-
const result = await cleanupWebDappRuntime({
|
|
60
|
-
target: value('--target') ?? process.cwd(),
|
|
61
|
-
cdpPort: rawPort ? Number(rawPort) : undefined,
|
|
62
|
-
resetProfile: argv.includes('--reset-profile'),
|
|
63
|
-
});
|
|
64
|
-
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
65
|
-
} catch (error) {
|
|
66
|
-
process.stdout.write(`${JSON.stringify({ status: 'fail', error: error.message })}\n`);
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// inject.mjs — `mm-harness install --adapter <web-dapp adapter>`: prepare the slot
|
|
3
|
-
// runtime directory and install the wallet fixture the web-dapp actions read.
|
|
4
|
-
//
|
|
5
|
-
// Inputs: --target <checkout> [--fixture <wallet-fixture.json>] [--force]
|
|
6
|
-
// env RECIPE_WALLET_FIXTURE (fixture source when --fixture is absent)
|
|
7
|
-
// Outputs: <target>/<harness root>/<adapter id>/install.json (overlay marker),
|
|
8
|
-
// <target>/<runtime>/<adapter id>/ (0700), <runtime>/wallet-fixture.json (0600);
|
|
9
|
-
// JSON summary on stdout (account names only). Exit 0 installed; 1 failure; 2 usage.
|
|
10
|
-
// Never touches: product source files; never prints key material.
|
|
11
|
-
|
|
12
|
-
import { copyFileSync, chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
|
-
import path from 'node:path';
|
|
14
|
-
import { fileURLToPath } from 'node:url';
|
|
15
|
-
|
|
16
|
-
import { recipeHarnessPath, recipeRuntimeDir } from '@farmslot/recipe-cli/harness';
|
|
17
|
-
import { walletFixturePath } from '../../library/actions/harness-exports.mjs';
|
|
18
|
-
import { webDappPolicy, webDappRuntimeDir } from '../../library/actions/web-dapp/platform/runtime.mjs';
|
|
19
|
-
|
|
20
|
-
const usage = 'Usage: inject.mjs --target <checkout> [--fixture <wallet-fixture.json>] [--force]';
|
|
21
|
-
|
|
22
|
-
export function installWebDappRuntime({ target, fixture, force = false }) {
|
|
23
|
-
const root = path.resolve(target);
|
|
24
|
-
const { adapterId, checkout } = webDappPolicy();
|
|
25
|
-
if (!checkout.matches(root)) throw new Error(`${root} is not a ${checkout.name} checkout (needs ${checkout.needs}).`);
|
|
26
|
-
mkdirSync(path.join(root, recipeRuntimeDir()), { recursive: true, mode: 0o700 });
|
|
27
|
-
mkdirSync(webDappRuntimeDir(root), { recursive: true, mode: 0o700 });
|
|
28
|
-
const destination = walletFixturePath(root);
|
|
29
|
-
let fixtureAction = 'kept';
|
|
30
|
-
if (fixture && (force || !existsSync(destination))) {
|
|
31
|
-
const parsed = JSON.parse(readFileSync(fixture, 'utf8'));
|
|
32
|
-
if (!Array.isArray(parsed.accounts) || parsed.accounts.length === 0) throw new Error(`${fixture} has no accounts.`);
|
|
33
|
-
copyFileSync(fixture, destination);
|
|
34
|
-
chmodSync(destination, 0o600);
|
|
35
|
-
fixtureAction = 'installed';
|
|
36
|
-
} else if (!existsSync(destination)) {
|
|
37
|
-
fixtureAction = 'missing';
|
|
38
|
-
}
|
|
39
|
-
const accounts = existsSync(destination)
|
|
40
|
-
? JSON.parse(readFileSync(destination, 'utf8')).accounts.map((account) => account?.name).filter(Boolean)
|
|
41
|
-
: [];
|
|
42
|
-
const marker = { schemaVersion: 1, adapter: adapterId, installedAt: new Date().toISOString(), fixture: fixtureAction };
|
|
43
|
-
// The overlay directory is the install marker run/call auto-ensure checks.
|
|
44
|
-
const overlay = recipeHarnessPath(root, adapterId);
|
|
45
|
-
mkdirSync(overlay, { recursive: true, mode: 0o700 });
|
|
46
|
-
writeFileSync(path.join(overlay, 'install.json'), `${JSON.stringify(marker, null, 2)}\n`, { mode: 0o600 });
|
|
47
|
-
return { status: fixtureAction === 'missing' ? 'fail' : 'pass', target: root, fixture: fixtureAction, fixturePath: destination, accounts };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
51
|
-
const argv = process.argv.slice(2);
|
|
52
|
-
if (argv.includes('--help') || argv.includes('-h')) {
|
|
53
|
-
process.stdout.write(`${usage}\n`);
|
|
54
|
-
process.exit(0);
|
|
55
|
-
}
|
|
56
|
-
const value = (flag) => {
|
|
57
|
-
const index = argv.indexOf(flag);
|
|
58
|
-
return index >= 0 ? argv[index + 1] : undefined;
|
|
59
|
-
};
|
|
60
|
-
try {
|
|
61
|
-
const result = installWebDappRuntime({
|
|
62
|
-
target: value('--target') ?? process.cwd(),
|
|
63
|
-
fixture: value('--fixture') ?? process.env.RECIPE_WALLET_FIXTURE,
|
|
64
|
-
force: argv.includes('--force'),
|
|
65
|
-
});
|
|
66
|
-
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
67
|
-
if (result.status !== 'pass') {
|
|
68
|
-
process.stderr.write('No wallet fixture installed. Next: set RECIPE_WALLET_FIXTURE or pass --fixture <wallet-fixture.json>.\n');
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
} catch (error) {
|
|
72
|
-
process.stderr.write(`${error.message}\n`);
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
}
|