@deeeed/metamask-harness 0.79.0 → 0.80.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 +47 -0
- package/README.md +1 -1
- package/adapters/extension/ensure-browser.sh +2 -2
- package/adapters/extension/inject.mjs +0 -1
- package/adapters/extension/launch-browser.cjs +39 -613
- package/adapters/extension/readiness.mjs +2 -5
- package/adapters/extension/reattach.sh +2 -2
- package/adapters/extension/stamp-runtime-title.cjs +1 -1
- package/adapters/manifest.json +2 -34
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +57 -1358
- package/adapters/mobile/bridge-runtime/teaching.cjs +20 -0
- package/adapters/mobile/inject.sh +16 -0
- package/adapters/mobile/launch-console-forwarder.cjs +1 -2
- package/adapters/mobile/open-device.sh +2 -2
- package/adapters/mobile/reload-app.mjs +4 -4
- package/adapters/mobile/reset-app-data.sh +1 -1
- package/adapters/mobile/start-console-forwarder.sh +3 -2
- package/adapters/mobile/start-metro.sh +17 -12
- package/adapters/mobile/wait-for-bridge.cjs +2 -2
- package/adapters/shared/harness-source-fingerprint.mjs +13 -8
- package/adapters/shared/open-debug.mjs +2 -2
- package/adapters/shared/sync-wallet-fixture.sh +8 -5
- package/adapters/terminal/cleanup.mjs +2 -1
- package/adapters/terminal/inject.mjs +2 -1
- package/bin/mm-harness +9 -28
- package/dist/adapters/checkout-files.js +16 -0
- package/dist/adapters/console-capture.js +14 -1
- package/dist/adapters/core/actions.js +4 -3
- package/dist/adapters/core/overlay.js +1 -1
- package/dist/adapters/core/readiness.js +28 -1
- package/dist/adapters/core/run.js +2 -2
- package/dist/adapters/core/surface.js +16 -1
- package/dist/adapters/extension/browser-pid.js +56 -0
- package/dist/adapters/extension/console-capture.js +1 -1
- package/dist/adapters/extension/doctor-checks.js +1 -1
- package/dist/adapters/extension/ensure-ready.js +5 -4
- package/dist/adapters/extension/extension-id.js +1 -1
- package/dist/adapters/extension/feature-flags.js +1 -1
- package/dist/adapters/extension/fixtures.js +1 -2
- package/dist/adapters/extension/launch.js +4 -5
- package/dist/adapters/extension/launched-browser.js +1 -2
- package/dist/adapters/extension/network-observer.js +1 -1
- package/dist/adapters/extension/overlay.js +1 -1
- package/dist/adapters/extension/product-config.js +1 -1
- package/dist/adapters/extension/readiness.js +1 -1
- package/dist/adapters/extension/reload.js +1 -1
- package/dist/adapters/extension/run.js +24 -10
- package/dist/adapters/extension/runtime-decision.js +2 -1
- package/dist/adapters/extension/runtime.js +9 -5
- package/dist/adapters/extension/surface.js +15 -2
- package/dist/adapters/extension/verify.js +2 -2
- package/dist/adapters/harness-freshness.js +2 -1
- package/dist/adapters/mobile/android-gfxinfo-performance.js +1 -3
- package/dist/adapters/mobile/app-identity.js +10 -0
- package/dist/adapters/mobile/doctor-checks.js +1 -1
- package/dist/adapters/mobile/fixtures.js +1 -1
- package/dist/adapters/mobile/launch.js +27 -28
- package/dist/adapters/mobile/lifecycle.js +1 -1
- package/dist/adapters/mobile/metro-env.js +9 -59
- package/dist/adapters/mobile/performance-observer.js +1 -1
- package/dist/adapters/mobile/prepare-target.js +1 -1
- package/dist/adapters/mobile/prepare.js +3 -4
- package/dist/adapters/mobile/provision.js +1 -1
- package/dist/adapters/mobile/readiness.js +1 -1
- package/dist/adapters/mobile/recording-target.js +15 -0
- package/dist/adapters/mobile/release-artifact-state.js +1 -1
- package/dist/adapters/mobile/reload.js +1 -1
- package/dist/adapters/mobile/run.js +8 -7
- package/dist/adapters/mobile/runtime-decision.js +2 -4
- package/dist/adapters/mobile/source-freshness.js +22 -126
- package/dist/adapters/mobile/surface.js +26 -3
- package/dist/adapters/mobile/ui.js +16 -5
- package/dist/adapters/mobile/verify.js +2 -3
- package/dist/adapters/performance/cdp-trace.js +1 -1
- package/dist/adapters/performance/device-platform.js +1 -1
- package/dist/adapters/slot-ports.js +1 -1
- package/dist/adapters/surface.js +1 -0
- package/dist/adapters/terminal/console-capture.js +1 -1
- package/dist/adapters/terminal/launch.js +1 -1
- package/dist/adapters/terminal/run.js +25 -2
- package/dist/adapters/terminal/surface.js +7 -1
- package/dist/adapters.js +8 -4
- package/dist/advisor-policy.js +1 -1
- package/dist/checkout-view.js +1 -2
- package/dist/cli.js +28 -15
- package/dist/command-contract.js +1 -26
- package/dist/commands/call.js +10 -24
- package/dist/commands/check.js +3 -4
- package/dist/commands/checklist.js +1 -1
- package/dist/commands/completion-candidates.js +2 -10
- package/dist/commands/config.js +2 -2
- package/dist/commands/core-readiness.js +12 -7
- package/dist/commands/debug.js +3 -3
- package/dist/commands/device-target.js +5 -5
- package/dist/commands/doctor.js +2 -14
- package/dist/commands/domain.js +2 -2
- package/dist/commands/ensure-ready.js +1 -7
- package/dist/commands/fixtures.js +5 -6
- package/dist/commands/help.js +3 -4
- package/dist/commands/launch/device.js +10 -17
- package/dist/commands/launch/extension.js +2 -8
- package/dist/commands/launch/mobile.js +1 -1
- package/dist/commands/list-executables.js +1 -3
- package/dist/commands/logs.js +3 -3
- package/dist/commands/manifest.js +1 -12
- package/dist/commands/mobile-device-view.js +4 -2
- package/dist/commands/overlay.js +110 -0
- package/dist/commands/pin.js +1 -1
- package/dist/commands/pr-body.js +2 -2
- package/dist/commands/prepare.js +4 -9
- package/dist/commands/provision.js +3 -12
- package/dist/commands/recipe-advice.js +1 -2
- package/dist/commands/recipe-quality.js +2 -3
- package/dist/commands/resolve-extension.js +1 -7
- package/dist/commands/review.js +4 -4
- package/dist/commands/run-engine.js +12 -51
- package/dist/commands/run.js +4 -25
- package/dist/commands/runtime-decision.js +1 -6
- package/dist/commands/runtime-health.js +1 -7
- package/dist/commands/runtime-launch.js +1 -13
- package/dist/commands/self-test.js +2 -6
- package/dist/commands/status-watch.js +1 -2
- package/dist/commands/status.js +2 -5
- package/dist/commands/task-init.js +2 -3
- package/dist/commands/tutorial.js +2 -2
- package/dist/commands/update.js +1 -1
- package/dist/completions-cache.js +1 -1
- package/dist/console-classifier.js +19 -0
- package/dist/doctor.js +2 -3
- package/dist/funding-execution-context.js +15 -1
- package/dist/host.js +32 -0
- package/dist/index.js +1 -6
- package/dist/live-tsx.js +49 -0
- package/dist/mm-harness-cli.js +2 -4
- package/dist/network-observation.js +4 -4
- package/dist/paths.js +34 -64
- package/dist/performance-observation.js +2 -2
- package/dist/progress.js +1 -1
- package/dist/recipe-assistance-context.js +1 -3
- package/dist/recipe-security.js +2 -59
- package/dist/review/knowledge.js +2 -2
- package/dist/runner.js +5 -3
- package/dist/runtime-context.js +1 -1
- package/dist/task-view.js +2 -3
- package/docs/CONTRIBUTING.md +1 -1
- package/docs/cli-contract.md +2 -2
- package/docs/feature-guard.md +90 -0
- package/library/README.md +3 -1
- package/library/actions/core/perps/_controller.mjs +153 -130
- package/library/actions/core/perps/quote_fees.mjs +58 -0
- package/library/actions/core/wallet/_fixture-accounts.mjs +69 -0
- package/library/actions/core/wallet/list_accounts.mjs +14 -57
- package/library/actions/extension/feature_flags/state.mjs +1 -1
- package/library/actions/extension/platform/cdp.mjs +2 -8
- package/library/actions/harness-exports.mjs +1 -6
- package/library/actions/mobile/app/network-control.mjs +6 -8
- package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +1 -1
- package/library/actions/mobile/platform/bridge.mjs +8 -8
- package/library/actions/mobile/platform/native-session-name.mjs +21 -1
- package/library/actions/mobile/platform/native-session.mjs +3 -4
- package/library/actions/mobile/platform/observe-ui.mjs +12 -6
- package/library/actions/mobile/ui/native-navigation.mjs +4 -3
- package/library/actions/mobile/wallet/native-ui.mjs +3 -2
- package/library/actions/shared/analytics/_adapter.mjs +1 -1
- package/library/actions/shared/console-findings.mjs +1 -1
- package/library/actions/terminal/platform/runtime.mjs +2 -1
- package/library/manifests/core.action-manifest.json +83 -13
- package/package.json +8 -9
- package/adapters/extension/lib/slot-title.cjs +0 -292
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +0 -472
- package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +0 -125
- package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +0 -1055
- package/adapters/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -110
- package/adapters/mobile/bridge-runtime/lib/config.cjs +0 -61
- package/adapters/mobile/bridge-runtime/lib/console-format.cjs +0 -53
- package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +0 -177
- package/adapters/mobile/bridge-runtime/lib/issue-capture.cjs +0 -446
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +0 -84
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +0 -403
- package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +0 -116
- package/adapters/mobile/coalesce-metro-log.cjs +0 -24
- package/adapters/mobile/launch-metro.cjs +0 -77
- package/adapters/mobile/metro-config.cjs +0 -98
- package/adapters/mobile/metro-log-generation.cjs +0 -106
- package/dist/adapters/mobile/frame-metrics.js +0 -118
- package/dist/adapters/mobile/video-recorder.js +0 -289
- package/dist/artifact-files.js +0 -92
- package/dist/checkout-lock.js +0 -149
- package/dist/cli-color.js +0 -98
- package/dist/command-journal.js +0 -308
- package/dist/commands/last.js +0 -60
- package/dist/commands/launch/index.js +0 -109
- package/dist/commands/parse-args.js +0 -279
- package/dist/commands/reload.js +0 -41
- package/dist/commands/run-report.js +0 -241
- package/dist/commands/shared.js +0 -323
- package/dist/commands/stop.js +0 -60
- package/dist/devices.js +0 -100
- package/dist/execution-provenance.js +0 -332
- package/dist/harness.js +0 -451
- package/dist/heal-bounds.js +0 -137
- package/dist/json-stream.js +0 -60
- package/dist/leaf-invoke.js +0 -19
- package/dist/library-provenance.js +0 -33
- package/dist/live-adapter-contract.js +0 -380
- package/dist/recording-target.js +0 -120
- package/dist/run-diagnostics.js +0 -364
- package/dist/run-recording.js +0 -481
- package/dist/runtime-proof.js +0 -90
- package/library/actions/mobile/platform/tool-paths.mjs +0 -123
|
@@ -649,7 +649,7 @@
|
|
|
649
649
|
},
|
|
650
650
|
"account": {
|
|
651
651
|
"type": "string",
|
|
652
|
-
"description": "
|
|
652
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
|
|
653
653
|
},
|
|
654
654
|
"network": {
|
|
655
655
|
"type": "string",
|
|
@@ -759,7 +759,7 @@
|
|
|
759
759
|
},
|
|
760
760
|
"account": {
|
|
761
761
|
"type": "string",
|
|
762
|
-
"description": "
|
|
762
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
|
|
763
763
|
},
|
|
764
764
|
"network": {
|
|
765
765
|
"type": "string",
|
|
@@ -793,7 +793,7 @@
|
|
|
793
793
|
"properties": {
|
|
794
794
|
"account": {
|
|
795
795
|
"type": "string",
|
|
796
|
-
"description": "
|
|
796
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
|
|
797
797
|
},
|
|
798
798
|
"timeout_ms": {
|
|
799
799
|
"type": "number"
|
|
@@ -818,6 +818,76 @@
|
|
|
818
818
|
],
|
|
819
819
|
"execution_capabilities": []
|
|
820
820
|
},
|
|
821
|
+
"metamask.perps.quote_fees": {
|
|
822
|
+
"description": "core Quote an order's fees through the perps controller's calculateFees (HyperLiquid, testnet by default, read-only): protocol and MetaMask fee rates and amounts (plus the winning fee source on a core that reports it), with an optional rewards discount supplied as RewardsController:getPerpsDiscountForAccount.",
|
|
823
|
+
"schema": {
|
|
824
|
+
"type": "object",
|
|
825
|
+
"properties": {
|
|
826
|
+
"market": {
|
|
827
|
+
"type": "string",
|
|
828
|
+
"description": "Market to quote, e.g. BTC."
|
|
829
|
+
},
|
|
830
|
+
"symbol": {
|
|
831
|
+
"type": "string",
|
|
832
|
+
"description": "Alias for market."
|
|
833
|
+
},
|
|
834
|
+
"order_type": {
|
|
835
|
+
"type": "string",
|
|
836
|
+
"enum": [
|
|
837
|
+
"market",
|
|
838
|
+
"limit"
|
|
839
|
+
],
|
|
840
|
+
"description": "Fee schedule to quote; defaults to market."
|
|
841
|
+
},
|
|
842
|
+
"is_maker": {
|
|
843
|
+
"type": "boolean",
|
|
844
|
+
"description": "Quote maker fees (limit orders that rest)."
|
|
845
|
+
},
|
|
846
|
+
"amount": {
|
|
847
|
+
"type": [
|
|
848
|
+
"string",
|
|
849
|
+
"number"
|
|
850
|
+
],
|
|
851
|
+
"description": "Order notional in USD; adds fee amounts and the blended rate for that size."
|
|
852
|
+
},
|
|
853
|
+
"rewards_discount_bips": {
|
|
854
|
+
"type": "integer",
|
|
855
|
+
"description": "Rewards discount the headless RewardsController answers with, an integer in basis points from 0 through 10000 (6500 = 65%). Omitted: no rewards source, the controller's 'not known yet'."
|
|
856
|
+
},
|
|
857
|
+
"account": {
|
|
858
|
+
"type": "string",
|
|
859
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
|
|
860
|
+
},
|
|
861
|
+
"account_name": {
|
|
862
|
+
"type": "string",
|
|
863
|
+
"description": "Alias for a wallet fixture account name."
|
|
864
|
+
},
|
|
865
|
+
"network": {
|
|
866
|
+
"type": "string",
|
|
867
|
+
"enum": [
|
|
868
|
+
"testnet",
|
|
869
|
+
"mainnet"
|
|
870
|
+
],
|
|
871
|
+
"description": "Venue to quote. Defaults to testnet."
|
|
872
|
+
},
|
|
873
|
+
"timeout_ms": {
|
|
874
|
+
"type": "number"
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
"additionalProperties": false
|
|
878
|
+
},
|
|
879
|
+
"examples": [
|
|
880
|
+
{
|
|
881
|
+
"action": "metamask.perps.quote_fees",
|
|
882
|
+
"market": "BTC",
|
|
883
|
+
"amount": "100",
|
|
884
|
+
"rewards_discount_bips": 6500,
|
|
885
|
+
"intent": "Quote a $100 BTC market order with a 65% rewards discount",
|
|
886
|
+
"next": "done"
|
|
887
|
+
}
|
|
888
|
+
],
|
|
889
|
+
"execution_capabilities": []
|
|
890
|
+
},
|
|
821
891
|
"metamask.perps.read_snapshot": {
|
|
822
892
|
"description": "Read global market and account snapshots through the public PerpsController API used by wallet clients.",
|
|
823
893
|
"schema": {
|
|
@@ -835,7 +905,7 @@
|
|
|
835
905
|
},
|
|
836
906
|
"account": {
|
|
837
907
|
"type": "string",
|
|
838
|
-
"description": "Wallet fixture account name or
|
|
908
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
|
|
839
909
|
},
|
|
840
910
|
"account_name": {
|
|
841
911
|
"type": "string",
|
|
@@ -1025,7 +1095,7 @@
|
|
|
1025
1095
|
},
|
|
1026
1096
|
"account": {
|
|
1027
1097
|
"type": "string",
|
|
1028
|
-
"description": "
|
|
1098
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
|
|
1029
1099
|
},
|
|
1030
1100
|
"timeout_ms": {
|
|
1031
1101
|
"type": "number"
|
|
@@ -1344,7 +1414,7 @@
|
|
|
1344
1414
|
},
|
|
1345
1415
|
"account": {
|
|
1346
1416
|
"type": "string",
|
|
1347
|
-
"description": "
|
|
1417
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
|
|
1348
1418
|
},
|
|
1349
1419
|
"timeout_ms": {
|
|
1350
1420
|
"type": "number"
|
|
@@ -1471,7 +1541,7 @@
|
|
|
1471
1541
|
},
|
|
1472
1542
|
"account": {
|
|
1473
1543
|
"type": "string",
|
|
1474
|
-
"description": "
|
|
1544
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
|
|
1475
1545
|
},
|
|
1476
1546
|
"network": {
|
|
1477
1547
|
"type": "string",
|
|
@@ -1627,7 +1697,7 @@
|
|
|
1627
1697
|
},
|
|
1628
1698
|
"account": {
|
|
1629
1699
|
"type": "string",
|
|
1630
|
-
"description": "
|
|
1700
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
|
|
1631
1701
|
},
|
|
1632
1702
|
"timeout_ms": {
|
|
1633
1703
|
"type": "number"
|
|
@@ -1681,7 +1751,7 @@
|
|
|
1681
1751
|
},
|
|
1682
1752
|
"account": {
|
|
1683
1753
|
"type": "string",
|
|
1684
|
-
"description": "Wallet fixture account name
|
|
1754
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
|
|
1685
1755
|
},
|
|
1686
1756
|
"market": {
|
|
1687
1757
|
"type": "string",
|
|
@@ -1803,7 +1873,7 @@
|
|
|
1803
1873
|
"properties": {
|
|
1804
1874
|
"account": {
|
|
1805
1875
|
"type": "string",
|
|
1806
|
-
"description": "Wallet fixture account name,
|
|
1876
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or 0x address; defaults to dev1. An address outside the fixture is read as given."
|
|
1807
1877
|
},
|
|
1808
1878
|
"market": {
|
|
1809
1879
|
"type": "string",
|
|
@@ -2010,7 +2080,7 @@
|
|
|
2010
2080
|
"properties": {
|
|
2011
2081
|
"account": {
|
|
2012
2082
|
"type": "string",
|
|
2013
|
-
"description": "Wallet fixture account name
|
|
2083
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
|
|
2014
2084
|
},
|
|
2015
2085
|
"market": {
|
|
2016
2086
|
"type": "string",
|
|
@@ -2887,7 +2957,7 @@
|
|
|
2887
2957
|
},
|
|
2888
2958
|
"account": {
|
|
2889
2959
|
"type": "string",
|
|
2890
|
-
"description": "
|
|
2960
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
|
|
2891
2961
|
},
|
|
2892
2962
|
"timeout_ms": {
|
|
2893
2963
|
"type": "number"
|
|
@@ -3010,7 +3080,7 @@
|
|
|
3010
3080
|
},
|
|
3011
3081
|
"account": {
|
|
3012
3082
|
"type": "string",
|
|
3013
|
-
"description": "
|
|
3083
|
+
"description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
|
|
3014
3084
|
},
|
|
3015
3085
|
"timeout_ms": {
|
|
3016
3086
|
"type": "number"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deeeed/metamask-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.80.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mm-harness": "bin/mm-harness"
|
|
@@ -21,18 +21,18 @@
|
|
|
21
21
|
"check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@farmslot/adapter-
|
|
25
|
-
"@farmslot/adapter-
|
|
26
|
-
"@farmslot/
|
|
27
|
-
"@farmslot/
|
|
24
|
+
"@farmslot/adapter-rn": "0.17.0",
|
|
25
|
+
"@farmslot/adapter-sdk": "0.3.1",
|
|
26
|
+
"@farmslot/adapter-web": "0.2.0",
|
|
27
|
+
"@farmslot/agent-runtime": "0.17.0",
|
|
28
28
|
"@farmslot/handoff": "^0.3.1",
|
|
29
29
|
"@farmslot/protocol": "0.34.0",
|
|
30
|
-
"@farmslot/recipe-cli": "0.
|
|
31
|
-
"@farmslot/recipe-runner": "0.
|
|
30
|
+
"@farmslot/recipe-cli": "0.4.1",
|
|
31
|
+
"@farmslot/recipe-runner": "0.24.0",
|
|
32
32
|
"agent-device": "0.19.3",
|
|
33
33
|
"commander": "^12.0.0",
|
|
34
|
-
"es-module-lexer": "2.3.1",
|
|
35
34
|
"esbuild": "0.28.1",
|
|
35
|
+
"tsx": "^4.23.0",
|
|
36
36
|
"viem": "^2.54.3",
|
|
37
37
|
"ws": "8.21.0"
|
|
38
38
|
},
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
"eslint": "^9",
|
|
49
49
|
"globals": "^17.7.0",
|
|
50
50
|
"proper-lockfile": "^4.1.2",
|
|
51
|
-
"tsx": "^4.23.0",
|
|
52
51
|
"typescript": "^5.6.0",
|
|
53
52
|
"typescript-eslint": "^8",
|
|
54
53
|
"vitest": "^4.1.10"
|
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Operator slot title for headed extension Chrome.
|
|
5
|
-
*
|
|
6
|
-
* Chrome's window label is the active tab's document.title. Farmslot stamps
|
|
7
|
-
* home.html to "<slot-id> — MetaMask" so operators can tell which slot owns a
|
|
8
|
-
* window when several farms run side by side.
|
|
9
|
-
*
|
|
10
|
-
* Stamping is best-effort: missing slot context or an uninspectable tab must
|
|
11
|
-
* never fail launch/readiness. A MutationObserver keeps the prefix across
|
|
12
|
-
* MetaMask SPA title resets.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const fs = require('node:fs');
|
|
16
|
-
const http = require('node:http');
|
|
17
|
-
const path = require('node:path');
|
|
18
|
-
|
|
19
|
-
function sanitizeSlotId(value) {
|
|
20
|
-
const slotId = typeof value === 'string' ? value.trim() : '';
|
|
21
|
-
return /^[A-Za-z0-9._:-]{1,64}$/u.test(slotId) ? slotId : '';
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function readSlotId(target, runtimeDir) {
|
|
25
|
-
for (const key of ['RECIPE_SLOT_ID', 'SLOT_ID', 'FARMSLOT_SLOT_ID']) {
|
|
26
|
-
const value = sanitizeSlotId(process.env[key]);
|
|
27
|
-
if (value) return value;
|
|
28
|
-
}
|
|
29
|
-
if (!target || !runtimeDir) return '';
|
|
30
|
-
try {
|
|
31
|
-
const runtimeContext = JSON.parse(
|
|
32
|
-
fs.readFileSync(path.join(target, runtimeDir, 'agentic-runtime.json'), 'utf8'),
|
|
33
|
-
);
|
|
34
|
-
return sanitizeSlotId(runtimeContext.slotId);
|
|
35
|
-
} catch {
|
|
36
|
-
// Standalone harness users do not need a custom browser title.
|
|
37
|
-
return '';
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Serialized page-evaluation callback. Must stay a plain function (no closure).
|
|
43
|
-
*/
|
|
44
|
-
function applyPersistentSlotTitle(slot) {
|
|
45
|
-
// Inline sanitize because the serialized function cannot call Node helpers.
|
|
46
|
-
const id =
|
|
47
|
-
typeof slot === 'string' && /^[A-Za-z0-9._:-]{1,64}$/u.test(slot.trim())
|
|
48
|
-
? slot.trim()
|
|
49
|
-
: '';
|
|
50
|
-
if (!id) return document.title;
|
|
51
|
-
window.__farmslotSlotId = id;
|
|
52
|
-
|
|
53
|
-
const stripSlotPrefixes = (title) => {
|
|
54
|
-
let base = String(title || 'MetaMask');
|
|
55
|
-
// Accept em dash, en dash, or hyphen — historical stampers mixed them.
|
|
56
|
-
for (let i = 0; i < 32; i += 1) {
|
|
57
|
-
const stripped = base.replace(/^.+?\s+[—–-]\s+/u, '');
|
|
58
|
-
if (stripped === base) break;
|
|
59
|
-
base = stripped;
|
|
60
|
-
}
|
|
61
|
-
return base || 'MetaMask';
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const desiredTitle = () => {
|
|
65
|
-
const id = window.__farmslotSlotId;
|
|
66
|
-
if (!id) return document.title;
|
|
67
|
-
return `${id} — ${stripSlotPrefixes(document.title)}`;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const setTitle = () => {
|
|
71
|
-
if (window.__farmslotSlotTitleLock) {
|
|
72
|
-
window.__farmslotSlotTitlePending = true;
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const next = desiredTitle();
|
|
76
|
-
if (document.title === next) return;
|
|
77
|
-
window.__farmslotSlotTitleLock = true;
|
|
78
|
-
try {
|
|
79
|
-
document.title = next;
|
|
80
|
-
} finally {
|
|
81
|
-
// Release after the title mutation is delivered so the observer cannot
|
|
82
|
-
// re-enter and stack prefixes while React also mutates the DOM. If a
|
|
83
|
-
// mutation arrived while locked, re-apply once after unlock.
|
|
84
|
-
queueMicrotask(() => {
|
|
85
|
-
window.__farmslotSlotTitleLock = false;
|
|
86
|
-
if (window.__farmslotSlotTitlePending) {
|
|
87
|
-
window.__farmslotSlotTitlePending = false;
|
|
88
|
-
setTitle();
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
setTitle();
|
|
95
|
-
if (!window.__farmslotSlotTitleObserver) {
|
|
96
|
-
let node = document.querySelector('title');
|
|
97
|
-
if (!node) {
|
|
98
|
-
node = document.createElement('title');
|
|
99
|
-
(document.head || document.documentElement).appendChild(node);
|
|
100
|
-
}
|
|
101
|
-
window.__farmslotSlotTitleObserver = new MutationObserver(setTitle);
|
|
102
|
-
// Observe only the <title> element — watching head/documentElement with
|
|
103
|
-
// subtree:true re-fires on every React DOM mutation and can livelock CDP.
|
|
104
|
-
window.__farmslotSlotTitleObserver.observe(node, {
|
|
105
|
-
childList: true,
|
|
106
|
-
characterData: true,
|
|
107
|
-
subtree: true,
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
return document.title;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function buildStampExpression(slotId) {
|
|
114
|
-
const slot = sanitizeSlotId(slotId);
|
|
115
|
-
if (!slot) return 'document.title';
|
|
116
|
-
// Single source of truth: serialize the Playwright callback for CDP evaluate.
|
|
117
|
-
return `(${applyPersistentSlotTitle.toString()})(${JSON.stringify(slot)})`;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function assertLocalUrl(url) {
|
|
121
|
-
if (!/^http:\/\/127\.0\.0\.1:\d+\//u.test(url)) {
|
|
122
|
-
throw new Error(`slot-title: refusing non-local URL: ${url}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function assertLocalWebSocketUrl(url) {
|
|
127
|
-
if (!/^ws:\/\/(?:127\.0\.0\.1|localhost):\d+\//u.test(String(url))) {
|
|
128
|
-
throw new Error(`slot-title: refusing non-local websocket URL: ${url}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function httpJson(url, timeoutMs = 3000) {
|
|
133
|
-
assertLocalUrl(url);
|
|
134
|
-
return new Promise((resolve, reject) => {
|
|
135
|
-
const req = http.get(url, (res) => {
|
|
136
|
-
let data = '';
|
|
137
|
-
res.setEncoding('utf8');
|
|
138
|
-
res.on('data', (chunk) => {
|
|
139
|
-
data += chunk;
|
|
140
|
-
});
|
|
141
|
-
res.on('end', () => {
|
|
142
|
-
try {
|
|
143
|
-
resolve(JSON.parse(data));
|
|
144
|
-
} catch (err) {
|
|
145
|
-
reject(new Error(`invalid JSON from ${url}: ${err.message}`));
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
req.setTimeout(timeoutMs, () => {
|
|
150
|
-
req.destroy(new Error(`timeout from ${url}`));
|
|
151
|
-
});
|
|
152
|
-
req.on('error', reject);
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function resolveWebSocket(target) {
|
|
157
|
-
try {
|
|
158
|
-
// Prefer the target checkout's ws, then the harness install.
|
|
159
|
-
const { createRequire } = require('node:module');
|
|
160
|
-
const req = createRequire(__filename);
|
|
161
|
-
if (target) {
|
|
162
|
-
try {
|
|
163
|
-
return req(req.resolve('ws', { paths: [target] }));
|
|
164
|
-
} catch {
|
|
165
|
-
// Fall through to harness / global.
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return req('ws');
|
|
169
|
-
} catch {
|
|
170
|
-
return typeof WebSocket === 'function' ? WebSocket : null;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function cdpEvaluate(target, webSocketDebuggerUrl, expression, timeoutMs = 5000) {
|
|
175
|
-
assertLocalWebSocketUrl(webSocketDebuggerUrl);
|
|
176
|
-
const WebSocketImpl = resolveWebSocket(target);
|
|
177
|
-
if (!WebSocketImpl) return { skipped: true, reason: 'WebSocket unavailable' };
|
|
178
|
-
return new Promise((resolve, reject) => {
|
|
179
|
-
const ws = new WebSocketImpl(webSocketDebuggerUrl);
|
|
180
|
-
const timer = setTimeout(() => {
|
|
181
|
-
try {
|
|
182
|
-
ws.close();
|
|
183
|
-
} catch {
|
|
184
|
-
// Best-effort timeout cleanup.
|
|
185
|
-
}
|
|
186
|
-
reject(new Error('timeout evaluating extension page via CDP'));
|
|
187
|
-
}, timeoutMs);
|
|
188
|
-
const onOpen = () => {
|
|
189
|
-
ws.send(
|
|
190
|
-
JSON.stringify({
|
|
191
|
-
id: 1,
|
|
192
|
-
method: 'Runtime.evaluate',
|
|
193
|
-
params: { expression, awaitPromise: true, returnByValue: true },
|
|
194
|
-
}),
|
|
195
|
-
);
|
|
196
|
-
};
|
|
197
|
-
const onMessage = (event) => {
|
|
198
|
-
const raw = event?.data ?? event;
|
|
199
|
-
const msg = JSON.parse(Buffer.isBuffer(raw) ? raw.toString('utf8') : String(raw));
|
|
200
|
-
if (msg.id !== 1) return;
|
|
201
|
-
clearTimeout(timer);
|
|
202
|
-
ws.close();
|
|
203
|
-
if (msg.error) {
|
|
204
|
-
reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
resolve(msg.result?.result?.value ?? null);
|
|
208
|
-
};
|
|
209
|
-
const onError = (err) => {
|
|
210
|
-
clearTimeout(timer);
|
|
211
|
-
reject(new Error(`CDP websocket error while stamping slot title: ${err?.message || err || 'unknown'}`));
|
|
212
|
-
};
|
|
213
|
-
if (typeof ws.on === 'function') {
|
|
214
|
-
ws.on('open', onOpen);
|
|
215
|
-
ws.on('message', onMessage);
|
|
216
|
-
ws.on('error', onError);
|
|
217
|
-
} else {
|
|
218
|
-
ws.addEventListener('open', onOpen);
|
|
219
|
-
ws.addEventListener('message', onMessage);
|
|
220
|
-
ws.addEventListener('error', onError);
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function isHomePage(target, extensionId) {
|
|
226
|
-
if (!target || target.type !== 'page' || typeof target.url !== 'string') return false;
|
|
227
|
-
if (!target.url.startsWith(`chrome-extension://${extensionId}/`)) return false;
|
|
228
|
-
return target.url.includes('/home.html');
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Stamp every inspectable home.html tab for this extension over CDP.
|
|
233
|
-
* Attached tabs (no webSocketDebuggerUrl) are skipped — another client owns them.
|
|
234
|
-
*/
|
|
235
|
-
async function stampHomeTabsViaCdp(options) {
|
|
236
|
-
const {
|
|
237
|
-
target = process.cwd(),
|
|
238
|
-
cdpPort,
|
|
239
|
-
extensionId,
|
|
240
|
-
slotId: slotIdOption,
|
|
241
|
-
runtimeDir,
|
|
242
|
-
} = options || {};
|
|
243
|
-
const slotId = sanitizeSlotId(slotIdOption) || readSlotId(target, runtimeDir);
|
|
244
|
-
const empty = { slotId: slotId || '', stamped: 0, skipped: 0, titles: [] };
|
|
245
|
-
if (!slotId || !cdpPort || !extensionId) return empty;
|
|
246
|
-
|
|
247
|
-
let targets;
|
|
248
|
-
try {
|
|
249
|
-
targets = await httpJson(`http://127.0.0.1:${cdpPort}/json/list`);
|
|
250
|
-
} catch {
|
|
251
|
-
return empty;
|
|
252
|
-
}
|
|
253
|
-
if (!Array.isArray(targets)) return empty;
|
|
254
|
-
|
|
255
|
-
const homes = targets.filter((t) => isHomePage(t, extensionId));
|
|
256
|
-
const titles = [];
|
|
257
|
-
let stamped = 0;
|
|
258
|
-
let skipped = 0;
|
|
259
|
-
const expression = buildStampExpression(slotId);
|
|
260
|
-
|
|
261
|
-
for (const home of homes) {
|
|
262
|
-
if (typeof home.webSocketDebuggerUrl !== 'string') {
|
|
263
|
-
skipped += 1;
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
try {
|
|
267
|
-
const title = await cdpEvaluate(target, home.webSocketDebuggerUrl, expression);
|
|
268
|
-
if (title && typeof title === 'object' && title.skipped) {
|
|
269
|
-
skipped += 1;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
if (typeof title === 'string') {
|
|
273
|
-
titles.push(title);
|
|
274
|
-
stamped += 1;
|
|
275
|
-
} else {
|
|
276
|
-
skipped += 1;
|
|
277
|
-
}
|
|
278
|
-
} catch {
|
|
279
|
-
skipped += 1;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return { slotId, stamped, skipped, titles };
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
module.exports = {
|
|
287
|
-
sanitizeSlotId,
|
|
288
|
-
readSlotId,
|
|
289
|
-
applyPersistentSlotTitle,
|
|
290
|
-
buildStampExpression,
|
|
291
|
-
stampHomeTabsViaCdp,
|
|
292
|
-
};
|