@viyzhu/boss-cli-fork 0.7.2-rc.1 → 0.7.3
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 +85 -56
- package/LICENSE +674 -674
- package/README.md +284 -276
- package/dist/browser/cdp_browser.d.ts +170 -88
- package/dist/browser/cdp_browser.d.ts.map +1 -1
- package/dist/browser/cdp_browser.js +674 -400
- package/dist/browser/cdp_browser.js.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/cli/cliRouter.js +61 -61
- package/dist/common/auth.js +49 -49
- package/dist/common/boss_modal.js +42 -42
- package/dist/common/boss_page_guards.js +247 -247
- package/dist/common/boss_paywall_popup.js +87 -87
- package/dist/common/boss_session_page.d.ts.map +1 -1
- package/dist/common/boss_session_page.js +16 -15
- package/dist/common/boss_session_page.js.map +1 -1
- package/dist/common/boss_sidebar_nav.js +24 -24
- package/dist/common/c_resume_capture.js +61 -61
- package/dist/toolset/action.d.ts.map +1 -1
- package/dist/toolset/action.js +237 -233
- package/dist/toolset/action.js.map +1 -1
- package/dist/toolset/chat.js +437 -437
- package/dist/toolset/deep-search.js +570 -570
- package/dist/toolset/jd.js +132 -132
- package/dist/toolset/list.js +48 -48
- package/dist/toolset/normal-search.js +158 -158
- package/dist/toolset/preview.d.ts.map +1 -1
- package/dist/toolset/preview.js +3 -3
- package/dist/toolset/preview.js.map +1 -1
- package/dist/toolset/recommend.js +207 -207
- package/package.json +69 -69
- package/skills/boss-frontend-analysis/SKILL.md +58 -58
- package/skills/boss-frontend-analysis/agents/openai.yaml +4 -4
- package/skills/boss-frontend-analysis/scripts/capture_boss_frontend.mjs +604 -604
|
@@ -41,13 +41,13 @@ async function getSearchFrame(page) {
|
|
|
41
41
|
throw new Error(`已检测到常规搜索 iframe,但无法获取其页面上下文。iframe src:${iframeSrc || 'unknown'};frames:${frameUrls || 'empty'}`);
|
|
42
42
|
}
|
|
43
43
|
async function ensureSearchFrameReady(frame) {
|
|
44
|
-
await frame.waitForFunction(`(() => {
|
|
45
|
-
const input = document.querySelector(".search-input");
|
|
46
|
-
if (!(input instanceof HTMLInputElement)) return false;
|
|
47
|
-
const list = document.querySelector(".geek-list-wrap, .card-list");
|
|
48
|
-
const hasCard = document.querySelectorAll(".geek-info-card").length > 0;
|
|
49
|
-
const empty = document.querySelector(".empty-tips");
|
|
50
|
-
return !!list || hasCard || !!empty;
|
|
44
|
+
await frame.waitForFunction(`(() => {
|
|
45
|
+
const input = document.querySelector(".search-input");
|
|
46
|
+
if (!(input instanceof HTMLInputElement)) return false;
|
|
47
|
+
const list = document.querySelector(".geek-list-wrap, .card-list");
|
|
48
|
+
const hasCard = document.querySelectorAll(".geek-info-card").length > 0;
|
|
49
|
+
const empty = document.querySelector(".empty-tips");
|
|
50
|
+
return !!list || hasCard || !!empty;
|
|
51
51
|
})()`, { timeout: SEARCH_FRAME_READY_TIMEOUT_MS });
|
|
52
52
|
}
|
|
53
53
|
async function ensureInNormalSearchPage(page) {
|
|
@@ -70,38 +70,38 @@ export async function assertNormalSearchPageReadyForPreview(page) {
|
|
|
70
70
|
}
|
|
71
71
|
async function runKeywordSearch(frame, keyword) {
|
|
72
72
|
const kwLiteral = JSON.stringify(keyword);
|
|
73
|
-
const ok = (await frame.evaluate(`(() => {
|
|
74
|
-
const input = document.querySelector(".search-input");
|
|
75
|
-
if (!(input instanceof HTMLInputElement)) return false;
|
|
76
|
-
const kw = ${kwLiteral};
|
|
77
|
-
input.focus();
|
|
78
|
-
input.value = kw;
|
|
79
|
-
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
80
|
-
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
81
|
-
input.dispatchEvent(new KeyboardEvent("keydown", {
|
|
82
|
-
key: "Enter",
|
|
83
|
-
code: "Enter",
|
|
84
|
-
keyCode: 13,
|
|
85
|
-
which: 13,
|
|
86
|
-
bubbles: true,
|
|
87
|
-
cancelable: true,
|
|
88
|
-
}));
|
|
89
|
-
input.dispatchEvent(new KeyboardEvent("keyup", {
|
|
90
|
-
key: "Enter",
|
|
91
|
-
code: "Enter",
|
|
92
|
-
keyCode: 13,
|
|
93
|
-
which: 13,
|
|
94
|
-
bubbles: true,
|
|
95
|
-
cancelable: true,
|
|
96
|
-
}));
|
|
97
|
-
return true;
|
|
73
|
+
const ok = (await frame.evaluate(`(() => {
|
|
74
|
+
const input = document.querySelector(".search-input");
|
|
75
|
+
if (!(input instanceof HTMLInputElement)) return false;
|
|
76
|
+
const kw = ${kwLiteral};
|
|
77
|
+
input.focus();
|
|
78
|
+
input.value = kw;
|
|
79
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
80
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
81
|
+
input.dispatchEvent(new KeyboardEvent("keydown", {
|
|
82
|
+
key: "Enter",
|
|
83
|
+
code: "Enter",
|
|
84
|
+
keyCode: 13,
|
|
85
|
+
which: 13,
|
|
86
|
+
bubbles: true,
|
|
87
|
+
cancelable: true,
|
|
88
|
+
}));
|
|
89
|
+
input.dispatchEvent(new KeyboardEvent("keyup", {
|
|
90
|
+
key: "Enter",
|
|
91
|
+
code: "Enter",
|
|
92
|
+
keyCode: 13,
|
|
93
|
+
which: 13,
|
|
94
|
+
bubbles: true,
|
|
95
|
+
cancelable: true,
|
|
96
|
+
}));
|
|
97
|
+
return true;
|
|
98
98
|
})()`));
|
|
99
99
|
if (!ok) {
|
|
100
100
|
throw new Error('未找到常规搜索关键词输入框(.search-input)。');
|
|
101
101
|
}
|
|
102
|
-
await frame.waitForFunction(`((kw) => {
|
|
103
|
-
const input = document.querySelector(".search-input");
|
|
104
|
-
return input instanceof HTMLInputElement && input.value === kw;
|
|
102
|
+
await frame.waitForFunction(`((kw) => {
|
|
103
|
+
const input = document.querySelector(".search-input");
|
|
104
|
+
return input instanceof HTMLInputElement && input.value === kw;
|
|
105
105
|
})`, { timeout: 5_000 }, keyword);
|
|
106
106
|
await sleepRandom(SEARCH_RESULT_SETTLE_MS.min, SEARCH_RESULT_SETTLE_MS.max);
|
|
107
107
|
await ensureSearchFrameReady(frame);
|
|
@@ -128,28 +128,28 @@ export async function selectNormalSearchJob(frame, keyword) {
|
|
|
128
128
|
const before = await readCurrentSearchJob(frame);
|
|
129
129
|
const kwLiteral = JSON.stringify(kw);
|
|
130
130
|
// 岗位下拉的选项(.job-name)常驻 DOM,但先点触发器展开以兼容折叠态。
|
|
131
|
-
await frame.evaluate(`(() => {
|
|
132
|
-
const h = document.querySelector(".search-job-list-C .ui-dropmenu-label")
|
|
133
|
-
|| document.querySelector(".ui-dropmenu-label");
|
|
134
|
-
if (h instanceof HTMLElement) {
|
|
135
|
-
h.scrollIntoView({ block: "center", inline: "nearest" });
|
|
136
|
-
h.click();
|
|
137
|
-
}
|
|
131
|
+
await frame.evaluate(`(() => {
|
|
132
|
+
const h = document.querySelector(".search-job-list-C .ui-dropmenu-label")
|
|
133
|
+
|| document.querySelector(".ui-dropmenu-label");
|
|
134
|
+
if (h instanceof HTMLElement) {
|
|
135
|
+
h.scrollIntoView({ block: "center", inline: "nearest" });
|
|
136
|
+
h.click();
|
|
137
|
+
}
|
|
138
138
|
})()`);
|
|
139
139
|
await sleepRandom(JOB_SWITCH_SETTLE_MS.min, JOB_SWITCH_SETTLE_MS.max);
|
|
140
|
-
const picked = (await frame.evaluate(`(() => {
|
|
141
|
-
const kw = ${kwLiteral};
|
|
142
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, "").trim().toLowerCase();
|
|
143
|
-
const items = Array.from(document.querySelectorAll(".job-name"));
|
|
144
|
-
if (items.length === 0) return { ok: false, reason: "empty" };
|
|
145
|
-
const target = items.find((el) => norm(el.textContent).includes(norm(kw)));
|
|
146
|
-
if (!(target instanceof HTMLElement)) {
|
|
147
|
-
return { ok: false, reason: "not_found", available: items.map((el) => (el.textContent ?? "").replace(/\\s+/g, " ").trim()) };
|
|
148
|
-
}
|
|
149
|
-
const label = (target.textContent ?? "").replace(/\\s+/g, " ").trim();
|
|
150
|
-
target.scrollIntoView({ block: "center", inline: "nearest" });
|
|
151
|
-
target.click();
|
|
152
|
-
return { ok: true, label };
|
|
140
|
+
const picked = (await frame.evaluate(`(() => {
|
|
141
|
+
const kw = ${kwLiteral};
|
|
142
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "").trim().toLowerCase();
|
|
143
|
+
const items = Array.from(document.querySelectorAll(".job-name"));
|
|
144
|
+
if (items.length === 0) return { ok: false, reason: "empty" };
|
|
145
|
+
const target = items.find((el) => norm(el.textContent).includes(norm(kw)));
|
|
146
|
+
if (!(target instanceof HTMLElement)) {
|
|
147
|
+
return { ok: false, reason: "not_found", available: items.map((el) => (el.textContent ?? "").replace(/\\s+/g, " ").trim()) };
|
|
148
|
+
}
|
|
149
|
+
const label = (target.textContent ?? "").replace(/\\s+/g, " ").trim();
|
|
150
|
+
target.scrollIntoView({ block: "center", inline: "nearest" });
|
|
151
|
+
target.click();
|
|
152
|
+
return { ok: true, label };
|
|
153
153
|
})()`));
|
|
154
154
|
if (!picked.ok) {
|
|
155
155
|
if (picked.reason === 'not_found') {
|
|
@@ -160,9 +160,9 @@ export async function selectNormalSearchJob(frame, keyword) {
|
|
|
160
160
|
}
|
|
161
161
|
const label = picked.label ?? kw;
|
|
162
162
|
try {
|
|
163
|
-
await frame.waitForFunction(`((prev) => {
|
|
164
|
-
const cur = (document.querySelector(".search-current-job")?.textContent ?? "").replace(/\\s+/g, " ").trim();
|
|
165
|
-
return cur.length > 0 && cur !== prev;
|
|
163
|
+
await frame.waitForFunction(`((prev) => {
|
|
164
|
+
const cur = (document.querySelector(".search-current-job")?.textContent ?? "").replace(/\\s+/g, " ").trim();
|
|
165
|
+
return cur.length > 0 && cur !== prev;
|
|
166
166
|
})`, { timeout: 8_000 }, before);
|
|
167
167
|
}
|
|
168
168
|
catch {
|
|
@@ -173,33 +173,33 @@ export async function selectNormalSearchJob(frame, keyword) {
|
|
|
173
173
|
}
|
|
174
174
|
export async function openNormalSearchResumePreview(frame, target) {
|
|
175
175
|
const targetLiteral = JSON.stringify(target.trim());
|
|
176
|
-
const opened = (await frame.evaluate(`(() => {
|
|
177
|
-
const raw = ${targetLiteral};
|
|
178
|
-
const bare = raw.replace(/\\*/g, "");
|
|
179
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
180
|
-
const cards = Array.from(document.querySelectorAll(".geek-info-card"));
|
|
181
|
-
if (cards.length === 0) return false;
|
|
182
|
-
const targetCard =
|
|
183
|
-
cards.find((item) => {
|
|
184
|
-
const name = norm(item.querySelector(".name-label")?.textContent);
|
|
185
|
-
return name === raw || (!!bare && name.includes(bare));
|
|
186
|
-
}) ?? null;
|
|
187
|
-
if (!(targetCard instanceof HTMLElement)) return false;
|
|
188
|
-
|
|
189
|
-
function tryOpen(el) {
|
|
190
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
191
|
-
const st = window.getComputedStyle(el);
|
|
192
|
-
if (st.pointerEvents === "none" || Number(st.opacity) < 0.3) return false;
|
|
193
|
-
el.scrollIntoView({ block: "center", inline: "nearest" });
|
|
194
|
-
el.click();
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (tryOpen(targetCard.querySelector(".name-label"))) return true;
|
|
199
|
-
if (tryOpen(targetCard.querySelector(".info-detail"))) return true;
|
|
200
|
-
if (tryOpen(targetCard.querySelector(".geek-info-main, .geek-card-main, .card-content"))) return true;
|
|
201
|
-
if (tryOpen(targetCard.querySelector("a"))) return true;
|
|
202
|
-
return tryOpen(targetCard);
|
|
176
|
+
const opened = (await frame.evaluate(`(() => {
|
|
177
|
+
const raw = ${targetLiteral};
|
|
178
|
+
const bare = raw.replace(/\\*/g, "");
|
|
179
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
180
|
+
const cards = Array.from(document.querySelectorAll(".geek-info-card"));
|
|
181
|
+
if (cards.length === 0) return false;
|
|
182
|
+
const targetCard =
|
|
183
|
+
cards.find((item) => {
|
|
184
|
+
const name = norm(item.querySelector(".name-label")?.textContent);
|
|
185
|
+
return name === raw || (!!bare && name.includes(bare));
|
|
186
|
+
}) ?? null;
|
|
187
|
+
if (!(targetCard instanceof HTMLElement)) return false;
|
|
188
|
+
|
|
189
|
+
function tryOpen(el) {
|
|
190
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
191
|
+
const st = window.getComputedStyle(el);
|
|
192
|
+
if (st.pointerEvents === "none" || Number(st.opacity) < 0.3) return false;
|
|
193
|
+
el.scrollIntoView({ block: "center", inline: "nearest" });
|
|
194
|
+
el.click();
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (tryOpen(targetCard.querySelector(".name-label"))) return true;
|
|
199
|
+
if (tryOpen(targetCard.querySelector(".info-detail"))) return true;
|
|
200
|
+
if (tryOpen(targetCard.querySelector(".geek-info-main, .geek-card-main, .card-content"))) return true;
|
|
201
|
+
if (tryOpen(targetCard.querySelector("a"))) return true;
|
|
202
|
+
return tryOpen(targetCard);
|
|
203
203
|
})()`));
|
|
204
204
|
if (opened) {
|
|
205
205
|
await sleepRandom(RESUME_PREVIEW_OPEN_GAP_MS.min, RESUME_PREVIEW_OPEN_GAP_MS.max);
|
|
@@ -216,82 +216,82 @@ export async function openNormalSearchResumePreview(frame, target) {
|
|
|
216
216
|
async function clickSearchPoolContactButton(frame, locator) {
|
|
217
217
|
const indexLiteral = JSON.stringify(locator.index ?? null);
|
|
218
218
|
const targetLiteral = JSON.stringify((locator.target ?? '').trim());
|
|
219
|
-
return (await frame.evaluate(`(() => {
|
|
220
|
-
const wantIndex = ${indexLiteral};
|
|
221
|
-
const raw = ${targetLiteral};
|
|
222
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
223
|
-
const loose = (v) => norm(v).replace(/[**\\s]/g, "").toLowerCase();
|
|
224
|
-
const cards = Array.from(document.querySelectorAll(".geek-info-card"));
|
|
225
|
-
if (cards.length === 0) return { kind: "empty" };
|
|
226
|
-
|
|
227
|
-
const describe = (card, i) =>
|
|
228
|
-
(i + 1) + ". " + norm(card.querySelector(".name-label")?.textContent);
|
|
229
|
-
|
|
230
|
-
let picked = null;
|
|
231
|
-
let pickedIndex = -1;
|
|
232
|
-
if (wantIndex !== null) {
|
|
233
|
-
if (wantIndex < 1 || wantIndex > cards.length) {
|
|
234
|
-
return { kind: "not_found", hint: cards.map(describe) };
|
|
235
|
-
}
|
|
236
|
-
picked = cards[wantIndex - 1];
|
|
237
|
-
pickedIndex = wantIndex - 1;
|
|
238
|
-
} else {
|
|
239
|
-
const key = loose(raw);
|
|
240
|
-
const hits = [];
|
|
241
|
-
cards.forEach((card, i) => {
|
|
242
|
-
const name = loose(card.querySelector(".name-label")?.textContent);
|
|
243
|
-
const summary = loose(card.querySelector(".info-detail")?.textContent);
|
|
244
|
-
if ((key && name.includes(key)) || (key && summary.includes(key))) {
|
|
245
|
-
hits.push({ card, i });
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
if (hits.length === 0) return { kind: "not_found", hint: cards.map(describe) };
|
|
249
|
-
if (hits.length > 1) return { kind: "ambiguous", hits: hits.map((h) => describe(h.card, h.i)) };
|
|
250
|
-
picked = hits[0].card;
|
|
251
|
-
pickedIndex = hits[0].i;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const name = norm(picked.querySelector(".name-label")?.textContent);
|
|
255
|
-
const summary = norm(picked.querySelector(".info-detail")?.textContent).slice(0, 80);
|
|
256
|
-
const btn = picked.querySelector("button.btn-getcontact, .btn-getcontact");
|
|
257
|
-
if (!(btn instanceof HTMLElement)) return { kind: "no_btn", name };
|
|
258
|
-
const text = norm(btn.textContent);
|
|
259
|
-
const cls = btn.className || "";
|
|
260
|
-
if (/disabled|forbid|ban/i.test(cls) || btn.getAttribute("disabled") !== null) {
|
|
261
|
-
return { kind: "disabled", name, text };
|
|
262
|
-
}
|
|
263
|
-
btn.scrollIntoView({ block: "center", inline: "nearest" });
|
|
264
|
-
btn.click();
|
|
265
|
-
return { kind: "clicked", index: pickedIndex + 1, name, text, summary };
|
|
219
|
+
return (await frame.evaluate(`(() => {
|
|
220
|
+
const wantIndex = ${indexLiteral};
|
|
221
|
+
const raw = ${targetLiteral};
|
|
222
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
223
|
+
const loose = (v) => norm(v).replace(/[**\\s]/g, "").toLowerCase();
|
|
224
|
+
const cards = Array.from(document.querySelectorAll(".geek-info-card"));
|
|
225
|
+
if (cards.length === 0) return { kind: "empty" };
|
|
226
|
+
|
|
227
|
+
const describe = (card, i) =>
|
|
228
|
+
(i + 1) + ". " + norm(card.querySelector(".name-label")?.textContent);
|
|
229
|
+
|
|
230
|
+
let picked = null;
|
|
231
|
+
let pickedIndex = -1;
|
|
232
|
+
if (wantIndex !== null) {
|
|
233
|
+
if (wantIndex < 1 || wantIndex > cards.length) {
|
|
234
|
+
return { kind: "not_found", hint: cards.map(describe) };
|
|
235
|
+
}
|
|
236
|
+
picked = cards[wantIndex - 1];
|
|
237
|
+
pickedIndex = wantIndex - 1;
|
|
238
|
+
} else {
|
|
239
|
+
const key = loose(raw);
|
|
240
|
+
const hits = [];
|
|
241
|
+
cards.forEach((card, i) => {
|
|
242
|
+
const name = loose(card.querySelector(".name-label")?.textContent);
|
|
243
|
+
const summary = loose(card.querySelector(".info-detail")?.textContent);
|
|
244
|
+
if ((key && name.includes(key)) || (key && summary.includes(key))) {
|
|
245
|
+
hits.push({ card, i });
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
if (hits.length === 0) return { kind: "not_found", hint: cards.map(describe) };
|
|
249
|
+
if (hits.length > 1) return { kind: "ambiguous", hits: hits.map((h) => describe(h.card, h.i)) };
|
|
250
|
+
picked = hits[0].card;
|
|
251
|
+
pickedIndex = hits[0].i;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const name = norm(picked.querySelector(".name-label")?.textContent);
|
|
255
|
+
const summary = norm(picked.querySelector(".info-detail")?.textContent).slice(0, 80);
|
|
256
|
+
const btn = picked.querySelector("button.btn-getcontact, .btn-getcontact");
|
|
257
|
+
if (!(btn instanceof HTMLElement)) return { kind: "no_btn", name };
|
|
258
|
+
const text = norm(btn.textContent);
|
|
259
|
+
const cls = btn.className || "";
|
|
260
|
+
if (/disabled|forbid|ban/i.test(cls) || btn.getAttribute("disabled") !== null) {
|
|
261
|
+
return { kind: "disabled", name, text };
|
|
262
|
+
}
|
|
263
|
+
btn.scrollIntoView({ block: "center", inline: "nearest" });
|
|
264
|
+
btn.click();
|
|
265
|
+
return { kind: "clicked", index: pickedIndex + 1, name, text, summary };
|
|
266
266
|
})()`));
|
|
267
267
|
}
|
|
268
268
|
async function readNormalSearchCandidates(frame) {
|
|
269
|
-
return (await frame.evaluate(`(() => {
|
|
270
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
271
|
-
const unique = (items) => Array.from(new Set(items.map(norm).filter(Boolean)));
|
|
272
|
-
return Array.from(document.querySelectorAll(".geek-info-card")).map((card) => {
|
|
273
|
-
const labels = unique(Array.from(card.querySelectorAll(".card-label")).map((el) => el.textContent));
|
|
274
|
-
const tags = unique(
|
|
275
|
-
Array.from(card.querySelectorAll(".info-tags:not(.info-tags-measure) .info-tags-item"))
|
|
276
|
-
.map((el) => el.textContent),
|
|
277
|
-
);
|
|
278
|
-
const work = Array.from(card.querySelectorAll(".work-exp-box .work-exp-item"))
|
|
279
|
-
.map((el) => norm(el.textContent))
|
|
280
|
-
.filter(Boolean);
|
|
281
|
-
return {
|
|
282
|
-
name: norm(card.querySelector(".name-label")?.textContent),
|
|
283
|
-
active: norm(card.querySelector(".active-desc-text")?.textContent),
|
|
284
|
-
labels,
|
|
285
|
-
basicInfo: norm(card.querySelector(".info-labels")?.textContent),
|
|
286
|
-
summary: norm(card.querySelector(".info-detail")?.textContent),
|
|
287
|
-
tags,
|
|
288
|
-
expectation: norm(card.querySelector(".expect-exp-box")?.textContent),
|
|
289
|
-
work,
|
|
290
|
-
education: norm(card.querySelector(".edu-exp-box")?.textContent),
|
|
291
|
-
reason: norm(card.querySelector(".recommend-reason")?.textContent),
|
|
292
|
-
contactText: norm(card.querySelector(".btn-getcontact")?.textContent),
|
|
293
|
-
};
|
|
294
|
-
}).filter((item) => item.name);
|
|
269
|
+
return (await frame.evaluate(`(() => {
|
|
270
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
271
|
+
const unique = (items) => Array.from(new Set(items.map(norm).filter(Boolean)));
|
|
272
|
+
return Array.from(document.querySelectorAll(".geek-info-card")).map((card) => {
|
|
273
|
+
const labels = unique(Array.from(card.querySelectorAll(".card-label")).map((el) => el.textContent));
|
|
274
|
+
const tags = unique(
|
|
275
|
+
Array.from(card.querySelectorAll(".info-tags:not(.info-tags-measure) .info-tags-item"))
|
|
276
|
+
.map((el) => el.textContent),
|
|
277
|
+
);
|
|
278
|
+
const work = Array.from(card.querySelectorAll(".work-exp-box .work-exp-item"))
|
|
279
|
+
.map((el) => norm(el.textContent))
|
|
280
|
+
.filter(Boolean);
|
|
281
|
+
return {
|
|
282
|
+
name: norm(card.querySelector(".name-label")?.textContent),
|
|
283
|
+
active: norm(card.querySelector(".active-desc-text")?.textContent),
|
|
284
|
+
labels,
|
|
285
|
+
basicInfo: norm(card.querySelector(".info-labels")?.textContent),
|
|
286
|
+
summary: norm(card.querySelector(".info-detail")?.textContent),
|
|
287
|
+
tags,
|
|
288
|
+
expectation: norm(card.querySelector(".expect-exp-box")?.textContent),
|
|
289
|
+
work,
|
|
290
|
+
education: norm(card.querySelector(".edu-exp-box")?.textContent),
|
|
291
|
+
reason: norm(card.querySelector(".recommend-reason")?.textContent),
|
|
292
|
+
contactText: norm(card.querySelector(".btn-getcontact")?.textContent),
|
|
293
|
+
};
|
|
294
|
+
}).filter((item) => item.name);
|
|
295
295
|
})()`));
|
|
296
296
|
}
|
|
297
297
|
function renderNormalSearchCandidates(candidates, meta) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/toolset/preview.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/toolset/preview.ts"],"names":[],"mappings":"AAyCA,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAyFzE"}
|
package/dist/toolset/preview.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 在线简历预览:须在「推荐」页、「深度搜索 aiform」页或「常规搜索」页且列表已加载;不自动跳转,否则报错。
|
|
3
3
|
*/
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { ONLINE_RESUME_IFRAME_WAIT_MAX_MS, snapshotBossPageViewport } from '../browser/index.js';
|
|
5
|
+
import { ONLINE_RESUME_IFRAME_WAIT_MAX_MS, snapshotBossPageViewport, withWindowVisible, } from '../browser/index.js';
|
|
6
6
|
import { withBossSessionPage } from '../common/boss_session_page.js';
|
|
7
7
|
import { closeBossPaywallPopupIfPresent, describeBossPaywallPopupIfPresent, waitForCResumeIframeOrPaywall, } from '../common/boss_paywall_popup.js';
|
|
8
8
|
import { captureCResumeIframeToFile, closeCResumePanel, safeResumeScreenshotFileBase, waitForVisibleCResumeIframeReady, } from '../common/c_resume_capture.js';
|
|
@@ -17,7 +17,7 @@ export async function runPreview(options) {
|
|
|
17
17
|
throw new Error('请提供候选人姓名。');
|
|
18
18
|
}
|
|
19
19
|
try {
|
|
20
|
-
return await withBossSessionPage(async (
|
|
20
|
+
return await withBossSessionPage((page) => withWindowVisible(page, async () => {
|
|
21
21
|
const url = page.url();
|
|
22
22
|
let jobLine;
|
|
23
23
|
let savedOriginal;
|
|
@@ -92,7 +92,7 @@ export async function runPreview(options) {
|
|
|
92
92
|
const msg = e instanceof Error ? e.message : String(e);
|
|
93
93
|
throw new Error(`简历预览截图已保存,但 OCR 失败:${msg}`);
|
|
94
94
|
}
|
|
95
|
-
});
|
|
95
|
+
}));
|
|
96
96
|
}
|
|
97
97
|
catch (e) {
|
|
98
98
|
const message = e instanceof Error ? e.message : String(e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/toolset/preview.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/toolset/preview.ts"],"names":[],"mappings":"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"}
|