@deeeed/metamask-harness 0.38.1 → 0.40.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 +58 -0
- package/adapters/extension/lib/playwright-cdp.cjs +17 -1
- package/adapters/extension/sidepanel-toggle.sh +135 -13
- package/dist/adapters/mobile/release-artifact.js +65 -0
- package/dist/adapters.js +27 -17
- package/dist/command-contract.js +10 -2
- package/dist/commands/call.js +7 -4
- package/dist/commands/launch/index.js +55 -3
- package/dist/commands/parse-args.js +6 -0
- package/dist/commands/run-engine.js +75 -39
- package/dist/funding-execution-context.js +1386 -0
- package/dist/live-adapter-contract.js +3 -1
- package/dist/metamask-action-validation.js +170 -3
- package/dist/mm-harness-cli.js +8 -2
- package/dist/recipe-security.js +5 -12
- package/dist/runner.js +102 -6
- package/docs/RECIPES.md +74 -1
- package/docs/RELEASE-QA-CAPABILITY-MAP.md +6 -5
- package/library/actions/extension/assets/open_details.mjs +30 -0
- package/library/actions/extension/assets/set_token_visibility.mjs +5 -0
- package/library/actions/extension/networks/add_chainlist.mjs +4 -0
- package/library/actions/extension/networks/add_custom.mjs +4 -0
- package/library/actions/extension/networks/custom_network.mjs +48 -1
- package/library/actions/extension/perps/assert_visible_consistency.mjs +366 -0
- package/library/actions/extension/perps/close_visible_position.mjs +299 -37
- package/library/actions/extension/perps/compare_provider_market.mjs +77 -0
- package/library/actions/extension/perps/edit_margin.mjs +285 -44
- package/library/actions/extension/perps/mutation-receipt.mjs +781 -0
- package/library/actions/extension/perps/perps.mjs +1029 -363
- package/library/actions/extension/perps/read_funds_confirmation.mjs +126 -13
- package/library/actions/extension/perps/read_snapshot.mjs +124 -3
- package/library/actions/extension/perps/read_visible_state.mjs +63 -5
- package/library/actions/extension/perps/search_markets.mjs +32 -0
- package/library/actions/extension/perps/select_activity_filter.mjs +81 -18
- package/library/actions/extension/perps/select_market_filter.mjs +48 -4
- package/library/actions/extension/perps/set_market_favorite.mjs +6 -1
- package/library/actions/extension/perps/update_position_tpsl.mjs +251 -69
- package/library/actions/extension/perps/visible-mutation-identity.mjs +164 -0
- package/library/actions/extension/platform/cdp.mjs +88 -9
- package/library/actions/extension/settings/set_basic_functionality.mjs +35 -7
- package/library/actions/extension/swap_bridge/read_visible_state.mjs +2 -6
- package/library/actions/extension/swap_bridge/set_amount.mjs +11 -2
- package/library/actions/extension/ui/navigate.mjs +45 -5
- package/library/actions/extension/wallet/import.mjs +70 -29
- package/library/actions/extension/wallet/lock.mjs +62 -5
- package/library/actions/extension/wallet/visible-session.mjs +218 -0
- package/library/actions/mobile/assets/import_custom_token.mjs +181 -0
- package/library/actions/mobile/assets/open_details.mjs +43 -0
- package/library/actions/mobile/assets/read_details.mjs +96 -0
- package/library/actions/mobile/assets/set_token_visibility.mjs +302 -0
- package/library/actions/mobile/assets/verify_sorting.mjs +104 -0
- package/library/actions/mobile/networks/add_custom.mjs +125 -0
- package/library/actions/mobile/networks/network-management.mjs +132 -0
- package/library/actions/mobile/networks/read_visible_state.mjs +43 -0
- package/library/actions/mobile/networks/remove_custom.mjs +100 -0
- package/library/actions/mobile/perps/search_markets.mjs +46 -0
- package/library/actions/mobile/perps/set_market_favorite.mjs +117 -0
- package/library/actions/mobile/platform/native-session-name.mjs +14 -0
- package/library/actions/mobile/platform/native-session.mjs +59 -0
- package/library/actions/mobile/platform/observe-ui.mjs +9 -1
- package/library/actions/mobile/swap_bridge/native-session.mjs +10 -45
- package/library/actions/mobile/swap_bridge/select_assets.mjs +15 -1
- package/library/actions/mobile/swap_bridge/set_amount.mjs +1 -1
- package/library/actions/mobile/swap_bridge/set_max_amount.mjs +55 -0
- package/library/actions/mobile/swap_bridge/set_slippage.mjs +96 -0
- package/library/actions/mobile/swap_bridge/submit_transaction.mjs +141 -0
- package/library/actions/mobile/ui/native-navigation.mjs +120 -23
- package/library/actions/mobile/ui/navigate.mjs +13 -2
- package/library/actions/mobile/wallet/import.mjs +17 -10
- package/library/actions/mobile/wallet/lock.mjs +14 -0
- package/library/actions/mobile/wallet/native-ui.mjs +88 -15
- package/library/actions/shared/perps/visible-state.mjs +33 -11
- package/library/actions/shared/swap-bridge/visible-state.mjs +15 -0
- package/library/manifests/extension.action-manifest.json +1707 -1241
- package/library/manifests/mobile.action-manifest.json +308 -1
- package/library/recipes/assets/release-token-details.recipe.json +5 -4
- package/library/recipes/assets/release-token-hide-readd.recipe.json +2 -0
- package/library/recipes/{assets → extension/assets}/release-custom-network-token-persistence.recipe.json +1 -0
- package/library/recipes/{perps → extension/perps}/release-activity-filters.recipe.json +5 -5
- package/library/recipes/{perps → extension/perps}/release-funds-flows.recipe.json +3 -3
- package/library/recipes/extension/perps/release-live-limit-order.recipe.json +24 -19
- package/library/recipes/extension/perps/release-market-filters.recipe.json +97 -0
- package/library/recipes/extension/perps/release-order-entry.recipe.json +4 -1
- package/library/recipes/extension/perps/release-order-validation.recipe.json +53 -14
- package/library/recipes/extension/perps/release-visible-consistency.recipe.json +47 -0
- package/library/recipes/extension/perps/source-dev-snapshot-consistency.recipe.json +76 -0
- package/library/recipes/mobile/networks/release-custom-network-persistence.recipe.json +90 -0
- package/library/recipes/mobile/networks/release-multichain-visibility.recipe.json +58 -0
- package/library/recipes/perps/release-live-market-order.recipe.json +30 -13
- package/library/recipes/perps/release-market-details.recipe.json +1 -1
- package/library/recipes/perps/release-market-search.recipe.json +8 -4
- package/library/recipes/perps/release-watchlist.recipe.json +31 -19
- package/library/recipes/swap-bridge/release-custom-slippage.recipe.json +5 -13
- package/library/recipes/swap-bridge/release-quote.recipe.json +1 -0
- package/library/recipes/wallet/import.recipe.json +4 -14
- package/package.json +1 -1
- package/library/actions/extension/assets/finish_send.mjs +0 -128
- package/library/actions/extension/swap_bridge/submit_transaction.mjs +0 -363
- package/library/actions/extension/ui/activate_and_observe.mjs +0 -96
- package/library/recipes/assets/release-custom-gas-send.recipe.json +0 -88
- package/library/recipes/assets/release-native-max-cancel.recipe.json +0 -70
- package/library/recipes/assets/release-native-max-send.recipe.json +0 -78
- package/library/recipes/assets/release-own-account-send.recipe.json +0 -80
- package/library/recipes/extension/runner/action-validation.recipe.json +0 -343
|
@@ -37,6 +37,10 @@ import { acquireCheckoutLock } from "../../checkout-lock.js";
|
|
|
37
37
|
import { JsonStreamWriter } from "../../json-stream.js";
|
|
38
38
|
import { ensureExtensionConsoleCapture } from "../../adapters/extension/console-capture.js";
|
|
39
39
|
import { extensionProductConfigBlock } from "../../adapters/extension/product-config.js";
|
|
40
|
+
import {
|
|
41
|
+
releaseArtifactState,
|
|
42
|
+
runtimeDistCheck
|
|
43
|
+
} from "../../adapters/extension/runtime-decision.js";
|
|
40
44
|
const LAUNCH_BOOLEANS = /* @__PURE__ */ new Set([
|
|
41
45
|
"build",
|
|
42
46
|
"watch",
|
|
@@ -124,13 +128,41 @@ async function handleLaunchLocked(argv, stream) {
|
|
|
124
128
|
const wantWatch = flag(options, "watch");
|
|
125
129
|
const wantRunway = flag(options, "runway");
|
|
126
130
|
const wantClearMetro = flag(options, "clearMetro");
|
|
131
|
+
const requestedSurface = str(options, "surface");
|
|
132
|
+
const legacySidepanel = flag(options, "sidepanel");
|
|
133
|
+
const legacyFullscreen = flag(options, "fullscreen");
|
|
127
134
|
if (wantRunway && adapter !== "mobile") {
|
|
128
135
|
return launchUsage(jsonOutput, stream, "runway is mobile-only.", "drop --runway for the extension");
|
|
129
136
|
}
|
|
130
137
|
if (wantClearMetro && adapter !== "mobile") {
|
|
131
138
|
return launchUsage(jsonOutput, stream, "--clear-metro is mobile-only.", "drop --clear-metro for the extension");
|
|
132
139
|
}
|
|
133
|
-
|
|
140
|
+
if (requestedSurface !== void 0 && requestedSurface !== "fullscreen" && requestedSurface !== "sidepanel") {
|
|
141
|
+
return launchUsage(
|
|
142
|
+
jsonOutput,
|
|
143
|
+
stream,
|
|
144
|
+
`--surface must be fullscreen or sidepanel (got: ${requestedSurface}).`,
|
|
145
|
+
"pass --surface fullscreen or --surface sidepanel"
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
if (legacySidepanel && legacyFullscreen) {
|
|
149
|
+
return launchUsage(
|
|
150
|
+
jsonOutput,
|
|
151
|
+
stream,
|
|
152
|
+
"--sidepanel and --fullscreen conflict.",
|
|
153
|
+
"pass exactly one Extension surface"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
const legacySurface = legacySidepanel ? "sidepanel" : legacyFullscreen ? "fullscreen" : void 0;
|
|
157
|
+
if (requestedSurface && legacySurface && requestedSurface !== legacySurface) {
|
|
158
|
+
return launchUsage(
|
|
159
|
+
jsonOutput,
|
|
160
|
+
stream,
|
|
161
|
+
`--surface ${requestedSurface} conflicts with --${legacySurface}.`,
|
|
162
|
+
"pass one consistent Extension surface"
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const displayMode = requestedSurface ?? legacySurface ?? "fullscreen";
|
|
134
166
|
for (const portFlag of ["cdpPort", "watcherPort"]) {
|
|
135
167
|
const value = str(options, portFlag);
|
|
136
168
|
if (value !== void 0 && !/^\d+$/u.test(value)) {
|
|
@@ -189,7 +221,11 @@ async function handleLaunchLocked(argv, stream) {
|
|
|
189
221
|
}
|
|
190
222
|
stream.phase("healthcheck");
|
|
191
223
|
if (adapter === "extension") {
|
|
192
|
-
const
|
|
224
|
+
const artifact = releaseArtifactState(target);
|
|
225
|
+
const reusable = !wantWatch && tier !== "build" && await extensionRuntimeReusable(target, artifact);
|
|
226
|
+
const verifiedReleaseReuse = artifact.status === "valid" && reusable;
|
|
227
|
+
const verifiedReleaseLaunch = !wantWatch && tier !== "build" && artifact.status === "valid" && runtimeDistCheck(target, artifact).status === "fresh";
|
|
228
|
+
const willBuild = !reusable;
|
|
193
229
|
if (willBuild) {
|
|
194
230
|
const productConfigBlock = extensionProductConfigBlock(target);
|
|
195
231
|
if (productConfigBlock) {
|
|
@@ -202,7 +238,7 @@ async function handleLaunchLocked(argv, stream) {
|
|
|
202
238
|
});
|
|
203
239
|
}
|
|
204
240
|
}
|
|
205
|
-
const depsBlock = extensionDepsBlock(target);
|
|
241
|
+
const depsBlock = verifiedReleaseReuse || verifiedReleaseLaunch ? null : extensionDepsBlock(target);
|
|
206
242
|
if (depsBlock) {
|
|
207
243
|
if (heal === "off") {
|
|
208
244
|
return launchUsage(jsonOutput, stream, depsBlock.message, depsBlock.userAction);
|
|
@@ -498,6 +534,22 @@ async function executeComposition(adapter, mobileTarget, tier, wantWatch, target
|
|
|
498
534
|
return launchExtension(target, tier, wantWatch, displayMode);
|
|
499
535
|
}
|
|
500
536
|
async function finishLaunch(jsonOutput, machine, stream, adapter, mobileTarget, tier, displayMode, target, state, wantWatch, wantVerify, sidepanelRecoveryAllowed = true) {
|
|
537
|
+
if (adapter === "extension" && displayMode === "fullscreen") {
|
|
538
|
+
const sidepanelSh = path.join(runnerDir, "adapters/extension/sidepanel-toggle.sh");
|
|
539
|
+
const sidepanelArgs = ["close"];
|
|
540
|
+
if (process.env.CDP_PORT) sidepanelArgs.push("--cdp-port", process.env.CDP_PORT);
|
|
541
|
+
const fullscreen = spawnScript(sidepanelSh, sidepanelArgs, target, machine, { REPO: target });
|
|
542
|
+
if (fullscreen.status !== 0) {
|
|
543
|
+
return launchFail(jsonOutput, stream, adapter, mobileTarget, tier, state, target, {
|
|
544
|
+
code: "FULLSCREEN_OPEN_FAILED",
|
|
545
|
+
message: "app launched but restoring the fullscreen Extension surface failed.",
|
|
546
|
+
recoverable: false,
|
|
547
|
+
userAction: `bash adapters/extension/sidepanel-toggle.sh close --cdp-port ${process.env.CDP_PORT ?? "<CDP_PORT>"}`,
|
|
548
|
+
exitCode: EXIT.runtime,
|
|
549
|
+
originalError: fullscreen.output.trim() || void 0
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
501
553
|
if (adapter === "extension" && displayMode === "sidepanel") {
|
|
502
554
|
const sidepanelSh = path.join(runnerDir, "adapters/extension/sidepanel-toggle.sh");
|
|
503
555
|
const sidepanelArgs = ["open"];
|
|
@@ -196,6 +196,7 @@ function parsePort(value, errorMessage) {
|
|
|
196
196
|
}
|
|
197
197
|
function runtimeOptionsFromCli(options) {
|
|
198
198
|
const recordVideo = options.recordVideo;
|
|
199
|
+
const hud = optionString(options, "hud");
|
|
199
200
|
const trust = explicitRecipeTrustOptions({
|
|
200
201
|
sourceTrust: optionString(options, "sourceTrust"),
|
|
201
202
|
sourceKind: optionString(options, "sourceKind"),
|
|
@@ -210,6 +211,11 @@ function runtimeOptionsFromCli(options) {
|
|
|
210
211
|
slot: optionString(options, "slot"),
|
|
211
212
|
validationRuntimeDir: optionString(options, "validationRuntimeDir"),
|
|
212
213
|
recordVideo: recordVideo === "full-run" ? "full-run" : false,
|
|
214
|
+
autoHud: hud === "show" ? true : hud === "hide" ? false : void 0,
|
|
215
|
+
fundingRequest: optionString(options, "fundingRequest"),
|
|
216
|
+
fundingPreflight: optionString(options, "fundingPreflight"),
|
|
217
|
+
fundingReleaseProof: optionString(options, "fundingReleaseProof"),
|
|
218
|
+
fundingBalanceProof: optionString(options, "fundingBalanceProof"),
|
|
213
219
|
...trust
|
|
214
220
|
};
|
|
215
221
|
}
|
|
@@ -23,8 +23,15 @@ import {
|
|
|
23
23
|
} from "../paths.js";
|
|
24
24
|
import { captureHelperSupportsRecordSessionSnapshots } from "../recording-target.js";
|
|
25
25
|
import { startRecipeRecording, stopRecipeRecording } from "../run-recording.js";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
resolveMetaMaskParameterValue,
|
|
28
|
+
validateMetaMaskActionInputs
|
|
29
|
+
} from "../metamask-action-validation.js";
|
|
27
30
|
import { gitLibraryProvenance } from "../library-provenance.js";
|
|
31
|
+
import {
|
|
32
|
+
authorizeTrustedMutationPlan,
|
|
33
|
+
loadTrustedMutationBase
|
|
34
|
+
} from "../funding-execution-context.js";
|
|
28
35
|
import {
|
|
29
36
|
beginRunDiagnostics,
|
|
30
37
|
finishRunDiagnostics
|
|
@@ -139,9 +146,18 @@ async function resolveRecipeExecution(adapter, recipe, artifactsDir, projectRoot
|
|
|
139
146
|
);
|
|
140
147
|
const { createMetaMaskRunner } = await import("../runner.js");
|
|
141
148
|
const recipeDocument = runtimeRecipeDocument(recipe, absoluteRecipePath);
|
|
149
|
+
const trustedMutation = await loadTrustedMutationBase({
|
|
150
|
+
fundingRequest: runtimeOptions.fundingRequest,
|
|
151
|
+
fundingPreflight: runtimeOptions.fundingPreflight,
|
|
152
|
+
fundingReleaseProof: runtimeOptions.fundingReleaseProof,
|
|
153
|
+
fundingBalanceProof: runtimeOptions.fundingBalanceProof,
|
|
154
|
+
artifactsDir: absoluteArtifactsDir,
|
|
155
|
+
projectRoot,
|
|
156
|
+
rootRecipe: recipeDocument
|
|
157
|
+
});
|
|
142
158
|
const suppressHudForLifecycleTiming = adapter === "mobile" && mobileRecipeRequiresUninterruptedLifecycleTiming(recipeDocument);
|
|
143
159
|
const suppressHudForOpaqueMobile = adapter === "mobile" && process.env.METAMASK_RECIPE_MOBILE_OPAQUE_RUNTIME === "1";
|
|
144
|
-
const
|
|
160
|
+
const runnerOptions = {
|
|
145
161
|
quietStdout: runtimeOptions.stdoutIsMachineContract === true,
|
|
146
162
|
suppressLibraryResolutionLogs: runtimeOptions.suppressLibraryResolutionLogs,
|
|
147
163
|
autoHud: suppressHudForLifecycleTiming || suppressHudForOpaqueMobile ? false : trust.source && trust.source.trust !== "trusted" ? false : runtimeOptions.autoHud,
|
|
@@ -151,28 +167,41 @@ async function resolveRecipeExecution(adapter, recipe, artifactsDir, projectRoot
|
|
|
151
167
|
// task-local adapter. Inherited gateway provenance remains authoritative,
|
|
152
168
|
// so an untrusted worker cannot promote itself with CLI flags.
|
|
153
169
|
trustTaskActions: (trust.source?.trust ?? "trusted") === "trusted"
|
|
170
|
+
};
|
|
171
|
+
let runner = await createMetaMaskRunner(adapter, manifest, {
|
|
172
|
+
...runnerOptions,
|
|
173
|
+
trustedMutation
|
|
154
174
|
});
|
|
155
175
|
const useFramedExtensionRecording = adapter === "extension" && recordVideo === "full-run" && trust.source?.trust !== "untrusted" && trust.source?.trust !== "unknown" && captureHelperSupportsRecordSessionSnapshots(projectRoot);
|
|
176
|
+
const runRequest = {
|
|
177
|
+
...absoluteRecipePath ? { recipePath: absoluteRecipePath } : { recipeDocument: recipe },
|
|
178
|
+
artifactsDir: absoluteArtifactsDir,
|
|
179
|
+
projectRoot,
|
|
180
|
+
inheritProcessEnv: false,
|
|
181
|
+
env: {
|
|
182
|
+
...recipeProcessEnvironment(),
|
|
183
|
+
...recipeRunEnv(adapter, runtimeOptions),
|
|
184
|
+
METAMASK_RECIPE_ACTION_SOURCE_MAP: actionSourceMap(actionSources)
|
|
185
|
+
},
|
|
186
|
+
recordVideo: useFramedExtensionRecording ? false : recordVideo,
|
|
187
|
+
...trust,
|
|
188
|
+
...librarySources ? { librarySources } : {},
|
|
189
|
+
adapter,
|
|
190
|
+
...runtimeOptions.params ? { params: runtimeOptions.params } : {}
|
|
191
|
+
};
|
|
192
|
+
if (trustedMutation) {
|
|
193
|
+
const executionPlan = await runner.preflight(runRequest);
|
|
194
|
+
const authorizedMutation = await authorizeTrustedMutationPlan(trustedMutation, executionPlan);
|
|
195
|
+
runner = await createMetaMaskRunner(adapter, manifest, {
|
|
196
|
+
...runnerOptions,
|
|
197
|
+
trustedMutation: authorizedMutation
|
|
198
|
+
});
|
|
199
|
+
}
|
|
156
200
|
return {
|
|
157
201
|
runner,
|
|
158
202
|
absoluteArtifactsDir,
|
|
159
203
|
useFramedExtensionRecording,
|
|
160
|
-
runRequest
|
|
161
|
-
...absoluteRecipePath ? { recipePath: absoluteRecipePath } : { recipeDocument: recipe },
|
|
162
|
-
artifactsDir: absoluteArtifactsDir,
|
|
163
|
-
projectRoot,
|
|
164
|
-
inheritProcessEnv: false,
|
|
165
|
-
env: {
|
|
166
|
-
...recipeProcessEnvironment(),
|
|
167
|
-
...recipeRunEnv(adapter, runtimeOptions),
|
|
168
|
-
METAMASK_RECIPE_ACTION_SOURCE_MAP: actionSourceMap(actionSources)
|
|
169
|
-
},
|
|
170
|
-
recordVideo: useFramedExtensionRecording ? false : recordVideo,
|
|
171
|
-
...trust,
|
|
172
|
-
...librarySources ? { librarySources } : {},
|
|
173
|
-
adapter,
|
|
174
|
-
...runtimeOptions.params ? { params: runtimeOptions.params } : {}
|
|
175
|
-
}
|
|
204
|
+
runRequest
|
|
176
205
|
};
|
|
177
206
|
}
|
|
178
207
|
function runtimeRecipeDocument(recipe, absoluteRecipePath) {
|
|
@@ -201,8 +230,7 @@ function recipeProcessEnvironment() {
|
|
|
201
230
|
}
|
|
202
231
|
function executionLibrarySources(sources, invocationTrust) {
|
|
203
232
|
return sources?.map((source) => {
|
|
204
|
-
if (source.name === "metamask"
|
|
205
|
-
return source;
|
|
233
|
+
if (source.name === "metamask") return source;
|
|
206
234
|
return {
|
|
207
235
|
...source,
|
|
208
236
|
provenance: {
|
|
@@ -347,7 +375,7 @@ async function validateRecipeAdapterAware(adapter, recipe, manifest, librarySour
|
|
|
347
375
|
);
|
|
348
376
|
const findings = [
|
|
349
377
|
...withManifest.findings,
|
|
350
|
-
...validateMetaMaskActionInputs(recipe, adapter)
|
|
378
|
+
...validateMetaMaskActionInputs(recipe, adapter, params)
|
|
351
379
|
];
|
|
352
380
|
if (withManifest.status === "valid" && isRecord(recipe) && libraryResolution) {
|
|
353
381
|
try {
|
|
@@ -376,10 +404,26 @@ async function validateRecipeAdapterAware(adapter, recipe, manifest, librarySour
|
|
|
376
404
|
dependency.document,
|
|
377
405
|
manifest,
|
|
378
406
|
validationOptions
|
|
379
|
-
).findings
|
|
380
|
-
...validateMetaMaskActionInputs(dependency.document, adapter)
|
|
407
|
+
).findings
|
|
381
408
|
);
|
|
382
409
|
}
|
|
410
|
+
const validateDependencyInstances = (document, parentParams) => {
|
|
411
|
+
if (!isRecord(document) || !isRecord(document.workflow) || !isRecord(document.workflow.nodes)) return;
|
|
412
|
+
for (const rawNode of Object.values(document.workflow.nodes)) {
|
|
413
|
+
const node = resolveMetaMaskParameterValue(rawNode, parentParams);
|
|
414
|
+
if (!isRecord(node) || node.action !== "call" || typeof node.ref !== "string") continue;
|
|
415
|
+
const dependency = dependencies.recipes.get(protocol.normalizeRecipeRef(node.ref));
|
|
416
|
+
if (!dependency) continue;
|
|
417
|
+
const supplied = isRecord(node.params) ? node.params : {};
|
|
418
|
+
const childParams = protocol.applyRecipeParamDefaults(
|
|
419
|
+
supplied,
|
|
420
|
+
isRecord(dependency.document) ? dependency.document.paramsSchema : void 0
|
|
421
|
+
);
|
|
422
|
+
findings.push(...validateMetaMaskActionInputs(dependency.document, adapter, childParams));
|
|
423
|
+
validateDependencyInstances(dependency.document, childParams);
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
validateDependencyInstances(recipe, params ?? {});
|
|
383
427
|
} catch (error) {
|
|
384
428
|
const resolutionError = error instanceof harness.RecipeResolutionError ? error : void 0;
|
|
385
429
|
if (!resolutionError) throw error;
|
|
@@ -429,13 +473,19 @@ function effectiveLibrarySources(librarySources) {
|
|
|
429
473
|
}
|
|
430
474
|
async function listRunnableRecipes(adapter, librarySources) {
|
|
431
475
|
const harness = await importRecipeHarness();
|
|
476
|
+
const sources = effectiveLibrarySources(librarySources);
|
|
432
477
|
const resolution = await harness.loadRecipeLibraries(
|
|
433
|
-
|
|
478
|
+
sources,
|
|
434
479
|
{
|
|
435
480
|
adapter
|
|
436
481
|
}
|
|
437
482
|
);
|
|
438
|
-
|
|
483
|
+
const protocol = await importRecipeProtocol();
|
|
484
|
+
const { manifest } = await resolveActionManifest(adapter, void 0, sources);
|
|
485
|
+
const externalRecipeIds = new Set(resolution.recipes.keys());
|
|
486
|
+
return [...resolution.recipes.values()].filter((recipe) => protocol.validateRecipeWithManifest(recipe.document, manifest, {
|
|
487
|
+
externalRecipeIds
|
|
488
|
+
}).status === "valid").map(
|
|
439
489
|
(recipe) => recipeSummary(recipe.ref, adapter, recipe.document, recipe)
|
|
440
490
|
).sort((left, right) => left.name.localeCompare(right.name));
|
|
441
491
|
}
|
|
@@ -630,20 +680,6 @@ async function validateRunRecipeStatic(recipeArg, adapter, options, params = {})
|
|
|
630
680
|
findings.push(
|
|
631
681
|
...protocol.validateRecipeParams(effectiveParams, recipe.paramsSchema).findings
|
|
632
682
|
);
|
|
633
|
-
const workflow = isRecord(recipe.workflow) ? recipe.workflow : void 0;
|
|
634
|
-
const nodes = workflow && isRecord(workflow.nodes) ? workflow.nodes : void 0;
|
|
635
|
-
if (adapter === "extension" && nodes) {
|
|
636
|
-
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
637
|
-
if (isRecord(node) && node.action === "metamask.perps.start_state" && node.page === false) {
|
|
638
|
-
findings.push({
|
|
639
|
-
severity: "error",
|
|
640
|
-
code: "recipe.extension-perps-start-page",
|
|
641
|
-
path: `workflow.nodes.${nodeId}.page`,
|
|
642
|
-
message: "Extension metamask.perps.start_state must enter Perps; omit page or provide a string target instead of false"
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
683
|
}
|
|
648
684
|
const validation = manifestOk ? await validateRecipeAdapterAware(
|
|
649
685
|
adapter,
|