@deeeed/metamask-harness 0.41.1 → 0.43.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +12 -0
  3. package/adapters/manifest.json +8 -0
  4. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +108 -10
  5. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +117 -16
  6. package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +2 -0
  7. package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +921 -0
  8. package/adapters/mobile/bridge-runtime/lib/config.cjs +14 -4
  9. package/adapters/mobile/bridge-runtime/lib/devtools-proxy.cjs +177 -0
  10. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +13 -3
  11. package/adapters/mobile/reload-app.mjs +67 -0
  12. package/adapters/mobile/start-console-forwarder.sh +22 -3
  13. package/adapters/mobile/start-metro.sh +6 -11
  14. package/adapters/mobile/stop-metro.sh +10 -1
  15. package/adapters/shared/open-debug.mjs +172 -2
  16. package/dist/adapters/extension/browser-cdp.js +174 -0
  17. package/dist/adapters/extension/network-observer.js +209 -0
  18. package/dist/adapters/extension/performance-observer.js +75 -0
  19. package/dist/adapters/mobile/frame-metrics.js +45 -0
  20. package/dist/adapters/mobile/metro-env.js +0 -5
  21. package/dist/adapters/mobile/performance-observer.js +43 -0
  22. package/dist/adapters/mobile/prepare.js +1 -3
  23. package/dist/adapters/mobile/runtime-decision.js +6 -9
  24. package/dist/adapters/performance/cdp-trace.js +342 -0
  25. package/dist/adapters/performance/js-task-metrics.js +35 -0
  26. package/dist/adapters.js +29 -1
  27. package/dist/artifact-files.js +92 -0
  28. package/dist/async.js +19 -0
  29. package/dist/cli-commands.js +6 -3
  30. package/dist/cli.js +4 -0
  31. package/dist/command-contract.js +3 -0
  32. package/dist/commands/call.js +66 -20
  33. package/dist/commands/reload.js +80 -0
  34. package/dist/commands/run-engine.js +18 -0
  35. package/dist/commands/run.js +68 -22
  36. package/dist/mm-harness-cli.js +17 -1
  37. package/dist/network-observation.js +283 -0
  38. package/dist/performance-observation.js +465 -0
  39. package/docs/NETWORK-CAPTURE.md +98 -0
  40. package/docs/PERFORMANCE-CAPTURE.md +33 -0
  41. package/docs/RECIPES.md +17 -0
  42. package/library/actions/mobile/app/network_assert.mjs +14 -0
  43. package/library/actions/mobile/app/network_capture.mjs +72 -0
  44. package/library/actions/mobile/platform/bridge.mjs +10 -2
  45. package/library/actions/shared/app/network-artifact.mjs +10 -0
  46. package/library/actions/shared/app/network-assert.mjs +154 -0
  47. package/library/manifests/extension.action-manifest.json +173 -0
  48. package/library/manifests/mobile.action-manifest.json +204 -0
  49. package/library/recipes/mobile/perps/performance.recipe.json +11 -11
  50. package/package.json +1 -1
  51. package/scripts/completions.sh +2 -1
  52. package/scripts/site-contrast.mjs +43 -27
package/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.43.0 - 2026-08-21
6
+
7
+ ### Added
8
+
9
+ - Add platform-neutral `app.performance_capture` and `app.performance_assert` actions that attribute Mobile or Extension CDP trace samples to exact recipe-node boundaries and emit JSON plus self-contained HTML evidence.
10
+ - Mobile native UI FPS requires a development client that reports `unstable_frameRecordingEnabled: true`; unpatched clients report partial or unavailable native coverage. Extension uses Chromium CDP and needs no client patch.
11
+
12
+ ### Fixed
13
+
14
+ - Bound performance capture duration, discard raw samples after node attribution, correct iOS frame completion timestamps, fail closed on unresolved Extension renderers, and reset CDP collector state after trace failures.
15
+ - Scope Mobile Hermes broker ownership by checkout runtime and Metro port so a forwarder from a prior port cannot intercept bridge, HUD, or observation commands after a slot port change.
16
+ - Preserve explicit Mobile `--device` pins when slot defaults load so HUD and live-adapter child processes cannot target another device.
17
+ - Resume broker commands on the next Hermes session generation without losing the pinned logical device.
18
+ - Keep Hermes session recovery inside the caller's RPC deadline, pass only the remaining time to the recovered command, and classify broker connection or request timeouts so transient HUD updates do not abort a recipe.
19
+ - Accept React Native trace response chunks up to 16 MiB while keeping broker requests capped at 8 MiB, and never retry the non-idempotent `Tracing.end` command.
20
+ - Limit Mobile UI captures to clock and native-frame categories; keep browser timeline and JavaScript task categories on Extension.
21
+
22
+ ## 0.42.0 - 2026-08-20
23
+
24
+ ### Added
25
+
26
+ - Add `app.network_capture` and `app.network_assert` for redacted Mobile HTTP request windows, indexed evidence, and assertions that preserve failed-run diagnostics.
27
+ - Support the same bounded network observation contract on Extension and automatically index safe request metadata with recipe-node boundaries for live Mobile and Extension runs.
28
+ - Add `mm-harness reload` for Metro-equivalent Mobile reloads and in-place Extension CDP refreshes.
29
+
30
+ ### Changed
31
+
32
+ - Route Mobile console forwarding and recipe bridge commands through the same debugger connection so Network capture can coexist with HUD and wallet/domain actions.
33
+ - Make the canonical Perps performance recipe close positions and orders outside timing for its market-only cohort instead of failing on inherited account state.
34
+
35
+ ### Fixed
36
+
37
+ - Pass Expo `--clear` only for an explicit `mm-harness launch --clear-metro`, bound default Metro concurrency to four workers, and remove a stopped checkout from Watchman's root set.
38
+ - Route React Native DevTools through the harness CDP broker so debugger use, recipe actions, and app reloads no longer evict one another.
39
+
5
40
  ## 0.41.1 - 2026-08-19
6
41
 
7
42
  ### Fixed
package/README.md CHANGED
@@ -87,6 +87,7 @@ mm-harness status
87
87
  mm-harness stop
88
88
  mm-harness logs
89
89
  mm-harness debug
90
+ mm-harness reload # Mobile Metro reload / Extension CDP refresh
90
91
  mm-harness fixtures set
91
92
 
92
93
  # Delete the current wallet, then reapply the canonical fixture from clean state.
@@ -109,6 +110,17 @@ mm-harness logs --source app # Mobile app
109
110
  mm-harness logs --source metro # Mobile bundler
110
111
  ```
111
112
 
113
+ Live Mobile and Extension recipes automatically index a bounded, redacted
114
+ `network/run-summary.json` with request timing and recipe-node boundaries. Use
115
+ explicit `app.network_capture` and `app.network_assert` nodes for filtered
116
+ windows and machine-checked request expectations. See
117
+ [Recipe-scoped network capture](docs/NETWORK-CAPTURE.md).
118
+
119
+ Mobile and Extension recipes can bracket explicit UI-smoothness windows with
120
+ `app.performance_capture` and verify their evidence with
121
+ `app.performance_assert`. Capture is never automatic. See
122
+ [UI smoothness capture](docs/PERFORMANCE-CAPTURE.md).
123
+
112
124
  ## Discover and prove
113
125
 
114
126
  ```bash
@@ -74,6 +74,14 @@
74
74
  "inputs": "--target --port; env WATCHER_PORT",
75
75
  "outputs": "progress on stderr; exit 0 stopped-or-nothing / 1 stop failed / 2 bad args"
76
76
  },
77
+ {
78
+ "id": "mobile/reload-app",
79
+ "entry": "adapters/mobile/reload-app.mjs",
80
+ "kind": "node",
81
+ "purpose": "Request an in-place React Native app reload through Metro's protocol-v2 message endpoint.",
82
+ "inputs": "--port --json; env WATCHER_PORT, METRO_PORT",
83
+ "outputs": "reload result on stdout; exit 0 requested / 1 unavailable or invalid input"
84
+ },
77
85
  {
78
86
  "id": "mobile/prewarm-bundle",
79
87
  "entry": "adapters/mobile/prewarm-bundle.sh",
@@ -19,8 +19,15 @@
19
19
  const fs = require('node:fs');
20
20
  const path = require('node:path');
21
21
  const { loadPort } = require('./lib/config.cjs');
22
- const { discoverTarget } = require('./lib/target-discovery.cjs');
22
+ const {
23
+ discoverTarget,
24
+ } = require('./lib/target-discovery.cjs');
23
25
  const { createWSClient } = require('./lib/ws-client.cjs');
26
+ const {
27
+ brokerSocketPath,
28
+ createBrokerClient,
29
+ deviceIdFromUrl,
30
+ } = require('./lib/cdp-broker.cjs');
24
31
  const {
25
32
  EXIT_CODE_BY_ERROR_CODE,
26
33
  TEACHING_BY_ERROR_CODE,
@@ -924,6 +931,22 @@ const COMMANDS = {
924
931
  return result || { count: 0, entries: [] };
925
932
  },
926
933
 
934
+ async 'network-capture-start'(client, args) {
935
+ if (typeof client.control !== 'function') {
936
+ throw new Error('Network capture requires the CDP broker');
937
+ }
938
+ const options = JSON.parse(args[0] || '{}');
939
+ return client.control('capture-start', options);
940
+ },
941
+
942
+ async 'network-capture-end'(client, args) {
943
+ if (typeof client.control !== 'function') {
944
+ throw new Error('Network capture requires the CDP broker');
945
+ }
946
+ const options = JSON.parse(args[0] || '{}');
947
+ return client.control('capture-end', options);
948
+ },
949
+
927
950
  };
928
951
 
929
952
  // ---------------------------------------------------------------------------
@@ -1019,6 +1042,8 @@ Commands:
1019
1042
  issues-arm Install console/exception hooks that
1020
1043
  populate globalThis.__AGENTIC_ISSUES__
1021
1044
  issues-collect Snapshot + clear the in-app issue buffer
1045
+ network-capture-start <json> Start a broker-owned Network capture
1046
+ network-capture-end <json> End capture and return redacted summary
1022
1047
 
1023
1048
  Environment:
1024
1049
  WATCHER_PORT Metro port (default: 8081)
@@ -1035,24 +1060,94 @@ Environment:
1035
1060
  process.exit(1);
1036
1061
  }
1037
1062
 
1038
- // Only a real command takes the debugger slot; help/unknown paths above never
1039
- // suppress the console forwarder.
1040
- acquireBridgeLock();
1041
-
1042
1063
  const port = loadPort();
1064
+ const brokerSocketFile = brokerSocketPath(
1065
+ path.dirname(BRIDGE_LOCK_FILE),
1066
+ port,
1067
+ );
1043
1068
  const timeout = Number.parseInt(process.env.CDP_TIMEOUT || '5000', 10);
1069
+ const brokerAvailable = fs.existsSync(brokerSocketFile);
1070
+ if (!brokerAvailable) acquireBridgeLock();
1071
+
1072
+ function selectedTargetPin() {
1073
+ const platform = String(process.env.MM_HARNESS_EXPLICIT_PLATFORM || '').toLowerCase();
1074
+ const androidPin =
1075
+ process.env.ANDROID_TARGET_DEVICE_NAME || process.env.ANDROID_DEVICE || '';
1076
+ const iosPin = process.env.IOS_SIMULATOR || '';
1077
+ return String(
1078
+ platform === 'android'
1079
+ ? androidPin
1080
+ : platform === 'ios'
1081
+ ? iosPin
1082
+ : androidPin || iosPin,
1083
+ ).trim();
1084
+ }
1085
+
1086
+ async function brokerTargets({ retainIdentity = false } = {}) {
1087
+ const discoveryClient = await createBrokerClient(
1088
+ brokerSocketFile,
1089
+ '',
1090
+ timeout,
1091
+ );
1092
+ try {
1093
+ const targets = await discoveryClient.control(
1094
+ retainIdentity ? 'resolve-targets' : 'list-targets',
1095
+ retainIdentity ? { nameIncludes: selectedTargetPin() } : {},
1096
+ timeout,
1097
+ );
1098
+ return (Array.isArray(targets) ? targets : []).flatMap((target) => {
1099
+ const deviceId = String(target?.deviceId || '');
1100
+ return deviceId
1101
+ ? [{ wsUrl: deviceId, deviceName: String(target?.name || '') }]
1102
+ : [];
1103
+ });
1104
+ } finally {
1105
+ discoveryClient.close();
1106
+ }
1107
+ }
1108
+
1109
+ function selectBrokerTarget(targets) {
1110
+ const pin = selectedTargetPin();
1111
+ const candidates = pin
1112
+ ? targets.filter((target) =>
1113
+ target.deviceName.toLowerCase().includes(pin.toLowerCase()),
1114
+ )
1115
+ : targets;
1116
+ if (candidates.length !== 1) {
1117
+ throw new Error(
1118
+ `Mobile CDP broker target selection requires one target; found ${candidates.length}${pin ? ` for ${JSON.stringify(pin)}` : ''}.`,
1119
+ );
1120
+ }
1121
+ return candidates[0];
1122
+ }
1123
+
1124
+ async function clientFor(wsUrl) {
1125
+ if (brokerAvailable) {
1126
+ return createBrokerClient(
1127
+ brokerSocketFile,
1128
+ deviceIdFromUrl(wsUrl),
1129
+ timeout,
1130
+ );
1131
+ }
1132
+ if (command.startsWith('network-capture-')) {
1133
+ throw new Error('Network capture requires a running CDP broker');
1134
+ }
1135
+ acquireBridgeLock();
1136
+ return createWSClient(wsUrl, timeout);
1137
+ }
1044
1138
 
1045
1139
  // `status` probes ALL connected targets so both platforms are visible.
1046
1140
  // `status-selected` falls through to the normal pinned discovery path for
1047
1141
  // action code that must survive one device's Hermes runtime rotation.
1048
1142
  if (command === 'status') {
1049
- const { discoverAllTargets } = require('./lib/target-discovery.cjs');
1050
- const allTargets = await discoverAllTargets(port);
1143
+ const allTargets = brokerAvailable
1144
+ ? await brokerTargets()
1145
+ : await require('./lib/target-discovery.cjs').discoverAllTargets(port);
1051
1146
  const results = [];
1052
1147
  for (const target of allTargets) {
1053
1148
  let client;
1054
1149
  try {
1055
- client = await createWSClient(target.wsUrl, timeout);
1150
+ client = await clientFor(target.wsUrl);
1056
1151
  const platform = await cdpEval(client, 'globalThis.__AGENTIC__?.platform') || '';
1057
1152
  const result = await handler(client, args.slice(1), { deviceName: target.deviceName, platform });
1058
1153
  results.push(result);
@@ -1068,8 +1163,11 @@ Environment:
1068
1163
  return;
1069
1164
  }
1070
1165
 
1071
- const { wsUrl, deviceName } = await discoverTarget(port);
1072
- const client = await createWSClient(wsUrl, timeout);
1166
+ const { wsUrl, deviceName } = brokerAvailable
1167
+ ? selectBrokerTarget(await brokerTargets({ retainIdentity: true }))
1168
+ : await discoverTarget(port, { probe: true });
1169
+ if (!wsUrl) throw new Error('No broker-owned Hermes target is available');
1170
+ const client = await clientFor(wsUrl);
1073
1171
 
1074
1172
  try {
1075
1173
  // Detect platform from the running app (exposed by __AGENTIC__ bridge as Platform.OS)
@@ -29,9 +29,16 @@ const http = require('node:http');
29
29
  const path = require('node:path');
30
30
  const { rankRuntimeCandidates } = require('./lib/target-discovery.cjs');
31
31
  const { formatArgs: formatConsoleArgs } = require('./lib/console-format.cjs');
32
+ const {
33
+ brokerSocketPath,
34
+ createCdpBroker,
35
+ deviceIdFromUrl,
36
+ } = require('./lib/cdp-broker.cjs');
37
+ const { createDevtoolsProxy } = require('./lib/devtools-proxy.cjs');
38
+ const { resolvePort } = require('./lib/config.cjs');
32
39
 
33
- // Built-in WebSocket (Node 22+), same zero-dependency transport choice as
34
- // lib/ws-client.cjs — the published package must not depend on `ws`.
40
+ // Hermes uses Node's built-in WebSocket client. The local DevTools proxy uses
41
+ // the package's pinned `ws` server implementation.
35
42
  const HANDSHAKE_TIMEOUT_MS = 3000;
36
43
 
37
44
  const DISCOVER_ACTIVE_MS = 1000; // a device is unattached — look for it quickly
@@ -40,7 +47,7 @@ const FLUSH_MS = 100;
40
47
  const MAX_LINE_CHARS = 4000;
41
48
 
42
49
  function parseArgs(argv) {
43
- const args = { port: process.env.WATCHER_PORT || '8081', out: null };
50
+ const args = { port: resolvePort(), out: null };
44
51
  for (let i = 2; i < argv.length; i += 1) {
45
52
  if (argv[i] === '--port') args.port = argv[++i];
46
53
  else if (argv[i] === '--out') args.out = argv[++i];
@@ -101,9 +108,10 @@ function bridgeLockActive() {
101
108
  }
102
109
 
103
110
  function yieldSessions() {
104
- for (const ws of sessions.values()) {
111
+ for (const session of sessions.values()) {
112
+ broker?.onSessionClose(session.deviceId);
105
113
  try {
106
- ws.close();
114
+ session.ws.close();
107
115
  } catch {}
108
116
  }
109
117
  sessions.clear();
@@ -170,8 +178,56 @@ function serializeState() {
170
178
  return JSON.stringify(state);
171
179
  }
172
180
 
173
- /** deviceId -> live WebSocket session. */
181
+ /** deviceId -> live WebSocket session state. */
174
182
  const sessions = new Map();
183
+ let broker = null;
184
+ let devtoolsProxy = null;
185
+
186
+ function sendCommand(session, method, params = {}, timeoutMs = 10_000) {
187
+ return new Promise((resolve, reject) => {
188
+ if (!session.opened) {
189
+ reject(new Error('CDP session is not open'));
190
+ return;
191
+ }
192
+ const id = ++session.nextId;
193
+ const timer = setTimeout(() => {
194
+ session.pending.delete(id);
195
+ reject(new Error(`CDP command timed out: ${method}`));
196
+ }, timeoutMs);
197
+ session.pending.set(id, {
198
+ resolve: (result) => {
199
+ clearTimeout(timer);
200
+ resolve(result);
201
+ },
202
+ reject: (error) => {
203
+ clearTimeout(timer);
204
+ reject(error);
205
+ },
206
+ });
207
+ session.ws.send(JSON.stringify({ id, method, params }));
208
+ });
209
+ }
210
+
211
+ broker = createCdpBroker({
212
+ socketPath: brokerSocketPath(path.dirname(out), port),
213
+ sessions,
214
+ sendCommand,
215
+ requestDiscovery(deviceId) {
216
+ foreignDebuggerUntil.delete(deviceId);
217
+ discover();
218
+ },
219
+ onClientActivity: noteBridgeCoordination,
220
+ });
221
+ devtoolsProxy = createDevtoolsProxy({
222
+ descriptorPath: path.join(path.dirname(out), 'devtools-proxy.json'),
223
+ sessions,
224
+ sendCommand,
225
+ requestDiscovery: discover,
226
+ allowedOrigins: [
227
+ `http://127.0.0.1:${port}`,
228
+ `http://localhost:${port}`,
229
+ ],
230
+ });
175
231
 
176
232
  /** deviceId -> epoch ms until which a foreign debugger owns the slot. */
177
233
  const foreignDebuggerUntil = new Map();
@@ -226,11 +282,6 @@ function deviceNameFromTitle(title) {
226
282
  return m ? m[1] : (title || 'device');
227
283
  }
228
284
 
229
- function deviceIdFromUrl(wsUrl) {
230
- const m = /[?&]device=([^&]+)/.exec(wsUrl || '');
231
- return m ? m[1] : wsUrl;
232
- }
233
-
234
285
  function connect(target) {
235
286
  const deviceId = deviceIdFromUrl(target.webSocketDebuggerUrl);
236
287
  const name = deviceNameFromTitle(target.title);
@@ -240,7 +291,16 @@ function connect(target) {
240
291
  } catch {
241
292
  return;
242
293
  }
243
- sessions.set(deviceId, ws);
294
+ const session = {
295
+ deviceId,
296
+ name,
297
+ ws,
298
+ opened: false,
299
+ brokerReady: false,
300
+ nextId: 1,
301
+ pending: new Map(),
302
+ };
303
+ sessions.set(deviceId, session);
244
304
  // The built-in WebSocket has no handshake timeout; a hung connect would hold
245
305
  // the session slot forever and block every future re-attach for this device.
246
306
  const handshakeTimer = setTimeout(() => {
@@ -248,10 +308,29 @@ function connect(target) {
248
308
  ws.close();
249
309
  } catch {}
250
310
  }, HANDSHAKE_TIMEOUT_MS);
251
- ws.addEventListener('open', () => {
311
+ ws.addEventListener('open', async () => {
252
312
  clearTimeout(handshakeTimer);
253
- ws.send(JSON.stringify({ id: 1, method: 'Runtime.enable' }));
254
- process.stderr.write(`console-forwarder: attached ${name}\n`);
313
+ session.opened = true;
314
+ try {
315
+ await sendCommand(session, 'Runtime.enable');
316
+ const evaluation = await sendCommand(session, 'Runtime.evaluate', {
317
+ expression: "typeof globalThis.__AGENTIC__ === 'object'",
318
+ returnByValue: true,
319
+ awaitPromise: false,
320
+ });
321
+ if (evaluation?.result?.value !== true) {
322
+ throw new Error('CDP target does not expose __AGENTIC__');
323
+ }
324
+ session.brokerReady = true;
325
+ broker.onSessionOpen(deviceId);
326
+ devtoolsProxy.onSessionOpen(deviceId, session);
327
+ process.stderr.write(`console-forwarder: attached ${name}\n`);
328
+ } catch (error) {
329
+ process.stderr.write(
330
+ `console-forwarder: rejected ${name}: ${String(error?.message || error).slice(0, 256)}\n`,
331
+ );
332
+ ws.close();
333
+ }
255
334
  });
256
335
  ws.addEventListener('message', (event) => {
257
336
  let msg;
@@ -260,6 +339,17 @@ function connect(target) {
260
339
  } catch {
261
340
  return;
262
341
  }
342
+ if (msg.id && session.pending.has(msg.id)) {
343
+ const entry = session.pending.get(msg.id);
344
+ session.pending.delete(msg.id);
345
+ if (msg.error) entry.reject(new Error(JSON.stringify(msg.error)));
346
+ else entry.resolve(msg.result);
347
+ return;
348
+ }
349
+ if (msg.method) {
350
+ broker.onCdpEvent(deviceId, msg.method, msg.params || {});
351
+ devtoolsProxy.onCdpEvent(deviceId, msg.method, msg.params || {});
352
+ }
263
353
  if (msg.method !== 'Runtime.consoleAPICalled') return;
264
354
  const text = formatArgs(msg.params.args);
265
355
  // dev-middleware emits this NOTE on every debugger attach (i.e. ours). Drop it.
@@ -278,8 +368,15 @@ function connect(target) {
278
368
  });
279
369
  const drop = () => {
280
370
  clearTimeout(handshakeTimer);
281
- if (sessions.get(deviceId) === ws) {
371
+ session.opened = false;
372
+ session.brokerReady = false;
373
+ for (const entry of session.pending.values()) {
374
+ entry.reject(new Error('CDP session closed'));
375
+ }
376
+ session.pending.clear();
377
+ if (sessions.get(deviceId) === session) {
282
378
  sessions.delete(deviceId);
379
+ broker.onSessionClose(deviceId);
283
380
  process.stderr.write(`console-forwarder: detached ${name}; will re-attach\n`);
284
381
  }
285
382
  };
@@ -351,10 +448,14 @@ function schedule(ms) {
351
448
 
352
449
  process.on('SIGTERM', () => {
353
450
  flushSync();
451
+ devtoolsProxy.close();
452
+ broker.close();
354
453
  process.exit(0);
355
454
  });
356
455
  process.on('SIGINT', () => {
357
456
  flushSync();
457
+ devtoolsProxy.close();
458
+ broker.close();
358
459
  process.exit(0);
359
460
  });
360
461
 
@@ -63,6 +63,8 @@ const NEEDLE_CODES = [
63
63
  ['websocket error', BRIDGE_ERROR_CODES.WS_CLOSED],
64
64
  ['cdp connection timeout', BRIDGE_ERROR_CODES.CDP_TIMEOUT],
65
65
  ['cdp message timeout', BRIDGE_ERROR_CODES.CDP_TIMEOUT],
66
+ ['cdp broker connection timeout', BRIDGE_ERROR_CODES.CDP_TIMEOUT],
67
+ ['cdp broker request timeout', BRIDGE_ERROR_CODES.CDP_TIMEOUT],
66
68
  ['evaluation timed out', BRIDGE_ERROR_CODES.CDP_TIMEOUT],
67
69
  ['timed out', BRIDGE_ERROR_CODES.CDP_TIMEOUT],
68
70
  ];