@deeeed/metamask-harness 0.28.0 → 0.29.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 +19 -0
- package/README.md +41 -0
- package/adapters/extension/build-lavamoat.sh +2 -1
- package/adapters/extension/ensure-browser.sh +82 -9
- package/adapters/extension/inject.mjs +1 -0
- package/adapters/extension/launch-browser.cjs +83 -1
- package/adapters/extension/lib/chrome-args.cjs +325 -1
- package/adapters/extension/lib/playwright-cdp.cjs +34 -0
- package/adapters/extension/lib/slot-title.cjs +2 -4
- package/adapters/extension/lib/validation-launch-supervisor.cjs +292 -0
- package/adapters/extension/lib/validation-process-ownership.cjs +69 -0
- package/adapters/extension/reattach.sh +2 -1
- package/adapters/extension/sidepanel-toggle.sh +14 -96
- package/adapters/extension/wallet-fixture-state.cjs +8 -31
- package/adapters/manifest.json +16 -0
- package/adapters/shared/private-atomic-write.cjs +47 -0
- package/adapters/shared/setup-base.sh +864 -0
- package/dist/adapters/extension/runtime.js +367 -24
- package/dist/adapters/extension/validation-process-ownership.js +10 -0
- package/dist/cli-commands.js +1 -0
- package/dist/command-contract.js +12 -0
- package/dist/commands/launch/extension.js +130 -19
- package/dist/commands/setup-base.js +24 -0
- package/dist/mm-harness-cli.js +28 -2
- package/library/actions/extension/analytics/consent.mjs +203 -0
- package/library/actions/extension/analytics/set_consent.mjs +19 -143
- package/library/actions/extension/perps/perps.mjs +2 -16
- package/library/actions/extension/perps/state.mjs +20 -0
- package/library/actions/extension/wallet/list_accounts.mjs +3 -25
- package/library/actions/extension/wallet/read_state.mjs +3 -23
- package/library/actions/extension/wallet/select_account.mjs +6 -33
- package/library/actions/extension/wallet/setup.mjs +2 -20
- package/library/actions/extension/wallet/state.mjs +111 -0
- package/library/recipes/runner/action-validation.extension.recipe.json +1 -1
- package/library/recipes/runner/action-validation.mobile.recipe.json +1 -1
- package/package.json +7 -4
- package/scripts/site-contrast.mjs +538 -0
- package/site/architecture.html +415 -0
- package/site/assets/progress.mjs +272 -0
- package/site/assets/style.css +808 -0
- package/site/cheatsheet.html +305 -0
- package/site/index.html +643 -0
- package/site/recipes.html +396 -0
- package/site/reviewers.html +374 -0
- package/site/tutorials/index.html +180 -0
- package/site/tutorials/v1.html +211 -0
- package/site/tutorials/v2.html +207 -0
- package/site/tutorials/v3.html +214 -0
- package/site/tutorials/v4.html +195 -0
- package/site/tutorials/v5.html +163 -0
- package/site/tutorials/v6.html +165 -0
- package/site/tutorials/v7.html +184 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { pathToFileURL } from 'node:url';
|
|
2
2
|
import { dataTestId, marketSymbol, normalizeMarketSymbol, runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
3
|
+
import { readPerpsRuntimeStateExpression } from './state.mjs';
|
|
3
4
|
|
|
4
5
|
function sleep(ms) {
|
|
5
6
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -704,22 +705,7 @@ async function applyStateNavigation(input, config) {
|
|
|
704
705
|
|
|
705
706
|
|
|
706
707
|
async function readPerpsRuntimeState(page) {
|
|
707
|
-
return page.evaluate(
|
|
708
|
-
const hooks = globalThis.stateHooks || {};
|
|
709
|
-
const request = hooks.submitRequestToBackground;
|
|
710
|
-
const reduxState = hooks.store?.getState?.() || {};
|
|
711
|
-
const cleanState = (await hooks.getCleanAppState?.()) || hooks.getState?.() || {};
|
|
712
|
-
const metamask = reduxState.metamask || cleanState.metamask || {};
|
|
713
|
-
const nestedPerps = metamask.PerpsController || cleanState.PerpsController || cleanState.perps || {};
|
|
714
|
-
return {
|
|
715
|
-
available: typeof request === 'function',
|
|
716
|
-
activeProvider: metamask.activeProvider || nestedPerps.activeProvider || 'hyperliquid',
|
|
717
|
-
networkKnown: typeof nestedPerps.isTestnet === 'boolean',
|
|
718
|
-
isTestnet: Boolean(nestedPerps.isTestnet),
|
|
719
|
-
initializationState: metamask.initializationState || nestedPerps.initializationState || null,
|
|
720
|
-
isEligible: Boolean(metamask.isEligible ?? nestedPerps.isEligible),
|
|
721
|
-
};
|
|
722
|
-
})()`);
|
|
708
|
+
return page.evaluate(readPerpsRuntimeStateExpression());
|
|
723
709
|
}
|
|
724
710
|
|
|
725
711
|
async function ensureProvider(input, config) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function readPerpsRuntimeStateExpression() {
|
|
2
|
+
return `(async () => {
|
|
3
|
+
const hooks = globalThis.stateHooks || {};
|
|
4
|
+
const request = hooks.submitRequestToBackground;
|
|
5
|
+
const reduxState = hooks.store?.getState?.() || {};
|
|
6
|
+
const cleanState = (await hooks.getCleanAppState?.()) || hooks.getState?.() || {};
|
|
7
|
+
const reduxMetamask = reduxState.metamask || {};
|
|
8
|
+
const cleanMetamask = cleanState.metamask || {};
|
|
9
|
+
const metamask = { ...cleanMetamask, ...reduxMetamask };
|
|
10
|
+
const nestedPerps = reduxMetamask.PerpsController || cleanMetamask.PerpsController || cleanState.PerpsController || cleanState.perps || {};
|
|
11
|
+
return {
|
|
12
|
+
available: typeof request === 'function',
|
|
13
|
+
activeProvider: metamask.activeProvider || nestedPerps.activeProvider || 'hyperliquid',
|
|
14
|
+
networkKnown: typeof metamask.isTestnet === 'boolean' || typeof nestedPerps.isTestnet === 'boolean',
|
|
15
|
+
isTestnet: Boolean(metamask.isTestnet ?? nestedPerps.isTestnet),
|
|
16
|
+
initializationState: metamask.initializationState || nestedPerps.initializationState || null,
|
|
17
|
+
isEligible: Boolean(metamask.isEligible ?? nestedPerps.isEligible),
|
|
18
|
+
};
|
|
19
|
+
})()`;
|
|
20
|
+
}
|
|
@@ -1,37 +1,15 @@
|
|
|
1
1
|
import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
2
|
+
import { listWalletAccountsExpression } from './state.mjs';
|
|
2
3
|
|
|
3
4
|
runAdapter((input) => withExtensionPage(input, async (page) => {
|
|
4
5
|
const scope = input.node?.scope == null ? 'named' : String(input.node.scope);
|
|
5
6
|
if (scope !== 'selected' && scope !== 'named' && scope !== 'evm' && scope !== 'all') {
|
|
6
7
|
throw new Error('metamask.wallet.list_accounts scope must be selected, named, evm, or all.');
|
|
7
8
|
}
|
|
8
|
-
const result = await page.evaluate(
|
|
9
|
-
const store = globalThis.stateHooks?.store;
|
|
10
|
-
if (!store || typeof store.getState !== 'function') {
|
|
11
|
-
throw new Error('stateHooks.store.getState is unavailable.');
|
|
12
|
-
}
|
|
13
|
-
const internal = store.getState()?.metamask?.internalAccounts || {};
|
|
14
|
-
const selectedId = internal.selectedAccount || null;
|
|
15
|
-
const scope = ${JSON.stringify(scope)};
|
|
16
|
-
const accounts = Object.entries(internal.accounts || {})
|
|
17
|
-
.map(([id, account]) => ({
|
|
18
|
-
id,
|
|
19
|
-
address: account?.address || null,
|
|
20
|
-
name: account?.metadata?.name || null,
|
|
21
|
-
type: account?.type || null,
|
|
22
|
-
selected: id === selectedId
|
|
23
|
-
}))
|
|
24
|
-
.filter((account) => {
|
|
25
|
-
if (scope === 'selected') return account.selected;
|
|
26
|
-
if (scope === 'all') return true;
|
|
27
|
-
if (scope === 'evm') return /^0x[0-9a-f]{40}$/iu.test(account.address || '');
|
|
28
|
-
return account.selected || Boolean(String(account.name || '').trim());
|
|
29
|
-
});
|
|
30
|
-
return { accounts };
|
|
31
|
-
})()`);
|
|
9
|
+
const result = await page.evaluate(listWalletAccountsExpression(scope), { awaitPromise: true });
|
|
32
10
|
return {
|
|
33
11
|
action: input.action,
|
|
34
|
-
source: 'extension-stateHooks-
|
|
12
|
+
source: 'extension-stateHooks-clean-state',
|
|
35
13
|
scope,
|
|
36
14
|
...result,
|
|
37
15
|
count: result.accounts.length,
|
|
@@ -1,27 +1,7 @@
|
|
|
1
1
|
import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
2
|
+
import { readWalletStateExpression } from './state.mjs';
|
|
2
3
|
|
|
3
4
|
runAdapter((input) => withExtensionPage(input, async (page) => {
|
|
4
|
-
const state = await page.evaluate(
|
|
5
|
-
|
|
6
|
-
const store = hooks && hooks.store;
|
|
7
|
-
const root = store && typeof store.getState === 'function' ? store.getState() : {};
|
|
8
|
-
const metamask = root.metamask || {};
|
|
9
|
-
const internal = metamask.internalAccounts || {};
|
|
10
|
-
const accounts = internal.accounts || {};
|
|
11
|
-
const selectedId = internal.selectedAccount || null;
|
|
12
|
-
const selected = selectedId && accounts[selectedId] ? accounts[selectedId] : null;
|
|
13
|
-
const metadata = selected && selected.metadata ? selected.metadata : {};
|
|
14
|
-
return {
|
|
15
|
-
href: String(globalThis.location && globalThis.location.href || ''),
|
|
16
|
-
selectedAccount: selected ? {
|
|
17
|
-
id: selectedId,
|
|
18
|
-
address: selected.address || null,
|
|
19
|
-
name: metadata.name || null,
|
|
20
|
-
type: selected.type || null
|
|
21
|
-
} : null,
|
|
22
|
-
completedOnboarding: Boolean(metamask.completedOnboarding),
|
|
23
|
-
selectedNetworkClientId: metamask.selectedNetworkClientId || null
|
|
24
|
-
};
|
|
25
|
-
})()`);
|
|
26
|
-
return { action: input.action, source: 'extension-stateHooks-store', state, redacted: true, proofPath: 'extension-wallet-state' };
|
|
5
|
+
const state = await page.evaluate(readWalletStateExpression(), { awaitPromise: true });
|
|
6
|
+
return { action: input.action, source: 'extension-stateHooks-clean-state', state, redacted: true, proofPath: 'extension-wallet-state' };
|
|
27
7
|
}));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
2
|
+
import { selectWalletAccountExpression } from './state.mjs';
|
|
2
3
|
|
|
3
4
|
function normalize(value) {
|
|
4
5
|
return String(value ?? '').toLowerCase();
|
|
@@ -11,38 +12,10 @@ runAdapter((input) => withExtensionPage(input, async (page) => {
|
|
|
11
12
|
if (!requestedAddress && !requestedId && !requestedName) {
|
|
12
13
|
throw new Error('metamask.wallet.select_account extension live adapter requires node.address, node.id, or node.name.');
|
|
13
14
|
}
|
|
14
|
-
const selected = await page.evaluate(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (typeof request !== 'function') throw new Error('stateHooks.submitRequestToBackground is unavailable.');
|
|
20
|
-
const state = store.getState() || {};
|
|
21
|
-
const internal = state.metamask?.internalAccounts || {};
|
|
22
|
-
const accounts = internal.accounts || {};
|
|
23
|
-
const requestedAddress = ${JSON.stringify(requestedAddress)};
|
|
24
|
-
const requestedId = ${JSON.stringify(requestedId)};
|
|
25
|
-
const requestedName = ${JSON.stringify(requestedName)};
|
|
26
|
-
const entries = Object.entries(accounts);
|
|
27
|
-
const match = entries.find(([id, account]) => {
|
|
28
|
-
const metadata = account?.metadata || {};
|
|
29
|
-
return (requestedId && id === requestedId) ||
|
|
30
|
-
(requestedAddress && String(account?.address || '').toLowerCase() === requestedAddress) ||
|
|
31
|
-
(requestedName && metadata.name === requestedName);
|
|
32
|
-
});
|
|
33
|
-
if (!match) throw new Error('Requested account was not found in internalAccounts.');
|
|
34
|
-
const [id, account] = match;
|
|
35
|
-
await request('setSelectedInternalAccount', [id]);
|
|
36
|
-
const after = store.getState()?.metamask?.internalAccounts || {};
|
|
37
|
-
const selectedId = after.selectedAccount;
|
|
38
|
-
const selected = after.accounts?.[selectedId];
|
|
39
|
-
if (selectedId !== id) throw new Error('Expected selected account ' + id + ', got ' + selectedId);
|
|
40
|
-
return {
|
|
41
|
-
id: selectedId,
|
|
42
|
-
address: selected?.address || null,
|
|
43
|
-
name: selected?.metadata?.name || null,
|
|
44
|
-
type: selected?.type || null
|
|
45
|
-
};
|
|
46
|
-
})()`, { awaitPromise: true });
|
|
15
|
+
const selected = await page.evaluate(selectWalletAccountExpression({
|
|
16
|
+
address: requestedAddress,
|
|
17
|
+
id: requestedId,
|
|
18
|
+
name: requestedName,
|
|
19
|
+
}), { awaitPromise: true });
|
|
47
20
|
return { action: input.action, selected, proofPath: 'extension-account-selection' };
|
|
48
21
|
}));
|
|
@@ -1,26 +1,8 @@
|
|
|
1
1
|
import { runAdapter, withExtensionPage } from '../platform/cdp.mjs';
|
|
2
|
+
import { readWalletStateExpression } from './state.mjs';
|
|
2
3
|
|
|
3
4
|
runAdapter((input) => withExtensionPage(input, async (page) => {
|
|
4
|
-
const state = await page.evaluate(
|
|
5
|
-
const passwordInput = Boolean(document.querySelector('input[type="password"]'));
|
|
6
|
-
const hooks = globalThis.stateHooks;
|
|
7
|
-
const store = hooks && hooks.store;
|
|
8
|
-
const root = store && typeof store.getState === 'function' ? store.getState() : {};
|
|
9
|
-
const metamask = root.metamask || {};
|
|
10
|
-
const internal = metamask.internalAccounts || {};
|
|
11
|
-
const accounts = internal.accounts || {};
|
|
12
|
-
const selectedId = internal.selectedAccount || null;
|
|
13
|
-
const selected = selectedId && accounts[selectedId] ? accounts[selectedId] : null;
|
|
14
|
-
return {
|
|
15
|
-
href: String(globalThis.location && globalThis.location.href || ''),
|
|
16
|
-
passwordInput,
|
|
17
|
-
completedOnboarding: Boolean(metamask.completedOnboarding),
|
|
18
|
-
selectedAccount: selected ? {
|
|
19
|
-
id: selectedId,
|
|
20
|
-
address: selected.address || null,
|
|
21
|
-
} : null,
|
|
22
|
-
};
|
|
23
|
-
})()`);
|
|
5
|
+
const state = await page.evaluate(readWalletStateExpression(), { awaitPromise: true });
|
|
24
6
|
const seededProfilePresent = Boolean(state?.passwordInput || (state?.completedOnboarding && state?.selectedAccount?.address));
|
|
25
7
|
if (!seededProfilePresent) {
|
|
26
8
|
throw new Error(`Extension fixture profile is not ready; expected a locked password screen or completed onboarding with a selected account, got ${JSON.stringify(state)}`);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const walletStatePreamble = `
|
|
2
|
+
const readWalletState = async () => {
|
|
3
|
+
const hooks = globalThis.stateHooks || {};
|
|
4
|
+
let root = null;
|
|
5
|
+
if (typeof hooks.getCleanAppState === 'function') {
|
|
6
|
+
root = await hooks.getCleanAppState();
|
|
7
|
+
}
|
|
8
|
+
if ((!root || typeof root !== 'object') && typeof hooks.store?.getState === 'function') {
|
|
9
|
+
root = hooks.store.getState();
|
|
10
|
+
}
|
|
11
|
+
if (!root || typeof root !== 'object') {
|
|
12
|
+
throw new Error('stateHooks.getCleanAppState and stateHooks.store.getState are unavailable.');
|
|
13
|
+
}
|
|
14
|
+
const metamask = root.metamask || root;
|
|
15
|
+
return {
|
|
16
|
+
hooks,
|
|
17
|
+
metamask,
|
|
18
|
+
internal: metamask.internalAccounts || {},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
export function readWalletStateExpression() {
|
|
24
|
+
return `(async () => {
|
|
25
|
+
${walletStatePreamble}
|
|
26
|
+
const { metamask, internal } = await readWalletState();
|
|
27
|
+
const accounts = internal.accounts || {};
|
|
28
|
+
const selectedId = internal.selectedAccount || null;
|
|
29
|
+
const selected = selectedId && accounts[selectedId] ? accounts[selectedId] : null;
|
|
30
|
+
const metadata = selected?.metadata || {};
|
|
31
|
+
return {
|
|
32
|
+
href: String(globalThis.location?.href || ''),
|
|
33
|
+
passwordInput: Boolean(globalThis.document?.querySelector?.('input[type="password"]')),
|
|
34
|
+
selectedAccount: selected ? {
|
|
35
|
+
id: selectedId,
|
|
36
|
+
address: selected.address || null,
|
|
37
|
+
name: metadata.name || null,
|
|
38
|
+
type: selected.type || null,
|
|
39
|
+
} : null,
|
|
40
|
+
completedOnboarding: Boolean(metamask.completedOnboarding),
|
|
41
|
+
selectedNetworkClientId: metamask.selectedNetworkClientId || null,
|
|
42
|
+
};
|
|
43
|
+
})()`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function listWalletAccountsExpression(scope) {
|
|
47
|
+
return `(async () => {
|
|
48
|
+
${walletStatePreamble}
|
|
49
|
+
const { internal } = await readWalletState();
|
|
50
|
+
const selectedId = internal.selectedAccount || null;
|
|
51
|
+
const scope = ${JSON.stringify(scope)};
|
|
52
|
+
const accounts = Object.entries(internal.accounts || {})
|
|
53
|
+
.map(([id, account]) => ({
|
|
54
|
+
id,
|
|
55
|
+
address: account?.address || null,
|
|
56
|
+
name: account?.metadata?.name || null,
|
|
57
|
+
type: account?.type || null,
|
|
58
|
+
selected: id === selectedId,
|
|
59
|
+
}))
|
|
60
|
+
.filter((account) => {
|
|
61
|
+
if (scope === 'selected') return account.selected;
|
|
62
|
+
if (scope === 'all') return true;
|
|
63
|
+
if (scope === 'evm') return /^0x[0-9a-f]{40}$/iu.test(account.address || '');
|
|
64
|
+
return account.selected || Boolean(String(account.name || '').trim());
|
|
65
|
+
});
|
|
66
|
+
return { accounts };
|
|
67
|
+
})()`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function selectWalletAccountExpression(criteria, timeoutMs = 5000) {
|
|
71
|
+
return `(async () => {
|
|
72
|
+
${walletStatePreamble}
|
|
73
|
+
const before = await readWalletState();
|
|
74
|
+
const accounts = before.internal.accounts || {};
|
|
75
|
+
const requestedAddress = ${JSON.stringify(criteria.address)};
|
|
76
|
+
const requestedId = ${JSON.stringify(criteria.id)};
|
|
77
|
+
const requestedName = ${JSON.stringify(criteria.name)};
|
|
78
|
+
const match = Object.entries(accounts).find(([id, account]) => {
|
|
79
|
+
const metadata = account?.metadata || {};
|
|
80
|
+
return (requestedId && id === requestedId) ||
|
|
81
|
+
(requestedAddress && String(account?.address || '').toLowerCase() === requestedAddress) ||
|
|
82
|
+
(requestedName && metadata.name === requestedName);
|
|
83
|
+
});
|
|
84
|
+
if (!match) throw new Error('Requested account was not found in internalAccounts.');
|
|
85
|
+
if (typeof before.hooks.submitRequestToBackground !== 'function') {
|
|
86
|
+
throw new Error('stateHooks.submitRequestToBackground is unavailable.');
|
|
87
|
+
}
|
|
88
|
+
const [id] = match;
|
|
89
|
+
await before.hooks.submitRequestToBackground('setSelectedInternalAccount', [id]);
|
|
90
|
+
const deadline = Date.now() + ${Number(timeoutMs)};
|
|
91
|
+
let observedId = before.internal.selectedAccount || null;
|
|
92
|
+
while (Date.now() <= deadline) {
|
|
93
|
+
const after = await readWalletState();
|
|
94
|
+
observedId = after.internal.selectedAccount || null;
|
|
95
|
+
if (observedId === id) {
|
|
96
|
+
const selected = after.internal.accounts?.[observedId];
|
|
97
|
+
return {
|
|
98
|
+
id: observedId,
|
|
99
|
+
address: selected?.address || null,
|
|
100
|
+
name: selected?.metadata?.name || null,
|
|
101
|
+
type: selected?.type || null,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
105
|
+
}
|
|
106
|
+
throw new Error(
|
|
107
|
+
'Expected selected account ' + id + ', got ' + String(observedId) +
|
|
108
|
+
' after ${Number(timeoutMs)}ms.',
|
|
109
|
+
);
|
|
110
|
+
})()`;
|
|
111
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"nodes": {
|
|
8
8
|
"prepare-files": {
|
|
9
9
|
"action": "command",
|
|
10
|
-
"cmd": "mkdir -p temp/recipe-runner.action-validation && printf 'E2E_ACTION_VALIDATION extension\\n' > temp/recipe-runner.action-validation/probe.txt && printf '{\"platform\":\"extension\",\"ok\":true}\n' > temp/recipe-runner.action-validation/probe.json && printf 'extension log E2E_ACTION_VALIDATION\\n'
|
|
10
|
+
"cmd": "mkdir -p temp/recipe-runner.action-validation && printf 'E2E_ACTION_VALIDATION extension\\n' > temp/recipe-runner.action-validation/probe.txt && printf '{\"platform\":\"extension\",\"ok\":true}\n' > temp/recipe-runner.action-validation/probe.json && printf 'extension log E2E_ACTION_VALIDATION\\n' >> temp/recipe-runner.action-validation/validation.log && echo E2E_ACTION_VALIDATION extension",
|
|
11
11
|
"next": "assert-command-exit",
|
|
12
12
|
"intent": "Prepare runner.action-validation probe files"
|
|
13
13
|
},
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"nodes": {
|
|
8
8
|
"prepare-files": {
|
|
9
9
|
"action": "command",
|
|
10
|
-
"cmd": "mkdir -p temp/recipe-runner.action-validation && printf 'E2E_ACTION_VALIDATION mobile\\n' > temp/recipe-runner.action-validation/probe.txt && printf '{\"platform\":\"mobile\",\"ok\":true}\n' > temp/recipe-runner.action-validation/probe.json && printf 'mobile log E2E_ACTION_VALIDATION\\n'
|
|
10
|
+
"cmd": "mkdir -p temp/recipe-runner.action-validation && printf 'E2E_ACTION_VALIDATION mobile\\n' > temp/recipe-runner.action-validation/probe.txt && printf '{\"platform\":\"mobile\",\"ok\":true}\n' > temp/recipe-runner.action-validation/probe.json && printf 'mobile log E2E_ACTION_VALIDATION\\n' >> temp/recipe-runner.action-validation/validation.log && echo E2E_ACTION_VALIDATION mobile",
|
|
11
11
|
"next": "assert-command-exit",
|
|
12
12
|
"intent": "Prepare runner.action-validation probe files"
|
|
13
13
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -13,16 +13,17 @@
|
|
|
13
13
|
"test:unit": "vitest run --config scripts/vitest.config.mjs",
|
|
14
14
|
"test:coverage": "vitest run --coverage --config scripts/vitest.config.mjs",
|
|
15
15
|
"qa:human": "node scripts/validate-human-outcomes.mjs",
|
|
16
|
+
"site:contrast": "node scripts/site-contrast.mjs",
|
|
16
17
|
"self-test": "bin/mm-harness self-test",
|
|
17
18
|
"manifest:mobile": "bin/mm-harness actions --raw --adapter mobile --json",
|
|
18
19
|
"manifest:extension": "bin/mm-harness actions --raw --adapter extension --json",
|
|
19
20
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"@farmslot/agent-runtime": "^0.
|
|
23
|
+
"@farmslot/agent-runtime": "^0.5.0",
|
|
23
24
|
"@farmslot/handoff": "^0.3.1",
|
|
24
|
-
"@farmslot/protocol": "^0.
|
|
25
|
-
"@farmslot/recipe-harness": "^0.
|
|
25
|
+
"@farmslot/protocol": "^0.15.0",
|
|
26
|
+
"@farmslot/recipe-harness": "^0.11.0",
|
|
26
27
|
"commander": "^12.0.0",
|
|
27
28
|
"es-module-lexer": "2.3.1",
|
|
28
29
|
"esbuild": "0.28.1",
|
|
@@ -61,8 +62,10 @@
|
|
|
61
62
|
"dist",
|
|
62
63
|
"adapters",
|
|
63
64
|
"library",
|
|
65
|
+
"site",
|
|
64
66
|
"scripts/completions.sh",
|
|
65
67
|
"scripts/install-completions.sh",
|
|
68
|
+
"scripts/site-contrast.mjs",
|
|
66
69
|
"scripts/validate-human-outcomes.mjs",
|
|
67
70
|
"!scripts/README.md",
|
|
68
71
|
"docs",
|