@deeeed/metamask-harness 0.36.0 → 0.38.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 +51 -0
- package/adapters/extension/artifact-runtime-state.cjs +138 -0
- package/adapters/extension/inject.mjs +2 -0
- package/adapters/extension/launch-browser.cjs +24 -0
- package/adapters/extension/live.sh +82 -12
- package/adapters/extension/readiness.mjs +77 -36
- package/adapters/extension/snapshot-dist.sh +88 -3
- package/adapters/extension/verify.sh +6 -2
- package/adapters/manifest.json +17 -1
- package/adapters/shared/log-tui.mjs +1 -1
- package/dist/adapters/extension/artifact-integrity.js +38 -0
- package/dist/adapters/extension/extension-id.js +23 -4
- package/dist/adapters/extension/release-artifact.js +386 -0
- package/dist/adapters/extension/runtime-decision.js +164 -20
- package/dist/adapters/extension/runtime.js +169 -3
- package/dist/adapters/mobile/provision.js +38 -3
- package/dist/adapters/mobile/release-artifact-state.js +124 -0
- package/dist/adapters/mobile/release-artifact.js +295 -0
- package/dist/adapters.js +78 -6
- package/dist/app-lifecycle.js +4 -3
- package/dist/command-contract.js +16 -0
- package/dist/commands/call.js +6 -2
- package/dist/commands/launch/extension.js +74 -9
- package/dist/commands/launch/index.js +3 -0
- package/dist/commands/launch/mobile.js +2 -0
- package/dist/commands/provision.js +2 -0
- package/dist/commands/run-engine.js +109 -14
- package/dist/commands/run.js +4 -2
- package/dist/commands/runtime-launch.js +178 -10
- package/dist/heal-bounds.js +1 -1
- package/dist/live-adapter-contract.js +3 -1
- package/dist/metamask-action-validation.js +47 -1
- package/dist/mm-harness-cli.js +31 -1
- package/dist/recipe-security.js +40 -0
- package/dist/run-diagnostics.js +1 -1
- package/dist/runner.js +3 -0
- package/docs/RELEASE-QA-CAPABILITY-MAP.md +125 -0
- package/library/actions/extension/assets/finish_send.mjs +128 -0
- package/library/actions/extension/assets/import_custom_token.mjs +227 -0
- package/library/actions/extension/assets/prepare_send.mjs +53 -0
- package/library/actions/extension/assets/read_details.mjs +57 -0
- package/library/actions/extension/assets/read_visible_state.mjs +50 -0
- package/library/actions/extension/assets/review_send.mjs +23 -0
- package/library/actions/extension/assets/select_network_scope.mjs +30 -0
- package/library/actions/extension/assets/send.mjs +104 -0
- package/library/actions/extension/assets/set_custom_gas.mjs +56 -0
- package/library/actions/extension/assets/set_token_visibility.mjs +129 -0
- package/library/actions/extension/assets/verify_sorting.mjs +89 -0
- package/library/actions/extension/networks/add_chainlist.mjs +94 -0
- package/library/actions/extension/networks/add_custom.mjs +123 -0
- package/library/actions/extension/networks/custom_network.mjs +103 -0
- package/library/actions/extension/networks/read_visible_state.mjs +50 -0
- package/library/actions/extension/networks/remove_custom.mjs +68 -0
- package/library/actions/extension/perps/close_orders.mjs +1 -0
- package/library/actions/extension/perps/close_visible_position.mjs +133 -0
- package/library/actions/extension/perps/compare_provider_market.mjs +109 -0
- package/library/actions/extension/perps/edit_margin.mjs +82 -0
- package/library/actions/extension/perps/open_balance_action.mjs +58 -0
- package/library/actions/extension/perps/open_position_action.mjs +80 -0
- package/library/actions/extension/perps/perps.mjs +196 -2
- package/library/actions/extension/perps/place_order.mjs +5 -2
- package/library/actions/extension/perps/read_eligibility.mjs +34 -0
- package/library/actions/extension/perps/read_funds_confirmation.mjs +69 -0
- package/library/actions/extension/perps/read_snapshot.mjs +470 -0
- package/library/actions/extension/perps/read_visible_state.mjs +98 -0
- package/library/actions/extension/perps/select_activity_filter.mjs +44 -0
- package/library/actions/extension/perps/select_market_filter.mjs +71 -0
- package/library/actions/extension/perps/set_market_favorite.mjs +47 -0
- package/library/actions/extension/perps/update_position_tpsl.mjs +106 -0
- package/library/actions/extension/platform/cdp.mjs +109 -3
- package/library/actions/extension/settings/set_basic_functionality.mjs +58 -0
- package/library/actions/extension/swap_bridge/read_visible_state.mjs +71 -0
- package/library/actions/extension/swap_bridge/select_assets.mjs +103 -0
- package/library/actions/extension/swap_bridge/set_amount.mjs +36 -0
- package/library/actions/extension/swap_bridge/set_max_amount.mjs +58 -0
- package/library/actions/extension/swap_bridge/set_slippage.mjs +121 -0
- package/library/actions/extension/swap_bridge/submit_transaction.mjs +363 -0
- package/library/actions/extension/ui/activate_and_observe.mjs +96 -0
- package/library/actions/extension/ui/navigate.mjs +264 -8
- package/library/actions/extension/wallet/ensure_unlocked.mjs +2 -6
- package/library/actions/extension/wallet/import.mjs +13 -46
- package/library/actions/extension/wallet/lock.mjs +35 -0
- package/library/actions/extension/wallet/secret-input.mjs +98 -0
- package/library/actions/extension/wallet/select_account.mjs +71 -7
- package/library/actions/mobile/assets/read_visible_state.mjs +37 -0
- package/library/actions/mobile/perps/read_visible_state.mjs +31 -0
- package/library/actions/mobile/platform/native-session-name.mjs +5 -0
- package/library/actions/mobile/platform/observe-ui.mjs +86 -2
- package/library/actions/mobile/swap_bridge/native-session.mjs +45 -0
- package/library/actions/mobile/swap_bridge/read_visible_state.mjs +31 -0
- package/library/actions/mobile/swap_bridge/select_assets.mjs +94 -0
- package/library/actions/mobile/swap_bridge/set_amount.mjs +73 -0
- package/library/actions/mobile/ui/native-navigation.mjs +265 -0
- package/library/actions/mobile/ui/navigate.mjs +10 -1
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +17 -0
- package/library/actions/mobile/wallet/import.mjs +71 -2
- package/library/actions/mobile/wallet/native-ui.mjs +512 -0
- package/library/actions/mobile/wallet/read_state.mjs +16 -0
- package/library/actions/mobile/wallet/reset.mjs +17 -4
- package/library/actions/shared/assets/visible-state.mjs +176 -0
- package/library/actions/shared/networks/visible-state.mjs +70 -0
- package/library/actions/shared/perps/visible-state.mjs +354 -0
- package/library/actions/shared/swap-bridge/transaction.mjs +54 -0
- package/library/actions/shared/swap-bridge/visible-state.mjs +97 -0
- package/library/actions/shared/ui/locators.mjs +7 -0
- package/library/manifests/extension.action-manifest.json +1366 -184
- package/library/manifests/mobile.action-manifest.json +235 -1
- package/library/recipes/assets/release-custom-gas-send.recipe.json +88 -0
- package/library/recipes/assets/release-custom-network-token-persistence.recipe.json +106 -0
- package/library/recipes/assets/release-native-max-cancel.recipe.json +70 -0
- package/library/recipes/assets/release-native-max-send.recipe.json +78 -0
- package/library/recipes/assets/release-own-account-send.recipe.json +80 -0
- package/library/recipes/assets/release-token-address-import.recipe.json +47 -0
- package/library/recipes/assets/release-token-details.recipe.json +48 -0
- package/library/recipes/assets/release-token-hide-readd.recipe.json +55 -0
- package/library/recipes/assets/release-token-list.recipe.json +75 -0
- package/library/recipes/assets/release-token-persistence.recipe.json +73 -0
- package/library/recipes/assets/release-token-search-import.recipe.json +40 -0
- package/library/recipes/assets/release-token-sorting.recipe.json +29 -0
- package/library/recipes/extension/networks/release-balance-stability.recipe.json +51 -0
- package/library/recipes/extension/perps/release-live-limit-order.recipe.json +104 -0
- package/library/recipes/extension/perps/release-order-entry.recipe.json +155 -0
- package/library/recipes/extension/perps/release-order-validation.recipe.json +220 -0
- package/library/recipes/extension/runner/action-validation.recipe.json +12 -1
- package/library/recipes/perps/release-activity-filters.recipe.json +51 -0
- package/library/recipes/perps/release-funds-flows.recipe.json +76 -0
- package/library/recipes/perps/release-live-market-order.recipe.json +122 -0
- package/library/recipes/perps/release-market-details.recipe.json +83 -0
- package/library/recipes/perps/release-market-search.recipe.json +79 -0
- package/library/recipes/perps/release-watchlist.recipe.json +99 -0
- package/library/recipes/swap-bridge/release-custom-slippage.recipe.json +90 -0
- package/library/recipes/swap-bridge/release-live-max-swap.recipe.json +99 -0
- package/library/recipes/swap-bridge/release-live-swap.recipe.json +101 -0
- package/library/recipes/swap-bridge/release-max-quote.recipe.json +72 -0
- package/library/recipes/swap-bridge/release-quote.recipe.json +72 -0
- package/library/recipes/swap-bridge/release-surface.recipe.json +43 -0
- package/library/recipes/wallet/import.recipe.json +20 -1
- package/library/recipes/wallet/reset-import.recipe.json +20 -1
- package/package.json +6 -5
- package/scripts/validate-release-report.mjs +319 -0
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
#
|
|
10
10
|
# Inputs (flags):
|
|
11
11
|
# --dist <dir> source dist (required, e.g. <repo>/dist/chrome)
|
|
12
|
+
# --target <dir> absolute checkout containment root
|
|
13
|
+
# --runtime-root <dir> absolute directory that owns the snapshot
|
|
12
14
|
# --runtime-dist <dir> snapshot destination (required, recreated)
|
|
13
15
|
# --wait-iterations <n> manifest wait loop length, 2s each (default 180)
|
|
14
16
|
# --summary <file> optional standard summary.json
|
|
17
|
+
# --exact preserve every source entry (release artifacts)
|
|
15
18
|
#
|
|
16
19
|
# Outputs:
|
|
17
20
|
# <runtime-dist>/ snapshot (excludes _metadata); optional --summary file
|
|
@@ -24,24 +27,32 @@
|
|
|
24
27
|
set -euo pipefail
|
|
25
28
|
|
|
26
29
|
DIST=""
|
|
30
|
+
TARGET_ROOT=""
|
|
31
|
+
RUNTIME_ROOT=""
|
|
27
32
|
RUNTIME_DIST=""
|
|
28
33
|
WAIT_ITERATIONS=180
|
|
29
34
|
SUMMARY=""
|
|
35
|
+
EXACT=false
|
|
30
36
|
require_value() { [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; }
|
|
31
37
|
while [ "$#" -gt 0 ]; do
|
|
32
38
|
case "$1" in
|
|
33
39
|
--dist) require_value "$@"; DIST="$2"; shift 2 ;;
|
|
40
|
+
--target) require_value "$@"; TARGET_ROOT="$2"; shift 2 ;;
|
|
41
|
+
--runtime-root) require_value "$@"; RUNTIME_ROOT="$2"; shift 2 ;;
|
|
34
42
|
--runtime-dist) require_value "$@"; RUNTIME_DIST="$2"; shift 2 ;;
|
|
35
43
|
--wait-iterations) require_value "$@"; WAIT_ITERATIONS="$2"; shift 2 ;;
|
|
36
44
|
--summary) require_value "$@"; SUMMARY="$2"; shift 2 ;;
|
|
45
|
+
--exact) EXACT=true; shift ;;
|
|
37
46
|
-h|--help)
|
|
38
|
-
echo "Usage: snapshot-dist.sh --dist <dir> --runtime-dist <dir> [--wait-iterations <n>] [--summary <file>]"
|
|
47
|
+
echo "Usage: snapshot-dist.sh --dist <dir> --target <dir> --runtime-root <dir> --runtime-dist <dir> [--wait-iterations <n>] [--summary <file>]"
|
|
39
48
|
exit 0
|
|
40
49
|
;;
|
|
41
50
|
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
42
51
|
esac
|
|
43
52
|
done
|
|
44
53
|
[ -n "$DIST" ] || { echo "Missing --dist" >&2; exit 2; }
|
|
54
|
+
[ -n "$TARGET_ROOT" ] || { echo "Missing --target" >&2; exit 2; }
|
|
55
|
+
[ -n "$RUNTIME_ROOT" ] || { echo "Missing --runtime-root" >&2; exit 2; }
|
|
45
56
|
[ -n "$RUNTIME_DIST" ] || { echo "Missing --runtime-dist" >&2; exit 2; }
|
|
46
57
|
case "$WAIT_ITERATIONS" in ''|*[!0-9]*) echo "Invalid --wait-iterations (must be numeric): $WAIT_ITERATIONS" >&2; exit 2 ;; esac
|
|
47
58
|
|
|
@@ -73,8 +84,82 @@ done
|
|
|
73
84
|
test -f "$DIST/manifest.json" || { echo "snapshot-dist: no manifest at $DIST/manifest.json" >&2; exit 1; }
|
|
74
85
|
|
|
75
86
|
echo "[recipe-harness] snapshotting dist -> runtime-dist: $RUNTIME_DIST" >&2
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
node - "$DIST" "$TARGET_ROOT" "$RUNTIME_ROOT" "$RUNTIME_DIST" <<'NODE'
|
|
88
|
+
const fs = require('node:fs');
|
|
89
|
+
const path = require('node:path');
|
|
90
|
+
|
|
91
|
+
const [sourceInput, targetInput, rootInput, destinationInput] = process.argv.slice(2);
|
|
92
|
+
const source = path.resolve(sourceInput);
|
|
93
|
+
const target = path.resolve(targetInput);
|
|
94
|
+
const root = path.resolve(rootInput);
|
|
95
|
+
const destination = path.resolve(destinationInput);
|
|
96
|
+
|
|
97
|
+
function refuse(message) {
|
|
98
|
+
throw new Error(`snapshot-dist: ${message}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (![targetInput, rootInput, destinationInput].every(path.isAbsolute)) {
|
|
102
|
+
refuse('--target, --runtime-root, and --runtime-dist must be absolute paths.');
|
|
103
|
+
}
|
|
104
|
+
const targetStat = fs.lstatSync(target, { throwIfNoEntry: false });
|
|
105
|
+
if (!targetStat?.isDirectory() || targetStat.isSymbolicLink()) {
|
|
106
|
+
refuse(`target is not a regular directory: ${target}.`);
|
|
107
|
+
}
|
|
108
|
+
const rootRelative = path.relative(target, root);
|
|
109
|
+
if (!rootRelative || rootRelative === '..' || rootRelative.startsWith(`..${path.sep}`) || path.isAbsolute(rootRelative)) {
|
|
110
|
+
refuse(`runtime root must be a child of ${target}.`);
|
|
111
|
+
}
|
|
112
|
+
const relative = path.relative(root, destination);
|
|
113
|
+
if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
114
|
+
refuse(`runtime destination must be a child of ${root}.`);
|
|
115
|
+
}
|
|
116
|
+
if (
|
|
117
|
+
source === destination ||
|
|
118
|
+
source.startsWith(`${destination}${path.sep}`) ||
|
|
119
|
+
destination.startsWith(`${source}${path.sep}`)
|
|
120
|
+
) {
|
|
121
|
+
refuse('source and runtime destination must be separate directory trees.');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function ensureDirectoryTree(base, relativePath) {
|
|
125
|
+
let current = base;
|
|
126
|
+
const components = relativePath.split(path.sep).filter(Boolean);
|
|
127
|
+
for (const component of components) {
|
|
128
|
+
current = path.join(current, component);
|
|
129
|
+
const stat = fs.lstatSync(current, { throwIfNoEntry: false });
|
|
130
|
+
if (!stat) {
|
|
131
|
+
fs.mkdirSync(current, { mode: 0o700 });
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
135
|
+
refuse(`runtime path contains an unsafe entry: ${current}.`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ensureDirectoryTree(target, rootRelative);
|
|
141
|
+
ensureDirectoryTree(root, path.relative(root, path.dirname(destination)));
|
|
142
|
+
const realTarget = fs.realpathSync(target);
|
|
143
|
+
const realRoot = fs.realpathSync(root);
|
|
144
|
+
const realParent = fs.realpathSync(path.dirname(destination));
|
|
145
|
+
if (
|
|
146
|
+
!realRoot.startsWith(`${realTarget}${path.sep}`) ||
|
|
147
|
+
(realParent !== realRoot && !realParent.startsWith(`${realRoot}${path.sep}`))
|
|
148
|
+
) {
|
|
149
|
+
refuse(`runtime destination escapes ${root}.`);
|
|
150
|
+
}
|
|
151
|
+
const destinationStat = fs.lstatSync(destination, { throwIfNoEntry: false });
|
|
152
|
+
if (destinationStat && (!destinationStat.isDirectory() || destinationStat.isSymbolicLink())) {
|
|
153
|
+
refuse(`runtime destination is not a regular directory: ${destination}.`);
|
|
154
|
+
}
|
|
155
|
+
if (destinationStat) fs.rmSync(destination, { recursive: true });
|
|
156
|
+
fs.mkdirSync(destination, { mode: 0o700 });
|
|
157
|
+
NODE
|
|
158
|
+
if $EXACT; then
|
|
159
|
+
rsync -a --delete "$DIST/" "$RUNTIME_DIST/" || exit 1
|
|
160
|
+
else
|
|
161
|
+
rsync -a --delete --exclude _metadata "$DIST/" "$RUNTIME_DIST/" || exit 1
|
|
162
|
+
fi
|
|
78
163
|
echo "[recipe-harness] runtime-dist snapshot complete" >&2
|
|
79
164
|
|
|
80
165
|
# Freshness guard: the loaded runtime-dist must match dist/chrome's git id. A
|
|
@@ -344,14 +344,18 @@ let r = {};
|
|
|
344
344
|
try { r = JSON.parse(fs.readFileSync(dir + "/runtime-decision.json", "utf8")); } catch {}
|
|
345
345
|
const c = r.checks || {};
|
|
346
346
|
const dist = c.dist || { status: "unknown" };
|
|
347
|
-
const distMsg = dist.
|
|
347
|
+
const distMsg = dist.source === "release-artifact"
|
|
348
|
+
? ("release artifact " + (dist.manifestVersion || "?") + " sha256=" + (dist.artifactSha256 || "?") + " matches the loaded runtime snapshot.")
|
|
349
|
+
: dist.status === "fresh" ? "dist id matches HEAD; no uncommitted source."
|
|
348
350
|
: dist.status === "stale" ? (dist.reason === "uncommitted-source"
|
|
349
351
|
? ((dist.modified ? dist.modified.length : "some") + " uncommitted source file(s); rebuild or commit.")
|
|
350
352
|
: ("dist id " + (dist.distGitId || "?") + " != HEAD " + (dist.head || "?") + "; rebuild."))
|
|
351
353
|
: dist.status === "no-build" ? "no dist/chrome build."
|
|
352
354
|
: "no git id in dist or not a git checkout; cannot prove parity.";
|
|
353
355
|
fs.writeFileSync(dir + "/dist-freshness.json", JSON.stringify({ ...dist, message: distMsg }));
|
|
354
|
-
const bl = c.
|
|
356
|
+
const bl = c.releaseArtifact?.status === "valid"
|
|
357
|
+
? { status: "no-watch", source: "release-artifact" }
|
|
358
|
+
: (c.buildLog || { status: "unknown" });
|
|
355
359
|
const blMsg = bl.status === "ok" ? "webpack compiled."
|
|
356
360
|
: bl.status === "no-watch" ? "no webpack watch log; build-health n/a (e.g. one-shot build)."
|
|
357
361
|
: bl.status === "building" ? "webpack has not reported a successful compile yet."
|
package/adapters/manifest.json
CHANGED
|
@@ -186,6 +186,14 @@
|
|
|
186
186
|
"inputs": "--target --runtime-dist; optional --runtime-dir",
|
|
187
187
|
"outputs": "home.html and sidepanel.html titles updated in the isolated runtime only; exit 0/1"
|
|
188
188
|
},
|
|
189
|
+
{
|
|
190
|
+
"id": "extension/artifact-runtime-state",
|
|
191
|
+
"entry": "adapters/extension/artifact-runtime-state.cjs",
|
|
192
|
+
"kind": "node",
|
|
193
|
+
"purpose": "Record or clear the acquired release artifact identity bound to the loaded runtime snapshot.",
|
|
194
|
+
"inputs": "set|clear --target --runtime-dir; set also requires --source-dir --runtime-dist --provenance",
|
|
195
|
+
"outputs": "<runtime-dir>/extension-release-artifact.json or removal; exit 0/1/2"
|
|
196
|
+
},
|
|
189
197
|
{
|
|
190
198
|
"id": "extension/stop-viewers",
|
|
191
199
|
"entry": "adapters/extension/stop-viewers.sh",
|
|
@@ -207,7 +215,7 @@
|
|
|
207
215
|
"entry": "adapters/extension/snapshot-dist.sh",
|
|
208
216
|
"kind": "bash",
|
|
209
217
|
"purpose": "Runtime-dist snapshot (rsync, excludes _metadata) with from-git-id freshness guard.",
|
|
210
|
-
"inputs": "--dist --runtime-dist --wait-iterations --summary",
|
|
218
|
+
"inputs": "--dist --target --runtime-root --runtime-dist --wait-iterations --summary",
|
|
211
219
|
"outputs": "<runtime-dist>/ snapshot; optional summary; exit 0/1/2"
|
|
212
220
|
},
|
|
213
221
|
{
|
|
@@ -226,6 +234,14 @@
|
|
|
226
234
|
"inputs": "--cdp-port --chrome-bin --profile --extension-dir --chrome-log --chrome-pid",
|
|
227
235
|
"outputs": "pid file + append-log; exit 0 / non-zero"
|
|
228
236
|
},
|
|
237
|
+
{
|
|
238
|
+
"id": "extension/validation-process-ownership",
|
|
239
|
+
"entry": "adapters/extension/lib/validation-process-ownership.cjs",
|
|
240
|
+
"kind": "lib",
|
|
241
|
+
"purpose": "Resolve browser processes owned by one isolated Extension validation profile.",
|
|
242
|
+
"inputs": "profile path",
|
|
243
|
+
"outputs": "owned process identifiers"
|
|
244
|
+
},
|
|
229
245
|
{
|
|
230
246
|
"id": "extension/refresh-build",
|
|
231
247
|
"entry": "adapters/extension/refresh-build.sh",
|
|
@@ -236,7 +236,7 @@ async function watchLog(options) {
|
|
|
236
236
|
renderCompact({ label, logPath, events, quiet: mode === 'quiet' });
|
|
237
237
|
if (mode !== 'quiet') {
|
|
238
238
|
const elapsed = Math.round((Date.now() - started) / 1000);
|
|
239
|
-
process.stderr.write(`${color('label', 'mm-harness', { stream: process.stderr })} ${color('dim', '|', { stream: process.stderr })} ${color('ok',
|
|
239
|
+
process.stderr.write(`${color('label', 'mm-harness', { stream: process.stderr })} ${color('dim', '|', { stream: process.stderr })} ${color('ok', `${label} complete (${elapsed}s)`, { stream: process.stderr })}\n`);
|
|
240
240
|
}
|
|
241
241
|
return 0;
|
|
242
242
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
function extensionTreeSha256(root) {
|
|
5
|
+
const resolvedRoot = path.resolve(root);
|
|
6
|
+
const rootStat = fs.lstatSync(resolvedRoot);
|
|
7
|
+
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
|
|
8
|
+
throw new Error(`Extension artifact root must be a regular directory: ${resolvedRoot}`);
|
|
9
|
+
}
|
|
10
|
+
const digest = createHash("sha256");
|
|
11
|
+
walk(resolvedRoot, "");
|
|
12
|
+
return digest.digest("hex");
|
|
13
|
+
function walk(directory, relativeDirectory) {
|
|
14
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true }).sort((left, right) => Buffer.compare(Buffer.from(left.name), Buffer.from(right.name)));
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
const absolute = path.join(directory, entry.name);
|
|
17
|
+
const relative = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
|
|
18
|
+
const stat = fs.lstatSync(absolute);
|
|
19
|
+
if (stat.isSymbolicLink()) {
|
|
20
|
+
throw new Error(`Extension artifact tree contains a symbolic link: ${relative}`);
|
|
21
|
+
}
|
|
22
|
+
if (stat.isDirectory()) {
|
|
23
|
+
digest.update(`directory\0${relative}\0`);
|
|
24
|
+
walk(absolute, relative);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (!stat.isFile()) {
|
|
28
|
+
throw new Error(`Extension artifact tree contains an unsupported entry: ${relative}`);
|
|
29
|
+
}
|
|
30
|
+
digest.update(`file\0${relative}\0${stat.size}\0`);
|
|
31
|
+
digest.update(fs.readFileSync(absolute));
|
|
32
|
+
digest.update("\0");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
extensionTreeSha256
|
|
38
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { recipeRuntimeDir } from "../../paths.js";
|
|
5
|
+
import { releaseArtifactState, runtimeDistCheck } from "./runtime-decision.js";
|
|
4
6
|
const DIST_MANIFEST = "dist/chrome/manifest.json";
|
|
5
7
|
function extensionIdFromKey(keyBase64) {
|
|
6
8
|
const der = Buffer.from(keyBase64, "base64");
|
|
@@ -12,8 +14,8 @@ function extensionIdFromKey(keyBase64) {
|
|
|
12
14
|
}
|
|
13
15
|
return id;
|
|
14
16
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
+
function idFromManifest(manifestPath) {
|
|
18
|
+
if (!manifestPath) return null;
|
|
17
19
|
if (!fs.existsSync(manifestPath)) return null;
|
|
18
20
|
try {
|
|
19
21
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
@@ -39,7 +41,24 @@ async function cdpExtensionIds(cdpPort) {
|
|
|
39
41
|
}
|
|
40
42
|
async function resolveExtensionId(target, options = {}) {
|
|
41
43
|
const resolved = path.resolve(target);
|
|
42
|
-
const
|
|
44
|
+
const runtimeSnapshot = runtimeDistCheck(resolved, releaseArtifactState(resolved));
|
|
45
|
+
const releaseArtifact = runtimeSnapshot.source === "release-artifact";
|
|
46
|
+
if (releaseArtifact && runtimeSnapshot.status !== "fresh") {
|
|
47
|
+
return {
|
|
48
|
+
adapter: "extension",
|
|
49
|
+
target: resolved,
|
|
50
|
+
extensionId: null,
|
|
51
|
+
source: "none",
|
|
52
|
+
verified: false
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const manifestPath = releaseArtifact ? runtimeSnapshot.status === "fresh" ? path.join(
|
|
56
|
+
resolved,
|
|
57
|
+
recipeRuntimeDir(),
|
|
58
|
+
process.env.RECIPE_RUNTIME_DIST_DIR || "runtime-dist",
|
|
59
|
+
"manifest.json"
|
|
60
|
+
) : null : path.join(resolved, DIST_MANIFEST);
|
|
61
|
+
const fromKey = idFromManifest(manifestPath);
|
|
43
62
|
let cdpIds = null;
|
|
44
63
|
if (options.cdpPort) cdpIds = await cdpExtensionIds(options.cdpPort);
|
|
45
64
|
if (fromKey) {
|
|
@@ -47,7 +66,7 @@ async function resolveExtensionId(target, options = {}) {
|
|
|
47
66
|
adapter: "extension",
|
|
48
67
|
target: resolved,
|
|
49
68
|
extensionId: fromKey,
|
|
50
|
-
source: "manifest-key",
|
|
69
|
+
source: releaseArtifact ? "release-manifest-key" : "manifest-key",
|
|
51
70
|
verified: cdpIds ? cdpIds.includes(fromKey) : null
|
|
52
71
|
};
|
|
53
72
|
}
|