@viyzhu/boss-cli-fork 0.6.8
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/AGENTS.md +30 -0
- package/LICENSE +674 -0
- package/README.md +223 -0
- package/dist/browser/browser_session.d.ts +27 -0
- package/dist/browser/browser_session.d.ts.map +1 -0
- package/dist/browser/browser_session.js +246 -0
- package/dist/browser/browser_session.js.map +1 -0
- package/dist/browser/cdp_browser.d.ts +50 -0
- package/dist/browser/cdp_browser.d.ts.map +1 -0
- package/dist/browser/cdp_browser.js +315 -0
- package/dist/browser/cdp_browser.js.map +1 -0
- package/dist/browser/human_delay.d.ts +117 -0
- package/dist/browser/human_delay.d.ts.map +1 -0
- package/dist/browser/human_delay.js +71 -0
- package/dist/browser/human_delay.js.map +1 -0
- package/dist/browser/index.d.ts +9 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +9 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/timing.d.ts +7 -0
- package/dist/browser/timing.d.ts.map +1 -0
- package/dist/browser/timing.js +34 -0
- package/dist/browser/timing.js.map +1 -0
- package/dist/browser/viewport_temp.d.ts +15 -0
- package/dist/browser/viewport_temp.d.ts.map +1 -0
- package/dist/browser/viewport_temp.js +78 -0
- package/dist/browser/viewport_temp.js.map +1 -0
- package/dist/cli/banner.d.ts +2 -0
- package/dist/cli/banner.d.ts.map +1 -0
- package/dist/cli/banner.js +69 -0
- package/dist/cli/banner.js.map +1 -0
- package/dist/cli/cliRouter.d.ts +9 -0
- package/dist/cli/cliRouter.d.ts.map +1 -0
- package/dist/cli/cliRouter.js +604 -0
- package/dist/cli/cliRouter.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +34 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/version.d.ts +29 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/cli/version.js +180 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/common/auth.d.ts +44 -0
- package/dist/common/auth.d.ts.map +1 -0
- package/dist/common/auth.js +168 -0
- package/dist/common/auth.js.map +1 -0
- package/dist/common/baidu_user_env.d.ts +7 -0
- package/dist/common/baidu_user_env.d.ts.map +1 -0
- package/dist/common/baidu_user_env.js +56 -0
- package/dist/common/baidu_user_env.js.map +1 -0
- package/dist/common/boss_availability.d.ts +5 -0
- package/dist/common/boss_availability.d.ts.map +1 -0
- package/dist/common/boss_availability.js +245 -0
- package/dist/common/boss_availability.js.map +1 -0
- package/dist/common/boss_modal.d.ts +14 -0
- package/dist/common/boss_modal.d.ts.map +1 -0
- package/dist/common/boss_modal.js +81 -0
- package/dist/common/boss_modal.js.map +1 -0
- package/dist/common/boss_page_guards.d.ts +14 -0
- package/dist/common/boss_page_guards.d.ts.map +1 -0
- package/dist/common/boss_page_guards.js +596 -0
- package/dist/common/boss_page_guards.js.map +1 -0
- package/dist/common/boss_paywall_popup.d.ts +28 -0
- package/dist/common/boss_paywall_popup.d.ts.map +1 -0
- package/dist/common/boss_paywall_popup.js +158 -0
- package/dist/common/boss_paywall_popup.js.map +1 -0
- package/dist/common/boss_session_lock.d.ts +2 -0
- package/dist/common/boss_session_lock.d.ts.map +1 -0
- package/dist/common/boss_session_lock.js +127 -0
- package/dist/common/boss_session_lock.js.map +1 -0
- package/dist/common/boss_session_page.d.ts +27 -0
- package/dist/common/boss_session_page.d.ts.map +1 -0
- package/dist/common/boss_session_page.js +173 -0
- package/dist/common/boss_session_page.js.map +1 -0
- package/dist/common/boss_sidebar_nav.d.ts +7 -0
- package/dist/common/boss_sidebar_nav.d.ts.map +1 -0
- package/dist/common/boss_sidebar_nav.js +53 -0
- package/dist/common/boss_sidebar_nav.js.map +1 -0
- package/dist/common/c_resume_capture.d.ts +19 -0
- package/dist/common/c_resume_capture.d.ts.map +1 -0
- package/dist/common/c_resume_capture.js +185 -0
- package/dist/common/c_resume_capture.js.map +1 -0
- package/dist/common/ensure_page.d.ts +9 -0
- package/dist/common/ensure_page.d.ts.map +1 -0
- package/dist/common/ensure_page.js +14 -0
- package/dist/common/ensure_page.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/ocr/baidu_ocr.d.ts +13 -0
- package/dist/ocr/baidu_ocr.d.ts.map +1 -0
- package/dist/ocr/baidu_ocr.js +80 -0
- package/dist/ocr/baidu_ocr.js.map +1 -0
- package/dist/ocr/index.d.ts +3 -0
- package/dist/ocr/index.d.ts.map +1 -0
- package/dist/ocr/index.js +3 -0
- package/dist/ocr/index.js.map +1 -0
- package/dist/ocr/resume_ocr.d.ts +13 -0
- package/dist/ocr/resume_ocr.d.ts.map +1 -0
- package/dist/ocr/resume_ocr.js +38 -0
- package/dist/ocr/resume_ocr.js.map +1 -0
- package/dist/toolset/action.d.ts +12 -0
- package/dist/toolset/action.d.ts.map +1 -0
- package/dist/toolset/action.js +521 -0
- package/dist/toolset/action.js.map +1 -0
- package/dist/toolset/baidu_credentials.d.ts +5 -0
- package/dist/toolset/baidu_credentials.d.ts.map +1 -0
- package/dist/toolset/baidu_credentials.js +24 -0
- package/dist/toolset/baidu_credentials.js.map +1 -0
- package/dist/toolset/chat.d.ts +10 -0
- package/dist/toolset/chat.d.ts.map +1 -0
- package/dist/toolset/chat.js +763 -0
- package/dist/toolset/chat.js.map +1 -0
- package/dist/toolset/deep-search.d.ts +35 -0
- package/dist/toolset/deep-search.d.ts.map +1 -0
- package/dist/toolset/deep-search.js +1505 -0
- package/dist/toolset/deep-search.js.map +1 -0
- package/dist/toolset/greet.d.ts +6 -0
- package/dist/toolset/greet.d.ts.map +1 -0
- package/dist/toolset/greet.js +81 -0
- package/dist/toolset/greet.js.map +1 -0
- package/dist/toolset/index.d.ts +48 -0
- package/dist/toolset/index.d.ts.map +1 -0
- package/dist/toolset/index.js +71 -0
- package/dist/toolset/index.js.map +1 -0
- package/dist/toolset/jd.d.ts +10 -0
- package/dist/toolset/jd.d.ts.map +1 -0
- package/dist/toolset/jd.js +406 -0
- package/dist/toolset/jd.js.map +1 -0
- package/dist/toolset/list.d.ts +6 -0
- package/dist/toolset/list.d.ts.map +1 -0
- package/dist/toolset/list.js +146 -0
- package/dist/toolset/list.js.map +1 -0
- package/dist/toolset/login.d.ts +6 -0
- package/dist/toolset/login.d.ts.map +1 -0
- package/dist/toolset/login.js +76 -0
- package/dist/toolset/login.js.map +1 -0
- package/dist/toolset/normal-search.d.ts +12 -0
- package/dist/toolset/normal-search.d.ts.map +1 -0
- package/dist/toolset/normal-search.js +301 -0
- package/dist/toolset/normal-search.js.map +1 -0
- package/dist/toolset/preview.d.ts +5 -0
- package/dist/toolset/preview.d.ts.map +1 -0
- package/dist/toolset/preview.js +100 -0
- package/dist/toolset/preview.js.map +1 -0
- package/dist/toolset/recommend.d.ts +36 -0
- package/dist/toolset/recommend.d.ts.map +1 -0
- package/dist/toolset/recommend.js +428 -0
- package/dist/toolset/recommend.js.map +1 -0
- package/dist/toolset/send.d.ts +7 -0
- package/dist/toolset/send.d.ts.map +1 -0
- package/dist/toolset/send.js +52 -0
- package/dist/toolset/send.js.map +1 -0
- package/package.json +69 -0
- package/skills/boss-frontend-analysis/SKILL.md +58 -0
- package/skills/boss-frontend-analysis/agents/openai.yaml +4 -0
- package/skills/boss-frontend-analysis/scripts/capture_boss_frontend.mjs +604 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { GREET_PAYWALL_WAIT_MAX_MS, ONLINE_RESUME_IFRAME_WAIT_MAX_MS } from '../browser/human_delay.js';
|
|
2
|
+
import { sleepRandom } from '../browser/timing.js';
|
|
3
|
+
import { frameHasVisibleCResumeIframe } from './c_resume_capture.js';
|
|
4
|
+
/** 付费墙仅在主文档轮询(遮罩层挂在顶层);与 {@link describeBossPaywallPopupIfPresent} 判定一致 */
|
|
5
|
+
const HAS_PAYWALL_SCRIPT = `(() => {
|
|
6
|
+
const roots = Array.from(
|
|
7
|
+
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
8
|
+
);
|
|
9
|
+
for (const root of roots) {
|
|
10
|
+
if (!(root instanceof HTMLElement)) continue;
|
|
11
|
+
const st = window.getComputedStyle(root);
|
|
12
|
+
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const rect = root.getBoundingClientRect();
|
|
16
|
+
if (rect.width < 20 || rect.height < 20) continue;
|
|
17
|
+
const hasVipUi = root.querySelector(
|
|
18
|
+
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
19
|
+
);
|
|
20
|
+
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
21
|
+
const hasPayText =
|
|
22
|
+
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
23
|
+
text,
|
|
24
|
+
);
|
|
25
|
+
if (!hasVipUi && !hasPayText) continue;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
})()`;
|
|
30
|
+
/**
|
|
31
|
+
* 轮询直到出现 c-resume iframe、或出现付费墙、或超时。
|
|
32
|
+
* c-resume 可能在主文档,也可能在 `recommendFrame` 等子 frame 内(推荐预览),故遍历 {@link Page.frames}。
|
|
33
|
+
*/
|
|
34
|
+
export async function waitForCResumeIframeOrPaywall(page, timeoutMs = ONLINE_RESUME_IFRAME_WAIT_MAX_MS) {
|
|
35
|
+
const deadline = Date.now() + timeoutMs;
|
|
36
|
+
while (Date.now() < deadline) {
|
|
37
|
+
const paywall = (await page.evaluate(HAS_PAYWALL_SCRIPT));
|
|
38
|
+
if (paywall) {
|
|
39
|
+
return 'paywall';
|
|
40
|
+
}
|
|
41
|
+
const frames = page.frames();
|
|
42
|
+
for (const frame of frames) {
|
|
43
|
+
if (await frameHasVisibleCResumeIframe(frame)) {
|
|
44
|
+
return 'iframe';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
await sleepRandom(160, 240);
|
|
48
|
+
}
|
|
49
|
+
return 'neither';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 单次探测:主文档是否当前正显示付费弹层(与 {@link describeBossPaywallPopupIfPresent} 判定一致)。
|
|
53
|
+
* 供合并轮询(同一循环内同时检测多类弹层)使用,避免重复编写检测脚本。
|
|
54
|
+
*/
|
|
55
|
+
export async function detectBossPaywallPopup(page) {
|
|
56
|
+
return (await page.evaluate(HAS_PAYWALL_SCRIPT));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 打招呼等操作后,轮询主文档是否出现付费弹层(与 {@link describeBossPaywallPopupIfPresent} 判定一致)。
|
|
60
|
+
* 命中则返回 true;超时未出现则返回 false。
|
|
61
|
+
*/
|
|
62
|
+
export async function waitForBossPaywallPopup(page, timeoutMs = GREET_PAYWALL_WAIT_MAX_MS) {
|
|
63
|
+
const deadline = Date.now() + timeoutMs;
|
|
64
|
+
while (Date.now() < deadline) {
|
|
65
|
+
if (await detectBossPaywallPopup(page)) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
await sleepRandom(160, 240);
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 若当前存在 VIP/付费类弹层(判定规则与 {@link describeBossPaywallPopupIfPresent} 一致),
|
|
74
|
+
* 则点击关闭按钮以恢复页面可操作状态。返回是否执行了关闭。
|
|
75
|
+
*/
|
|
76
|
+
export async function closeBossPaywallPopupIfPresent(page) {
|
|
77
|
+
const closed = (await page.evaluate(`(() => {
|
|
78
|
+
const roots = Array.from(
|
|
79
|
+
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
80
|
+
);
|
|
81
|
+
for (const root of roots) {
|
|
82
|
+
if (!(root instanceof HTMLElement)) continue;
|
|
83
|
+
const st = window.getComputedStyle(root);
|
|
84
|
+
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const rect = root.getBoundingClientRect();
|
|
88
|
+
if (rect.width < 20 || rect.height < 20) continue;
|
|
89
|
+
const hasVipUi = root.querySelector(
|
|
90
|
+
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
91
|
+
);
|
|
92
|
+
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
93
|
+
const hasPayText =
|
|
94
|
+
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
95
|
+
text,
|
|
96
|
+
);
|
|
97
|
+
if (!hasVipUi && !hasPayText) continue;
|
|
98
|
+
const closeBtn = root.querySelector(
|
|
99
|
+
".boss-popup__close, .boss-dialog__close, .drawer-close, .icon-close",
|
|
100
|
+
);
|
|
101
|
+
if (closeBtn instanceof HTMLElement) {
|
|
102
|
+
closeBtn.click();
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
})()`));
|
|
108
|
+
if (closed) {
|
|
109
|
+
await sleepRandom(220, 480);
|
|
110
|
+
}
|
|
111
|
+
return closed;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 检测 Boss 页面上是否出现 VIP/付费购买类弹层(如点击「在线简历」、推荐预览或打招呼后拦截权益时)。
|
|
115
|
+
* 命中则返回简短中文说明,供与「未出现 c-resume iframe」类错误拼接。
|
|
116
|
+
* @param purpose `greet` 时将「查看在线简历」类措辞改为适合打招呼的说明。
|
|
117
|
+
*/
|
|
118
|
+
export async function describeBossPaywallPopupIfPresent(page, purpose = 'resume') {
|
|
119
|
+
const msg = (await page.evaluate(`(() => {
|
|
120
|
+
const roots = Array.from(
|
|
121
|
+
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
122
|
+
);
|
|
123
|
+
for (const root of roots) {
|
|
124
|
+
if (!(root instanceof HTMLElement)) continue;
|
|
125
|
+
const st = window.getComputedStyle(root);
|
|
126
|
+
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const rect = root.getBoundingClientRect();
|
|
130
|
+
if (rect.width < 20 || rect.height < 20) continue;
|
|
131
|
+
const hasVipUi = root.querySelector(
|
|
132
|
+
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
133
|
+
);
|
|
134
|
+
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
135
|
+
const hasPayText =
|
|
136
|
+
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
137
|
+
text,
|
|
138
|
+
);
|
|
139
|
+
if (!hasVipUi && !hasPayText) continue;
|
|
140
|
+
const h = root.querySelector("h3.title, h4.title, .card-header .title");
|
|
141
|
+
const title = (h?.textContent || "").replace(/\\s+/g, " ").trim();
|
|
142
|
+
if (title) {
|
|
143
|
+
return (
|
|
144
|
+
"页面出现付费弹层(" +
|
|
145
|
+
title +
|
|
146
|
+
"),可能需开通 VIP 或购买权益后才能查看在线简历。"
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return "页面出现 VIP/付费购买弹层,可能需开通权益后才能查看在线简历。";
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
})()`));
|
|
153
|
+
if (!msg || purpose === 'resume') {
|
|
154
|
+
return msg;
|
|
155
|
+
}
|
|
156
|
+
return msg.replace(/后才能查看在线简历/g, '后才能完成打招呼');
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=boss_paywall_popup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boss_paywall_popup.js","sourceRoot":"","sources":["../../src/common/boss_paywall_popup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,2BAA2B,CAAC;AACxG,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAErE,2EAA2E;AAC3E,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;KAwBtB,CAAC;AAEN;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,IAAU,EACV,YAAoB,gCAAgC;IAEpD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAY,CAAC;QACrE,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,MAAM,4BAA4B,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9C,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,MAAM,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,IAAU;IACrD,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAY,CAAC;AAC9D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,IAAU,EACV,YAAoB,yBAAyB;IAE7C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,IAAI,MAAM,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,IAAU;IAC7D,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8B/B,CAAC,CAAY,CAAC;IACnB,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,IAAU,EACV,UAA8B,QAAQ;IAEtC,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiC5B,CAAC,CAAkB,CAAC;IACzB,IAAI,CAAC,GAAG,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boss_session_lock.d.ts","sourceRoot":"","sources":["../../src/common/boss_session_lock.ts"],"names":[],"mappings":"AAmGA,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA4CnF"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { open, readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { hostname } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { sleepRandom } from '../browser/timing.js';
|
|
5
|
+
import { CACHE_DIR, ensureAppDataLayout } from '../config.js';
|
|
6
|
+
const SESSION_LOCK_FILE = join(CACHE_DIR, 'session.lock');
|
|
7
|
+
const SESSION_LOCK_WAIT_MAX_MS = 30_000;
|
|
8
|
+
const SESSION_LOCK_POLL_MS = 250;
|
|
9
|
+
function buildSessionLockMeta() {
|
|
10
|
+
return {
|
|
11
|
+
pid: process.pid,
|
|
12
|
+
createdAt: Date.now(),
|
|
13
|
+
hostname: hostname(),
|
|
14
|
+
cwd: process.cwd(),
|
|
15
|
+
command: process.argv.join(' ').trim(),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function readSessionLockMeta() {
|
|
19
|
+
try {
|
|
20
|
+
const raw = await readFile(SESSION_LOCK_FILE, 'utf8');
|
|
21
|
+
const parsed = JSON.parse(raw);
|
|
22
|
+
if (typeof parsed.pid !== 'number' ||
|
|
23
|
+
typeof parsed.createdAt !== 'number' ||
|
|
24
|
+
typeof parsed.hostname !== 'string' ||
|
|
25
|
+
typeof parsed.cwd !== 'string' ||
|
|
26
|
+
typeof parsed.command !== 'string') {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
pid: parsed.pid,
|
|
31
|
+
createdAt: parsed.createdAt,
|
|
32
|
+
hostname: parsed.hostname,
|
|
33
|
+
cwd: parsed.cwd,
|
|
34
|
+
command: parsed.command,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function processExists(pid) {
|
|
42
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
process.kill(pid, 0);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const code = error && typeof error === 'object' && 'code' in error ? error.code : '';
|
|
51
|
+
return code === 'EPERM';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function clearStaleSessionLockIfNeeded() {
|
|
55
|
+
const meta = await readSessionLockMeta();
|
|
56
|
+
if (!meta) {
|
|
57
|
+
await rm(SESSION_LOCK_FILE, { force: true }).catch(() => { });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (meta.hostname !== hostname()) {
|
|
61
|
+
if (!(await processExists(meta.pid))) {
|
|
62
|
+
await rm(SESSION_LOCK_FILE, { force: true }).catch(() => { });
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (await processExists(meta.pid)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
await rm(SESSION_LOCK_FILE, { force: true }).catch(() => { });
|
|
70
|
+
}
|
|
71
|
+
function formatSessionLockOwner(meta) {
|
|
72
|
+
if (!meta) {
|
|
73
|
+
return 'unknown';
|
|
74
|
+
}
|
|
75
|
+
const ageSeconds = Math.max(0, Math.floor((Date.now() - meta.createdAt) / 1000));
|
|
76
|
+
return [
|
|
77
|
+
'pid=' + meta.pid,
|
|
78
|
+
'host=' + meta.hostname,
|
|
79
|
+
'age=' + ageSeconds + 's',
|
|
80
|
+
meta.command ? 'cmd=' + meta.command : '',
|
|
81
|
+
]
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
.join(', ');
|
|
84
|
+
}
|
|
85
|
+
export async function withBossSessionLock(callback) {
|
|
86
|
+
ensureAppDataLayout();
|
|
87
|
+
const deadline = Date.now() + SESSION_LOCK_WAIT_MAX_MS;
|
|
88
|
+
while (true) {
|
|
89
|
+
try {
|
|
90
|
+
const handle = await open(SESSION_LOCK_FILE, 'wx');
|
|
91
|
+
let lockCreated = false;
|
|
92
|
+
try {
|
|
93
|
+
await handle.writeFile(JSON.stringify(buildSessionLockMeta()), 'utf8');
|
|
94
|
+
lockCreated = true;
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
await handle.close().catch(() => { });
|
|
98
|
+
}
|
|
99
|
+
if (!lockCreated) {
|
|
100
|
+
await rm(SESSION_LOCK_FILE, { force: true }).catch(() => { });
|
|
101
|
+
throw new Error('Boss session lock creation failed.');
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
return await callback();
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await rm(SESSION_LOCK_FILE, { force: true }).catch(() => { });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
const code = error && typeof error === 'object' && 'code' in error ? error.code : '';
|
|
112
|
+
if (code !== 'EEXIST') {
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
await clearStaleSessionLockIfNeeded();
|
|
116
|
+
if (Date.now() >= deadline) {
|
|
117
|
+
const meta = await readSessionLockMeta();
|
|
118
|
+
throw new Error('Boss session is busy for more than 30s. Lock owner: ' +
|
|
119
|
+
formatSessionLockOwner(meta) +
|
|
120
|
+
'. If stale, delete ' +
|
|
121
|
+
SESSION_LOCK_FILE);
|
|
122
|
+
}
|
|
123
|
+
await sleepRandom(SESSION_LOCK_POLL_MS, SESSION_LOCK_POLL_MS);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=boss_session_lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boss_session_lock.js","sourceRoot":"","sources":["../../src/common/boss_session_lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boss B 端「主壳」会话:选页、必要时进入沟通页、侧栏 `.menu-list` 探测,
|
|
3
|
+
* 再执行 {@link withBossSessionPage} 回调。与 `src/toolset/chat.ts`(按姓名打开会话等业务)无关。
|
|
4
|
+
*/
|
|
5
|
+
import type { Page } from 'puppeteer-core';
|
|
6
|
+
type BossSessionPageOptions = {
|
|
7
|
+
ensureChatShell?: boolean;
|
|
8
|
+
ensureMenuList?: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* 先按 URL 判断:不在 Boss 已登录主壳页(`/web/chat/*`)时跳到沟通页 `/web/chat/index`,
|
|
12
|
+
* 再交由 {@link ensureMenuListMountedAfterLoad} 查 `.menu-list`。
|
|
13
|
+
* 已经在 `/web/chat/recommend`、`/web/chat/aiform` 等主壳子页时直接跳过 goto,
|
|
14
|
+
* 避免触发"先回到聊天页再切回业务页"的额外跳转。
|
|
15
|
+
*/
|
|
16
|
+
/** 页面守卫熔断(风控页反弹 / 自刷新循环)导致的中止,与普通页面异常区分开。 */
|
|
17
|
+
export declare class BossPageRiskError extends Error {
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 在已连接浏览器、且当前页为 Boss 主壳(含侧栏 `.menu-list`)的前提下执行回调。
|
|
22
|
+
* 默认会先按 URL 确保落在 `/web/chat/*` 主壳页(已在主壳子页则保留原路径,否则跳回沟通页 `/web/chat/index`),
|
|
23
|
+
* 再校验侧栏;需要严格使用当前页面的命令可通过 options 关闭这些预检查。
|
|
24
|
+
*/
|
|
25
|
+
export declare function withBossSessionPage<T>(callback: (page: Page) => Promise<T>, options?: BossSessionPageOptions): Promise<T>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=boss_session_page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boss_session_page.d.ts","sourceRoot":"","sources":["../../src/common/boss_session_page.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAmDpD,KAAK,sBAAsB,GAAG;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAwBF;;;;;GAKG;AACH,6CAA6C;AAC7C,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAsDD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EACpC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,CAAC,CAAC,CAoEZ"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { BOSS_CHAT_INDEX_URL, isBossChatShellUrl } from './auth.js';
|
|
2
|
+
import { ensureBrowserSession, getBrowserRef, getPageRef, setSessionPage, } from '../browser/browser_session.js';
|
|
3
|
+
import { CONTEXT_DESTROY_RETRY_MS } from '../browser/human_delay.js';
|
|
4
|
+
import { sleepRandom } from '../browser/timing.js';
|
|
5
|
+
import { getBossPageRiskState, installBossPageGuards } from './boss_page_guards.js';
|
|
6
|
+
import { withBossSessionLock } from './boss_session_lock.js';
|
|
7
|
+
const SHOULD_DISABLE_JS = process.env.BOSS_BROWSER_DISABLE_JS === 'true' || process.env.BOSS_BROWSER_DISABLE_JS === '1';
|
|
8
|
+
/** Boss 为 SPA:`load` 后侧栏可能尚未挂载,需单独等待 `.menu-list` 出现 */
|
|
9
|
+
const MENU_LIST_MOUNT_TIMEOUT_MS = 30_000;
|
|
10
|
+
async function pickExistingPage(browser) {
|
|
11
|
+
const pages = (await browser.pages()).filter((p) => !p.isClosed());
|
|
12
|
+
if (pages.length === 0)
|
|
13
|
+
return null;
|
|
14
|
+
const urls = await Promise.all(pages.map((p) => {
|
|
15
|
+
try {
|
|
16
|
+
return p.url();
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
const zhipin = pages.find((p, i) => {
|
|
23
|
+
const u = urls[i] ?? '';
|
|
24
|
+
return u.length > 0 && u !== 'about:blank' && u.includes('zhipin.com');
|
|
25
|
+
});
|
|
26
|
+
if (zhipin)
|
|
27
|
+
return zhipin;
|
|
28
|
+
const nonBlank = pages.find((p, i) => {
|
|
29
|
+
const u = urls[i] ?? '';
|
|
30
|
+
return u.length > 0 && u !== 'about:blank';
|
|
31
|
+
});
|
|
32
|
+
return nonBlank ?? null;
|
|
33
|
+
}
|
|
34
|
+
function normalizeMenuText(raw) {
|
|
35
|
+
return (raw ?? '').replace(/\s+/g, ' ').trim();
|
|
36
|
+
}
|
|
37
|
+
async function readMenuListSnapshot(page) {
|
|
38
|
+
return (await page.evaluate(`(() => {
|
|
39
|
+
const root = document.querySelector(".menu-list");
|
|
40
|
+
if (!root) {
|
|
41
|
+
return { exists: false, signature: "" };
|
|
42
|
+
}
|
|
43
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
44
|
+
const links = Array.from(root.querySelectorAll("dl > dt > a"));
|
|
45
|
+
const entries = links.map((a) => {
|
|
46
|
+
const href = a.getAttribute("href") ?? "";
|
|
47
|
+
const labelNode = a.querySelector(".menu-item-content span");
|
|
48
|
+
const label = norm(labelNode?.textContent || a.textContent || "");
|
|
49
|
+
return label + "::" + href;
|
|
50
|
+
});
|
|
51
|
+
return { exists: true, signature: entries.join("|") };
|
|
52
|
+
})()`));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 先按 URL 判断:不在 Boss 已登录主壳页(`/web/chat/*`)时跳到沟通页 `/web/chat/index`,
|
|
56
|
+
* 再交由 {@link ensureMenuListMountedAfterLoad} 查 `.menu-list`。
|
|
57
|
+
* 已经在 `/web/chat/recommend`、`/web/chat/aiform` 等主壳子页时直接跳过 goto,
|
|
58
|
+
* 避免触发"先回到聊天页再切回业务页"的额外跳转。
|
|
59
|
+
*/
|
|
60
|
+
/** 页面守卫熔断(风控页反弹 / 自刷新循环)导致的中止,与普通页面异常区分开。 */
|
|
61
|
+
export class BossPageRiskError extends Error {
|
|
62
|
+
constructor(message) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = 'BossPageRiskError';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 熔断后立刻停下:继续自动操作既拿不到正确页面,也会让风控进一步升级。
|
|
69
|
+
* 错误信息直接给出人工处置动作。
|
|
70
|
+
*/
|
|
71
|
+
function assertNoPageRisk(page) {
|
|
72
|
+
const risk = getBossPageRiskState(page);
|
|
73
|
+
if (!risk)
|
|
74
|
+
return;
|
|
75
|
+
throw new BossPageRiskError(`${risk.message}\n处理方式:在浏览器中完成验证/重新登录,确认能正常停留在沟通页后再重试;` +
|
|
76
|
+
`若确认无需拦截,可设 BOSS_BROWSER_ALLOW_RISK_NAV=1 让验证页直接渲染。`);
|
|
77
|
+
}
|
|
78
|
+
async function ensureBossChatShellUrlBeforeMenuList(page) {
|
|
79
|
+
if (isBossChatShellUrl(page.url())) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
await page.goto(BOSS_CHAT_INDEX_URL, { waitUntil: 'load', timeout: 60_000 });
|
|
83
|
+
}
|
|
84
|
+
async function ensureMenuListMountedAfterLoad(page) {
|
|
85
|
+
await page.waitForFunction(`(() => document.readyState === "complete" || document.readyState === "interactive")()`, { timeout: 12_000 });
|
|
86
|
+
try {
|
|
87
|
+
await page.waitForFunction(`(() => !!document.querySelector(".menu-list"))()`, { timeout: MENU_LIST_MOUNT_TIMEOUT_MS });
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
91
|
+
const timedOut = err.name === 'TimeoutError' || /timeout|waiting failed/i.test(err.message);
|
|
92
|
+
if (timedOut) {
|
|
93
|
+
throw new Error(`在 ${MENU_LIST_MOUNT_TIMEOUT_MS / 1000}s 内未出现侧栏 .menu-list(页面或仍在加载,或未登录无法进入主壳)。`);
|
|
94
|
+
}
|
|
95
|
+
throw e;
|
|
96
|
+
}
|
|
97
|
+
const first = await readMenuListSnapshot(page);
|
|
98
|
+
if (!first.exists) {
|
|
99
|
+
throw new Error('当前页面可能未登录或未进入 Boss 主界面。');
|
|
100
|
+
}
|
|
101
|
+
if (!normalizeMenuText(first.signature)) {
|
|
102
|
+
throw new Error('检测到 .menu-list 但菜单内容为空,当前页面状态异常。');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 在已连接浏览器、且当前页为 Boss 主壳(含侧栏 `.menu-list`)的前提下执行回调。
|
|
107
|
+
* 默认会先按 URL 确保落在 `/web/chat/*` 主壳页(已在主壳子页则保留原路径,否则跳回沟通页 `/web/chat/index`),
|
|
108
|
+
* 再校验侧栏;需要严格使用当前页面的命令可通过 options 关闭这些预检查。
|
|
109
|
+
*/
|
|
110
|
+
export async function withBossSessionPage(callback, options = {}) {
|
|
111
|
+
const shouldEnsureChatShell = options.ensureChatShell !== false;
|
|
112
|
+
const shouldEnsureMenuList = options.ensureMenuList !== false;
|
|
113
|
+
return withBossSessionLock(async () => {
|
|
114
|
+
const isContextDestroyed = (e) => {
|
|
115
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
116
|
+
return (msg.includes('Execution context was destroyed') ||
|
|
117
|
+
msg.includes('Cannot find context with specified id') ||
|
|
118
|
+
msg.includes('Most likely because of a navigation'));
|
|
119
|
+
};
|
|
120
|
+
const maxAttempts = 2;
|
|
121
|
+
let lastErr;
|
|
122
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
123
|
+
let page = null;
|
|
124
|
+
try {
|
|
125
|
+
await ensureBrowserSession();
|
|
126
|
+
const browser = getBrowserRef();
|
|
127
|
+
if (!browser) {
|
|
128
|
+
throw new Error('无法获取浏览器实例。');
|
|
129
|
+
}
|
|
130
|
+
page = getPageRef();
|
|
131
|
+
if (!page || page.isClosed()) {
|
|
132
|
+
page = (await pickExistingPage(browser)) ?? (await browser.newPage());
|
|
133
|
+
}
|
|
134
|
+
setSessionPage(page);
|
|
135
|
+
await page.bringToFront();
|
|
136
|
+
await installBossPageGuards(page);
|
|
137
|
+
if (shouldEnsureChatShell) {
|
|
138
|
+
await ensureBossChatShellUrlBeforeMenuList(page);
|
|
139
|
+
}
|
|
140
|
+
assertNoPageRisk(page);
|
|
141
|
+
if (SHOULD_DISABLE_JS) {
|
|
142
|
+
await page.setJavaScriptEnabled(false);
|
|
143
|
+
}
|
|
144
|
+
if (shouldEnsureMenuList) {
|
|
145
|
+
try {
|
|
146
|
+
await ensureMenuListMountedAfterLoad(page);
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
// 主壳加载不出来时,风控熔断信息比「未出现侧栏」更能定位问题。
|
|
150
|
+
assertNoPageRisk(page);
|
|
151
|
+
throw e;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return await callback(page);
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
lastErr = e;
|
|
158
|
+
// 守卫熔断时,把「上下文被销毁」这类次生错误换成风控原因,并且不再重试。
|
|
159
|
+
if (page && !(e instanceof BossPageRiskError)) {
|
|
160
|
+
assertNoPageRisk(page);
|
|
161
|
+
}
|
|
162
|
+
if (attempt < maxAttempts - 1 && isContextDestroyed(e)) {
|
|
163
|
+
// Boss 页面偶发跳转/重渲染会销毁执行上下文;短暂等待并重试一次即可。
|
|
164
|
+
await sleepRandom(CONTEXT_DESTROY_RETRY_MS.min, CONTEXT_DESTROY_RETRY_MS.max);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
throw e;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=boss_session_page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boss_session_page.js","sourceRoot":"","sources":["../../src/common/boss_session_page.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Page } from 'puppeteer-core';
|
|
2
|
+
/**
|
|
3
|
+
* 点击 Boss 左侧 `.menu-list` 中的菜单项,并等待导航到给定 pathname(如 `/web/chat/index`)。
|
|
4
|
+
* 若合成点击未触发导航(Boss v10718+ SPA 对程序化点击无响应),回退到 `page.goto` 直达目标 URL。
|
|
5
|
+
*/
|
|
6
|
+
export declare function clickBossSidebarMenuToPath(page: Page, menuLabel: string, targetPath: string): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=boss_sidebar_nav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boss_sidebar_nav.d.ts","sourceRoot":"","sources":["../../src/common/boss_sidebar_nav.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAkB3C;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { SIDEBAR_NAV_AFTER_CLICK_MS, sleepRandom } from '../browser/index.js';
|
|
2
|
+
const SIDEBAR_NAV_WAIT_MS = 15_000;
|
|
3
|
+
// 2026-07 起 Boss(index v10718+)SPA 侧栏菜单项对程序化 click / dispatchMouseEvent
|
|
4
|
+
// 不再触发路由导航,只有真实指针事件才跳转。故点击后先短等,超时则回退到 page.goto 直达。
|
|
5
|
+
const SIDEBAR_CLICK_NAV_WAIT_MS = 5_000;
|
|
6
|
+
const buildPathReachedPredicate = () => `((path) => {
|
|
7
|
+
try {
|
|
8
|
+
const p = window.location.pathname.replace(/\\/+$/, "") || "/";
|
|
9
|
+
return p === path;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
})`;
|
|
14
|
+
/**
|
|
15
|
+
* 点击 Boss 左侧 `.menu-list` 中的菜单项,并等待导航到给定 pathname(如 `/web/chat/index`)。
|
|
16
|
+
* 若合成点击未触发导航(Boss v10718+ SPA 对程序化点击无响应),回退到 `page.goto` 直达目标 URL。
|
|
17
|
+
*/
|
|
18
|
+
export async function clickBossSidebarMenuToPath(page, menuLabel, targetPath) {
|
|
19
|
+
const clicked = (await page.evaluate(`(({ label, path }) => {
|
|
20
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
21
|
+
const links = Array.from(document.querySelectorAll(".menu-list a"));
|
|
22
|
+
const target = links.find((a) => {
|
|
23
|
+
const href = a.getAttribute("href") ?? "";
|
|
24
|
+
if (href.includes(path)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const text = norm(a.querySelector(".menu-item-content span")?.textContent ?? a.textContent);
|
|
28
|
+
return text.includes(label);
|
|
29
|
+
});
|
|
30
|
+
if (!(target instanceof HTMLElement)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
target.scrollIntoView({ block: "center", inline: "nearest" });
|
|
34
|
+
target.click();
|
|
35
|
+
return true;
|
|
36
|
+
})`, { label: menuLabel, path: targetPath }));
|
|
37
|
+
if (!clicked) {
|
|
38
|
+
throw new Error(`未找到侧边栏菜单“${menuLabel}”,无法跳转到 ${targetPath}。`);
|
|
39
|
+
}
|
|
40
|
+
await sleepRandom(SIDEBAR_NAV_AFTER_CLICK_MS.min, SIDEBAR_NAV_AFTER_CLICK_MS.max);
|
|
41
|
+
const pathReached = buildPathReachedPredicate();
|
|
42
|
+
try {
|
|
43
|
+
await page.waitForFunction(pathReached, { timeout: SIDEBAR_CLICK_NAV_WAIT_MS }, targetPath);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// 合成点击未导航(Boss v10718+),回退到直接 goto 目标 URL。
|
|
48
|
+
}
|
|
49
|
+
const targetUrl = new URL(targetPath, page.url()).toString();
|
|
50
|
+
await page.goto(targetUrl, { waitUntil: 'load', timeout: 60_000 });
|
|
51
|
+
await page.waitForFunction(pathReached, { timeout: SIDEBAR_NAV_WAIT_MS }, targetPath);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=boss_sidebar_nav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boss_sidebar_nav.js","sourceRoot":"","sources":["../../src/common/boss_sidebar_nav.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE9E,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,wEAAwE;AACxE,oDAAoD;AACpD,MAAM,yBAAyB,GAAG,KAAK,CAAC;AAExC,MAAM,yBAAyB,GAAG,GAAW,EAAE,CAC7C;;;;;;;OAOK,CAAC;AAER;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,IAAU,EACV,SAAiB,EACjB,UAAkB;IAElB,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAClC;;;;;;;;;;;;;;;;;OAiBG,EACH,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CACvC,CAAY,CAAC;IAEd,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,YAAY,SAAS,WAAW,UAAU,GAAG,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,WAAW,CAAC,0BAA0B,CAAC,GAAG,EAAE,0BAA0B,CAAC,GAAG,CAAC,CAAC;IAElF,MAAM,WAAW,GAAG,yBAAyB,EAAE,CAAC;IAEhD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE,UAAU,CAAC,CAAC;QAC5F,OAAO;IACT,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;IAC7C,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC7D,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACnE,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE,EAAE,UAAU,CAAC,CAAC;AACxF,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ElementHandle, Frame, Page } from 'puppeteer-core';
|
|
2
|
+
/** 在线简历 iframe:`src` 常为相对路径 `/web/frame/c-resume/...`,故用子串匹配 */
|
|
3
|
+
export declare const C_RESUME_IFRAME_SELECTOR: "iframe[src*=\"c-resume\"], iframe[src*=\"frame/c-resume\"]";
|
|
4
|
+
export declare function frameHasVisibleCResumeIframe(frame: Frame): Promise<boolean>;
|
|
5
|
+
/** 截图文件名安全段(在线简历 / 推荐预览共用) */
|
|
6
|
+
export declare function safeResumeScreenshotFileBase(name: string): string;
|
|
7
|
+
/** 关闭含 `c-resume` iframe 的弹层(聊天「在线简历」与推荐「预览」共用)。含 `.boss-popup__close`、`.btn-quxiao`(取消)等。会在主文档与各子 frame 中尝试。 */
|
|
8
|
+
export declare function closeCResumePanel(page: Page): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* 在任意 frame(含主 frame、`recommendFrame` 等)中查找已挂载且尺寸可见的 c-resume iframe。
|
|
11
|
+
*/
|
|
12
|
+
export declare function findVisibleCResumeIframeHandle(page: Page): Promise<ElementHandle<Element> | null>;
|
|
13
|
+
export declare function waitForVisibleCResumeIframeReady(page: Page, timeoutMs?: number): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* 在已出现 `c-resume` iframe 的页面上,对 iframe 整框截图并关闭弹层。
|
|
16
|
+
* `preOpenViewport` 为打开弹层前的视口快照,请用 `snapshotBossPageViewport(page)`(`page.viewport()` 常为 null 时勿直接用默认尺寸)。
|
|
17
|
+
*/
|
|
18
|
+
export declare function captureCResumeIframeToFile(page: Page, preOpenViewport: Awaited<ReturnType<Page['viewport']>>, absPath: string): Promise<boolean>;
|
|
19
|
+
//# sourceMappingURL=c_resume_capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"c_resume_capture.d.ts","sourceRoot":"","sources":["../../src/common/c_resume_capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAIjE,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,EACnC,4DAAiE,CAAC;AAmDpE,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAMjF;AAED,8BAA8B;AAC9B,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,iHAAiH;AACjH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBjE;AAED;;GAEG;AACH,wBAAsB,8BAA8B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAevG;AAED,wBAAsB,gCAAgC,CACpD,IAAI,EAAE,IAAI,EACV,SAAS,SAAQ,GAChB,OAAO,CAAC,OAAO,CAAC,CAoClB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EACtD,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAuClB"}
|