@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.
- package/README.md +5 -0
- package/bin/boss-recommend-mcp.js +4 -4
- package/package.json +14 -8
- package/scripts/install-macos.sh +280 -280
- package/scripts/postinstall.cjs +44 -44
- package/skills/boss-chat/README.md +43 -42
- package/skills/boss-chat/SKILL.md +107 -106
- package/skills/boss-recommend-pipeline/README.md +13 -13
- package/skills/boss-recommend-pipeline/SKILL.md +47 -47
- package/skills/boss-recruit-pipeline/README.md +19 -19
- package/skills/boss-recruit-pipeline/SKILL.md +89 -89
- package/src/chat-mcp.js +301 -127
- package/src/core/boss-cards/index.js +199 -199
- package/src/core/browser/index.js +291 -114
- package/src/core/capture/index.js +2930 -1201
- package/src/core/cv-acquisition/index.js +512 -238
- package/src/core/cv-capture-target/index.js +513 -299
- package/src/core/greet-quota/index.js +71 -71
- package/src/core/infinite-list/index.js +11 -2
- package/src/core/reporting/legacy-csv.js +12 -12
- package/src/core/run/detached-launcher.js +305 -0
- package/src/core/run/index.js +112 -42
- package/src/core/run/timing.js +33 -33
- package/src/core/run/windows-detached-worker.ps1 +106 -0
- package/src/core/screening/index.js +2135 -2135
- package/src/core/self-heal/index.js +989 -973
- package/src/core/self-heal/viewport.js +1505 -564
- package/src/detached-worker.js +99 -99
- package/src/domains/chat/action-journal.js +443 -0
- package/src/domains/chat/cards.js +137 -137
- package/src/domains/chat/constants.js +9 -9
- package/src/domains/chat/detail.js +1684 -401
- package/src/domains/chat/index.js +8 -7
- package/src/domains/chat/jobs.js +620 -620
- package/src/domains/chat/page-guard.js +157 -122
- package/src/domains/chat/roots.js +56 -56
- package/src/domains/chat/run-service.js +1801 -760
- package/src/domains/common/account-rights-panel.js +314 -314
- package/src/domains/common/recovery-settle.js +159 -159
- package/src/domains/recommend/actions.js +515 -472
- package/src/domains/recommend/cards.js +243 -243
- package/src/domains/recommend/colleague-contact.js +333 -333
- package/src/domains/recommend/constants.js +92 -92
- package/src/domains/recommend/detail.js +12 -3
- package/src/domains/recommend/filters.js +611 -611
- package/src/domains/recommend/index.js +9 -9
- package/src/domains/recommend/jobs.js +542 -542
- package/src/domains/recommend/location.js +736 -736
- package/src/domains/recommend/refresh.js +410 -329
- package/src/domains/recommend/roots.js +80 -80
- package/src/domains/recommend/run-service.js +1783 -592
- package/src/domains/recommend/scopes.js +246 -246
- package/src/domains/recruit/actions.js +277 -277
- package/src/domains/recruit/cards.js +74 -74
- package/src/domains/recruit/constants.js +236 -236
- package/src/domains/recruit/detail.js +588 -588
- package/src/domains/recruit/index.js +9 -9
- package/src/domains/recruit/instruction-parser.js +866 -866
- package/src/domains/recruit/refresh.js +45 -45
- package/src/domains/recruit/roots.js +68 -68
- package/src/domains/recruit/run-service.js +1817 -1620
- package/src/domains/recruit/search.js +3229 -3229
- package/src/index.js +124 -5
- package/src/parser.js +1296 -1296
- package/src/recommend-mcp.js +515 -80
- 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([
|
|
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.
|
|
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
|
|
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
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
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
|
-
|
|
1713
|
-
const
|
|
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 (
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
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
|
-
|
|
1752
|
-
}) {
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
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
|
-
|
|
1771
|
-
|
|
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
|
-
|
|
1798
|
-
|
|
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
|
|
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
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
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
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
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,
|