@reconcrap/boss-recommend-mcp 2.0.45 → 2.0.47

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 (56) hide show
  1. package/bin/boss-recommend-mcp.js +4 -4
  2. package/config/screening-config.example.json +27 -27
  3. package/package.json +1 -1
  4. package/scripts/postinstall.cjs +44 -44
  5. package/skills/boss-chat/README.md +39 -39
  6. package/skills/boss-chat/SKILL.md +93 -93
  7. package/skills/boss-recommend-pipeline/README.md +12 -12
  8. package/skills/boss-recommend-pipeline/SKILL.md +180 -180
  9. package/skills/boss-recruit-pipeline/README.md +17 -17
  10. package/skills/boss-recruit-pipeline/SKILL.md +58 -58
  11. package/src/chat-mcp.js +1780 -1780
  12. package/src/chat-runtime-config.js +749 -749
  13. package/src/cli.js +3054 -3054
  14. package/src/core/boss-cards/index.js +199 -199
  15. package/src/core/browser/index.js +1453 -1446
  16. package/src/core/capture/index.js +1201 -1201
  17. package/src/core/cv-acquisition/index.js +238 -238
  18. package/src/core/cv-capture-target/index.js +299 -299
  19. package/src/core/greet-quota/index.js +54 -54
  20. package/src/core/infinite-list/index.js +1326 -1326
  21. package/src/core/reporting/legacy-csv.js +341 -341
  22. package/src/core/run/timing.js +33 -33
  23. package/src/core/screening/index.js +50 -3
  24. package/src/core/self-heal/index.js +973 -973
  25. package/src/core/self-heal/viewport.js +564 -564
  26. package/src/domains/chat/cards.js +137 -137
  27. package/src/domains/chat/constants.js +221 -221
  28. package/src/domains/chat/detail.js +1668 -1661
  29. package/src/domains/chat/index.js +7 -7
  30. package/src/domains/chat/jobs.js +592 -588
  31. package/src/domains/chat/page-guard.js +98 -98
  32. package/src/domains/chat/roots.js +56 -56
  33. package/src/domains/chat/run-service.js +1977 -1955
  34. package/src/domains/recommend/actions.js +457 -457
  35. package/src/domains/recommend/cards.js +243 -243
  36. package/src/domains/recommend/constants.js +165 -165
  37. package/src/domains/recommend/detail.js +36 -28
  38. package/src/domains/recommend/filters.js +610 -581
  39. package/src/domains/recommend/index.js +10 -10
  40. package/src/domains/recommend/jobs.js +316 -263
  41. package/src/domains/recommend/refresh.js +472 -472
  42. package/src/domains/recommend/roots.js +80 -80
  43. package/src/domains/recommend/run-service.js +75 -35
  44. package/src/domains/recommend/scopes.js +246 -245
  45. package/src/domains/recruit/actions.js +277 -277
  46. package/src/domains/recruit/cards.js +74 -74
  47. package/src/domains/recruit/constants.js +167 -167
  48. package/src/domains/recruit/detail.js +461 -460
  49. package/src/domains/recruit/index.js +9 -9
  50. package/src/domains/recruit/instruction-parser.js +451 -451
  51. package/src/domains/recruit/refresh.js +44 -44
  52. package/src/domains/recruit/roots.js +68 -68
  53. package/src/domains/recruit/run-service.js +1207 -1161
  54. package/src/domains/recruit/search.js +1202 -1149
  55. package/src/recommend-mcp.js +22 -22
  56. package/src/recruit-mcp.js +1338 -1338
@@ -1,165 +1,165 @@
1
- export const RECOMMEND_TARGET_URL = "https://www.zhipin.com/web/chat/recommend";
2
-
3
- export const RECOMMEND_PAGE_SCOPE_DEFAULT = "recommend";
4
-
5
- export const RECOMMEND_PAGE_SCOPE_STATUS = Object.freeze({
6
- recommend: "0",
7
- latest: "1",
8
- featured: "3"
9
- });
10
-
11
- export const RECOMMEND_PAGE_SCOPE_LABELS = Object.freeze({
12
- recommend: "推荐",
13
- latest: "最新",
14
- featured: "精选"
15
- });
16
-
17
- export const RECOMMEND_IFRAME_SELECTORS = Object.freeze([
18
- 'iframe[name="recommendFrame"]',
19
- 'iframe[src*="/web/frame/recommend/"]',
20
- "iframe"
21
- ]);
22
-
23
- export const RECOMMEND_PAGE_SCOPE_TAB_SELECTOR = [
24
- ".tab-list .tab-item[data-status]",
25
- ".tab-wrap .tab-item[data-status]",
26
- ".tab-item[data-status]",
27
- "[data-status]"
28
- ].join(", ");
29
-
30
- export const RECOMMEND_FILTER_SELECTORS = Object.freeze({
31
- trigger: ".filter-label-wrap",
32
- panel: ".filter-panel",
33
- groups: Object.freeze({
34
- recentNotView: ".filter-panel .check-box.recentNotView",
35
- degree: ".filter-panel .check-box.degree",
36
- gender: ".filter-panel .check-box.gender",
37
- school: ".filter-panel .check-box.school"
38
- }),
39
- option: ".default.option, .options .option, .option",
40
- activeOption: ".default.option.active, .options .option.active, .option.active",
41
- confirmButton: ".filter-panel .btn, .filter-panel button",
42
- checkBox: ".filter-panel .check-box"
43
- });
44
-
45
- export const RECOMMEND_FILTER_GROUP_ORDER = Object.freeze([
46
- "recentNotView",
47
- "degree",
48
- "gender",
49
- "school"
50
- ]);
51
-
52
- export const RECOMMEND_RECENT_NOT_VIEW_LABEL = "近14天没有";
53
-
54
- export const RECOMMEND_CARD_SELECTOR = [
55
- ".candidate-card-wrap .card-inner[data-geek]",
56
- ".candidate-card-wrap [data-geek]",
57
- "li.geek-info-card a[data-geekid]",
58
- "a[data-geekid]"
59
- ].join(", ");
60
-
61
- export const RECOMMEND_LIST_CONTAINER_SELECTORS = Object.freeze([
62
- ".recommend-list",
63
- ".recommend-list-wrap",
64
- ".candidate-list",
65
- ".candidate-card-list",
66
- ".candidate-card-wrap-list",
67
- ".geek-list",
68
- ".geek-list-wrap",
69
- ".card-list",
70
- ".list-wrap",
71
- ".content-list"
72
- ]);
73
-
74
- export const RECOMMEND_END_REFRESH_SELECTOR = [
75
- ".btn",
76
- "button",
77
- '[role="button"]',
78
- '[class*="refresh"]',
79
- '[ka*="refresh"]',
80
- "a"
81
- ].join(", ");
82
-
83
- export const RECOMMEND_BOTTOM_MARKER_SELECTORS = Object.freeze([
84
- ".finished-wrap",
85
- ".no-data-refresh",
86
- ".load-tips",
87
- ".empty-tip",
88
- ".empty-text",
89
- ".no-data",
90
- "[class*=\"finished\"]",
91
- "[class*=\"load-tips\"]"
92
- ]);
93
-
94
- export const DETAIL_POPUP_SELECTORS = Object.freeze([
95
- ".dialog-wrap.active",
96
- ".boss-popup__wrapper",
97
- ".boss-popup_wrapper",
98
- ".boss-dialog_wrapper",
99
- ".boss-dialog",
100
- ".resume-item-detail",
101
- ".geek-detail-modal",
102
- '[class*="popup"][class*="wrapper"]',
103
- '[class*="dialog"][class*="wrapper"]'
104
- ]);
105
-
106
- export const DETAIL_RESUME_IFRAME_SELECTORS = Object.freeze([
107
- 'iframe[src*="/web/frame/c-resume/"]',
108
- 'iframe[name*="resume"]'
109
- ]);
110
-
111
- export const DETAIL_CLOSE_SELECTORS = Object.freeze([
112
- ".boss-popup__close",
113
- ".popup-close",
114
- ".modal-close",
115
- ".dialog-close",
116
- ".close-btn",
117
- 'button[aria-label*="关闭"]',
118
- 'button[title*="关闭"]',
119
- ".icon-close",
120
- '[aria-label*="关闭"]',
121
- '[title*="关闭"]',
122
- '[class*="close"]'
123
- ]);
124
-
125
- export const DETAIL_NETWORK_PATTERNS = Object.freeze([
126
- /\/wapi\/zpjob\/view\/geek\/info\b/i,
127
- /\/wapi\/zpitem\/web\/boss\/[^?#]*\/geek\/info\b/i,
128
- /\/boss\/[^?#]*\/geek\/info\b/i,
129
- /\/geek\/info\b/i,
130
- /\/web\/frame\/c-resume\//i,
131
- /resume/i
132
- ]);
133
-
134
- export const FAVORITE_BUTTON_SELECTORS = Object.freeze([
135
- ".like-icon-and-text",
136
- ".resume-footer.item-operate [class*=\"collect\"]",
137
- ".resume-footer.item-operate [class*=\"favorite\"]",
138
- ".resume-footer.item-operate [class*=\"like\"]",
139
- ".resume-footer-wrap [class*=\"collect\"]",
140
- ".resume-footer-wrap [class*=\"favorite\"]",
141
- ".resume-footer-wrap [class*=\"like\"]",
142
- ".resume-footer [class*=\"collect\"]",
143
- ".resume-footer [class*=\"favorite\"]",
144
- ".resume-footer [class*=\"like\"]",
145
- ".resume-footer.item-operate button",
146
- ".resume-footer.item-operate .btn",
147
- ".resume-footer.item-operate span",
148
- ".resume-footer-wrap button",
149
- ".resume-footer-wrap .btn",
150
- ".resume-footer-wrap span",
151
- ".resume-footer button",
152
- ".resume-footer .btn",
153
- ".resume-footer span"
154
- ]);
155
-
156
- export const GREET_BUTTON_RECOMMEND_SELECTORS = Object.freeze([
157
- "button.btn-v2.btn-sure-v2.btn-greet",
158
- ".resume-footer.item-operate button.btn-v2",
159
- ".resume-footer-wrap button.btn-v2",
160
- ".resume-footer.item-operate button",
161
- ".resume-footer-wrap button",
162
- ".resume-footer button",
163
- "button[class*=\"greet\"]",
164
- "button[class*=\"sure\"]"
165
- ]);
1
+ export const RECOMMEND_TARGET_URL = "https://www.zhipin.com/web/chat/recommend";
2
+
3
+ export const RECOMMEND_PAGE_SCOPE_DEFAULT = "recommend";
4
+
5
+ export const RECOMMEND_PAGE_SCOPE_STATUS = Object.freeze({
6
+ recommend: "0",
7
+ latest: "1",
8
+ featured: "3"
9
+ });
10
+
11
+ export const RECOMMEND_PAGE_SCOPE_LABELS = Object.freeze({
12
+ recommend: "推荐",
13
+ latest: "最新",
14
+ featured: "精选"
15
+ });
16
+
17
+ export const RECOMMEND_IFRAME_SELECTORS = Object.freeze([
18
+ 'iframe[name="recommendFrame"]',
19
+ 'iframe[src*="/web/frame/recommend/"]',
20
+ "iframe"
21
+ ]);
22
+
23
+ export const RECOMMEND_PAGE_SCOPE_TAB_SELECTOR = [
24
+ ".tab-list .tab-item[data-status]",
25
+ ".tab-wrap .tab-item[data-status]",
26
+ ".tab-item[data-status]",
27
+ "[data-status]"
28
+ ].join(", ");
29
+
30
+ export const RECOMMEND_FILTER_SELECTORS = Object.freeze({
31
+ trigger: ".filter-label-wrap",
32
+ panel: ".filter-panel",
33
+ groups: Object.freeze({
34
+ recentNotView: ".filter-panel .check-box.recentNotView",
35
+ degree: ".filter-panel .check-box.degree",
36
+ gender: ".filter-panel .check-box.gender",
37
+ school: ".filter-panel .check-box.school"
38
+ }),
39
+ option: ".default.option, .options .option, .option",
40
+ activeOption: ".default.option.active, .options .option.active, .option.active",
41
+ confirmButton: ".filter-panel .btn, .filter-panel button",
42
+ checkBox: ".filter-panel .check-box"
43
+ });
44
+
45
+ export const RECOMMEND_FILTER_GROUP_ORDER = Object.freeze([
46
+ "recentNotView",
47
+ "degree",
48
+ "gender",
49
+ "school"
50
+ ]);
51
+
52
+ export const RECOMMEND_RECENT_NOT_VIEW_LABEL = "近14天没有";
53
+
54
+ export const RECOMMEND_CARD_SELECTOR = [
55
+ ".candidate-card-wrap .card-inner[data-geek]",
56
+ ".candidate-card-wrap [data-geek]",
57
+ "li.geek-info-card a[data-geekid]",
58
+ "a[data-geekid]"
59
+ ].join(", ");
60
+
61
+ export const RECOMMEND_LIST_CONTAINER_SELECTORS = Object.freeze([
62
+ ".recommend-list",
63
+ ".recommend-list-wrap",
64
+ ".candidate-list",
65
+ ".candidate-card-list",
66
+ ".candidate-card-wrap-list",
67
+ ".geek-list",
68
+ ".geek-list-wrap",
69
+ ".card-list",
70
+ ".list-wrap",
71
+ ".content-list"
72
+ ]);
73
+
74
+ export const RECOMMEND_END_REFRESH_SELECTOR = [
75
+ ".btn",
76
+ "button",
77
+ '[role="button"]',
78
+ '[class*="refresh"]',
79
+ '[ka*="refresh"]',
80
+ "a"
81
+ ].join(", ");
82
+
83
+ export const RECOMMEND_BOTTOM_MARKER_SELECTORS = Object.freeze([
84
+ ".finished-wrap",
85
+ ".no-data-refresh",
86
+ ".load-tips",
87
+ ".empty-tip",
88
+ ".empty-text",
89
+ ".no-data",
90
+ "[class*=\"finished\"]",
91
+ "[class*=\"load-tips\"]"
92
+ ]);
93
+
94
+ export const DETAIL_POPUP_SELECTORS = Object.freeze([
95
+ ".dialog-wrap.active",
96
+ ".boss-popup__wrapper",
97
+ ".boss-popup_wrapper",
98
+ ".boss-dialog_wrapper",
99
+ ".boss-dialog",
100
+ ".resume-item-detail",
101
+ ".geek-detail-modal",
102
+ '[class*="popup"][class*="wrapper"]',
103
+ '[class*="dialog"][class*="wrapper"]'
104
+ ]);
105
+
106
+ export const DETAIL_RESUME_IFRAME_SELECTORS = Object.freeze([
107
+ 'iframe[src*="/web/frame/c-resume/"]',
108
+ 'iframe[name*="resume"]'
109
+ ]);
110
+
111
+ export const DETAIL_CLOSE_SELECTORS = Object.freeze([
112
+ ".boss-popup__close",
113
+ ".popup-close",
114
+ ".modal-close",
115
+ ".dialog-close",
116
+ ".close-btn",
117
+ 'button[aria-label*="关闭"]',
118
+ 'button[title*="关闭"]',
119
+ ".icon-close",
120
+ '[aria-label*="关闭"]',
121
+ '[title*="关闭"]',
122
+ '[class*="close"]'
123
+ ]);
124
+
125
+ export const DETAIL_NETWORK_PATTERNS = Object.freeze([
126
+ /\/wapi\/zpjob\/view\/geek\/info\b/i,
127
+ /\/wapi\/zpitem\/web\/boss\/[^?#]*\/geek\/info\b/i,
128
+ /\/boss\/[^?#]*\/geek\/info\b/i,
129
+ /\/geek\/info\b/i,
130
+ /\/web\/frame\/c-resume\//i,
131
+ /resume/i
132
+ ]);
133
+
134
+ export const FAVORITE_BUTTON_SELECTORS = Object.freeze([
135
+ ".like-icon-and-text",
136
+ ".resume-footer.item-operate [class*=\"collect\"]",
137
+ ".resume-footer.item-operate [class*=\"favorite\"]",
138
+ ".resume-footer.item-operate [class*=\"like\"]",
139
+ ".resume-footer-wrap [class*=\"collect\"]",
140
+ ".resume-footer-wrap [class*=\"favorite\"]",
141
+ ".resume-footer-wrap [class*=\"like\"]",
142
+ ".resume-footer [class*=\"collect\"]",
143
+ ".resume-footer [class*=\"favorite\"]",
144
+ ".resume-footer [class*=\"like\"]",
145
+ ".resume-footer.item-operate button",
146
+ ".resume-footer.item-operate .btn",
147
+ ".resume-footer.item-operate span",
148
+ ".resume-footer-wrap button",
149
+ ".resume-footer-wrap .btn",
150
+ ".resume-footer-wrap span",
151
+ ".resume-footer button",
152
+ ".resume-footer .btn",
153
+ ".resume-footer span"
154
+ ]);
155
+
156
+ export const GREET_BUTTON_RECOMMEND_SELECTORS = Object.freeze([
157
+ "button.btn-v2.btn-sure-v2.btn-greet",
158
+ ".resume-footer.item-operate button.btn-v2",
159
+ ".resume-footer-wrap button.btn-v2",
160
+ ".resume-footer.item-operate button",
161
+ ".resume-footer-wrap button",
162
+ ".resume-footer button",
163
+ "button[class*=\"greet\"]",
164
+ "button[class*=\"sure\"]"
165
+ ]);
@@ -1,7 +1,8 @@
1
- import {
2
- clickNodeCenter,
3
- clickPoint,
4
- getFrameDocumentNodeId,
1
+ import {
2
+ clickNodeCenter,
3
+ clickPoint,
4
+ DETERMINISTIC_CLICK_OPTIONS,
5
+ getFrameDocumentNodeId,
5
6
  getNodeBox,
6
7
  getOuterHTML,
7
8
  pressKey,
@@ -309,12 +310,17 @@ export async function readRecommendDetailHtml(client, detailState) {
309
310
  };
310
311
  }
311
312
 
312
- export function isStaleRecommendNodeError(error) {
313
- const message = String(error?.message || error || "");
314
- return /Could not find node with given id|No node with given id|Node is detached|Cannot find node/i.test(message);
315
- }
316
-
317
- export async function findRecommendCardNodeForCandidateKey(client, {
313
+ export function isStaleRecommendNodeError(error) {
314
+ const message = String(error?.message || error || "");
315
+ return /Could not find node with given id|No node with given id|Node is detached|Cannot find node/i.test(message);
316
+ }
317
+
318
+ export function isRecommendDetailOpenMissError(error) {
319
+ const message = String(error?.message || error || "");
320
+ return /Candidate detail did not open|no known detail selectors mounted/i.test(message);
321
+ }
322
+
323
+ export async function findRecommendCardNodeForCandidateKey(client, {
318
324
  candidateKey = "",
319
325
  rootState = null,
320
326
  targetUrl = "",
@@ -447,18 +453,20 @@ export async function openRecommendCardDetailWithFreshRetry(client, {
447
453
  card_candidate: currentCandidate,
448
454
  retry_attempts: attempts
449
455
  };
450
- } catch (error) {
451
- const stale = isStaleRecommendNodeError(error);
452
- attempts.push({
453
- attempt: attemptIndex + 1,
454
- node_id: currentNodeId,
455
- stale_node: stale,
456
- error: error?.message || String(error)
457
- });
458
- if (!stale || attemptIndex >= limit - 1 || !candidateKey) {
459
- error.recommend_detail_open_attempts = attempts;
460
- throw error;
461
- }
456
+ } catch (error) {
457
+ const stale = isStaleRecommendNodeError(error);
458
+ const detailOpenMiss = isRecommendDetailOpenMissError(error);
459
+ attempts.push({
460
+ attempt: attemptIndex + 1,
461
+ node_id: currentNodeId,
462
+ stale_node: stale,
463
+ detail_open_miss: detailOpenMiss,
464
+ error: error?.message || String(error)
465
+ });
466
+ if ((!stale && !detailOpenMiss) || attemptIndex >= limit - 1 || !candidateKey) {
467
+ error.recommend_detail_open_attempts = attempts;
468
+ throw error;
469
+ }
462
470
 
463
471
  const resolved = await findRecommendCardNodeForCandidateKey(client, {
464
472
  candidateKey,
@@ -507,11 +515,11 @@ export async function closeRecommendDetail(client, {
507
515
  const closeTarget = await findVisibleCloseTarget(client, rootState.roots, DETAIL_CLOSE_SELECTORS);
508
516
  if (closeTarget) {
509
517
  try {
510
- if (closeTarget.center) {
511
- await clickPoint(client, closeTarget.center.x, closeTarget.center.y);
512
- } else {
513
- await clickNodeCenter(client, closeTarget.node_id);
514
- }
518
+ if (closeTarget.center) {
519
+ await clickPoint(client, closeTarget.center.x, closeTarget.center.y, DETERMINISTIC_CLICK_OPTIONS);
520
+ } else {
521
+ await clickNodeCenter(client, closeTarget.node_id, DETERMINISTIC_CLICK_OPTIONS);
522
+ }
515
523
  attempts.push({
516
524
  mode: "close-selector",
517
525
  selector: closeTarget.selector,
@@ -732,7 +740,7 @@ async function clickOutsideRecommendDetail(client, detailState) {
732
740
  root: target?.root || null
733
741
  };
734
742
  }
735
- await clickPoint(client, point.x, point.y);
743
+ await clickPoint(client, point.x, point.y, DETERMINISTIC_CLICK_OPTIONS);
736
744
  return {
737
745
  clicked: true,
738
746
  mode: "outside-modal-click",