@reconcrap/boss-recommend-mcp 1.1.12 → 1.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "1.1.12",
3
+ "version": "1.2.1",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -43,7 +43,7 @@ description: "Use when users ask to run Boss recommend-page filtering and screen
43
43
 
44
44
  阶段 A(页面就绪前,禁止问岗位):
45
45
 
46
- - 页面范围(`page_scope`)必须先确认:`recommend`(推荐)或 `featured`(精选)
46
+ - 页面范围(`page_scope`)必须先确认:`recommend`(推荐)/ `latest`(最新)/ `featured`(精选)
47
47
  - 即使 instruction 里已出现“推荐/精选”关键词,也必须显式二次确认 `page_confirmed=true` 与 `page_value`
48
48
  - 学校标签(`school_tag`,支持多选)
49
49
  - 若输入混合了有效与无效选项(如 `985,211,qs100`),必须忽略无效项并保留有效项;不要直接回退到“不限”
@@ -97,7 +97,7 @@ description: "Use when users ask to run Boss recommend-page filtering and screen
97
97
  - `instruction` (required)
98
98
  - `confirmation`
99
99
  - `page_confirmed`
100
- - `page_value` (`recommend|featured`)
100
+ - `page_value` (`recommend|latest|featured`)
101
101
  - `filters_confirmed`
102
102
  - `school_tag_confirmed`
103
103
  - `school_tag_value`(建议回传最终确认值,避免二轮调用丢失)
@@ -118,7 +118,7 @@ description: "Use when users ask to run Boss recommend-page filtering and screen
118
118
  - `max_greet_count_confirmed`
119
119
  - `max_greet_count_value` (integer)
120
120
  - `overrides`
121
- - `page_scope` (`recommend|featured`)
121
+ - `page_scope` (`recommend|latest|featured`)
122
122
  - `school_tag`(可传单值或数组,如 `["985","211"]`)
123
123
  - `degree`(可传单值或数组;如“本科及以上”应展开为 `["本科","硕士","博士"]`)
124
124
  - `gender`
@@ -161,6 +161,7 @@ description: "Use when users ask to run Boss recommend-page filtering and screen
161
161
  - recommend-search-cli 只负责应用推荐页筛选项。
162
162
  - 若 `page_scope=featured`:必须严格按 `search -> 切换精选tab(data-status=3) -> screen` 顺序执行。
163
163
  - 若 `page_scope=featured` 且校准文件缺失:必须先触发 `boss-recommend-mcp calibrate` 自动校准,成功后再继续。
164
+ - 若 `page_scope=latest`:执行路径与 `recommend` 一致(search + screen),但 screen 列表选择器应按最新 tab 结构读取(data-status=1)。
164
165
  - recommend-screen-cli 负责滚动推荐列表、打开详情、提取完整简历图、调用多模态模型判断,并按单次确认的 `post_action` 执行收藏或打招呼。
165
166
  - 精选页收藏仅允许“校准坐标 + 模拟点击”,成功判定仅看 network `add/del` 信号。
166
167
  - 详情页处理完成后必须关闭详情页并确认已关闭。
package/src/adapters.js CHANGED
@@ -20,6 +20,7 @@ const screenConfigTemplateDefaults = {
20
20
  const DEFAULT_RECOMMEND_SCREEN_TIMEOUT_MS = 24 * 60 * 60 * 1000;
21
21
  const PAGE_SCOPE_TO_TAB_STATUS = {
22
22
  recommend: "0",
23
+ latest: "1",
23
24
  featured: "3"
24
25
  };
25
26
 
@@ -76,6 +77,7 @@ function normalizePageScope(value) {
76
77
  const normalized = normalizeText(value).toLowerCase();
77
78
  if (!normalized) return null;
78
79
  if (["recommend", "推荐", "推荐页", "推荐页面"].includes(normalized)) return "recommend";
80
+ if (["latest", "最新", "最新页", "最新页面"].includes(normalized)) return "latest";
79
81
  if (["featured", "精选", "精选页", "精选页面", "精选牛人"].includes(normalized)) return "featured";
80
82
  return null;
81
83
  }
@@ -1568,10 +1570,12 @@ function buildRecommendTabStateExpression() {
1568
1570
  const activeTab = tabs.find((item) => item.active && item.status) || null;
1569
1571
  const featuredCount = doc.querySelectorAll('li.geek-info-card').length;
1570
1572
  const recommendCount = doc.querySelectorAll('ul.card-list > li.card-item').length;
1573
+ const latestCount = doc.querySelectorAll('.candidate-card-wrap .card-inner[data-geek], .candidate-card-wrap [data-geek]').length;
1571
1574
  let inferredStatus = activeTab?.status || null;
1572
1575
  if (!inferredStatus) {
1573
- if (featuredCount > 0 && recommendCount === 0) inferredStatus = '3';
1574
- else if (recommendCount > 0 && featuredCount === 0) inferredStatus = '0';
1576
+ if (featuredCount > 0 && recommendCount === 0 && latestCount === 0) inferredStatus = '3';
1577
+ else if (latestCount > 0 && featuredCount === 0 && recommendCount === 0) inferredStatus = '1';
1578
+ else if (recommendCount > 0 && featuredCount === 0 && latestCount === 0) inferredStatus = '0';
1575
1579
  }
1576
1580
  return {
1577
1581
  ok: true,
@@ -1579,7 +1583,8 @@ function buildRecommendTabStateExpression() {
1579
1583
  tabs,
1580
1584
  layout: {
1581
1585
  featured_count: featuredCount,
1582
- recommend_count: recommendCount
1586
+ recommend_count: recommendCount,
1587
+ latest_count: latestCount
1583
1588
  }
1584
1589
  };
1585
1590
  })()`;
@@ -1958,25 +1963,34 @@ function buildRecommendRefreshStateExpression() {
1958
1963
  const finishedWrap = Array.from(doc.querySelectorAll('.finished-wrap')).find((el) => isVisible(el)) || null;
1959
1964
  const refreshButton = Array.from(doc.querySelectorAll('.finished-wrap .btn.btn-refresh, .finished-wrap .btn-refresh, .no-data-refresh .btn-refresh'))
1960
1965
  .find((el) => isVisible(el)) || null;
1961
- const cards = Array.from(doc.querySelectorAll('ul.card-list > li.card-item'));
1962
- const candidateCards = cards.filter((card) => card.querySelector('.card-inner[data-geekid]'));
1963
- const finishedText = finishedWrap ? String(finishedWrap.textContent || '').replace(/\\s+/g, ' ').trim() : '';
1964
- const buttonText = refreshButton ? String(refreshButton.textContent || '').replace(/\\s+/g, ' ').trim() : '';
1965
- return {
1966
- ok: true,
1966
+ const cards = Array.from(doc.querySelectorAll('ul.card-list > li.card-item'));
1967
+ const candidateCards = cards.filter((card) => card.querySelector('.card-inner[data-geekid]'));
1968
+ const latestCards = Array.from(doc.querySelectorAll('.candidate-card-wrap .card-inner[data-geek], .candidate-card-wrap [data-geek]'));
1969
+ const tabs = Array.from(doc.querySelectorAll('li.tab-item[data-status], li[data-status][class*="tab"]'));
1970
+ const activeTab = tabs.find((node) => /(?:^|\\s)(?:curr|current|active|selected)(?:\\s|$)/i.test(String(node.className || ''))) || null;
1971
+ const activeStatus = activeTab ? String(activeTab.getAttribute('data-status') || '') : '';
1972
+ const inferredStatus = activeStatus
1973
+ || (latestCards.length > 0 && candidateCards.length === 0 ? '1' : candidateCards.length > 0 ? '0' : '');
1974
+ const effectiveCandidates = inferredStatus === '1' ? latestCards : candidateCards;
1975
+ const finishedText = finishedWrap ? String(finishedWrap.textContent || '').replace(/\\s+/g, ' ').trim() : '';
1976
+ const buttonText = refreshButton ? String(refreshButton.textContent || '').replace(/\\s+/g, ' ').trim() : '';
1977
+ return {
1978
+ ok: true,
1967
1979
  frame_url: (() => {
1968
1980
  try { return String(frame.contentWindow.location.href || ''); } catch { return ''; }
1969
1981
  })(),
1970
- finished_wrap_visible: Boolean(finishedWrap),
1971
- finished_wrap_text: finishedText || null,
1972
- refresh_button_visible: Boolean(refreshButton),
1973
- refresh_button_text: buttonText || null,
1974
- candidate_count: candidateCards.length,
1975
- total_card_count: cards.length,
1976
- list_ready: candidateCards.length > 0
1977
- };
1978
- })()`;
1979
- }
1982
+ finished_wrap_visible: Boolean(finishedWrap),
1983
+ finished_wrap_text: finishedText || null,
1984
+ refresh_button_visible: Boolean(refreshButton),
1985
+ refresh_button_text: buttonText || null,
1986
+ candidate_count: effectiveCandidates.length,
1987
+ recommend_candidate_count: candidateCards.length,
1988
+ latest_candidate_count: latestCards.length,
1989
+ total_card_count: Math.max(cards.length, latestCards.length),
1990
+ list_ready: effectiveCandidates.length > 0
1991
+ };
1992
+ })()`;
1993
+ }
1980
1994
 
1981
1995
  function buildRecommendRefreshClickExpression() {
1982
1996
  return `(() => {
package/src/cli.js CHANGED
@@ -187,6 +187,7 @@ function normalizePageScope(value) {
187
187
  const normalized = String(value || "").trim().toLowerCase();
188
188
  if (!normalized) return null;
189
189
  if (["recommend", "推荐", "推荐页", "推荐页面"].includes(normalized)) return "recommend";
190
+ if (["latest", "最新", "最新页", "最新页面"].includes(normalized)) return "latest";
190
191
  if (["featured", "精选", "精选页", "精选页面", "精选牛人"].includes(normalized)) return "featured";
191
192
  return null;
192
193
  }