@reconcrap/boss-recommend-mcp 2.0.43 → 2.0.45
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/README.md +7 -1
- package/config/screening-config.example.json +11 -1
- package/package.json +1 -2
- package/src/chat-mcp.js +4 -0
- package/src/chat-runtime-config.js +85 -1
- package/src/cli.js +13 -1
- package/src/core/browser/index.js +652 -4
- package/src/core/capture/index.js +118 -14
- package/src/core/infinite-list/index.js +122 -8
- package/src/domains/chat/run-service.js +121 -5
- package/src/domains/recommend/run-service.js +200 -95
- package/src/domains/recruit/run-service.js +108 -4
- package/src/index.js +58 -0
- package/src/recommend-mcp.js +22 -18
- package/src/recruit-mcp.js +44 -0
- package/scripts/live-recommend-recovery-smoke.js +0 -305
package/README.md
CHANGED
|
@@ -259,7 +259,13 @@ config/screening-config.example.json
|
|
|
259
259
|
- `debugPort`:未显式传 `port` 时,recommend / search / chat CDP-only MCP run 和健康检查默认连接这个 Chrome 调试端口。
|
|
260
260
|
- `outputDir`:recommend / search / chat 完成后的最终 CSV 与 report JSON 会写入这里;run state / checkpoint 仍保留在各自状态目录,方便 pause/resume/cancel。
|
|
261
261
|
- `llmThinkingLevel`:默认 `low`。可设为 `off/minimal/low/medium/high/auto/current`,用于控制 OpenAI-compatible LLM 的 thinking/reasoning 强度。
|
|
262
|
-
- `
|
|
262
|
+
- `humanBehavior`:默认 `{ "enabled": true, "profile": "paced_with_rests" }`。用于 recommend / search / chat 的可靠性实验,支持:
|
|
263
|
+
- `profile: "baseline"`:关闭人类节奏,保持确定性行为。
|
|
264
|
+
- `profile: "paced"`:启用 CDP-only Bezier 鼠标移动、较大按钮的安全 inset 点击点、分块 `Input.insertText`、列表 wheel/settle jitter,以及小的动作前后读秒。
|
|
265
|
+
- `profile: "paced_with_rests"`:在 `paced` 基础上启用候选人短休和批次休息。
|
|
266
|
+
- `humanRestEnabled`:兼容旧配置。设为 `true` 时等价于 `humanBehavior.profile="paced_with_rests"`;设为 `false` 时不会关闭当前默认节奏。如需关闭,请显式设置 `humanBehavior.enabled=false` 或 `humanBehavior.profile="baseline"`。
|
|
267
|
+
- recommend / search / chat 图片简历 fallback 与主列表滚动都会在启用 `listScrollJitter` 时使用 coverage-safe scroll jitter:每次 wheel delta 在安全范围内变化,并保留截图重叠、重复检测、bottom-marker / stop-boundary 逻辑,实际 delta 和 settle 时间会写入 artifact metadata。
|
|
268
|
+
- chat/recommend/search run 也兼容显式参数 `safe_pacing` 与 `batch_rest_enabled`:run 参数优先于配置文件。
|
|
263
269
|
|
|
264
270
|
## 常用命令
|
|
265
271
|
|
|
@@ -12,7 +12,17 @@
|
|
|
12
12
|
"llmImageDetail": "low",
|
|
13
13
|
"debugPort": 9222,
|
|
14
14
|
"outputDir": "",
|
|
15
|
-
"humanRestEnabled":
|
|
15
|
+
"humanRestEnabled": true,
|
|
16
|
+
"humanBehavior": {
|
|
17
|
+
"enabled": true,
|
|
18
|
+
"profile": "paced_with_rests",
|
|
19
|
+
"clickMovement": true,
|
|
20
|
+
"textEntry": true,
|
|
21
|
+
"listScrollJitter": true,
|
|
22
|
+
"shortRest": true,
|
|
23
|
+
"batchRest": true,
|
|
24
|
+
"actionCooldown": true
|
|
25
|
+
},
|
|
16
26
|
"openaiOrganization": "optional-org-id",
|
|
17
27
|
"openaiProject": "optional-project-id"
|
|
18
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reconcrap/boss-recommend-mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.45",
|
|
4
4
|
"description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"boss",
|
|
@@ -83,7 +83,6 @@
|
|
|
83
83
|
"config/screening-config.example.json",
|
|
84
84
|
"skills",
|
|
85
85
|
"scripts/postinstall.cjs",
|
|
86
|
-
"scripts/live-recommend-recovery-smoke.js",
|
|
87
86
|
"src/core",
|
|
88
87
|
"src/domains",
|
|
89
88
|
"src/chat-mcp.js",
|
package/src/chat-mcp.js
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
normalizeTargetCountInput,
|
|
49
49
|
resolveBossConfiguredOutputDir,
|
|
50
50
|
resolveBossChatRuntimeLayout,
|
|
51
|
+
resolveHumanBehaviorForRun,
|
|
51
52
|
resolveBossScreeningConfig
|
|
52
53
|
} from "./chat-runtime-config.js";
|
|
53
54
|
import { DEFAULT_MAX_IMAGE_PAGES } from "./core/cv-acquisition/index.js";
|
|
@@ -1078,6 +1079,7 @@ function getRunOptions(args, normalized, session, { workspaceRoot = "", configRe
|
|
|
1078
1079
|
const shouldRequestResume = shouldRequestChatResume(args);
|
|
1079
1080
|
const useLlm = shouldUseChatLlm(args);
|
|
1080
1081
|
const resolvedConfig = configResolution || (useLlm ? resolveBossScreeningConfig(workspaceRoot) : { ok: false });
|
|
1082
|
+
const humanBehavior = resolveHumanBehaviorForRun(args, resolvedConfig?.config || {});
|
|
1081
1083
|
return {
|
|
1082
1084
|
client: session.client,
|
|
1083
1085
|
targetUrl: CHAT_TARGET_URL,
|
|
@@ -1124,6 +1126,8 @@ function getRunOptions(args, normalized, session, { workspaceRoot = "", configRe
|
|
|
1124
1126
|
listSettleMs: parsePositiveInteger(args.list_settle_ms, slowLive ? 1800 : 1200),
|
|
1125
1127
|
listFallbackPoint: null,
|
|
1126
1128
|
imageOutputDir: resolveBossConfiguredOutputDir("", getChatRunsDir()),
|
|
1129
|
+
humanRestEnabled: humanBehavior.restEnabled,
|
|
1130
|
+
humanBehavior,
|
|
1127
1131
|
name: "mcp-boss-chat-run"
|
|
1128
1132
|
};
|
|
1129
1133
|
}
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
|
+
import { normalizeHumanBehaviorOptions } from "./core/browser/index.js";
|
|
5
6
|
|
|
6
7
|
const BOSS_CHAT_RUNTIME_SUBDIR = "boss-chat";
|
|
7
8
|
const TARGET_COUNT_WRAPPER_KEYS = ["target_count", "targetCount", "value", "count", "limit"];
|
|
@@ -238,6 +239,84 @@ function parseConfigBoolean(raw, fallback = false) {
|
|
|
238
239
|
return fallback;
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
function readFirstOwn(source, keys = []) {
|
|
243
|
+
if (!source || typeof source !== "object") return undefined;
|
|
244
|
+
for (const key of keys) {
|
|
245
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) return source[key];
|
|
246
|
+
}
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function parseOptionalConfigBoolean(raw) {
|
|
251
|
+
if (raw === undefined || raw === null || raw === "") return null;
|
|
252
|
+
return parseConfigBoolean(raw, null);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function applyHumanBehaviorProfileDefaults(target, profileRaw) {
|
|
256
|
+
const defaults = normalizeHumanBehaviorOptions(String(profileRaw || ""));
|
|
257
|
+
Object.assign(target, {
|
|
258
|
+
enabled: defaults.enabled,
|
|
259
|
+
profile: defaults.profile,
|
|
260
|
+
clickMovement: defaults.clickMovement,
|
|
261
|
+
textEntry: defaults.textEntry,
|
|
262
|
+
listScrollJitter: defaults.listScrollJitter,
|
|
263
|
+
shortRest: defaults.shortRest,
|
|
264
|
+
batchRest: defaults.batchRest,
|
|
265
|
+
actionCooldown: defaults.actionCooldown
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function resolveHumanBehaviorForRun(args = {}, config = {}) {
|
|
270
|
+
const base = normalizeHumanBehaviorOptions(config.humanBehavior || config.human_behavior || null, {
|
|
271
|
+
legacyEnabled: config.humanRestEnabled === true
|
|
272
|
+
});
|
|
273
|
+
const override = {};
|
|
274
|
+
const rawBehavior = readFirstOwn(args, ["human_behavior", "humanBehavior"]);
|
|
275
|
+
if (typeof rawBehavior === "boolean") {
|
|
276
|
+
override.enabled = rawBehavior;
|
|
277
|
+
} else if (typeof rawBehavior === "string") {
|
|
278
|
+
applyHumanBehaviorProfileDefaults(override, rawBehavior);
|
|
279
|
+
} else if (rawBehavior && typeof rawBehavior === "object" && !Array.isArray(rawBehavior)) {
|
|
280
|
+
const rawProfile = readFirstOwn(rawBehavior, ["profile", "mode", "behaviorProfile", "behavior_profile"]);
|
|
281
|
+
if (rawProfile !== undefined) applyHumanBehaviorProfileDefaults(override, rawProfile);
|
|
282
|
+
Object.assign(override, rawBehavior);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const profile = readFirstOwn(args, ["human_behavior_profile", "humanBehaviorProfile"]);
|
|
286
|
+
if (profile !== undefined) applyHumanBehaviorProfileDefaults(override, profile);
|
|
287
|
+
const enabled = parseOptionalConfigBoolean(readFirstOwn(args, [
|
|
288
|
+
"human_behavior_enabled",
|
|
289
|
+
"humanBehaviorEnabled"
|
|
290
|
+
]));
|
|
291
|
+
if (enabled !== null) {
|
|
292
|
+
override.enabled = enabled;
|
|
293
|
+
if (enabled === true && !override.profile) applyHumanBehaviorProfileDefaults(override, "paced");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const safePacing = parseOptionalConfigBoolean(readFirstOwn(args, ["safe_pacing", "safePacing"]));
|
|
297
|
+
if (safePacing === true) {
|
|
298
|
+
applyHumanBehaviorProfileDefaults(override, "paced");
|
|
299
|
+
} else if (safePacing === false) {
|
|
300
|
+
override.enabled = false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const batchRest = parseOptionalConfigBoolean(readFirstOwn(args, [
|
|
304
|
+
"batch_rest_enabled",
|
|
305
|
+
"batchRestEnabled",
|
|
306
|
+
"batch_rest"
|
|
307
|
+
]));
|
|
308
|
+
if (batchRest === true) {
|
|
309
|
+
applyHumanBehaviorProfileDefaults(override, "paced_with_rests");
|
|
310
|
+
} else if (batchRest === false) {
|
|
311
|
+
override.batchRest = false;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return normalizeHumanBehaviorOptions({
|
|
315
|
+
...base,
|
|
316
|
+
...override
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
241
320
|
function normalizeLlmThinkingLevel(raw, fallback = "low") {
|
|
242
321
|
const normalized = normalizeText(raw).toLowerCase();
|
|
243
322
|
return LLM_THINKING_LEVELS.has(normalized) ? normalized : fallback;
|
|
@@ -455,6 +534,10 @@ export function resolveBossScreeningConfig(workspaceRoot) {
|
|
|
455
534
|
|| parsed.greeting_text
|
|
456
535
|
|| parsed.greeting
|
|
457
536
|
);
|
|
537
|
+
const humanRestEnabled = parseConfigBoolean(parsed.humanRestEnabled, false);
|
|
538
|
+
const humanBehavior = normalizeHumanBehaviorOptions(parsed.humanBehavior || parsed.human_behavior || null, {
|
|
539
|
+
legacyEnabled: humanRestEnabled
|
|
540
|
+
});
|
|
458
541
|
return {
|
|
459
542
|
ok: true,
|
|
460
543
|
config: {
|
|
@@ -469,7 +552,8 @@ export function resolveBossScreeningConfig(workspaceRoot) {
|
|
|
469
552
|
greetingText,
|
|
470
553
|
debugPort: parsePositiveInteger(parsed.debugPort, 9222),
|
|
471
554
|
outputDir: resolveConfigPathValue(parsed.outputDir, configDir),
|
|
472
|
-
humanRestEnabled
|
|
555
|
+
humanRestEnabled,
|
|
556
|
+
humanBehavior
|
|
473
557
|
},
|
|
474
558
|
config_path: configPath,
|
|
475
559
|
config_dir: configDir,
|
package/src/cli.js
CHANGED
|
@@ -63,7 +63,11 @@ const installConfigDefaults = Object.freeze({
|
|
|
63
63
|
llmMaxRetries: 3,
|
|
64
64
|
llmImageLimit: 8,
|
|
65
65
|
llmImageDetail: "low",
|
|
66
|
-
humanRestEnabled:
|
|
66
|
+
humanRestEnabled: true,
|
|
67
|
+
humanBehavior: {
|
|
68
|
+
enabled: true,
|
|
69
|
+
profile: "paced_with_rests"
|
|
70
|
+
}
|
|
67
71
|
});
|
|
68
72
|
const bossChatRuntimeChildDirs = ["logs", "runs", "profiles", "reports", "artifacts", "state"];
|
|
69
73
|
const bossChatCliUnsupportedStartCode = "CHAT_CLI_ASYNC_UNSUPPORTED_CDP_ONLY";
|
|
@@ -2687,6 +2691,10 @@ async function runPipelineOnce(options = {}) {
|
|
|
2687
2691
|
"action_timeout_ms",
|
|
2688
2692
|
"action_interval_ms",
|
|
2689
2693
|
"action_after_click_delay_ms",
|
|
2694
|
+
"human_behavior_enabled",
|
|
2695
|
+
"human_behavior_profile",
|
|
2696
|
+
"safe_pacing",
|
|
2697
|
+
"batch_rest_enabled",
|
|
2690
2698
|
"llm_timeout_ms",
|
|
2691
2699
|
"llm_image_limit",
|
|
2692
2700
|
"llm_image_detail"
|
|
@@ -2761,6 +2769,10 @@ function buildBossChatCliInput(options = {}) {
|
|
|
2761
2769
|
max_candidates: parseNonNegativeInteger(options["max-candidates"] ?? options.max_candidates),
|
|
2762
2770
|
dry_run: options["dry-run"] === true || options.dryRun === true,
|
|
2763
2771
|
no_state: options["no-state"] === true || options.noState === true,
|
|
2772
|
+
human_behavior_enabled: parseBooleanOption(options["human-behavior-enabled"] ?? options.human_behavior_enabled),
|
|
2773
|
+
human_behavior_profile: typeof (options["human-behavior-profile"] ?? options.human_behavior_profile) === "string"
|
|
2774
|
+
? (options["human-behavior-profile"] ?? options.human_behavior_profile).trim()
|
|
2775
|
+
: undefined,
|
|
2764
2776
|
safe_pacing: parseBooleanOption(options["safe-pacing"] ?? options.safe_pacing),
|
|
2765
2777
|
batch_rest_enabled: parseBooleanOption(options["batch-rest"] ?? options.batch_rest_enabled)
|
|
2766
2778
|
};
|