@reconcrap/boss-recommend-mcp 1.1.11 → 1.2.0
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 +1 -1
- package/skills/boss-recommend-pipeline/SKILL.md +4 -3
- package/src/adapters.js +33 -19
- package/src/cli.js +1 -0
- package/src/index.js +2 -2
- package/src/parser.js +9 -4
- package/src/pipeline.js +7 -3
- package/src/test-adapters-runtime.js +107 -0
- package/src/test-parser.js +16 -0
- package/src/test-pipeline.js +78 -1
- package/vendor/boss-recommend-screen-cli/boss-recommend-screen-cli.cjs +528 -104
- package/vendor/boss-recommend-screen-cli/test-recoverable-resume-failures.cjs +187 -1
- package/vendor/boss-recommend-search-cli/src/cli.js +14 -6
- package/vendor/boss-recommend-search-cli/src/test-job-selection.js +2 -0
package/package.json
CHANGED
|
@@ -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
|
|
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 (
|
|
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
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
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:
|
|
1975
|
-
|
|
1976
|
-
|
|
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
|
}
|
package/src/index.js
CHANGED
|
@@ -151,7 +151,7 @@ function createRunInputSchema() {
|
|
|
151
151
|
page_confirmed: { type: "boolean" },
|
|
152
152
|
page_value: {
|
|
153
153
|
type: "string",
|
|
154
|
-
enum: ["recommend", "featured"]
|
|
154
|
+
enum: ["recommend", "featured", "latest"]
|
|
155
155
|
},
|
|
156
156
|
filters_confirmed: { type: "boolean" },
|
|
157
157
|
school_tag_confirmed: { type: "boolean" },
|
|
@@ -227,7 +227,7 @@ function createRunInputSchema() {
|
|
|
227
227
|
properties: {
|
|
228
228
|
page_scope: {
|
|
229
229
|
type: "string",
|
|
230
|
-
enum: ["recommend", "featured"]
|
|
230
|
+
enum: ["recommend", "featured", "latest"]
|
|
231
231
|
},
|
|
232
232
|
school_tag: {
|
|
233
233
|
oneOf: [
|
package/src/parser.js
CHANGED
|
@@ -34,10 +34,11 @@ const POST_ACTION_LABELS = {
|
|
|
34
34
|
greet: "直接沟通",
|
|
35
35
|
none: "什么也不做"
|
|
36
36
|
};
|
|
37
|
-
const PAGE_SCOPE_OPTIONS = ["recommend", "featured"];
|
|
37
|
+
const PAGE_SCOPE_OPTIONS = ["recommend", "featured", "latest"];
|
|
38
38
|
const PAGE_SCOPE_LABELS = {
|
|
39
39
|
recommend: "推荐",
|
|
40
|
-
featured: "精选"
|
|
40
|
+
featured: "精选",
|
|
41
|
+
latest: "最新"
|
|
41
42
|
};
|
|
42
43
|
const LEADING_NOISE_PATTERNS = [
|
|
43
44
|
/^使用boss-recommend-pipeline skills/i,
|
|
@@ -103,6 +104,7 @@ const META_CLAUSE_PATTERNS = [
|
|
|
103
104
|
/帮我|请|运行|skill/i
|
|
104
105
|
];
|
|
105
106
|
const FEATURED_SCOPE_PATTERN = /(?:精选牛人|精选页|精选页面|精选tab|精选标签|tab[^。;;\n]{0,6}精选|精选)/i;
|
|
107
|
+
const LATEST_SCOPE_PATTERN = /(?:最新页|最新页面|最新tab|最新标签|tab[^。;;\n]{0,6}最新|最新)/i;
|
|
106
108
|
const RECOMMEND_SCOPE_PATTERN = /(?:推荐页|推荐页面|推荐tab|推荐标签|tab[^。;;\n]{0,6}推荐|推荐)/i;
|
|
107
109
|
|
|
108
110
|
function normalizeText(input) {
|
|
@@ -278,11 +280,13 @@ function normalizePageScope(value) {
|
|
|
278
280
|
if (!normalized) return null;
|
|
279
281
|
if (["recommend", "推荐", "推荐页", "推荐页面"].includes(normalized)) return "recommend";
|
|
280
282
|
if (["featured", "精选", "精选页", "精选页面", "精选牛人"].includes(normalized)) return "featured";
|
|
283
|
+
if (["latest", "最新", "最新页", "最新页面"].includes(normalized)) return "latest";
|
|
281
284
|
return PAGE_SCOPE_OPTIONS.includes(normalized) ? normalized : null;
|
|
282
285
|
}
|
|
283
286
|
|
|
284
287
|
function extractPageScope(text) {
|
|
285
288
|
if (FEATURED_SCOPE_PATTERN.test(text)) return "featured";
|
|
289
|
+
if (LATEST_SCOPE_PATTERN.test(text)) return "latest";
|
|
286
290
|
if (RECOMMEND_SCOPE_PATTERN.test(text)) return "recommend";
|
|
287
291
|
return null;
|
|
288
292
|
}
|
|
@@ -612,11 +616,12 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
|
|
|
612
616
|
if (needs_page_confirmation) {
|
|
613
617
|
pending_questions.push({
|
|
614
618
|
field: "page_scope",
|
|
615
|
-
question: "请确认本次在推荐里的哪个页面执行筛选:推荐
|
|
619
|
+
question: "请确认本次在推荐里的哪个页面执行筛选:推荐 / 精选 / 最新。",
|
|
616
620
|
value: pageScopeResolution.proposed_page_scope,
|
|
617
621
|
options: [
|
|
618
622
|
{ label: PAGE_SCOPE_LABELS.recommend, value: "recommend" },
|
|
619
|
-
{ label: PAGE_SCOPE_LABELS.featured, value: "featured" }
|
|
623
|
+
{ label: PAGE_SCOPE_LABELS.featured, value: "featured" },
|
|
624
|
+
{ label: PAGE_SCOPE_LABELS.latest, value: "latest" }
|
|
620
625
|
]
|
|
621
626
|
});
|
|
622
627
|
}
|
package/src/pipeline.js
CHANGED
|
@@ -18,14 +18,17 @@ const FORCED_RECENT_NOT_VIEW_ON_SCREEN_RECOVERY = "近14天没有";
|
|
|
18
18
|
const MAX_SCREEN_AUTO_RECOVERY_ATTEMPTS = 5;
|
|
19
19
|
const PAGE_SCOPE_TO_TAB_STATUS = {
|
|
20
20
|
recommend: "0",
|
|
21
|
+
latest: "1",
|
|
21
22
|
featured: "3"
|
|
22
23
|
};
|
|
23
24
|
const TAB_STATUS_TO_PAGE_SCOPE = {
|
|
24
25
|
"0": "recommend",
|
|
26
|
+
"1": "latest",
|
|
25
27
|
"3": "featured"
|
|
26
28
|
};
|
|
27
29
|
const PAGE_SCOPE_LABELS = {
|
|
28
30
|
recommend: "推荐",
|
|
31
|
+
latest: "最新",
|
|
29
32
|
featured: "精选"
|
|
30
33
|
};
|
|
31
34
|
|
|
@@ -41,6 +44,7 @@ function normalizePageScope(value) {
|
|
|
41
44
|
const normalized = normalizeText(value).toLowerCase();
|
|
42
45
|
if (!normalized) return null;
|
|
43
46
|
if (["recommend", "推荐", "推荐页", "推荐页面"].includes(normalized)) return "recommend";
|
|
47
|
+
if (["latest", "最新", "最新页", "最新页面"].includes(normalized)) return "latest";
|
|
44
48
|
if (["featured", "精选", "精选页", "精选页面", "精选牛人"].includes(normalized)) return "featured";
|
|
45
49
|
return null;
|
|
46
50
|
}
|
|
@@ -892,7 +896,7 @@ export async function runRecommendPipeline(
|
|
|
892
896
|
};
|
|
893
897
|
|
|
894
898
|
const ensureSelectedPageTab = async () => {
|
|
895
|
-
if (selectedPage
|
|
899
|
+
if (selectedPage === "recommend") {
|
|
896
900
|
activeTabStatus = selectedTabStatus;
|
|
897
901
|
return {
|
|
898
902
|
ok: true,
|
|
@@ -1073,8 +1077,8 @@ export async function runRecommendPipeline(
|
|
|
1073
1077
|
ensurePipelineNotAborted(runtimeHooks.signal);
|
|
1074
1078
|
runtimeHooks.setStage(
|
|
1075
1079
|
"screen",
|
|
1076
|
-
selectedPage
|
|
1077
|
-
?
|
|
1080
|
+
selectedPage !== "recommend"
|
|
1081
|
+
? `search 完成,已切换到${PAGE_SCOPE_LABELS[selectedPage]} tab,开始执行 recommend screen。`
|
|
1078
1082
|
: "search 完成,开始执行 recommend screen。"
|
|
1079
1083
|
);
|
|
1080
1084
|
} else {
|
|
@@ -177,6 +177,13 @@ function testPreflightRecommendShouldKeepFavoriteCalibrationOptional() {
|
|
|
177
177
|
assert.equal(check.optional, true);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
function testPreflightLatestShouldKeepFavoriteCalibrationOptional() {
|
|
181
|
+
const preflight = runPipelinePreflight(process.cwd(), { pageScope: "latest" });
|
|
182
|
+
const check = (preflight.checks || []).find((item) => item?.key === "favorite_calibration");
|
|
183
|
+
assert.equal(Boolean(check), true);
|
|
184
|
+
assert.equal(check.optional, true);
|
|
185
|
+
}
|
|
186
|
+
|
|
180
187
|
async function testEnsureFeaturedCalibrationReadyShouldAutoCalibrate() {
|
|
181
188
|
const previousHome = process.env.BOSS_RECOMMEND_HOME;
|
|
182
189
|
const previousCodexHome = process.env.CODEX_HOME;
|
|
@@ -282,6 +289,43 @@ async function testSearchCliShouldPassPageScopeArgument() {
|
|
|
282
289
|
}
|
|
283
290
|
}
|
|
284
291
|
|
|
292
|
+
async function testSearchCliShouldPassLatestPageScopeWithoutCalibration() {
|
|
293
|
+
const workspaceRoot = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-search-page-scope-latest-"));
|
|
294
|
+
const cliDir = path.join(workspaceRoot, "boss-recommend-search-cli", "src");
|
|
295
|
+
fs.mkdirSync(cliDir, { recursive: true });
|
|
296
|
+
const cliPath = path.join(cliDir, "cli.js");
|
|
297
|
+
fs.writeFileSync(
|
|
298
|
+
cliPath,
|
|
299
|
+
[
|
|
300
|
+
"#!/usr/bin/env node",
|
|
301
|
+
"console.log(JSON.stringify({ status: 'COMPLETED', result: { argv: process.argv.slice(2) } }));"
|
|
302
|
+
].join("\n"),
|
|
303
|
+
"utf8"
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const result = await runRecommendSearchCli({
|
|
308
|
+
workspaceRoot,
|
|
309
|
+
searchParams: {
|
|
310
|
+
school_tag: ["不限"],
|
|
311
|
+
degree: ["不限"],
|
|
312
|
+
gender: "不限",
|
|
313
|
+
recent_not_view: "不限"
|
|
314
|
+
},
|
|
315
|
+
selectedJob: null,
|
|
316
|
+
pageScope: "latest"
|
|
317
|
+
});
|
|
318
|
+
assert.equal(result.ok, true);
|
|
319
|
+
const argv = result.summary?.argv || [];
|
|
320
|
+
const pageScopeIndex = argv.indexOf("--page-scope");
|
|
321
|
+
assert.equal(pageScopeIndex >= 0, true);
|
|
322
|
+
assert.equal(argv[pageScopeIndex + 1], "latest");
|
|
323
|
+
assert.equal(argv.includes("--calibration"), false);
|
|
324
|
+
} finally {
|
|
325
|
+
fs.rmSync(workspaceRoot, { recursive: true, force: true });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
285
329
|
async function testScreenCliShouldPassPageScopeArgument() {
|
|
286
330
|
const previousHome = process.env.BOSS_RECOMMEND_HOME;
|
|
287
331
|
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-screen-page-scope-home-"));
|
|
@@ -342,6 +386,66 @@ async function testScreenCliShouldPassPageScopeArgument() {
|
|
|
342
386
|
}
|
|
343
387
|
}
|
|
344
388
|
|
|
389
|
+
async function testScreenCliShouldPassLatestPageScopeArgument() {
|
|
390
|
+
const previousHome = process.env.BOSS_RECOMMEND_HOME;
|
|
391
|
+
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-screen-page-scope-latest-home-"));
|
|
392
|
+
const workspaceRoot = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-screen-page-scope-latest-workspace-"));
|
|
393
|
+
const cliDir = path.join(workspaceRoot, "boss-recommend-screen-cli");
|
|
394
|
+
fs.mkdirSync(cliDir, { recursive: true });
|
|
395
|
+
const cliPath = path.join(cliDir, "boss-recommend-screen-cli.cjs");
|
|
396
|
+
fs.writeFileSync(
|
|
397
|
+
cliPath,
|
|
398
|
+
[
|
|
399
|
+
"#!/usr/bin/env node",
|
|
400
|
+
"console.log(JSON.stringify({",
|
|
401
|
+
" status: 'COMPLETED',",
|
|
402
|
+
" result: {",
|
|
403
|
+
" processed_count: 0,",
|
|
404
|
+
" passed_count: 0,",
|
|
405
|
+
" skipped_count: 0,",
|
|
406
|
+
" argv: process.argv.slice(2),",
|
|
407
|
+
" resume_source: 'image_fallback',",
|
|
408
|
+
" active_tab_status: '1'",
|
|
409
|
+
" }",
|
|
410
|
+
"}));"
|
|
411
|
+
].join("\n"),
|
|
412
|
+
"utf8"
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
process.env.BOSS_RECOMMEND_HOME = tempHome;
|
|
416
|
+
fs.writeFileSync(path.join(tempHome, "screening-config.json"), JSON.stringify({
|
|
417
|
+
baseUrl: "https://api.openai.com/v1",
|
|
418
|
+
apiKey: "sk-valid-test",
|
|
419
|
+
model: "gpt-4.1-mini"
|
|
420
|
+
}, null, 2));
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
const result = await runRecommendScreenCli({
|
|
424
|
+
workspaceRoot,
|
|
425
|
+
screenParams: {
|
|
426
|
+
criteria: "有 MCP 经验",
|
|
427
|
+
target_count: null,
|
|
428
|
+
post_action: "none",
|
|
429
|
+
max_greet_count: null
|
|
430
|
+
},
|
|
431
|
+
pageScope: "latest"
|
|
432
|
+
});
|
|
433
|
+
assert.equal(result.ok, true);
|
|
434
|
+
const argv = result.summary?.argv || [];
|
|
435
|
+
const pageScopeIndex = argv.indexOf("--page-scope");
|
|
436
|
+
assert.equal(pageScopeIndex >= 0, true);
|
|
437
|
+
assert.equal(argv[pageScopeIndex + 1], "latest");
|
|
438
|
+
} finally {
|
|
439
|
+
if (previousHome === undefined) {
|
|
440
|
+
delete process.env.BOSS_RECOMMEND_HOME;
|
|
441
|
+
} else {
|
|
442
|
+
process.env.BOSS_RECOMMEND_HOME = previousHome;
|
|
443
|
+
}
|
|
444
|
+
fs.rmSync(tempHome, { recursive: true, force: true });
|
|
445
|
+
fs.rmSync(workspaceRoot, { recursive: true, force: true });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
345
449
|
async function main() {
|
|
346
450
|
await testRunProcessHeartbeatAndOutput();
|
|
347
451
|
await testRunProcessAbortSignal();
|
|
@@ -353,9 +457,12 @@ async function main() {
|
|
|
353
457
|
testPreflightShouldCheckSharpInsteadOfPython();
|
|
354
458
|
testPreflightFeaturedShouldRequireFavoriteCalibration();
|
|
355
459
|
testPreflightRecommendShouldKeepFavoriteCalibrationOptional();
|
|
460
|
+
testPreflightLatestShouldKeepFavoriteCalibrationOptional();
|
|
356
461
|
await testEnsureFeaturedCalibrationReadyShouldAutoCalibrate();
|
|
357
462
|
await testSearchCliShouldPassPageScopeArgument();
|
|
463
|
+
await testSearchCliShouldPassLatestPageScopeWithoutCalibration();
|
|
358
464
|
await testScreenCliShouldPassPageScopeArgument();
|
|
465
|
+
await testScreenCliShouldPassLatestPageScopeArgument();
|
|
359
466
|
console.log("adapters runtime tests passed");
|
|
360
467
|
}
|
|
361
468
|
|
package/src/test-parser.js
CHANGED
|
@@ -488,6 +488,21 @@ function testFeaturedKeywordShouldProposeFeaturedPageScope() {
|
|
|
488
488
|
assert.equal(result.pending_questions.some((item) => item.field === "page_scope"), true);
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
+
function testLatestKeywordShouldProposeLatestPageScope() {
|
|
492
|
+
const result = parseRecommendInstruction({
|
|
493
|
+
instruction: "在推荐页最新里筛选候选人,有 Agent 经验,符合标准收藏",
|
|
494
|
+
confirmation: null,
|
|
495
|
+
overrides: null
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
assert.equal(result.proposed_page_scope, "latest");
|
|
499
|
+
assert.equal(result.needs_page_confirmation, true);
|
|
500
|
+
const pageQuestion = result.pending_questions.find((item) => item.field === "page_scope");
|
|
501
|
+
assert.equal(Boolean(pageQuestion), true);
|
|
502
|
+
assert.equal(Array.isArray(pageQuestion.options), true);
|
|
503
|
+
assert.equal(pageQuestion.options.some((item) => item.value === "latest"), true);
|
|
504
|
+
}
|
|
505
|
+
|
|
491
506
|
function testConfirmedPageScopeShouldBeResolved() {
|
|
492
507
|
const result = parseRecommendInstruction({
|
|
493
508
|
instruction: "在推荐页筛选候选人,有 Agent 经验,符合标准收藏",
|
|
@@ -543,6 +558,7 @@ function main() {
|
|
|
543
558
|
testPostActionNoneCanBeConfirmed();
|
|
544
559
|
testJobSelectionHintCanComeFromOverrides();
|
|
545
560
|
testFeaturedKeywordShouldProposeFeaturedPageScope();
|
|
561
|
+
testLatestKeywordShouldProposeLatestPageScope();
|
|
546
562
|
testConfirmedPageScopeShouldBeResolved();
|
|
547
563
|
testPageScopeOverrideShouldNotBypassConfirmation();
|
|
548
564
|
console.log("parser tests passed");
|
package/src/test-pipeline.js
CHANGED
|
@@ -1066,7 +1066,7 @@ async function testFeaturedPipelineShouldRunSearchThenSwitchTabThenScreen() {
|
|
|
1066
1066
|
active_status: "0",
|
|
1067
1067
|
tab_state: { active_status: "0" }
|
|
1068
1068
|
}),
|
|
1069
|
-
switchRecommendTab: async ({ target_status }) => {
|
|
1069
|
+
switchRecommendTab: async (_workspaceRoot, { target_status }) => {
|
|
1070
1070
|
calls.push({ type: "switch", target_status });
|
|
1071
1071
|
return {
|
|
1072
1072
|
ok: true,
|
|
@@ -1102,6 +1102,82 @@ async function testFeaturedPipelineShouldRunSearchThenSwitchTabThenScreen() {
|
|
|
1102
1102
|
assert.equal(result.result.resume_source, "network");
|
|
1103
1103
|
}
|
|
1104
1104
|
|
|
1105
|
+
async function testLatestPipelineShouldRunSearchThenSwitchTabThenScreen() {
|
|
1106
|
+
const calls = [];
|
|
1107
|
+
const result = await runRecommendPipeline(
|
|
1108
|
+
{
|
|
1109
|
+
workspaceRoot: process.cwd(),
|
|
1110
|
+
instruction: "test",
|
|
1111
|
+
confirmation: {
|
|
1112
|
+
...createJobConfirmedConfirmation(),
|
|
1113
|
+
page_confirmed: true,
|
|
1114
|
+
page_value: "latest"
|
|
1115
|
+
},
|
|
1116
|
+
overrides: {
|
|
1117
|
+
page_scope: "latest"
|
|
1118
|
+
}
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
parseRecommendInstruction: () => createParsed({
|
|
1122
|
+
page_scope: "latest",
|
|
1123
|
+
proposed_page_scope: "latest"
|
|
1124
|
+
}),
|
|
1125
|
+
runPipelinePreflight: () => ({ ok: true, checks: [], debug_port: 9222 }),
|
|
1126
|
+
ensureBossRecommendPageReady: async () => ({ ok: true, state: "RECOMMEND_READY", page_state: { state: "RECOMMEND_READY" } }),
|
|
1127
|
+
listRecommendJobs: async () => createJobListResult(),
|
|
1128
|
+
runRecommendSearchCli: async ({ pageScope }) => {
|
|
1129
|
+
calls.push({ type: "search", pageScope });
|
|
1130
|
+
return {
|
|
1131
|
+
ok: true,
|
|
1132
|
+
summary: {
|
|
1133
|
+
candidate_count: 8,
|
|
1134
|
+
applied_filters: { degree: ["本科"] },
|
|
1135
|
+
page_state: { state: "RECOMMEND_READY" }
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
},
|
|
1139
|
+
readRecommendTabState: async () => ({
|
|
1140
|
+
ok: true,
|
|
1141
|
+
active_status: "0",
|
|
1142
|
+
tab_state: { active_status: "0" }
|
|
1143
|
+
}),
|
|
1144
|
+
switchRecommendTab: async (_workspaceRoot, { target_status }) => {
|
|
1145
|
+
calls.push({ type: "switch", target_status });
|
|
1146
|
+
return {
|
|
1147
|
+
ok: true,
|
|
1148
|
+
state: "TAB_SWITCHED",
|
|
1149
|
+
active_status: "1",
|
|
1150
|
+
tab_state: { active_status: "1" }
|
|
1151
|
+
};
|
|
1152
|
+
},
|
|
1153
|
+
runRecommendScreenCli: async ({ pageScope }) => {
|
|
1154
|
+
calls.push({ type: "screen", pageScope });
|
|
1155
|
+
return {
|
|
1156
|
+
ok: true,
|
|
1157
|
+
summary: {
|
|
1158
|
+
processed_count: 8,
|
|
1159
|
+
passed_count: 3,
|
|
1160
|
+
skipped_count: 5,
|
|
1161
|
+
output_csv: "C:/temp/result.csv",
|
|
1162
|
+
completion_reason: "page_exhausted",
|
|
1163
|
+
active_tab_status: "1",
|
|
1164
|
+
resume_source: "image_fallback"
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
assert.equal(result.status, "COMPLETED");
|
|
1172
|
+
assert.deepEqual(calls.map((item) => item.type), ["search", "switch", "screen"]);
|
|
1173
|
+
assert.equal(calls[0].pageScope, "latest");
|
|
1174
|
+
assert.equal(calls[1].target_status, "1");
|
|
1175
|
+
assert.equal(calls[2].pageScope, "latest");
|
|
1176
|
+
assert.equal(result.result.selected_page, "latest");
|
|
1177
|
+
assert.equal(result.result.active_tab_status, "1");
|
|
1178
|
+
assert.equal(result.result.resume_source, "image_fallback");
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1105
1181
|
async function testFeaturedMissingCalibrationShouldAutoCalibrateThenContinue() {
|
|
1106
1182
|
const calls = [];
|
|
1107
1183
|
let preflightCallCount = 0;
|
|
@@ -1855,6 +1931,7 @@ async function main() {
|
|
|
1855
1931
|
await testNeedMaxGreetCountConfirmationGate();
|
|
1856
1932
|
await testNeedInputGate();
|
|
1857
1933
|
await testFeaturedPipelineShouldRunSearchThenSwitchTabThenScreen();
|
|
1934
|
+
await testLatestPipelineShouldRunSearchThenSwitchTabThenScreen();
|
|
1858
1935
|
await testFeaturedMissingCalibrationShouldAutoCalibrateThenContinue();
|
|
1859
1936
|
await testFeaturedCalibrationFailureShouldReturnCalibrationRequired();
|
|
1860
1937
|
await testFeaturedTabSwitchFailureShouldReturnRetryableError();
|