@reconcrap/boss-recommend-mcp 2.1.0 → 2.1.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/README.md CHANGED
@@ -20,6 +20,7 @@ Boss 推荐 / 搜索 / 聊天筛选 MCP(stdio)服务。
20
20
  MCP 工具:
21
21
 
22
22
  - `list_recommend_jobs`(只读读取推荐页岗位下拉框,返回可直接用于 cron/一次性任务的 `job_names`)
23
+ - `prepare_recommend_pipeline_run`(只校验完整 payload 是否已可用于 cron/一次性任务;不启动筛选,返回 `READY + cron_ready=true` 后才应创建 cron)
23
24
  - `start_recommend_pipeline_run`(异步启动;同样先经过前置门禁,通过后返回 run_id)
24
25
  - `get_recommend_pipeline_run`(轮询 run_id 状态)
25
26
  - `cancel_recommend_pipeline_run`(取消运行中任务)
@@ -53,10 +54,19 @@ boss-recommend-mcp list-jobs --slow-live --port 9222
53
54
 
54
55
  返回的 `job_names` 可直接作为后续 `start_recommend_pipeline_run` 的 `confirmation.job_value` / `overrides.job`。
55
56
 
57
+ Cron / 一次性定时任务设置建议先在设置阶段完成 Chrome/登录/岗位发现与全部确认:
58
+
59
+ ```bash
60
+ boss-recommend-mcp prepare-run --instruction-file boss-recommend-instruction.txt --overrides-file boss-recommend-overrides.json --confirmation-file boss-recommend-confirmation.json --slow-live --port 9222
61
+ ```
62
+
63
+ 只有输出 `status: "READY"` 且 `cron_ready: true` 后,才把同一份 payload 写入 cron 并在到点时调用 `start_recommend_pipeline_run` / `boss-recommend-mcp run --detached`。如果设置阶段返回 `BOSS_LOGIN_REQUIRED`、`NEED_INPUT` 或 `NEED_CONFIRMATION`,先让用户登录或补齐确认,不要创建 cron。
64
+
56
65
  状态机:
57
66
 
58
67
  - `NEED_INPUT`
59
68
  - `NEED_CONFIRMATION`
69
+ - `READY`(仅准备工具)
60
70
  - `COMPLETED`
61
71
  - `FAILED`
62
72
 
@@ -374,6 +384,7 @@ Trae-CN / 长对话防循环建议:
374
384
  说明:
375
385
 
376
386
  - `start_recommend_pipeline_run` 为异步入口,但不会跳过同步确认流程。
387
+ - `prepare_recommend_pipeline_run` / `boss-recommend-mcp prepare-run` 用于 cron 设置阶段;它不启动筛选,只确认 payload 已经不需要到点后再问用户。
377
388
  - 定时心跳默认 120 秒一次;`updated_at` 仍会在阶段或进度变化时刷新。
378
389
  - 每个 run 会持久化到 `~/.boss-recommend-mcp/runs/<run_id>.json`(可通过 `BOSS_RECOMMEND_HOME` 覆盖)。
379
390
  - screen 阶段会持久化 checkpoint:`~/.boss-recommend-mcp/runs/<run_id>.checkpoint.json`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -89,9 +89,13 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
89
89
  ## Tool Usage
90
90
 
91
91
  - 岗位发现工具:`list_recommend_jobs`
92
- - 用途:当用户需要为 cron / 一次性自动任务提前填写完整参数时,先用它读取推荐页岗位下拉框的全部可用岗位名。
92
+ - 用途:当用户需要为 cron / 一次性自动任务提前填写完整参数时,先用它读取推荐页岗位下拉框的全部可用岗位名;默认会复用/自动打开本机 9222 Chrome 并导航到推荐页。
93
93
  - 输出:优先把 `job_names` 里的值作为后续 `overrides.job` / `confirmation.job_value`。
94
- - 限制:只读岗位列表,不启动筛选任务;仍然必须在正式 `start_recommend_pipeline_run` 前完成岗位与最终确认。
94
+ - 限制:只读岗位列表,不启动筛选任务;若返回 `BOSS_LOGIN_REQUIRED`,必须让用户在自动打开的 Chrome 完成登录后重试,本次 cron 不得创建。
95
+ - Cron 准备工具:`prepare_recommend_pipeline_run`
96
+ - 用途:只校验参数是否已可用于 cron / 一次性任务,不启动筛选任务。
97
+ - 要求:只有返回 `status=READY` 且 `cron_ready=true` 后,才允许创建 cron。
98
+ - 若返回 `NEED_INPUT` / `NEED_CONFIRMATION` / `FAILED`:继续补齐 `pending_questions` 或修复登录/页面/config;不得先创建 cron。
95
99
  - 主工具:`start_recommend_pipeline_run`
96
100
  - 必填:`instruction`
97
101
  - 关键输入:
@@ -107,6 +111,25 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
107
111
  - 拿到 `ACCEPTED + run_id` 后默认停止本轮,不自动轮询。
108
112
  - 在拿到 `ACCEPTED + run_id` 之前,禁止以“我已帮你确认参数”为由越过必填确认阶段。
109
113
 
114
+ ## Cron / Scheduled Run Policy
115
+
116
+ 创建 cron 时必须在设置 cron 的当下完成全部交互:
117
+
118
+ 1. 锁定用户原始 instruction,不改写、不摘要,criteria 放入 `overrides.criteria` 时必须逐字保留。
119
+ 2. 收集 Stage A 全部筛选项、`target_count`、`post_action`、`max_greet_count`(如需要)和本次 `rest_level`。
120
+ 3. 调用 `list_recommend_jobs`;若 Chrome 未打开,工具会尝试自动打开本机 9222 Chrome 并进入推荐页。若返回 `BOSS_LOGIN_REQUIRED` 或页面不可用,停止 cron 创建,让用户登录/修复后重试。
121
+ 4. 用 `job_names` 中的精确岗位名完成岗位确认,并完成最终总确认,写入 `job_confirmed=true` 与 `final_confirmed=true`。
122
+ 5. 调用 `prepare_recommend_pipeline_run` 传入将来要执行的完整 payload;只有 `READY + cron_ready=true` 才能创建 cron。
123
+ 6. cron 到点时只调用 `start_recommend_pipeline_run`,并传入准备阶段验证过的同一份 payload。到点后若 Chrome/登录异常,应作为运行失败处理,不得再向用户追问参数。
124
+
125
+ Shell-only OpenClaw/QClaw cron 设置同样先运行:
126
+
127
+ ```powershell
128
+ npx -y @reconcrap/boss-recommend-mcp@latest prepare-run --instruction-file .\boss-recommend-instruction.txt --overrides-file .\boss-recommend-overrides.json --confirmation-file .\boss-recommend-confirmation.json --slow-live --port 9222
129
+ ```
130
+
131
+ 仅当上述命令输出 `READY` 且 `cron_ready=true` 后,才把对应的 detached `run` 命令写入 cron。
132
+
110
133
  ## Async Run Policy
111
134
 
112
135
  - 用户未明确要求“持续跟进”时,不自动 `sleep + get_recommend_pipeline_run`。
@@ -150,13 +173,14 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
150
173
  推荐做法:
151
174
 
152
175
  1. 将锁定的用户原文写入 instruction 文件,将已确认参数写入 `overrides` 与 `confirmation` JSON 文件。
153
- 2. detached CLI 启动,让父命令返回启动证据,子进程继续持有 CDP 会话:
176
+ 2. 先用 `prepare-run` 校验完整 payload cron-ready;未返回 `READY + cron_ready=true` 不得创建 cron 或启动 run。
177
+ 3. 用 detached CLI 启动,让父命令返回启动证据,子进程继续持有 CDP 会话:
154
178
 
155
179
  ```powershell
156
180
  npx -y @reconcrap/boss-recommend-mcp@latest run --detached --instruction-file .\boss-recommend-instruction.txt --overrides-file .\boss-recommend-overrides.json --confirmation-file .\boss-recommend-confirmation.json --slow-live --port 9222
157
181
  ```
158
182
 
159
- 3. 若返回 `ACCEPTED + run_id`,即任务已启动;记录 `run_id/stdout_path/stderr_path`。若返回 `NEED_INPUT` 或 `NEED_CONFIRMATION`,只补 `pending_questions`,不要重写已锁定的用户原文。
183
+ 4. 若返回 `ACCEPTED + run_id`,即任务已启动;记录 `run_id/stdout_path/stderr_path`。若返回 `NEED_INPUT` 或 `NEED_CONFIRMATION`,说明 cron 设置阶段漏掉了准备门禁,应回到 `prepare-run` 补齐,不能在到点后继续问用户确认。
160
184
 
161
185
  兼容路径:
162
186
 
package/src/cli.js CHANGED
@@ -23,10 +23,11 @@ import {
23
23
  prepareBossChatRunTool,
24
24
  resumeBossChatRunTool
25
25
  } from "./chat-mcp.js";
26
- import {
27
- listRecommendJobsTool,
28
- startRecommendPipelineRunTool
29
- } from "./recommend-mcp.js";
26
+ import {
27
+ listRecommendJobsTool,
28
+ prepareRecommendPipelineRunTool,
29
+ startRecommendPipelineRunTool
30
+ } from "./recommend-mcp.js";
30
31
  import {
31
32
  getBossScreenConfigResolution,
32
33
  resolveBossChatRuntimeLayout as resolveCdpBossChatRuntimeLayout,
@@ -53,7 +54,7 @@ const defaultMcpServerName = "boss-recommend";
53
54
  const defaultMcpCommand = "npx";
54
55
  const recommendMcpPackageName = "@reconcrap/boss-recommend-mcp";
55
56
  const recommendMcpBinaryName = "boss-recommend-mcp";
56
- const autoSyncSkipCommands = new Set(["install", "install-skill", "where", "help", "--help", "-h", "list-jobs", "jobs", "recommend-jobs"]);
57
+ const autoSyncSkipCommands = new Set(["install", "install-skill", "where", "help", "--help", "-h", "list-jobs", "jobs", "recommend-jobs"]);
57
58
  const externalMcpTargetsEnv = "BOSS_RECOMMEND_MCP_CONFIG_TARGETS";
58
59
  const externalSkillDirsEnv = "BOSS_RECOMMEND_EXTERNAL_SKILL_DIRS";
59
60
  const installConfigDefaults = Object.freeze({
@@ -2621,10 +2622,11 @@ function printHelp() {
2621
2622
  console.log("boss-recommend-mcp");
2622
2623
  console.log("");
2623
2624
  console.log("Usage:");
2624
- console.log(" boss-recommend-mcp Start the MCP server");
2625
- console.log(" boss-recommend-mcp start Start the MCP server");
2626
- console.log(" boss-recommend-mcp run Start a CDP-only recommend run through the shared run service");
2627
- console.log(" boss-recommend-mcp list-jobs CDP-only list of exact recommend job names for cron/one-shot inputs");
2625
+ console.log(" boss-recommend-mcp Start the MCP server");
2626
+ console.log(" boss-recommend-mcp start Start the MCP server");
2627
+ console.log(" boss-recommend-mcp prepare-run Validate a cron-ready recommend run payload without starting screening");
2628
+ console.log(" boss-recommend-mcp run Start a CDP-only recommend run through the shared run service");
2629
+ console.log(" boss-recommend-mcp list-jobs CDP-only list of exact recommend job names for cron/one-shot inputs");
2628
2630
  console.log(" boss-recommend-mcp chat <subcommand> Run CDP-only boss-chat health/prepare/status commands");
2629
2631
  console.log(" boss-recommend-mcp install Install/migrate skills and MCP configs; replaces legacy Boss MCP routes (supports --agent trae-cn/openclaw/qclaw/...)");
2630
2632
  console.log(" boss-recommend-mcp install-skill Install bundled Codex skills (recommend/recruit/chat)");
@@ -2636,10 +2638,11 @@ function printHelp() {
2636
2638
  console.log(" boss-recommend-mcp calibrate Disabled until CDP-only featured calibration is live-verified");
2637
2639
  console.log(" boss-recommend-mcp launch-chrome Launch or reuse Chrome debug instance and open Boss recommend page");
2638
2640
  console.log(" boss-recommend-mcp where Print installed package, skill, and config paths");
2639
- console.log("");
2640
- console.log("Run command:");
2641
- console.log(" boss-recommend-mcp run --instruction \"推荐页上筛选211男生,近14天没有,有大模型平台经验\" --overrides-file overrides.json --confirmation-file confirmation.json");
2642
- console.log(" boss-recommend-mcp run --detached --instruction \"...\" --overrides-file overrides.json --confirmation-file confirmation.json");
2641
+ console.log("");
2642
+ console.log("Run command:");
2643
+ console.log(" boss-recommend-mcp prepare-run --instruction \"...\" --overrides-file overrides.json --confirmation-file confirmation.json");
2644
+ console.log(" boss-recommend-mcp run --instruction \"推荐页上筛选211男生,近14天没有,有大模型平台经验\" --overrides-file overrides.json --confirmation-file confirmation.json");
2645
+ console.log(" boss-recommend-mcp run --detached --instruction \"...\" --overrides-file overrides.json --confirmation-file confirmation.json");
2643
2646
  console.log(" boss-recommend-mcp list-jobs --slow-live --port 9222");
2644
2647
  console.log(" boss-recommend-mcp chat prepare-run --slow-live --port 9222 # CDP-only preflight; start runs through MCP start_boss_chat_run");
2645
2648
  console.log(" boss-recommend-mcp config set --base-url <url> --api-key <key> --model <model> [--thinking-level off|low|medium|high|current] [--greeting-message <text>] [--openai-organization <id>] [--openai-project <id>]");
@@ -2733,14 +2736,13 @@ async function installAll(options = {}) {
2733
2736
  }
2734
2737
  }
2735
2738
 
2736
- async function runPipelineOnce(options = {}) {
2737
- const instruction = getRunInstruction(options);
2738
- const confirmation = getRunConfirmation(options);
2739
- const overrides = getRunOverrides(options);
2740
- const followUp = getRunFollowUp(options);
2741
- const workspaceRoot = getWorkspaceRoot(options);
2742
- const port = parsePositivePort(options.port) || parsePositivePort(process.env.BOSS_RECOMMEND_CHROME_PORT) || 9222;
2743
-
2739
+ function buildRecommendRunCliInput(options = {}) {
2740
+ const instruction = getRunInstruction(options);
2741
+ const confirmation = getRunConfirmation(options);
2742
+ const overrides = getRunOverrides(options);
2743
+ const followUp = getRunFollowUp(options);
2744
+ const port = parsePositivePort(options.port) || parsePositivePort(process.env.BOSS_RECOMMEND_CHROME_PORT) || 9222;
2745
+
2744
2746
  const args = {
2745
2747
  instruction,
2746
2748
  confirmation: confirmation ?? undefined,
@@ -2797,15 +2799,46 @@ async function runPipelineOnce(options = {}) {
2797
2799
  "llm_image_limit",
2798
2800
  "llm_image_detail"
2799
2801
  ];
2800
- for (const key of optionalPassthrough) {
2801
- const kebab = key.replace(/_/g, "-");
2802
- if (options[key] !== undefined) args[key] = options[key];
2803
- else if (options[kebab] !== undefined) args[key] = options[kebab];
2804
- }
2805
-
2806
- const result = await startRecommendPipelineRunTool({
2807
- workspaceRoot,
2808
- args
2802
+ for (const key of optionalPassthrough) {
2803
+ const kebab = key.replace(/_/g, "-");
2804
+ if (options[key] !== undefined) args[key] = options[key];
2805
+ else if (options[kebab] !== undefined) args[key] = options[kebab];
2806
+ }
2807
+
2808
+ return {
2809
+ args,
2810
+ port
2811
+ };
2812
+ }
2813
+
2814
+ async function preparePipelineOnce(options = {}) {
2815
+ const workspaceRoot = getWorkspaceRoot(options);
2816
+ const { args, port } = buildRecommendRunCliInput(options);
2817
+ const result = prepareRecommendPipelineRunTool({
2818
+ workspaceRoot,
2819
+ args
2820
+ });
2821
+ printJson({
2822
+ ...result,
2823
+ cli: {
2824
+ command: "prepare-run",
2825
+ cdp_only: true,
2826
+ shared_run_service: true,
2827
+ workspace_root: workspaceRoot,
2828
+ port
2829
+ }
2830
+ });
2831
+ if (result.status !== "READY" || result.cron_ready !== true) {
2832
+ process.exitCode = 1;
2833
+ }
2834
+ }
2835
+
2836
+ async function runPipelineOnce(options = {}) {
2837
+ const workspaceRoot = getWorkspaceRoot(options);
2838
+ const { args, port } = buildRecommendRunCliInput(options);
2839
+ const result = await startRecommendPipelineRunTool({
2840
+ workspaceRoot,
2841
+ args
2809
2842
  });
2810
2843
  printJson({
2811
2844
  ...result,
@@ -2974,11 +3007,11 @@ export async function runCli(argv = process.argv) {
2974
3007
  case "mcp":
2975
3008
  startServer();
2976
3009
  break;
2977
- case "run":
2978
- try {
2979
- if (
2980
- (options.detached === true || options.background === true)
2981
- && process.env[detachedRecommendRunChildEnv] !== "1"
3010
+ case "run":
3011
+ try {
3012
+ if (
3013
+ (options.detached === true || options.background === true)
3014
+ && process.env[detachedRecommendRunChildEnv] !== "1"
2982
3015
  ) {
2983
3016
  await runPipelineDetached(argv.slice(3), options);
2984
3017
  } else {
@@ -2993,9 +3026,26 @@ export async function runCli(argv = process.argv) {
2993
3026
  retryable: false
2994
3027
  }
2995
3028
  });
2996
- process.exitCode = 1;
2997
- }
2998
- break;
3029
+ process.exitCode = 1;
3030
+ }
3031
+ break;
3032
+ case "prepare-run":
3033
+ case "prepare":
3034
+ try {
3035
+ await preparePipelineOnce(options);
3036
+ } catch (error) {
3037
+ printJson({
3038
+ status: "FAILED",
3039
+ cron_ready: false,
3040
+ error: {
3041
+ code: "INVALID_CLI_INPUT",
3042
+ message: error.message || "Invalid CLI input",
3043
+ retryable: false
3044
+ }
3045
+ });
3046
+ process.exitCode = 1;
3047
+ }
3048
+ break;
2999
3049
  case "list-jobs":
3000
3050
  case "jobs":
3001
3051
  case "recommend-jobs":
@@ -3153,10 +3203,11 @@ export const __testables = {
3153
3203
  installSkill,
3154
3204
  isInstalledPackageRoot,
3155
3205
  mergeMcpServerConfigFile,
3156
- resolveBossChatRuntimeLayout: resolveCdpBossChatRuntimeLayout,
3157
- runBossChatCliCommand,
3158
- runPipelineOnce
3159
- };
3206
+ resolveBossChatRuntimeLayout: resolveCdpBossChatRuntimeLayout,
3207
+ runBossChatCliCommand,
3208
+ preparePipelineOnce,
3209
+ runPipelineOnce
3210
+ };
3160
3211
 
3161
3212
  if (process.argv[1] && path.resolve(process.argv[1]) === currentFilePath) {
3162
3213
  await runCli(process.argv);
package/src/index.js CHANGED
@@ -87,8 +87,9 @@ import {
87
87
  } from "./run-state.js";
88
88
 
89
89
  const require = createRequire(import.meta.url);
90
- const { version: SERVER_VERSION } = require("../package.json");
91
-
90
+ const { version: SERVER_VERSION } = require("../package.json");
91
+
92
+ const TOOL_PREPARE_RUN = "prepare_recommend_pipeline_run";
92
93
  const TOOL_START_RUN = "start_recommend_pipeline_run";
93
94
  const TOOL_GET_RUN = "get_recommend_pipeline_run";
94
95
  const TOOL_CANCEL_RUN = "cancel_recommend_pipeline_run";
@@ -1089,10 +1090,15 @@ function createToolsSchema() {
1089
1090
  description: "CDP-only 读取 Boss 推荐页岗位下拉框,返回所有可用岗位完整名称,方便 cron/一次性任务提前填写 job 参数。不会启动筛选任务。",
1090
1091
  inputSchema: createListRecommendJobsInputSchema()
1091
1092
  },
1093
+ {
1094
+ name: TOOL_PREPARE_RUN,
1095
+ description: "只校验 Boss 推荐页流水线参数是否已可用于 cron/一次性任务;不会启动筛选任务。只有返回 READY/cron_ready=true 后才应创建定时任务。",
1096
+ inputSchema: createRunInputSchema()
1097
+ },
1092
1098
  {
1093
1099
  name: TOOL_START_RUN,
1094
1100
  description: "异步启动 Boss 推荐页流水线(含同步门禁预检);只有在前置确认与页面就绪通过后才返回 run_id。",
1095
- inputSchema: createRunInputSchema()
1101
+ inputSchema: createRunInputSchema()
1096
1102
  },
1097
1103
  {
1098
1104
  name: TOOL_GET_RUN,
@@ -2618,6 +2624,8 @@ async function handleRequest(message, workspaceRoot) {
2618
2624
  let payload;
2619
2625
  if (toolName === TOOL_LIST_RECOMMEND_JOBS) {
2620
2626
  payload = await listRecommendJobsTool({ workspaceRoot, args });
2627
+ } else if (toolName === TOOL_PREPARE_RUN) {
2628
+ payload = prepareRecommendPipelineRunTool({ workspaceRoot, args });
2621
2629
  } else if (toolName === TOOL_START_RUN) {
2622
2630
  payload = await handleStartRunTool({ workspaceRoot, args });
2623
2631
  } else if (toolName === TOOL_GET_RUN) {
@@ -1464,15 +1464,21 @@ async function startRecommendPipelineRunInternal(args = {}, { workspaceRoot = ""
1464
1464
  };
1465
1465
  }
1466
1466
 
1467
- export function prepareRecommendPipelineRunTool({ workspaceRoot = "", args = {} } = {}) {
1468
- const prepared = prepareRecommendPipelineStart(args, { workspaceRoot });
1469
- if (prepared.response) return prepared.response;
1470
- const { parsed, normalized } = prepared;
1471
- return {
1472
- status: "READY",
1473
- review: parsed.review,
1474
- post_action: {
1475
- requested: normalized.postAction,
1467
+ export function prepareRecommendPipelineRunTool({ workspaceRoot = "", args = {} } = {}) {
1468
+ const prepared = prepareRecommendPipelineStart(args, { workspaceRoot });
1469
+ if (prepared.response) {
1470
+ return {
1471
+ ...prepared.response,
1472
+ cron_ready: false
1473
+ };
1474
+ }
1475
+ const { parsed, normalized } = prepared;
1476
+ return {
1477
+ status: "READY",
1478
+ cron_ready: true,
1479
+ review: parsed.review,
1480
+ post_action: {
1481
+ requested: normalized.postAction,
1476
1482
  execute_post_action: args.dry_run_post_action === true ? false : args.execute_post_action !== false,
1477
1483
  max_greet_count: normalized.maxGreetCount
1478
1484
  },