@deeeed/metamask-harness 0.94.3 → 0.94.5

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 CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.94.5 - 2026-10-28
6
+
7
+ ### Fixed
8
+
9
+ - Extension readiness uses the registered EVM background action and preserves its result when the optional Perps read times out, keeping genuine RPC failures fatal.
10
+
11
+ ## 0.94.4 - 2026-10-27
12
+
13
+ ### Changed
14
+
15
+ - Shorten Extension testnet action descriptions while preserving mainnet guards.
16
+
5
17
  ## 0.94.3 - 2026-10-25
6
18
 
7
19
  ### Fixed
@@ -328,7 +328,7 @@ async function boundedCdpCall(session, method, params, timeoutMs) {
328
328
  async function evaluateHealth(session, timeoutMs) {
329
329
  const backgroundProbeTimeoutMs = extensionBackgroundProbeTimeoutMs(timeoutMs);
330
330
  const result = await boundedCdpCall(session, "Runtime.evaluate", {
331
- expression: `(() => {
331
+ expression: `(async () => {
332
332
  const hooks = globalThis.stateHooks || {};
333
333
  const bodyText = document.body?.innerText || '';
334
334
  const storeState = hooks.store?.getState?.() || {};
@@ -336,78 +336,80 @@ async function evaluateHealth(session, timeoutMs) {
336
336
  const accountCache = manager?.account?.cache;
337
337
  const networkConnectionBanner = storeState.metamask?.networkConnectionBanner;
338
338
  const networkClientId = storeState.metamask?.selectedNetworkClientId || networkConnectionBanner?.networkClientId;
339
- return Promise.race([
340
- (async () => {
341
- const [backgroundProbe, evmRpcProbe] = await Promise.all([
342
- (async () => {
343
- if (typeof hooks.submitRequestToBackground !== 'function') {
344
- return { ok: false, error: 'submitRequestToBackground is not a function' };
345
- }
346
- try {
347
- const accountState = await hooks.submitRequestToBackground('perpsGetAccountState', []);
348
- const ok = Boolean(accountState && typeof accountState === 'object');
349
- return { ok, error: ok ? null : 'perpsGetAccountState returned an empty result' };
350
- } catch (error) {
351
- return { ok: false, error: String(error?.message || error) };
352
- }
353
- })(),
354
- (async () => {
355
- if (typeof hooks.submitRequestToBackground !== 'function') {
356
- return { ok: false, error: 'submitRequestToBackground is not a function' };
357
- }
358
- if (typeof networkClientId !== 'string' || networkClientId.length === 0) {
359
- return { ok: false, error: 'selected EVM network client is unavailable' };
360
- }
361
- try {
362
- const code = await hooks.submitRequestToBackground('getCode', [
363
- '0x0000000000000000000000000000000000000000',
364
- networkClientId,
365
- ]);
366
- const ok = typeof code === 'string' && code.startsWith('0x');
367
- return { ok, error: ok ? null : 'background EVM RPC returned an invalid result' };
368
- } catch (error) {
369
- return { ok: false, error: String(error?.message || error) };
370
- }
371
- })(),
339
+ async function withDeadline(read, error) {
340
+ let timer;
341
+ try {
342
+ return await Promise.race([
343
+ read,
344
+ new Promise((resolve) => {
345
+ timer = setTimeout(() => resolve({ ok: false, error }), ${backgroundProbeTimeoutMs});
346
+ }),
372
347
  ]);
373
- return {
374
- href: location.href,
375
- title: document.title,
376
- hasPageContent: bodyText.trim().length > 0,
377
- hookKeys: Object.keys(hooks),
378
- hasSubmitRequest: typeof hooks.submitRequestToBackground === 'function',
379
- hasStore: Boolean(hooks.store),
380
- hasPerpsStreamManager: typeof hooks.getPerpsStreamManager === 'function',
381
- backgroundUnresponsive: bodyText.includes('Background connection unresponsive') || bodyText.includes('MetaMask had trouble starting'),
382
- ethereumConnectionUnavailable: bodyText.includes('Unable to connect to Ethereum'),
383
- activeProvider: storeState.metamask?.activeProvider || null,
384
- isTestnet: Boolean(storeState.metamask?.isTestnet),
385
- perpsManagerInitialized: Boolean(manager?.isInitialized?.()),
386
- positionsCacheIsArray: Array.isArray(manager?.positions?.cache),
387
- ordersCacheIsArray: Array.isArray(manager?.orders?.cache),
388
- accountCachePresent: Boolean(accountCache && typeof accountCache === 'object'),
389
- backgroundProbeOk: backgroundProbe.ok,
390
- backgroundProbeError: backgroundProbe.error,
391
- evmRpcProbeOk: evmRpcProbe.ok,
392
- evmRpcProbeError: evmRpcProbe.error,
393
- };
394
- })(),
395
- new Promise((resolve) => setTimeout(() => resolve({
396
- href: location.href,
397
- title: document.title,
398
- hasPageContent: bodyText.trim().length > 0,
399
- hookKeys: Object.keys(hooks),
400
- hasSubmitRequest: typeof hooks.submitRequestToBackground === 'function',
401
- hasStore: Boolean(hooks.store),
402
- hasPerpsStreamManager: typeof hooks.getPerpsStreamManager === 'function',
403
- backgroundUnresponsive: bodyText.includes('Background connection unresponsive') || bodyText.includes('MetaMask had trouble starting'),
404
- ethereumConnectionUnavailable: bodyText.includes('Unable to connect to Ethereum'),
405
- backgroundProbeOk: false,
406
- backgroundProbeError: 'perpsGetAccountState timed out after ${backgroundProbeTimeoutMs}ms',
407
- evmRpcProbeOk: false,
408
- evmRpcProbeError: 'background EVM RPC timed out after ${backgroundProbeTimeoutMs}ms',
409
- }), ${backgroundProbeTimeoutMs})),
348
+ } finally {
349
+ clearTimeout(timer);
350
+ }
351
+ }
352
+ const [backgroundProbe, evmRpcProbe] = await Promise.all([
353
+ withDeadline((async () => {
354
+ if (typeof hooks.submitRequestToBackground !== 'function') {
355
+ return { ok: false, error: 'submitRequestToBackground is not a function' };
356
+ }
357
+ try {
358
+ const accountState = await hooks.submitRequestToBackground('perpsGetAccountState', []);
359
+ const ok = Boolean(accountState && typeof accountState === 'object');
360
+ return { ok, error: ok ? null : 'perpsGetAccountState returned an empty result' };
361
+ } catch (error) {
362
+ return { ok: false, error: String(error?.message || error) };
363
+ }
364
+ })(), 'perpsGetAccountState timed out after ${backgroundProbeTimeoutMs}ms'),
365
+ withDeadline((async () => {
366
+ if (typeof hooks.submitRequestToBackground !== 'function') {
367
+ return { ok: false, error: 'submitRequestToBackground is not a function' };
368
+ }
369
+ if (typeof networkClientId !== 'string' || networkClientId.length === 0) {
370
+ return { ok: false, error: 'selected EVM network client is unavailable' };
371
+ }
372
+ try {
373
+ const args = ['0x0000000000000000000000000000000000000000', networkClientId];
374
+ let code;
375
+ try {
376
+ code = await hooks.submitRequestToBackground('messengerCall', ['LegacyBackgroundApiService:getCode', args]);
377
+ } catch (error) {
378
+ // Older builds may lack the messenger API or its EVM action.
379
+ const message = String(error?.message || error);
380
+ const missingApi = error?.code === -32601 && message === 'messengerCall not found';
381
+ const missingAction = message.includes('A handler for LegacyBackgroundApiService:getCode has not been registered');
382
+ if (!missingApi && !missingAction) throw error;
383
+ code = await hooks.submitRequestToBackground('getCode', args);
384
+ }
385
+ const ok = typeof code === 'string' && code.startsWith('0x');
386
+ return { ok, error: ok ? null : 'background EVM RPC returned an invalid result' };
387
+ } catch (error) {
388
+ return { ok: false, error: String(error?.message || error) };
389
+ }
390
+ })(), 'background EVM RPC timed out after ${backgroundProbeTimeoutMs}ms'),
410
391
  ]);
392
+ return {
393
+ href: location.href,
394
+ title: document.title,
395
+ hasPageContent: bodyText.trim().length > 0,
396
+ hookKeys: Object.keys(hooks),
397
+ hasSubmitRequest: typeof hooks.submitRequestToBackground === 'function',
398
+ hasStore: Boolean(hooks.store),
399
+ hasPerpsStreamManager: typeof hooks.getPerpsStreamManager === 'function',
400
+ backgroundUnresponsive: bodyText.includes('Background connection unresponsive') || bodyText.includes('MetaMask had trouble starting'),
401
+ ethereumConnectionUnavailable: bodyText.includes('Unable to connect to Ethereum'),
402
+ activeProvider: storeState.metamask?.activeProvider || null,
403
+ isTestnet: Boolean(storeState.metamask?.isTestnet),
404
+ perpsManagerInitialized: Boolean(manager?.isInitialized?.()),
405
+ positionsCacheIsArray: Array.isArray(manager?.positions?.cache),
406
+ ordersCacheIsArray: Array.isArray(manager?.orders?.cache),
407
+ accountCachePresent: Boolean(accountCache && typeof accountCache === 'object'),
408
+ backgroundProbeOk: backgroundProbe.ok,
409
+ backgroundProbeError: backgroundProbe.error,
410
+ evmRpcProbeOk: evmRpcProbe.ok,
411
+ evmRpcProbeError: evmRpcProbe.error,
412
+ };
411
413
  })()`,
412
414
  awaitPromise: true,
413
415
  returnByValue: true
@@ -4138,7 +4138,7 @@
4138
4138
  "execution_capabilities": []
4139
4139
  },
4140
4140
  "metamask.perps.close_orders": {
4141
- "description": "extension Cancel selected live Perps open orders. Use selector/mode params to cancel matching symbols or all orders. On mainnet, the fixture-policy lane cancels only orders this run placed; any other cancel needs the funded release lane. Extension ensure_* only assert existing state; converge an owned testnet account through an explicitly funded leaf action.",
4141
+ "description": "extension Cancel selected live Perps open orders. Use selector/mode params to cancel matching symbols or all orders. On mainnet, the fixture-policy lane cancels only orders this run placed.",
4142
4142
  "schema": {
4143
4143
  "type": "object",
4144
4144
  "properties": {
@@ -4719,7 +4719,7 @@
4719
4719
  "execution_capabilities": []
4720
4720
  },
4721
4721
  "metamask.perps.ensure_positions": {
4722
- "description": "Read and assert the selected Extension Perps position state without creating or closing a position. Use an explicitly funded leaf action for any transition. Extension ensure_* only assert existing state; converge an owned testnet account through an explicitly funded leaf action. Mainnet is never mutated.",
4722
+ "description": "Read and assert the selected Extension Perps position state without creating or closing a position. Mainnet is never mutated.",
4723
4723
  "schema": {
4724
4724
  "type": "object",
4725
4725
  "properties": {
@@ -4848,7 +4848,7 @@
4848
4848
  ]
4849
4849
  },
4850
4850
  "metamask.perps.ensure_orders": {
4851
- "description": "Read and assert the selected Extension Perps order state without creating or canceling an order. Use an explicitly funded leaf action for any transition. Extension ensure_* only assert existing state; converge an owned testnet account through an explicitly funded leaf action. Mainnet is never mutated.",
4851
+ "description": "Read and assert the selected Extension Perps order state without creating or canceling an order. Mainnet is never mutated.",
4852
4852
  "schema": {
4853
4853
  "type": "object",
4854
4854
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.94.3",
3
+ "version": "0.94.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"