@viyzhu/boss-cli-fork 0.7.2 → 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.
@@ -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) {