@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.
Files changed (159) hide show
  1. package/AGENTS.md +30 -0
  2. package/LICENSE +674 -0
  3. package/README.md +223 -0
  4. package/dist/browser/browser_session.d.ts +27 -0
  5. package/dist/browser/browser_session.d.ts.map +1 -0
  6. package/dist/browser/browser_session.js +246 -0
  7. package/dist/browser/browser_session.js.map +1 -0
  8. package/dist/browser/cdp_browser.d.ts +50 -0
  9. package/dist/browser/cdp_browser.d.ts.map +1 -0
  10. package/dist/browser/cdp_browser.js +315 -0
  11. package/dist/browser/cdp_browser.js.map +1 -0
  12. package/dist/browser/human_delay.d.ts +117 -0
  13. package/dist/browser/human_delay.d.ts.map +1 -0
  14. package/dist/browser/human_delay.js +71 -0
  15. package/dist/browser/human_delay.js.map +1 -0
  16. package/dist/browser/index.d.ts +9 -0
  17. package/dist/browser/index.d.ts.map +1 -0
  18. package/dist/browser/index.js +9 -0
  19. package/dist/browser/index.js.map +1 -0
  20. package/dist/browser/timing.d.ts +7 -0
  21. package/dist/browser/timing.d.ts.map +1 -0
  22. package/dist/browser/timing.js +34 -0
  23. package/dist/browser/timing.js.map +1 -0
  24. package/dist/browser/viewport_temp.d.ts +15 -0
  25. package/dist/browser/viewport_temp.d.ts.map +1 -0
  26. package/dist/browser/viewport_temp.js +78 -0
  27. package/dist/browser/viewport_temp.js.map +1 -0
  28. package/dist/cli/banner.d.ts +2 -0
  29. package/dist/cli/banner.d.ts.map +1 -0
  30. package/dist/cli/banner.js +69 -0
  31. package/dist/cli/banner.js.map +1 -0
  32. package/dist/cli/cliRouter.d.ts +9 -0
  33. package/dist/cli/cliRouter.d.ts.map +1 -0
  34. package/dist/cli/cliRouter.js +604 -0
  35. package/dist/cli/cliRouter.js.map +1 -0
  36. package/dist/cli/index.d.ts +3 -0
  37. package/dist/cli/index.d.ts.map +1 -0
  38. package/dist/cli/index.js +34 -0
  39. package/dist/cli/index.js.map +1 -0
  40. package/dist/cli/version.d.ts +29 -0
  41. package/dist/cli/version.d.ts.map +1 -0
  42. package/dist/cli/version.js +180 -0
  43. package/dist/cli/version.js.map +1 -0
  44. package/dist/common/auth.d.ts +44 -0
  45. package/dist/common/auth.d.ts.map +1 -0
  46. package/dist/common/auth.js +168 -0
  47. package/dist/common/auth.js.map +1 -0
  48. package/dist/common/baidu_user_env.d.ts +7 -0
  49. package/dist/common/baidu_user_env.d.ts.map +1 -0
  50. package/dist/common/baidu_user_env.js +56 -0
  51. package/dist/common/baidu_user_env.js.map +1 -0
  52. package/dist/common/boss_availability.d.ts +5 -0
  53. package/dist/common/boss_availability.d.ts.map +1 -0
  54. package/dist/common/boss_availability.js +245 -0
  55. package/dist/common/boss_availability.js.map +1 -0
  56. package/dist/common/boss_modal.d.ts +14 -0
  57. package/dist/common/boss_modal.d.ts.map +1 -0
  58. package/dist/common/boss_modal.js +81 -0
  59. package/dist/common/boss_modal.js.map +1 -0
  60. package/dist/common/boss_page_guards.d.ts +14 -0
  61. package/dist/common/boss_page_guards.d.ts.map +1 -0
  62. package/dist/common/boss_page_guards.js +596 -0
  63. package/dist/common/boss_page_guards.js.map +1 -0
  64. package/dist/common/boss_paywall_popup.d.ts +28 -0
  65. package/dist/common/boss_paywall_popup.d.ts.map +1 -0
  66. package/dist/common/boss_paywall_popup.js +158 -0
  67. package/dist/common/boss_paywall_popup.js.map +1 -0
  68. package/dist/common/boss_session_lock.d.ts +2 -0
  69. package/dist/common/boss_session_lock.d.ts.map +1 -0
  70. package/dist/common/boss_session_lock.js +127 -0
  71. package/dist/common/boss_session_lock.js.map +1 -0
  72. package/dist/common/boss_session_page.d.ts +27 -0
  73. package/dist/common/boss_session_page.d.ts.map +1 -0
  74. package/dist/common/boss_session_page.js +173 -0
  75. package/dist/common/boss_session_page.js.map +1 -0
  76. package/dist/common/boss_sidebar_nav.d.ts +7 -0
  77. package/dist/common/boss_sidebar_nav.d.ts.map +1 -0
  78. package/dist/common/boss_sidebar_nav.js +53 -0
  79. package/dist/common/boss_sidebar_nav.js.map +1 -0
  80. package/dist/common/c_resume_capture.d.ts +19 -0
  81. package/dist/common/c_resume_capture.d.ts.map +1 -0
  82. package/dist/common/c_resume_capture.js +185 -0
  83. package/dist/common/c_resume_capture.js.map +1 -0
  84. package/dist/common/ensure_page.d.ts +9 -0
  85. package/dist/common/ensure_page.d.ts.map +1 -0
  86. package/dist/common/ensure_page.js +14 -0
  87. package/dist/common/ensure_page.js.map +1 -0
  88. package/dist/config.d.ts +22 -0
  89. package/dist/config.d.ts.map +1 -0
  90. package/dist/config.js +57 -0
  91. package/dist/config.js.map +1 -0
  92. package/dist/ocr/baidu_ocr.d.ts +13 -0
  93. package/dist/ocr/baidu_ocr.d.ts.map +1 -0
  94. package/dist/ocr/baidu_ocr.js +80 -0
  95. package/dist/ocr/baidu_ocr.js.map +1 -0
  96. package/dist/ocr/index.d.ts +3 -0
  97. package/dist/ocr/index.d.ts.map +1 -0
  98. package/dist/ocr/index.js +3 -0
  99. package/dist/ocr/index.js.map +1 -0
  100. package/dist/ocr/resume_ocr.d.ts +13 -0
  101. package/dist/ocr/resume_ocr.d.ts.map +1 -0
  102. package/dist/ocr/resume_ocr.js +38 -0
  103. package/dist/ocr/resume_ocr.js.map +1 -0
  104. package/dist/toolset/action.d.ts +12 -0
  105. package/dist/toolset/action.d.ts.map +1 -0
  106. package/dist/toolset/action.js +521 -0
  107. package/dist/toolset/action.js.map +1 -0
  108. package/dist/toolset/baidu_credentials.d.ts +5 -0
  109. package/dist/toolset/baidu_credentials.d.ts.map +1 -0
  110. package/dist/toolset/baidu_credentials.js +24 -0
  111. package/dist/toolset/baidu_credentials.js.map +1 -0
  112. package/dist/toolset/chat.d.ts +10 -0
  113. package/dist/toolset/chat.d.ts.map +1 -0
  114. package/dist/toolset/chat.js +763 -0
  115. package/dist/toolset/chat.js.map +1 -0
  116. package/dist/toolset/deep-search.d.ts +35 -0
  117. package/dist/toolset/deep-search.d.ts.map +1 -0
  118. package/dist/toolset/deep-search.js +1505 -0
  119. package/dist/toolset/deep-search.js.map +1 -0
  120. package/dist/toolset/greet.d.ts +6 -0
  121. package/dist/toolset/greet.d.ts.map +1 -0
  122. package/dist/toolset/greet.js +81 -0
  123. package/dist/toolset/greet.js.map +1 -0
  124. package/dist/toolset/index.d.ts +48 -0
  125. package/dist/toolset/index.d.ts.map +1 -0
  126. package/dist/toolset/index.js +71 -0
  127. package/dist/toolset/index.js.map +1 -0
  128. package/dist/toolset/jd.d.ts +10 -0
  129. package/dist/toolset/jd.d.ts.map +1 -0
  130. package/dist/toolset/jd.js +406 -0
  131. package/dist/toolset/jd.js.map +1 -0
  132. package/dist/toolset/list.d.ts +6 -0
  133. package/dist/toolset/list.d.ts.map +1 -0
  134. package/dist/toolset/list.js +146 -0
  135. package/dist/toolset/list.js.map +1 -0
  136. package/dist/toolset/login.d.ts +6 -0
  137. package/dist/toolset/login.d.ts.map +1 -0
  138. package/dist/toolset/login.js +76 -0
  139. package/dist/toolset/login.js.map +1 -0
  140. package/dist/toolset/normal-search.d.ts +12 -0
  141. package/dist/toolset/normal-search.d.ts.map +1 -0
  142. package/dist/toolset/normal-search.js +301 -0
  143. package/dist/toolset/normal-search.js.map +1 -0
  144. package/dist/toolset/preview.d.ts +5 -0
  145. package/dist/toolset/preview.d.ts.map +1 -0
  146. package/dist/toolset/preview.js +100 -0
  147. package/dist/toolset/preview.js.map +1 -0
  148. package/dist/toolset/recommend.d.ts +36 -0
  149. package/dist/toolset/recommend.d.ts.map +1 -0
  150. package/dist/toolset/recommend.js +428 -0
  151. package/dist/toolset/recommend.js.map +1 -0
  152. package/dist/toolset/send.d.ts +7 -0
  153. package/dist/toolset/send.d.ts.map +1 -0
  154. package/dist/toolset/send.js +52 -0
  155. package/dist/toolset/send.js.map +1 -0
  156. package/package.json +69 -0
  157. package/skills/boss-frontend-analysis/SKILL.md +58 -0
  158. package/skills/boss-frontend-analysis/agents/openai.yaml +4 -0
  159. package/skills/boss-frontend-analysis/scripts/capture_boss_frontend.mjs +604 -0
@@ -0,0 +1,76 @@
1
+ import { detachBrowserSession, disconnectBrowserSession, ensureAndGetBrowser, ensureBrowserSession, getBrowserRef, getPageRef, setSessionPage, wasLastChromeLaunchHeadless, } from '../browser/index.js';
2
+ const BOSS_LOGIN_URL = 'https://www.zhipin.com/web/user/?ka=header-login';
3
+ async function pickExistingPage(browser) {
4
+ const pages = (await browser.pages()).filter((p) => !p.isClosed());
5
+ if (pages.length === 0)
6
+ return null;
7
+ const urls = await Promise.all(pages.map((p) => {
8
+ try {
9
+ return p.url();
10
+ }
11
+ catch {
12
+ return '';
13
+ }
14
+ }));
15
+ const zhipin = pages.find((p, i) => {
16
+ const u = urls[i] ?? '';
17
+ return u.length > 0 && u !== 'about:blank' && u.includes('zhipin.com');
18
+ });
19
+ if (zhipin)
20
+ return zhipin;
21
+ const nonBlank = pages.find((p, i) => {
22
+ const u = urls[i] ?? '';
23
+ return u.length > 0 && u !== 'about:blank';
24
+ });
25
+ return nonBlank ?? null;
26
+ }
27
+ /**
28
+ * 登录(手动):只负责打开 Boss 登录页,让用户在浏览器中自行完成登录。
29
+ * 不做登录态校验/等待/超时判断;成功与否由后续命令自行体现。
30
+ */
31
+ export async function runLogin() {
32
+ // 登录必须可见:即使之前已启动 headless 会话,也需要重启为 headful。
33
+ process.env.BOSS_BROWSER_HEADLESS = 'false';
34
+ const existing = getBrowserRef();
35
+ try {
36
+ const args = existing?.process?.()?.spawnargs ?? [];
37
+ const isHeadless = wasLastChromeLaunchHeadless() ||
38
+ args.some((a) => typeof a === 'string' && a.startsWith('--headless'));
39
+ if (existing?.connected && isHeadless) {
40
+ await disconnectBrowserSession().catch(() => { });
41
+ }
42
+ }
43
+ catch {
44
+ // ignore
45
+ }
46
+ let browser = null;
47
+ try {
48
+ browser = (await ensureAndGetBrowser()) ?? (getBrowserRef() ?? null);
49
+ if (!browser) {
50
+ await ensureBrowserSession();
51
+ browser = getBrowserRef() ?? null;
52
+ }
53
+ }
54
+ catch (e) {
55
+ const msg = e instanceof Error ? e.message : String(e);
56
+ throw new Error(`连接浏览器失败:${msg}`);
57
+ }
58
+ if (!browser) {
59
+ throw new Error('无法获取浏览器实例,登录失败。');
60
+ }
61
+ let page = getPageRef() ?? null;
62
+ if (!page || page.isClosed()) {
63
+ page = (await pickExistingPage(browser)) ?? (await browser.newPage());
64
+ }
65
+ setSessionPage(page);
66
+ await page.bringToFront();
67
+ await page.goto(BOSS_LOGIN_URL, { waitUntil: 'load', timeout: 60_000 });
68
+ await detachBrowserSession();
69
+ // 不做任何登录校验:只把浏览器打开到登录页;立即断开 CDP,CLI 不与浏览器进程长期绑定。
70
+ return [
71
+ `已在浏览器中打开 Boss 登录页:${BOSS_LOGIN_URL}`,
72
+ '本命令已同步结束并立即返回,CLI 不会等待 / 轮询 / 校验登录结果。',
73
+ 'Agent调用方:把控制权交还给人类,不要在这一步自行 sleep / poll / retry',
74
+ ].join('\n');
75
+ }
76
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/toolset/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Frame, Page } from 'puppeteer-core';
2
+ export declare function isBossChatSearchUrl(url: string): boolean;
3
+ export declare function assertNormalSearchPageReadyForPreview(page: Page): Promise<Frame>;
4
+ export declare function readNormalSearchSelectedJobLabel(frame: Frame): Promise<string>;
5
+ /**
6
+ * 在常规搜索页(iframe)切换当前岗位。岗位选项常驻 `.job-name`(即账号自己的开放职位),
7
+ * 模糊匹配 keyword 后点击,等待 `.search-current-job` 变更。keyword 为空则返回当前岗位。
8
+ */
9
+ export declare function selectNormalSearchJob(frame: Frame, keyword: string): Promise<string>;
10
+ export declare function openNormalSearchResumePreview(frame: Frame, target: string): Promise<boolean>;
11
+ export declare function runNormalSearch(keyword?: string, jobKeyword?: string): Promise<string>;
12
+ //# sourceMappingURL=normal-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normal-search.d.ts","sourceRoot":"","sources":["../../src/toolset/normal-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAwBlD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWxD;AAqDD,wBAAsB,qCAAqC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAOtF;AA0DD,wBAAsB,gCAAgC,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpF;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyD1F;AAED,wBAAsB,6BAA6B,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAkClG;AAwED,wBAAsB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B5F"}
@@ -0,0 +1,301 @@
1
+ import { RESUME_PREVIEW_OPEN_GAP_MS, sleepRandom } from '../browser/index.js';
2
+ import { withBossSessionPage } from '../common/boss_session_page.js';
3
+ import { ensurePage } from '../common/ensure_page.js';
4
+ const BOSS_CHAT_SEARCH_URL = 'https://www.zhipin.com/web/chat/search';
5
+ const SEARCH_FRAME_READY_TIMEOUT_MS = 18_000;
6
+ const SEARCH_RESULT_SETTLE_MS = { min: 900, max: 1600 };
7
+ const JOB_SWITCH_SETTLE_MS = { min: 700, max: 1300 };
8
+ export function isBossChatSearchUrl(url) {
9
+ try {
10
+ const u = new URL(url);
11
+ if (!u.hostname.includes('zhipin.com')) {
12
+ return false;
13
+ }
14
+ const p = u.pathname.replace(/\/+$/, '') || '/';
15
+ return p === '/web/chat/search';
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ async function getSearchFrame(page) {
22
+ const iframe = await page.waitForSelector('iframe[name="searchFrame"]', {
23
+ timeout: SEARCH_FRAME_READY_TIMEOUT_MS,
24
+ });
25
+ if (!iframe) {
26
+ throw new Error('未找到常规搜索 iframe(iframe[name="searchFrame"])。');
27
+ }
28
+ const deadline = Date.now() + SEARCH_FRAME_READY_TIMEOUT_MS;
29
+ while (Date.now() < deadline) {
30
+ const frame = await iframe.contentFrame();
31
+ if (frame && frame.url().includes('/web/frame/search')) {
32
+ return frame;
33
+ }
34
+ await sleepRandom(120, 220);
35
+ }
36
+ const iframeSrc = (await page.evaluate(`(() => document.querySelector('iframe[name="searchFrame"]')?.getAttribute("src") ?? "")()`));
37
+ const frameUrls = page.frames().map((f) => f.url()).join(' | ');
38
+ throw new Error(`已检测到常规搜索 iframe,但无法获取其页面上下文。iframe src:${iframeSrc || 'unknown'};frames:${frameUrls || 'empty'}`);
39
+ }
40
+ async function ensureSearchFrameReady(frame) {
41
+ await frame.waitForFunction(`(() => {
42
+ const input = document.querySelector(".search-input");
43
+ if (!(input instanceof HTMLInputElement)) return false;
44
+ const list = document.querySelector(".geek-list-wrap, .card-list");
45
+ const hasCard = document.querySelectorAll(".geek-info-card").length > 0;
46
+ const empty = document.querySelector(".empty-tips");
47
+ return !!list || hasCard || !!empty;
48
+ })()`, { timeout: SEARCH_FRAME_READY_TIMEOUT_MS });
49
+ }
50
+ async function ensureInNormalSearchPage(page) {
51
+ await ensurePage(page, {
52
+ name: '常规搜索页',
53
+ targetUrl: BOSS_CHAT_SEARCH_URL,
54
+ matches: isBossChatSearchUrl,
55
+ });
56
+ const frame = await getSearchFrame(page);
57
+ await ensureSearchFrameReady(frame);
58
+ return frame;
59
+ }
60
+ export async function assertNormalSearchPageReadyForPreview(page) {
61
+ if (!isBossChatSearchUrl(page.url())) {
62
+ throw new Error('当前不在常规搜索页(/web/chat/search),请先通过 boss search 进入。');
63
+ }
64
+ const frame = await getSearchFrame(page);
65
+ await ensureSearchFrameReady(frame);
66
+ return frame;
67
+ }
68
+ async function runKeywordSearch(frame, keyword) {
69
+ const kwLiteral = JSON.stringify(keyword);
70
+ const ok = (await frame.evaluate(`(() => {
71
+ const input = document.querySelector(".search-input");
72
+ if (!(input instanceof HTMLInputElement)) return false;
73
+ const kw = ${kwLiteral};
74
+ input.focus();
75
+ input.value = kw;
76
+ input.dispatchEvent(new Event("input", { bubbles: true }));
77
+ input.dispatchEvent(new Event("change", { bubbles: true }));
78
+ input.dispatchEvent(new KeyboardEvent("keydown", {
79
+ key: "Enter",
80
+ code: "Enter",
81
+ keyCode: 13,
82
+ which: 13,
83
+ bubbles: true,
84
+ cancelable: true,
85
+ }));
86
+ input.dispatchEvent(new KeyboardEvent("keyup", {
87
+ key: "Enter",
88
+ code: "Enter",
89
+ keyCode: 13,
90
+ which: 13,
91
+ bubbles: true,
92
+ cancelable: true,
93
+ }));
94
+ return true;
95
+ })()`));
96
+ if (!ok) {
97
+ throw new Error('未找到常规搜索关键词输入框(.search-input)。');
98
+ }
99
+ await frame.waitForFunction(`((kw) => {
100
+ const input = document.querySelector(".search-input");
101
+ return input instanceof HTMLInputElement && input.value === kw;
102
+ })`, { timeout: 5_000 }, keyword);
103
+ await sleepRandom(SEARCH_RESULT_SETTLE_MS.min, SEARCH_RESULT_SETTLE_MS.max);
104
+ await ensureSearchFrameReady(frame);
105
+ }
106
+ async function readNormalSearchKeyword(frame) {
107
+ return (await frame.evaluate(`(() => document.querySelector(".search-input")?.value?.trim() ?? "")()`));
108
+ }
109
+ async function readCurrentSearchJob(frame) {
110
+ return (await frame.evaluate(`(() => (document.querySelector(".search-current-job")?.textContent ?? "").replace(/\\s+/g, " ").trim())()`));
111
+ }
112
+ export async function readNormalSearchSelectedJobLabel(frame) {
113
+ const label = await readCurrentSearchJob(frame);
114
+ return label || '默认';
115
+ }
116
+ /**
117
+ * 在常规搜索页(iframe)切换当前岗位。岗位选项常驻 `.job-name`(即账号自己的开放职位),
118
+ * 模糊匹配 keyword 后点击,等待 `.search-current-job` 变更。keyword 为空则返回当前岗位。
119
+ */
120
+ export async function selectNormalSearchJob(frame, keyword) {
121
+ const kw = keyword.trim();
122
+ if (!kw) {
123
+ return readNormalSearchSelectedJobLabel(frame);
124
+ }
125
+ const before = await readCurrentSearchJob(frame);
126
+ const kwLiteral = JSON.stringify(kw);
127
+ // 岗位下拉的选项(.job-name)常驻 DOM,但先点触发器展开以兼容折叠态。
128
+ await frame.evaluate(`(() => {
129
+ const h = document.querySelector(".search-job-list-C .ui-dropmenu-label")
130
+ || document.querySelector(".ui-dropmenu-label");
131
+ if (h instanceof HTMLElement) {
132
+ h.scrollIntoView({ block: "center", inline: "nearest" });
133
+ h.click();
134
+ }
135
+ })()`);
136
+ await sleepRandom(JOB_SWITCH_SETTLE_MS.min, JOB_SWITCH_SETTLE_MS.max);
137
+ const picked = (await frame.evaluate(`(() => {
138
+ const kw = ${kwLiteral};
139
+ const norm = (v) => (v ?? "").replace(/\\s+/g, "").trim().toLowerCase();
140
+ const items = Array.from(document.querySelectorAll(".job-name"));
141
+ if (items.length === 0) return { ok: false, reason: "empty" };
142
+ const target = items.find((el) => norm(el.textContent).includes(norm(kw)));
143
+ if (!(target instanceof HTMLElement)) {
144
+ return { ok: false, reason: "not_found", available: items.map((el) => (el.textContent ?? "").replace(/\\s+/g, " ").trim()) };
145
+ }
146
+ const label = (target.textContent ?? "").replace(/\\s+/g, " ").trim();
147
+ target.scrollIntoView({ block: "center", inline: "nearest" });
148
+ target.click();
149
+ return { ok: true, label };
150
+ })()`));
151
+ if (!picked.ok) {
152
+ if (picked.reason === 'not_found') {
153
+ const avail = (picked.available ?? []).join('|');
154
+ throw new Error(`未找到匹配岗位“${kw}”。可选岗位:${avail || '(空)'}`);
155
+ }
156
+ throw new Error('未找到常规搜索岗位下拉选项(.job-name)。');
157
+ }
158
+ const label = picked.label ?? kw;
159
+ try {
160
+ await frame.waitForFunction(`((prev) => {
161
+ const cur = (document.querySelector(".search-current-job")?.textContent ?? "").replace(/\\s+/g, " ").trim();
162
+ return cur.length > 0 && cur !== prev;
163
+ })`, { timeout: 8_000 }, before);
164
+ }
165
+ catch {
166
+ // 当前岗位文案未变(可能本就是该岗)——不阻断,返回读到的 label。
167
+ }
168
+ await sleepRandom(JOB_SWITCH_SETTLE_MS.min, JOB_SWITCH_SETTLE_MS.max);
169
+ return label;
170
+ }
171
+ export async function openNormalSearchResumePreview(frame, target) {
172
+ const targetLiteral = JSON.stringify(target.trim());
173
+ const opened = (await frame.evaluate(`(() => {
174
+ const raw = ${targetLiteral};
175
+ const bare = raw.replace(/\\*/g, "");
176
+ const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
177
+ const cards = Array.from(document.querySelectorAll(".geek-info-card"));
178
+ if (cards.length === 0) return false;
179
+ const targetCard =
180
+ cards.find((item) => {
181
+ const name = norm(item.querySelector(".name-label")?.textContent);
182
+ return name === raw || (!!bare && name.includes(bare));
183
+ }) ?? null;
184
+ if (!(targetCard instanceof HTMLElement)) return false;
185
+
186
+ function tryOpen(el) {
187
+ if (!(el instanceof HTMLElement)) return false;
188
+ const st = window.getComputedStyle(el);
189
+ if (st.pointerEvents === "none" || Number(st.opacity) < 0.3) return false;
190
+ el.scrollIntoView({ block: "center", inline: "nearest" });
191
+ el.click();
192
+ return true;
193
+ }
194
+
195
+ if (tryOpen(targetCard.querySelector(".name-label"))) return true;
196
+ if (tryOpen(targetCard.querySelector(".info-detail"))) return true;
197
+ if (tryOpen(targetCard.querySelector(".geek-info-main, .geek-card-main, .card-content"))) return true;
198
+ if (tryOpen(targetCard.querySelector("a"))) return true;
199
+ return tryOpen(targetCard);
200
+ })()`));
201
+ if (opened) {
202
+ await sleepRandom(RESUME_PREVIEW_OPEN_GAP_MS.min, RESUME_PREVIEW_OPEN_GAP_MS.max);
203
+ }
204
+ return opened;
205
+ }
206
+ async function readNormalSearchCandidates(frame) {
207
+ return (await frame.evaluate(`(() => {
208
+ const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
209
+ const unique = (items) => Array.from(new Set(items.map(norm).filter(Boolean)));
210
+ return Array.from(document.querySelectorAll(".geek-info-card")).map((card) => {
211
+ const labels = unique(Array.from(card.querySelectorAll(".card-label")).map((el) => el.textContent));
212
+ const tags = unique(
213
+ Array.from(card.querySelectorAll(".info-tags:not(.info-tags-measure) .info-tags-item"))
214
+ .map((el) => el.textContent),
215
+ );
216
+ const work = Array.from(card.querySelectorAll(".work-exp-box .work-exp-item"))
217
+ .map((el) => norm(el.textContent))
218
+ .filter(Boolean);
219
+ return {
220
+ name: norm(card.querySelector(".name-label")?.textContent),
221
+ active: norm(card.querySelector(".active-desc-text")?.textContent),
222
+ labels,
223
+ basicInfo: norm(card.querySelector(".info-labels")?.textContent),
224
+ summary: norm(card.querySelector(".info-detail")?.textContent),
225
+ tags,
226
+ expectation: norm(card.querySelector(".expect-exp-box")?.textContent),
227
+ work,
228
+ education: norm(card.querySelector(".edu-exp-box")?.textContent),
229
+ reason: norm(card.querySelector(".recommend-reason")?.textContent),
230
+ contactText: norm(card.querySelector(".btn-getcontact")?.textContent),
231
+ };
232
+ }).filter((item) => item.name);
233
+ })()`));
234
+ }
235
+ function renderNormalSearchCandidates(candidates, meta) {
236
+ const titleKeyword = meta.keyword ? `关键词:${meta.keyword}` : '关键词:默认/热门词';
237
+ const lines = [
238
+ `常规搜索结果(${titleKeyword}${meta.job ? `;当前岗位:${meta.job}` : ''})`,
239
+ `共 ${candidates.length} 人`,
240
+ ];
241
+ if (candidates.length === 0) {
242
+ return lines.join('\n');
243
+ }
244
+ lines.push('');
245
+ candidates.forEach((item, idx) => {
246
+ const labelText = item.labels.length > 0 ? `|标签:${item.labels.join('/')}` : '';
247
+ const activeText = item.active ? `|${item.active}` : '';
248
+ lines.push(`${idx + 1}. ${item.name}${activeText}${item.basicInfo ? `|${item.basicInfo}` : ''}${labelText}`);
249
+ if (item.summary) {
250
+ lines.push(` 摘要: ${item.summary}`);
251
+ }
252
+ if (item.tags.length > 0) {
253
+ lines.push(` 亮点: ${item.tags.join(' / ')}`);
254
+ }
255
+ if (item.expectation) {
256
+ lines.push(` ${item.expectation}`);
257
+ }
258
+ if (item.work.length > 0) {
259
+ lines.push(` 经历: ${item.work.join(';')}`);
260
+ }
261
+ if (item.education) {
262
+ lines.push(` ${item.education}`);
263
+ }
264
+ if (item.reason) {
265
+ lines.push(` ${item.reason}`);
266
+ }
267
+ });
268
+ return lines.join('\n');
269
+ }
270
+ export async function runNormalSearch(keyword, jobKeyword) {
271
+ const kw = (keyword ?? '').trim();
272
+ const jobKw = (jobKeyword ?? '').trim();
273
+ if (kw.length > 20) {
274
+ throw new Error('常规搜索关键词最多 20 个字符。');
275
+ }
276
+ try {
277
+ return await withBossSessionPage(async (page) => {
278
+ const frame = await ensureInNormalSearchPage(page);
279
+ if (jobKw) {
280
+ await selectNormalSearchJob(frame, jobKw);
281
+ }
282
+ if (kw) {
283
+ await runKeywordSearch(frame, kw);
284
+ }
285
+ const [currentKeyword, currentJob, candidates] = await Promise.all([
286
+ readNormalSearchKeyword(frame),
287
+ readCurrentSearchJob(frame),
288
+ readNormalSearchCandidates(frame),
289
+ ]);
290
+ return renderNormalSearchCandidates(candidates, {
291
+ keyword: currentKeyword || kw,
292
+ job: currentJob,
293
+ });
294
+ });
295
+ }
296
+ catch (e) {
297
+ const message = e instanceof Error ? e.message : String(e);
298
+ throw new Error(`读取常规搜索列表失败:${message}`);
299
+ }
300
+ }
301
+ //# sourceMappingURL=normal-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normal-search.js","sourceRoot":"","sources":["../../src/toolset/normal-search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export type PreviewOptions = {
2
+ candidateTarget: string;
3
+ };
4
+ export declare function runPreview(options: PreviewOptions): Promise<string>;
5
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/toolset/preview.ts"],"names":[],"mappings":"AAqCA,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAuFzE"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * 在线简历预览:须在「推荐」页、「深度搜索 aiform」页或「常规搜索」页且列表已加载;不自动跳转,否则报错。
3
+ */
4
+ import { join } from 'node:path';
5
+ import { ONLINE_RESUME_IFRAME_WAIT_MAX_MS, snapshotBossPageViewport } from '../browser/index.js';
6
+ import { withBossSessionPage } from '../common/boss_session_page.js';
7
+ import { closeBossPaywallPopupIfPresent, describeBossPaywallPopupIfPresent, waitForCResumeIframeOrPaywall, } from '../common/boss_paywall_popup.js';
8
+ import { captureCResumeIframeToFile, closeCResumePanel, safeResumeScreenshotFileBase, waitForVisibleCResumeIframeReady, } from '../common/c_resume_capture.js';
9
+ import { ensureAppDataLayout, RESUME_SCREENSHOTS_DIR } from '../config.js';
10
+ import { isResumeOcrEnabled, ocrResumePngToTextFile } from '../ocr/index.js';
11
+ import { ensureInDeepSearchPage, isBossChatAiFormUrl, openDeepSearchResumePreview, readAiFormSelectedJobLabel, } from './deep-search.js';
12
+ import { assertRecommendPageReadyForPreview, isBossChatRecommendUrl, openRecommendResumePreview, } from './recommend.js';
13
+ import { assertNormalSearchPageReadyForPreview, isBossChatSearchUrl, openNormalSearchResumePreview, readNormalSearchSelectedJobLabel, } from './normal-search.js';
14
+ export async function runPreview(options) {
15
+ const target = options.candidateTarget.trim();
16
+ if (!target) {
17
+ throw new Error('请提供候选人姓名。');
18
+ }
19
+ try {
20
+ return await withBossSessionPage(async (page) => {
21
+ const url = page.url();
22
+ let jobLine;
23
+ let savedOriginal;
24
+ let opened;
25
+ if (isBossChatAiFormUrl(url)) {
26
+ await ensureInDeepSearchPage(page);
27
+ const label = await readAiFormSelectedJobLabel(page);
28
+ jobLine = `当前岗位:${label}`;
29
+ savedOriginal = await snapshotBossPageViewport(page);
30
+ opened = await openDeepSearchResumePreview(page, target);
31
+ }
32
+ else if (isBossChatRecommendUrl(url)) {
33
+ const frame = await assertRecommendPageReadyForPreview(page);
34
+ jobLine = '当前岗位:当前推荐列表';
35
+ savedOriginal = await snapshotBossPageViewport(page);
36
+ opened = await openRecommendResumePreview(frame, target);
37
+ }
38
+ else if (isBossChatSearchUrl(url)) {
39
+ const frame = await assertNormalSearchPageReadyForPreview(page);
40
+ const label = await readNormalSearchSelectedJobLabel(frame);
41
+ jobLine = `当前岗位:${label}`;
42
+ savedOriginal = await snapshotBossPageViewport(page);
43
+ opened = await openNormalSearchResumePreview(frame, target);
44
+ }
45
+ else {
46
+ throw new Error('当前不在推荐列表页、深度搜索页或常规搜索页,无法预览候选人。');
47
+ }
48
+ if (!opened) {
49
+ throw new Error('未在列表中找到该候选人,或点击未能打开简历预览。');
50
+ }
51
+ const outcome = await waitForCResumeIframeOrPaywall(page, ONLINE_RESUME_IFRAME_WAIT_MAX_MS);
52
+ if (outcome !== 'iframe') {
53
+ const paywall = await describeBossPaywallPopupIfPresent(page);
54
+ await closeBossPaywallPopupIfPresent(page);
55
+ if (paywall) {
56
+ throw new Error(paywall);
57
+ }
58
+ throw new Error('点击后未出现在线简历 iframe(c-resume)。');
59
+ }
60
+ const ready = await waitForVisibleCResumeIframeReady(page);
61
+ if (!ready) {
62
+ throw new Error('在线简历 iframe 已出现,但内容未在预期时间内渲染完成。');
63
+ }
64
+ ensureAppDataLayout();
65
+ const fileName = `preview-${safeResumeScreenshotFileBase(target)}-${Date.now()}.png`;
66
+ const absPath = join(RESUME_SCREENSHOTS_DIR, fileName);
67
+ const ok = await captureCResumeIframeToFile(page, savedOriginal, absPath);
68
+ if (!ok) {
69
+ await closeCResumePanel(page);
70
+ throw new Error('在线简历 iframe 截图失败。');
71
+ }
72
+ const disclaimer = '说明:平台对在线简历的每日可查看次数有限,请按需使用、谨慎查看。';
73
+ if (!isResumeOcrEnabled()) {
74
+ return [jobLine, `简历预览截图:${absPath}`, '', disclaimer].join('\n');
75
+ }
76
+ try {
77
+ const ocr = await ocrResumePngToTextFile(absPath);
78
+ return [
79
+ jobLine,
80
+ `简历预览截图:${absPath}`,
81
+ '',
82
+ '在线简历 OCR 正文:',
83
+ '',
84
+ ocr.text,
85
+ '',
86
+ disclaimer,
87
+ ].join('\n');
88
+ }
89
+ catch (e) {
90
+ const msg = e instanceof Error ? e.message : String(e);
91
+ throw new Error(`简历预览截图已保存,但 OCR 失败:${msg}`);
92
+ }
93
+ });
94
+ }
95
+ catch (e) {
96
+ const message = e instanceof Error ? e.message : String(e);
97
+ throw new Error(`简历预览失败:${message}`);
98
+ }
99
+ }
100
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/toolset/preview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { Frame, Page } from 'puppeteer-core';
2
+ export type RecommendCandidate = {
3
+ geekId: string;
4
+ name: string;
5
+ salary: string;
6
+ baseInfo: string;
7
+ expect: string;
8
+ experience: string;
9
+ advantage: string;
10
+ highlights: string[];
11
+ canGreet: boolean;
12
+ hasHistoryChat: boolean;
13
+ /** 卡片为灰色「已看过」样式(如 `.candidate-card-wrap.has-viewed` / `.card-inner.has-viewed`) */
14
+ hasViewed: boolean;
15
+ };
16
+ export declare function isBossChatRecommendUrl(url: string): boolean;
17
+ export declare function selectRecommendJob(frame: Frame, keyword: string): Promise<string>;
18
+ export declare function ensureInRecommendPage(page: Page): Promise<Frame>;
19
+ /**
20
+ * 供 `preview` 使用:不导航;若当前主页面不在推荐页或未就绪推荐 iframe,直接抛错。
21
+ */
22
+ export declare function assertRecommendPageReady(page: Page, actionName: string): Promise<Frame>;
23
+ export declare function assertRecommendPageReadyForPreview(page: Page): Promise<Frame>;
24
+ export declare function readRecommendList(frame: Frame): Promise<RecommendCandidate[]>;
25
+ export declare function renderRecommendList(candidates: RecommendCandidate[]): string;
26
+ export declare function clickGreet(frame: Frame, target: string): Promise<{
27
+ message: string;
28
+ }>;
29
+ export declare function markGreetProduced(before: RecommendCandidate[], after: RecommendCandidate[]): void;
30
+ /**
31
+ * 在推荐 iframe 内根据姓名打开在线简历预览:点击候选人卡片主体 `.card-inner`(与侧栏「打招呼」分离)。
32
+ * 父页随后出现 `c-resume` iframe(如 `source=recommend`)。旧版仅有「在线简历」链接时仍尝试点击链接。
33
+ */
34
+ export declare function openRecommendResumePreview(frame: Frame, target: string): Promise<boolean>;
35
+ export declare function runRecommend(jobKeyword?: string): Promise<string>;
36
+ //# sourceMappingURL=recommend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend.d.ts","sourceRoot":"","sources":["../../src/toolset/recommend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAYlD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,mFAAmF;IACnF,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAWF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW3D;AA8FD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4DvF;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAStE;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,KAAK,CAAC,CAOhB;AAED,wBAAsB,kCAAkC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAEnF;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAmEnF;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAkD5E;AAED,wBAAsB,UAAU,CAC9B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAmE9B;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,kBAAkB,EAAE,EAC5B,KAAK,EAAE,kBAAkB,EAAE,GAC1B,IAAI,CAON;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAmD/F;AAED,wBAAsB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAavE"}