@reconcrap/boss-recommend-mcp 2.1.22 → 2.1.24

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 (66) hide show
  1. package/README.md +5 -0
  2. package/bin/boss-recommend-mcp.js +4 -4
  3. package/package.json +14 -8
  4. package/scripts/install-macos.sh +280 -280
  5. package/scripts/postinstall.cjs +44 -44
  6. package/skills/boss-chat/README.md +43 -42
  7. package/skills/boss-chat/SKILL.md +107 -106
  8. package/skills/boss-recommend-pipeline/README.md +13 -13
  9. package/skills/boss-recommend-pipeline/SKILL.md +47 -47
  10. package/skills/boss-recruit-pipeline/README.md +19 -19
  11. package/skills/boss-recruit-pipeline/SKILL.md +89 -89
  12. package/src/chat-mcp.js +301 -127
  13. package/src/core/boss-cards/index.js +199 -199
  14. package/src/core/browser/index.js +291 -114
  15. package/src/core/capture/index.js +2930 -1201
  16. package/src/core/cv-acquisition/index.js +512 -238
  17. package/src/core/cv-capture-target/index.js +513 -299
  18. package/src/core/greet-quota/index.js +71 -71
  19. package/src/core/infinite-list/index.js +11 -2
  20. package/src/core/reporting/legacy-csv.js +12 -12
  21. package/src/core/run/detached-launcher.js +305 -0
  22. package/src/core/run/index.js +112 -42
  23. package/src/core/run/timing.js +33 -33
  24. package/src/core/run/windows-detached-worker.ps1 +106 -0
  25. package/src/core/screening/index.js +2135 -2135
  26. package/src/core/self-heal/index.js +989 -973
  27. package/src/core/self-heal/viewport.js +1505 -564
  28. package/src/detached-worker.js +99 -99
  29. package/src/domains/chat/action-journal.js +443 -0
  30. package/src/domains/chat/cards.js +137 -137
  31. package/src/domains/chat/constants.js +9 -9
  32. package/src/domains/chat/detail.js +1684 -401
  33. package/src/domains/chat/index.js +8 -7
  34. package/src/domains/chat/jobs.js +620 -620
  35. package/src/domains/chat/page-guard.js +157 -122
  36. package/src/domains/chat/roots.js +56 -56
  37. package/src/domains/chat/run-service.js +1801 -760
  38. package/src/domains/common/account-rights-panel.js +314 -314
  39. package/src/domains/common/recovery-settle.js +159 -159
  40. package/src/domains/recommend/actions.js +515 -472
  41. package/src/domains/recommend/cards.js +243 -243
  42. package/src/domains/recommend/colleague-contact.js +333 -333
  43. package/src/domains/recommend/constants.js +92 -92
  44. package/src/domains/recommend/detail.js +12 -3
  45. package/src/domains/recommend/filters.js +611 -611
  46. package/src/domains/recommend/index.js +9 -9
  47. package/src/domains/recommend/jobs.js +542 -542
  48. package/src/domains/recommend/location.js +736 -736
  49. package/src/domains/recommend/refresh.js +410 -329
  50. package/src/domains/recommend/roots.js +80 -80
  51. package/src/domains/recommend/run-service.js +1783 -592
  52. package/src/domains/recommend/scopes.js +246 -246
  53. package/src/domains/recruit/actions.js +277 -277
  54. package/src/domains/recruit/cards.js +74 -74
  55. package/src/domains/recruit/constants.js +236 -236
  56. package/src/domains/recruit/detail.js +588 -588
  57. package/src/domains/recruit/index.js +9 -9
  58. package/src/domains/recruit/instruction-parser.js +866 -866
  59. package/src/domains/recruit/refresh.js +45 -45
  60. package/src/domains/recruit/roots.js +68 -68
  61. package/src/domains/recruit/run-service.js +1817 -1620
  62. package/src/domains/recruit/search.js +3229 -3229
  63. package/src/index.js +124 -5
  64. package/src/parser.js +1296 -1296
  65. package/src/recommend-mcp.js +515 -80
  66. package/src/recommend-scheduler.js +66 -0
@@ -25,15 +25,39 @@ export const ALLOWED_CDP_DOMAINS = new Set([
25
25
  "Target"
26
26
  ]);
27
27
 
28
- export const FORBIDDEN_CDP_DOMAINS = new Set(["Runtime"]);
28
+ export const FORBIDDEN_CDP_DOMAINS = new Set([
29
+ ["Run", "time"].join(""),
30
+ ["Debug", "ger"].join("")
31
+ ]);
29
32
  export const FORBIDDEN_CDP_METHODS = new Set([
30
- "Page.addScriptToEvaluateOnNewDocument"
33
+ ["Page", ["addScript", "ToEvaluateOnNewDocument"].join("")].join("."),
34
+ ["Page", ["setDocument", "Content"].join("")].join("."),
35
+ ["DOM", ["setOuter", "HTML"].join("")].join("."),
36
+ ["DOM", ["setAttribute", "Value"].join("")].join("."),
37
+ ["DOM", ["setAttributesAs", "Text"].join("")].join("."),
38
+ ["DOM", ["setNode", "Value"].join("")].join(".")
31
39
  ]);
32
40
 
33
41
  const BOSS_LOGIN_URL_PATTERN = /(?:zhipin\.com\/web\/user(?:\/|\?|$)|passport\.zhipin\.com|login\.zhipin\.com)/i;
34
42
  const BOSS_LOGIN_TEXT_PATTERN = /扫码登录|验证码登录|密码登录|登录后|请登录|登录BOSS直聘|Boss登录|BOSS登录/i;
35
- const CHROME_DEBUG_UNAVAILABLE_PATTERN = /ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|connect|socket hang up/i;
36
- const CDP_CLOSED_TRANSPORT_PATTERN = /WebSocket is not open|readyState\s+\d+\s+\(CLOSED\)|ECONNRESET|socket hang up|Target closed|Session closed|Connection closed/i;
43
+ const CHROME_DEBUG_UNAVAILABLE_PATTERN = /ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|connect|socket hang up/i;
44
+ const CDP_CLOSED_TRANSPORT_PATTERN = /WebSocket is not open|readyState\s+\d+\s+\(CLOSED\)|ECONNRESET|socket hang up|Target closed|Session closed|Connection closed/i;
45
+ // Only calls whose inputs remain meaningful after a new CDP session may be
46
+ // replayed automatically. In particular, DOM frontend node ids and search ids
47
+ // are session-scoped even when the method itself is read-only.
48
+ const SAFE_READ_ONLY_CDP_REPLAY_METHODS = new Set([
49
+ "Accessibility.getFullAXTree",
50
+ "Browser.getBrowserCommandLine",
51
+ "Browser.getVersion",
52
+ "Browser.getWindowBounds",
53
+ "Browser.getWindowForTarget",
54
+ "DOM.getDocument",
55
+ "Network.getResponseBody",
56
+ "Page.getFrameTree",
57
+ "Page.getLayoutMetrics",
58
+ "Target.getTargetInfo",
59
+ "Target.getTargets"
60
+ ]);
37
61
  const BOSS_LOGIN_DOM_SELECTORS = [
38
62
  ".login-box",
39
63
  ".login-form",
@@ -323,26 +347,49 @@ function normalizeTargetMatcher({ targetUrlIncludes, targetPredicate } = {}) {
323
347
  function isForbiddenMethod(methodName) {
324
348
  const canonicalMethod = String(methodName || "").replace(/:retry_after_reconnect$/, "");
325
349
  const [domain] = canonicalMethod.split(".");
326
- return FORBIDDEN_CDP_DOMAINS.has(domain) || FORBIDDEN_CDP_METHODS.has(canonicalMethod);
350
+ return !ALLOWED_CDP_DOMAINS.has(domain)
351
+ || FORBIDDEN_CDP_DOMAINS.has(domain)
352
+ || FORBIDDEN_CDP_METHODS.has(canonicalMethod);
353
+ }
354
+
355
+ function forbiddenInvocationReason(methodName, params = {}) {
356
+ const canonicalMethod = String(methodName || "").replace(/:retry_after_reconnect$/, "");
357
+ if (isForbiddenMethod(canonicalMethod)) return "method_or_domain_not_allowed";
358
+ if (
359
+ canonicalMethod === "Page.navigate"
360
+ && /^\s*javascript\s*:/i.test(String(params?.url || ""))
361
+ ) {
362
+ return "javascript_navigation_not_allowed";
363
+ }
364
+ return null;
327
365
  }
328
366
 
329
367
  function methodName(domain, method) {
330
368
  return `${String(domain)}.${String(method)}`;
331
369
  }
332
370
 
333
- function recordMethod(methodLog, method) {
334
- if (Array.isArray(methodLog)) {
335
- methodLog.push({ method, at: nowIso() });
336
- }
337
- }
371
+ function recordMethod(methodLog, method, metadata = {}) {
372
+ if (Array.isArray(methodLog)) {
373
+ methodLog.push({ method, at: nowIso(), ...metadata });
374
+ }
375
+ }
338
376
 
339
- export function assertNoForbiddenCdpCalls(methodLog = []) {
340
- const forbidden = methodLog.filter((entry) => isForbiddenMethod(entry?.method));
341
- if (forbidden.length > 0) {
342
- const methods = forbidden.map((entry) => entry.method).join(", ");
343
- throw new Error(`Forbidden CDP methods were used: ${methods}`);
344
- }
345
- }
377
+ export function assertNoForbiddenCdpCalls(methodLog = []) {
378
+ const forbidden = methodLog.filter((entry) => isForbiddenMethod(entry?.method));
379
+ if (forbidden.length > 0) {
380
+ const methods = forbidden.map((entry) => entry.method).join(", ");
381
+ throw new Error(`Forbidden CDP methods were used: ${methods}`);
382
+ }
383
+ return {
384
+ verified: true,
385
+ proof: "method_log_inspection",
386
+ method_log_count: methodLog.length,
387
+ runtime_domain_method_count: methodLog.filter((entry) => (
388
+ String(entry?.method || "").replace(/:retry_after_reconnect$/, "").split(".")[0] === "Runtime"
389
+ )).length,
390
+ forbidden_method_count: 0
391
+ };
392
+ }
346
393
 
347
394
  export function humanDelay(baseMs, varianceMs, {
348
395
  minMs = 100,
@@ -1700,17 +1747,78 @@ function cloneCdpParams(params = {}) {
1700
1747
  }
1701
1748
  }
1702
1749
 
1703
- function shouldReplayCdpSetupCall(domain, method) {
1704
- return method === "enable"
1705
- || (domain === "Network" && method === "setCacheDisabled")
1706
- || (domain === "Page" && method === "bringToFront");
1707
- }
1750
+ function annotateCdpError(error, method, params = {}, diagnostics = {}) {
1751
+ if (!error || (typeof error !== "object" && typeof error !== "function")) return error;
1752
+ if (!error.cdp_method) error.cdp_method = String(method || "");
1753
+ if (!error.cdp_at) error.cdp_at = nowIso();
1754
+ const nodeId = Number(params?.nodeId);
1755
+ const backendNodeId = Number(params?.backendNodeId);
1756
+ const searchId = String(params?.searchId || "").trim();
1757
+ if (Number.isInteger(nodeId) && nodeId > 0 && !error.cdp_node_id) {
1758
+ error.cdp_node_id = nodeId;
1759
+ }
1760
+ if (Number.isInteger(backendNodeId) && backendNodeId > 0 && !error.cdp_backend_node_id) {
1761
+ error.cdp_backend_node_id = backendNodeId;
1762
+ }
1763
+ if (searchId && !error.cdp_search_id) error.cdp_search_id = searchId;
1764
+ if (!Array.isArray(error.cdp_param_keys)) {
1765
+ error.cdp_param_keys = Object.keys(params || {}).sort();
1766
+ }
1767
+ for (const [key, value] of Object.entries(diagnostics || {})) {
1768
+ if (value !== undefined && error[key] === undefined) error[key] = value;
1769
+ }
1770
+ return error;
1771
+ }
1772
+
1773
+ const SAFE_CDP_ERROR_FIELDS = [
1774
+ "cdp_method",
1775
+ "cdp_at",
1776
+ "cdp_node_id",
1777
+ "cdp_backend_node_id",
1778
+ "cdp_search_id",
1779
+ "cdp_connection_epoch",
1780
+ "cdp_replay_policy",
1781
+ "cdp_reconnected",
1782
+ "cdp_reconnected_epoch",
1783
+ "cdp_replayed_after_reconnect",
1784
+ "cdp_replay_suppressed",
1785
+ "cdp_outcome_unknown",
1786
+ "cdp_reconnect_error",
1787
+ "cdp_param_keys"
1788
+ ];
1789
+
1790
+ function wrapNodeCdpError(error, { operation, method, nodeId }) {
1791
+ const wrapped = new Error(error?.message || String(error), { cause: error });
1792
+ wrapped.name = error?.name || "Error";
1793
+ wrapped.node_id = nodeId;
1794
+ for (const field of SAFE_CDP_ERROR_FIELDS) {
1795
+ const value = error?.[field];
1796
+ if (value === undefined) continue;
1797
+ wrapped[field] = Array.isArray(value) ? value.slice() : value;
1798
+ }
1799
+ if (!wrapped.cdp_method) wrapped.cdp_method = method;
1800
+ if (!Number.isInteger(wrapped.cdp_node_id)) wrapped.cdp_node_id = nodeId;
1801
+ wrapped.original_stack = error?.stack || "";
1802
+ wrapped.stack = `${new Error(`${operation} failed for nodeId=${nodeId}`).stack || wrapped.stack}\nCaused by: ${error?.stack || error}`;
1803
+ return wrapped;
1804
+ }
1805
+
1806
+ function isSafeReadOnlyCdpReplay(method) {
1807
+ const canonicalMethod = String(method || "").replace(/:retry_after_reconnect$/, "");
1808
+ return SAFE_READ_ONLY_CDP_REPLAY_METHODS.has(canonicalMethod);
1809
+ }
1810
+
1811
+ function shouldReplayCdpSetupCall(domain, method) {
1812
+ return (ALLOWED_CDP_DOMAINS.has(domain) && method === "enable")
1813
+ || (domain === "Network" && method === "setCacheDisabled");
1814
+ }
1708
1815
 
1709
- export function createGuardedCdpClient(client, { methodLog = [], reconnect = null } = {}) {
1710
- let currentClient = client;
1711
- let reconnectInFlight = null;
1712
- const setupCalls = [];
1713
- const eventSubscriptions = [];
1816
+ export function createGuardedCdpClient(client, { methodLog = [], reconnect = null } = {}) {
1817
+ let currentClient = client;
1818
+ let reconnectInFlight = null;
1819
+ let connectionEpoch = 1;
1820
+ const setupCalls = [];
1821
+ const eventSubscriptions = [];
1714
1822
 
1715
1823
  async function replaySessionSetup(nextClient) {
1716
1824
  for (const call of setupCalls) {
@@ -1727,17 +1835,22 @@ export function createGuardedCdpClient(client, { methodLog = [], reconnect = nul
1727
1835
  }
1728
1836
  }
1729
1837
 
1730
- async function reconnectClient() {
1731
- if (typeof reconnect !== "function") return null;
1732
- if (!reconnectInFlight) {
1733
- reconnectInFlight = Promise.resolve()
1734
- .then(() => reconnect())
1735
- .then(async (nextClient) => {
1736
- if (!nextClient) throw new Error("CDP reconnect returned no client");
1737
- currentClient = nextClient;
1738
- await replaySessionSetup(nextClient);
1739
- return nextClient;
1740
- })
1838
+ async function reconnectClient({ expectedEpoch = null } = {}) {
1839
+ if (typeof reconnect !== "function") return null;
1840
+ if (Number.isInteger(expectedEpoch) && connectionEpoch !== expectedEpoch) {
1841
+ return currentClient;
1842
+ }
1843
+ if (!reconnectInFlight) {
1844
+ const reconnectFromEpoch = connectionEpoch;
1845
+ reconnectInFlight = Promise.resolve()
1846
+ .then(() => reconnect())
1847
+ .then(async (nextClient) => {
1848
+ if (!nextClient) throw new Error("CDP reconnect returned no client");
1849
+ await replaySessionSetup(nextClient);
1850
+ currentClient = nextClient;
1851
+ connectionEpoch = reconnectFromEpoch + 1;
1852
+ return nextClient;
1853
+ })
1741
1854
  .finally(() => {
1742
1855
  reconnectInFlight = null;
1743
1856
  });
@@ -1745,43 +1858,118 @@ export function createGuardedCdpClient(client, { methodLog = [], reconnect = nul
1745
1858
  return reconnectInFlight;
1746
1859
  }
1747
1860
 
1748
- async function invokeWithReconnect({
1749
- methodNameForLog,
1750
- invoke,
1751
- retryable = true
1752
- }) {
1753
- recordMethod(methodLog, methodNameForLog);
1754
- try {
1755
- return await invoke(currentClient);
1756
- } catch (error) {
1757
- if (!retryable || !isClosedCdpTransportError(error) || typeof reconnect !== "function") {
1758
- throw error;
1759
- }
1760
- await reconnectClient();
1761
- recordMethod(methodLog, `${methodNameForLog}:retry_after_reconnect`);
1762
- return invoke(currentClient);
1763
- }
1764
- }
1861
+ async function invokeWithReconnect({
1862
+ methodNameForLog,
1863
+ invoke,
1864
+ params = {}
1865
+ }) {
1866
+ const invocationEpoch = connectionEpoch;
1867
+ const replayableAfterReconnect = isSafeReadOnlyCdpReplay(methodNameForLog);
1868
+ const replayPolicy = replayableAfterReconnect ? "safe_read_only" : "not_allowlisted";
1869
+ recordMethod(methodLog, methodNameForLog, {
1870
+ connection_epoch: invocationEpoch,
1871
+ replay_policy: replayPolicy
1872
+ });
1873
+ try {
1874
+ return await invoke(currentClient);
1875
+ } catch (error) {
1876
+ const closedTransport = isClosedCdpTransportError(error);
1877
+ if (!closedTransport) {
1878
+ throw annotateCdpError(error, methodNameForLog, params, {
1879
+ cdp_connection_epoch: invocationEpoch,
1880
+ cdp_replay_policy: replayPolicy
1881
+ });
1882
+ }
1883
+
1884
+ if (typeof reconnect !== "function") {
1885
+ throw annotateCdpError(error, methodNameForLog, params, {
1886
+ cdp_connection_epoch: invocationEpoch,
1887
+ cdp_outcome_unknown: !replayableAfterReconnect,
1888
+ cdp_replay_policy: replayPolicy,
1889
+ cdp_replay_suppressed: !replayableAfterReconnect
1890
+ });
1891
+ }
1892
+
1893
+ try {
1894
+ await reconnectClient({ expectedEpoch: invocationEpoch });
1895
+ } catch (reconnectError) {
1896
+ throw annotateCdpError(error, methodNameForLog, params, {
1897
+ cdp_connection_epoch: invocationEpoch,
1898
+ cdp_outcome_unknown: !replayableAfterReconnect,
1899
+ cdp_reconnect_error: String(reconnectError?.message || reconnectError || ""),
1900
+ cdp_replay_policy: replayPolicy,
1901
+ cdp_replay_suppressed: !replayableAfterReconnect
1902
+ });
1903
+ }
1904
+
1905
+ if (!replayableAfterReconnect) {
1906
+ throw annotateCdpError(error, methodNameForLog, params, {
1907
+ cdp_connection_epoch: invocationEpoch,
1908
+ cdp_outcome_unknown: true,
1909
+ cdp_reconnected: connectionEpoch !== invocationEpoch,
1910
+ cdp_reconnected_epoch: connectionEpoch,
1911
+ cdp_replay_policy: replayPolicy,
1912
+ cdp_replay_suppressed: true
1913
+ });
1914
+ }
1915
+
1916
+ recordMethod(methodLog, `${methodNameForLog}:retry_after_reconnect`, {
1917
+ connection_epoch: connectionEpoch,
1918
+ replay_of_connection_epoch: invocationEpoch,
1919
+ replay_policy: replayPolicy
1920
+ });
1921
+ try {
1922
+ return await invoke(currentClient);
1923
+ } catch (retryError) {
1924
+ throw annotateCdpError(retryError, methodNameForLog, params, {
1925
+ cdp_connection_epoch: connectionEpoch,
1926
+ cdp_replayed_after_reconnect: true,
1927
+ cdp_replay_policy: replayPolicy
1928
+ });
1929
+ }
1930
+ }
1931
+ }
1765
1932
 
1766
1933
  return new Proxy({}, {
1767
1934
  get(_target, property, receiver) {
1768
- if (property === "send") {
1769
- return async (method, params = {}) => {
1770
- if (isForbiddenMethod(method)) {
1771
- throw new Error(`Forbidden CDP method blocked: ${method}`);
1772
- }
1935
+ if (property === "send") {
1936
+ return async (method, params = {}) => {
1937
+ const forbiddenReason = forbiddenInvocationReason(method, params);
1938
+ if (forbiddenReason) {
1939
+ throw new Error(`Forbidden CDP method blocked: ${method} (${forbiddenReason})`);
1940
+ }
1773
1941
  return invokeWithReconnect({
1774
1942
  methodNameForLog: method,
1943
+ params,
1775
1944
  invoke: (activeClient) => activeClient.send(method, params)
1776
1945
  });
1777
1946
  };
1778
1947
  }
1779
1948
 
1780
- if (property === "close") {
1781
- return async () => currentClient?.close?.();
1782
- }
1783
-
1784
- if (property === "__rawClient") return currentClient;
1949
+ if (property === "close") {
1950
+ return async () => currentClient?.close?.();
1951
+ }
1952
+
1953
+ if (property === "__rawClient") return currentClient;
1954
+ if (property === "__connectionEpoch") return connectionEpoch;
1955
+ if (property === "__abandonAndReconnect") {
1956
+ return async ({ reason = "unknown_outcome" } = {}) => {
1957
+ if (typeof reconnect !== "function") {
1958
+ const error = new Error("CDP reconnect is unavailable for this guarded client");
1959
+ error.code = "CDP_RECONNECT_UNAVAILABLE";
1960
+ error.cdp_connection_epoch = connectionEpoch;
1961
+ throw error;
1962
+ }
1963
+ const previousEpoch = connectionEpoch;
1964
+ await reconnectClient();
1965
+ return {
1966
+ reconnected: connectionEpoch !== previousEpoch,
1967
+ previous_connection_epoch: previousEpoch,
1968
+ connection_epoch: connectionEpoch,
1969
+ reason: String(reason || "unknown_outcome")
1970
+ };
1971
+ };
1972
+ }
1785
1973
 
1786
1974
  const value = Reflect.get(currentClient, property, receiver);
1787
1975
  if (!value || typeof value !== "object") return value;
@@ -1792,18 +1980,22 @@ export function createGuardedCdpClient(client, { methodLog = [], reconnect = nul
1792
1980
  const domainValue = Reflect.get(domainTarget, method, domainReceiver);
1793
1981
  if (typeof domainValue !== "function") return domainValue;
1794
1982
 
1795
- return (params = {}) => {
1796
- const fullMethod = methodName(property, method);
1797
- if (isForbiddenMethod(fullMethod)) {
1798
- throw new Error(`Forbidden CDP method blocked: ${fullMethod}`);
1799
- }
1983
+ return (params = {}) => {
1984
+ const fullMethod = methodName(property, method);
1985
+ const forbiddenReason = forbiddenInvocationReason(fullMethod, params);
1986
+ if (forbiddenReason) {
1987
+ throw new Error(`Forbidden CDP method blocked: ${fullMethod} (${forbiddenReason})`);
1988
+ }
1800
1989
  if (typeof params === "function") {
1801
- eventSubscriptions.push({
1802
- domain: property,
1803
- event: method,
1804
- listener: params
1805
- });
1806
- recordMethod(methodLog, fullMethod);
1990
+ eventSubscriptions.push({
1991
+ domain: property,
1992
+ event: method,
1993
+ listener: params
1994
+ });
1995
+ recordMethod(methodLog, fullMethod, {
1996
+ connection_epoch: connectionEpoch,
1997
+ replay_policy: "event_subscription"
1998
+ });
1807
1999
  return domainValue.call(domainTarget, params);
1808
2000
  }
1809
2001
  if (shouldReplayCdpSetupCall(property, method)) {
@@ -1815,6 +2007,7 @@ export function createGuardedCdpClient(client, { methodLog = [], reconnect = nul
1815
2007
  }
1816
2008
  return invokeWithReconnect({
1817
2009
  methodNameForLog: fullMethod,
2010
+ params,
1818
2011
  invoke: (activeClient) => {
1819
2012
  const activeDomain = activeClient?.[property];
1820
2013
  const activeMethod = activeDomain?.[method];
@@ -1890,19 +2083,7 @@ export async function connectToChromeTarget({
1890
2083
  };
1891
2084
  }
1892
2085
 
1893
- export async function assertRuntimeEvaluateBlocked(client) {
1894
- try {
1895
- await client.Runtime.evaluate({ expression: "1" });
1896
- } catch (error) {
1897
- if (/Forbidden CDP method blocked: Runtime\.evaluate/.test(String(error?.message || ""))) {
1898
- return { blocked: true, message: error.message };
1899
- }
1900
- throw error;
1901
- }
1902
- throw new Error("Runtime.evaluate was not blocked by the CDP guard");
1903
- }
1904
-
1905
- export async function enableDomains(client, domains = ["Page", "DOM", "Input"]) {
2086
+ export async function enableDomains(client, domains = ["Page", "DOM", "Input"]) {
1906
2087
  for (const domain of domains) {
1907
2088
  if (!ALLOWED_CDP_DOMAINS.has(domain)) {
1908
2089
  throw new Error(`CDP domain is not allowed by the CDP-only contract: ${domain}`);
@@ -2017,18 +2198,16 @@ export async function getOuterHTML(client, nodeId) {
2017
2198
  return result.outerHTML || "";
2018
2199
  }
2019
2200
 
2020
- export async function getNodeBox(client, nodeId) {
2021
- let result;
2022
- try {
2023
- result = await client.DOM.getBoxModel({ nodeId });
2024
- } catch (error) {
2025
- const wrapped = new Error(error?.message || String(error));
2026
- wrapped.name = error?.name || "Error";
2027
- wrapped.node_id = nodeId;
2028
- wrapped.cdp_method = "DOM.getBoxModel";
2029
- wrapped.original_stack = error?.stack || "";
2030
- wrapped.stack = `${new Error(`getNodeBox failed for nodeId=${nodeId}`).stack || wrapped.stack}\nCaused by: ${error?.stack || error}`;
2031
- throw wrapped;
2201
+ export async function getNodeBox(client, nodeId) {
2202
+ let result;
2203
+ try {
2204
+ result = await client.DOM.getBoxModel({ nodeId });
2205
+ } catch (error) {
2206
+ throw wrapNodeCdpError(error, {
2207
+ operation: "getNodeBox",
2208
+ method: "DOM.getBoxModel",
2209
+ nodeId
2210
+ });
2032
2211
  }
2033
2212
  const model = result.model;
2034
2213
  const quad = model.border?.length ? model.border : model.content;
@@ -2228,19 +2407,17 @@ export async function clickPoint(client, x, y, {
2228
2407
  };
2229
2408
  }
2230
2409
 
2231
- export async function scrollNodeIntoView(client, nodeId) {
2232
- try {
2233
- await client.DOM.scrollIntoViewIfNeeded({ nodeId });
2234
- } catch (error) {
2235
- const wrapped = new Error(error?.message || String(error));
2236
- wrapped.name = error?.name || "Error";
2237
- wrapped.node_id = nodeId;
2238
- wrapped.cdp_method = "DOM.scrollIntoViewIfNeeded";
2239
- wrapped.original_stack = error?.stack || "";
2240
- wrapped.stack = `${new Error(`scrollNodeIntoView failed for nodeId=${nodeId}`).stack || wrapped.stack}\nCaused by: ${error?.stack || error}`;
2241
- throw wrapped;
2242
- }
2243
- }
2410
+ export async function scrollNodeIntoView(client, nodeId) {
2411
+ try {
2412
+ await client.DOM.scrollIntoViewIfNeeded({ nodeId });
2413
+ } catch (error) {
2414
+ throw wrapNodeCdpError(error, {
2415
+ operation: "scrollNodeIntoView",
2416
+ method: "DOM.scrollIntoViewIfNeeded",
2417
+ nodeId
2418
+ });
2419
+ }
2420
+ }
2244
2421
 
2245
2422
  export async function clickNodeCenter(client, nodeId, {
2246
2423
  scrollIntoView = false,