@reconcrap/boss-recommend-mcp 2.1.0 → 2.1.2
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 +90 -1
- package/package.json +6 -5
- package/scripts/install-macos.sh +280 -0
- package/skills/boss-recommend-pipeline/SKILL.md +28 -4
- package/src/cli.js +215 -88
- package/src/index.js +11 -3
- package/src/recommend-mcp.js +15 -9
package/README.md
CHANGED
|
@@ -7,6 +7,68 @@ Boss 推荐 / 搜索 / 聊天筛选 MCP(stdio)服务。
|
|
|
7
7
|
- npm: `@reconcrap/boss-recommend-mcp`(https://www.npmjs.com/package/@reconcrap/boss-recommend-mcp)
|
|
8
8
|
- GitHub: `reconcrap-cpu/boss-recommend-mcp`(https://github.com/reconcrap-cpu/boss-recommend-mcp)
|
|
9
9
|
|
|
10
|
+
## AI harness install entrypoint
|
|
11
|
+
|
|
12
|
+
如果用户只把这个 GitHub 链接交给 Codex、Claude Code、Cursor、OpenClaw、QClaw、Trae 等 AI harness,并要求“安装这个工具”,请优先执行本节,而不是直接在 macOS 上裸跑 `npm install -g`。
|
|
13
|
+
|
|
14
|
+
AI harness 不能只完成 npm 安装就结束。安装流程还必须帮助用户完成必填 LLM 配置;如果用户没有提前提供,请向用户索取:
|
|
15
|
+
|
|
16
|
+
- LLM base URL(默认可用 `https://api.openai.com/v1`)
|
|
17
|
+
- LLM API key
|
|
18
|
+
- LLM model(默认可用 `gpt-4.1-mini`)
|
|
19
|
+
|
|
20
|
+
然后写入配置:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
boss-recommend-mcp config set --base-url <baseUrl> --api-key <apiKey> --model <model> --thinking-level low
|
|
24
|
+
boss-recommend-mcp doctor
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
如果用户用 macOS bootstrap,它会自动尝试交互式询问并写入这些字段。非交互式 AI harness 如果拿不到用户输入,应暂停并询问用户上述三项,不要把安装标记为完成。
|
|
28
|
+
|
|
29
|
+
macOS 首次安装推荐使用仓库里的 nvm bootstrap。它会把 Node/npm/global packages 放到用户目录,避免首次安装或后续升级时出现:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
EACCES: permission denied, mkdir '/usr/local/lib/node_modules/@reconcrap'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
这是 npm global prefix 权限问题,不是 `boss-recommend-mcp` 包内部创建目录失败。
|
|
36
|
+
|
|
37
|
+
macOS 一键安装:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
curl -fsSL https://raw.githubusercontent.com/reconcrap-cpu/boss-recommend-mcp/main/scripts/install-macos.sh | bash
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
如果要直接配置某个宿主:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
curl -fsSL https://raw.githubusercontent.com/reconcrap-cpu/boss-recommend-mcp/main/scripts/install-macos.sh | bash -s -- --agent openclaw
|
|
47
|
+
curl -fsSL https://raw.githubusercontent.com/reconcrap-cpu/boss-recommend-mcp/main/scripts/install-macos.sh | bash -s -- --agent qclaw
|
|
48
|
+
curl -fsSL https://raw.githubusercontent.com/reconcrap-cpu/boss-recommend-mcp/main/scripts/install-macos.sh | bash -s -- --agent trae-cn
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
安装后,未来升级只需要在同一个 nvm shell 里运行:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm -g i @reconcrap/boss-recommend-mcp@latest
|
|
55
|
+
boss-recommend-mcp where
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
如果升级前 `npm config get prefix` 返回 `/usr/local`,说明当前 shell 没有使用 nvm Node;请先重新加载 nvm,不要使用 `sudo npm install -g`。
|
|
59
|
+
|
|
60
|
+
macOS bootstrap 会执行 `boss-recommend-mcp install --mcp-launch global-wrapper`。该模式会把 MCP 宿主配置为启动稳定 wrapper:`~/.boss-recommend-mcp/bin/boss-recommend-mcp-mcp-server`。这个 wrapper 每次启动时都会调用当前全局 `boss-recommend-mcp start`,因此后续 `npm -g i @reconcrap/boss-recommend-mcp@latest` 会更新 MCP 宿主实际运行的版本,不需要每次升级后重写 MCP 配置。
|
|
61
|
+
|
|
62
|
+
如果 AI harness 已经从用户处拿到 LLM 信息,可以非交互式传给 bootstrap:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
curl -fsSL https://raw.githubusercontent.com/reconcrap-cpu/boss-recommend-mcp/main/scripts/install-macos.sh \
|
|
66
|
+
| BOSS_RECOMMEND_BASE_URL="https://api.openai.com/v1" \
|
|
67
|
+
BOSS_RECOMMEND_API_KEY="<apiKey>" \
|
|
68
|
+
BOSS_RECOMMEND_MODEL="gpt-4.1-mini" \
|
|
69
|
+
bash -s -- --agent openclaw
|
|
70
|
+
```
|
|
71
|
+
|
|
10
72
|
2.0.0 是 CDP-only 重写版本:活跃浏览器路径只允许 Chrome DevTools Protocol 的 `DOM` / `Input` / `Page` / `Network` / `Accessibility` 等域,不使用 `Runtime.evaluate` 或页面 JS。包内保留 recommend、search/recruit、chat 三条域服务,并共享浏览器、生命周期、筛选、CV 获取、无限滚动、自愈与 CSV 报告层。
|
|
11
73
|
|
|
12
74
|
安装 `boss-recommend-mcp` 后可以直接:
|
|
@@ -20,6 +82,7 @@ Boss 推荐 / 搜索 / 聊天筛选 MCP(stdio)服务。
|
|
|
20
82
|
MCP 工具:
|
|
21
83
|
|
|
22
84
|
- `list_recommend_jobs`(只读读取推荐页岗位下拉框,返回可直接用于 cron/一次性任务的 `job_names`)
|
|
85
|
+
- `prepare_recommend_pipeline_run`(只校验完整 payload 是否已可用于 cron/一次性任务;不启动筛选,返回 `READY + cron_ready=true` 后才应创建 cron)
|
|
23
86
|
- `start_recommend_pipeline_run`(异步启动;同样先经过前置门禁,通过后返回 run_id)
|
|
24
87
|
- `get_recommend_pipeline_run`(轮询 run_id 状态)
|
|
25
88
|
- `cancel_recommend_pipeline_run`(取消运行中任务)
|
|
@@ -53,10 +116,19 @@ boss-recommend-mcp list-jobs --slow-live --port 9222
|
|
|
53
116
|
|
|
54
117
|
返回的 `job_names` 可直接作为后续 `start_recommend_pipeline_run` 的 `confirmation.job_value` / `overrides.job`。
|
|
55
118
|
|
|
119
|
+
Cron / 一次性定时任务设置建议先在设置阶段完成 Chrome/登录/岗位发现与全部确认:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
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
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
只有输出 `status: "READY"` 且 `cron_ready: true` 后,才把同一份 payload 写入 cron 并在到点时调用 `start_recommend_pipeline_run` / `boss-recommend-mcp run --detached`。如果设置阶段返回 `BOSS_LOGIN_REQUIRED`、`NEED_INPUT` 或 `NEED_CONFIRMATION`,先让用户登录或补齐确认,不要创建 cron。
|
|
126
|
+
|
|
56
127
|
状态机:
|
|
57
128
|
|
|
58
129
|
- `NEED_INPUT`
|
|
59
130
|
- `NEED_CONFIRMATION`
|
|
131
|
+
- `READY`(仅准备工具)
|
|
60
132
|
- `COMPLETED`
|
|
61
133
|
- `FAILED`
|
|
62
134
|
|
|
@@ -115,6 +187,13 @@ npm install -g @reconcrap/boss-recommend-mcp@latest
|
|
|
115
187
|
boss-recommend-mcp start
|
|
116
188
|
```
|
|
117
189
|
|
|
190
|
+
macOS 首次安装如果没有确认 npm prefix 在用户目录,优先使用本文开头的 `scripts/install-macos.sh`。完成 bootstrap 后,后续升级仍然使用同一个 npm 命令:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm -g i @reconcrap/boss-recommend-mcp@latest
|
|
194
|
+
boss-recommend-mcp where
|
|
195
|
+
```
|
|
196
|
+
|
|
118
197
|
无需安装(npx 直接运行):
|
|
119
198
|
|
|
120
199
|
```bash
|
|
@@ -140,7 +219,8 @@ node src/cli.js start
|
|
|
140
219
|
如果检测到 legacy Boss server entries,installer 会:
|
|
141
220
|
|
|
142
221
|
- 保留非 Boss MCP server。
|
|
143
|
-
-
|
|
222
|
+
- 默认写入统一 server:`boss-recommend -> npx -y @reconcrap/boss-recommend-mcp@<installed-version> start`
|
|
223
|
+
- 如果传入 `--mcp-launch global-wrapper`,写入升级稳定入口:`boss-recommend -> ~/.boss-recommend-mcp/bin/boss-recommend-mcp-mcp-server`。该 wrapper 会加载 `~/.nvm/nvm.sh` 并执行当前全局 `boss-recommend-mcp start`,适合 macOS 上通过 `npm -g i @reconcrap/boss-recommend-mcp@latest` 持续升级。
|
|
144
224
|
- 从同一个 `mcp.json` 删除旧 `boss-recruit-mcp`、standalone `boss-chat`、旧本地 Boss repo 路径,避免 agent 继续调用 legacy 包。
|
|
145
225
|
- 在原文件旁生成 `mcp.json.boss-mcp-migration-*.bak`。
|
|
146
226
|
- 同步外部 skills 目录里的 `boss-recommend-pipeline`、`boss-recruit-pipeline`、`boss-chat`。
|
|
@@ -156,6 +236,14 @@ boss-recommend-mcp doctor --agent openclaw
|
|
|
156
236
|
boss-recommend-mcp doctor --agent qclaw
|
|
157
237
|
```
|
|
158
238
|
|
|
239
|
+
macOS 上如果希望 MCP 宿主总是使用全局 npm 最新安装版本:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
boss-recommend-mcp install --mcp-launch global-wrapper --agent openclaw
|
|
243
|
+
boss-recommend-mcp install --mcp-launch global-wrapper --agent qclaw
|
|
244
|
+
boss-recommend-mcp install --mcp-launch global-wrapper --agent trae-cn
|
|
245
|
+
```
|
|
246
|
+
|
|
159
247
|
自定义路径:
|
|
160
248
|
|
|
161
249
|
```bash
|
|
@@ -374,6 +462,7 @@ Trae-CN / 长对话防循环建议:
|
|
|
374
462
|
说明:
|
|
375
463
|
|
|
376
464
|
- `start_recommend_pipeline_run` 为异步入口,但不会跳过同步确认流程。
|
|
465
|
+
- `prepare_recommend_pipeline_run` / `boss-recommend-mcp prepare-run` 用于 cron 设置阶段;它不启动筛选,只确认 payload 已经不需要到点后再问用户。
|
|
377
466
|
- 定时心跳默认 120 秒一次;`updated_at` 仍会在阶段或进度变化时刷新。
|
|
378
467
|
- 每个 run 会持久化到 `~/.boss-recommend-mcp/runs/<run_id>.json`(可通过 `BOSS_RECOMMEND_HOME` 覆盖)。
|
|
379
468
|
- 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.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"boss",
|
|
@@ -79,10 +79,11 @@
|
|
|
79
79
|
"live:chat-image-screening": "node scripts/live-chat-image-screening-smoke.js"
|
|
80
80
|
},
|
|
81
81
|
"files": [
|
|
82
|
-
"bin",
|
|
83
|
-
"config/screening-config.example.json",
|
|
84
|
-
"skills",
|
|
85
|
-
"scripts/
|
|
82
|
+
"bin",
|
|
83
|
+
"config/screening-config.example.json",
|
|
84
|
+
"skills",
|
|
85
|
+
"scripts/install-macos.sh",
|
|
86
|
+
"scripts/postinstall.cjs",
|
|
86
87
|
"src/core",
|
|
87
88
|
"src/domains",
|
|
88
89
|
"src/chat-mcp.js",
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PACKAGE_NAME="@reconcrap/boss-recommend-mcp"
|
|
5
|
+
DEFAULT_NODE_VERSION="${BOSS_RECOMMEND_NODE_VERSION:-22}"
|
|
6
|
+
DEFAULT_NVM_VERSION="${BOSS_RECOMMEND_NVM_VERSION:-v0.40.4}"
|
|
7
|
+
|
|
8
|
+
NODE_VERSION="$DEFAULT_NODE_VERSION"
|
|
9
|
+
NVM_VERSION="$DEFAULT_NVM_VERSION"
|
|
10
|
+
AGENT="${BOSS_RECOMMEND_AGENT:-}"
|
|
11
|
+
DRY_RUN=0
|
|
12
|
+
SKIP_DOCTOR=0
|
|
13
|
+
SKIP_LLM_CONFIG=0
|
|
14
|
+
BASE_URL="${BOSS_RECOMMEND_BASE_URL:-https://api.openai.com/v1}"
|
|
15
|
+
API_KEY="${BOSS_RECOMMEND_API_KEY:-}"
|
|
16
|
+
MODEL="${BOSS_RECOMMEND_MODEL:-gpt-4.1-mini}"
|
|
17
|
+
THINKING_LEVEL="${BOSS_RECOMMEND_THINKING_LEVEL:-low}"
|
|
18
|
+
GREETING_MESSAGE="${BOSS_RECOMMEND_GREETING_MESSAGE:-Hi同学,能麻烦发下简历吗?}"
|
|
19
|
+
|
|
20
|
+
usage() {
|
|
21
|
+
cat <<'EOF'
|
|
22
|
+
Install boss-recommend-mcp on macOS with nvm-backed npm globals.
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
install-macos.sh [--agent <agent|all>] [--node-version <version>] [--nvm-version <tag>] [--dry-run] [--skip-doctor] [--skip-llm-config]
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
curl -fsSL https://raw.githubusercontent.com/reconcrap-cpu/boss-recommend-mcp/main/scripts/install-macos.sh | bash
|
|
29
|
+
curl -fsSL https://raw.githubusercontent.com/reconcrap-cpu/boss-recommend-mcp/main/scripts/install-macos.sh | bash -s -- --agent openclaw
|
|
30
|
+
|
|
31
|
+
Supported agents: cursor, trae, trae-cn, claude, openclaw, qclaw, all
|
|
32
|
+
|
|
33
|
+
LLM config can be supplied non-interactively with:
|
|
34
|
+
BOSS_RECOMMEND_BASE_URL, BOSS_RECOMMEND_API_KEY, BOSS_RECOMMEND_MODEL,
|
|
35
|
+
BOSS_RECOMMEND_THINKING_LEVEL, BOSS_RECOMMEND_GREETING_MESSAGE
|
|
36
|
+
EOF
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
log() {
|
|
40
|
+
printf '%s\n' "[boss-recommend-mcp] $*"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
die() {
|
|
44
|
+
printf '%s\n' "[boss-recommend-mcp] ERROR: $*" >&2
|
|
45
|
+
exit 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
run() {
|
|
49
|
+
log "+ $*"
|
|
50
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
51
|
+
"$@"
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
while [ "$#" -gt 0 ]; do
|
|
56
|
+
case "$1" in
|
|
57
|
+
--agent)
|
|
58
|
+
[ "$#" -ge 2 ] || die "--agent requires a value"
|
|
59
|
+
AGENT="$2"
|
|
60
|
+
shift 2
|
|
61
|
+
;;
|
|
62
|
+
--node-version)
|
|
63
|
+
[ "$#" -ge 2 ] || die "--node-version requires a value"
|
|
64
|
+
NODE_VERSION="$2"
|
|
65
|
+
shift 2
|
|
66
|
+
;;
|
|
67
|
+
--nvm-version)
|
|
68
|
+
[ "$#" -ge 2 ] || die "--nvm-version requires a value"
|
|
69
|
+
NVM_VERSION="$2"
|
|
70
|
+
shift 2
|
|
71
|
+
;;
|
|
72
|
+
--dry-run)
|
|
73
|
+
DRY_RUN=1
|
|
74
|
+
shift
|
|
75
|
+
;;
|
|
76
|
+
--skip-doctor)
|
|
77
|
+
SKIP_DOCTOR=1
|
|
78
|
+
shift
|
|
79
|
+
;;
|
|
80
|
+
--skip-llm-config)
|
|
81
|
+
SKIP_LLM_CONFIG=1
|
|
82
|
+
shift
|
|
83
|
+
;;
|
|
84
|
+
--base-url)
|
|
85
|
+
[ "$#" -ge 2 ] || die "--base-url requires a value"
|
|
86
|
+
BASE_URL="$2"
|
|
87
|
+
shift 2
|
|
88
|
+
;;
|
|
89
|
+
--api-key)
|
|
90
|
+
[ "$#" -ge 2 ] || die "--api-key requires a value"
|
|
91
|
+
API_KEY="$2"
|
|
92
|
+
shift 2
|
|
93
|
+
;;
|
|
94
|
+
--model)
|
|
95
|
+
[ "$#" -ge 2 ] || die "--model requires a value"
|
|
96
|
+
MODEL="$2"
|
|
97
|
+
shift 2
|
|
98
|
+
;;
|
|
99
|
+
--thinking-level)
|
|
100
|
+
[ "$#" -ge 2 ] || die "--thinking-level requires a value"
|
|
101
|
+
THINKING_LEVEL="$2"
|
|
102
|
+
shift 2
|
|
103
|
+
;;
|
|
104
|
+
--greeting-message)
|
|
105
|
+
[ "$#" -ge 2 ] || die "--greeting-message requires a value"
|
|
106
|
+
GREETING_MESSAGE="$2"
|
|
107
|
+
shift 2
|
|
108
|
+
;;
|
|
109
|
+
-h|--help)
|
|
110
|
+
usage
|
|
111
|
+
exit 0
|
|
112
|
+
;;
|
|
113
|
+
*)
|
|
114
|
+
die "Unknown option: $1"
|
|
115
|
+
;;
|
|
116
|
+
esac
|
|
117
|
+
done
|
|
118
|
+
|
|
119
|
+
prompt_text() {
|
|
120
|
+
local label="$1"
|
|
121
|
+
local default_value="$2"
|
|
122
|
+
local secret="${3:-0}"
|
|
123
|
+
local value
|
|
124
|
+
if [ ! -r /dev/tty ]; then
|
|
125
|
+
return 1
|
|
126
|
+
fi
|
|
127
|
+
if [ "$secret" -eq 1 ]; then
|
|
128
|
+
printf '%s' "${label}: " > /dev/tty
|
|
129
|
+
IFS= read -r -s value < /dev/tty || return 1
|
|
130
|
+
printf '\n' > /dev/tty
|
|
131
|
+
elif [ -n "$default_value" ]; then
|
|
132
|
+
printf '%s' "${label} [${default_value}]: " > /dev/tty
|
|
133
|
+
IFS= read -r value < /dev/tty || return 1
|
|
134
|
+
if [ -z "$value" ]; then
|
|
135
|
+
value="$default_value"
|
|
136
|
+
fi
|
|
137
|
+
else
|
|
138
|
+
printf '%s' "${label}: " > /dev/tty
|
|
139
|
+
IFS= read -r value < /dev/tty || return 1
|
|
140
|
+
fi
|
|
141
|
+
printf '%s' "$value"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if [ "$(uname -s)" != "Darwin" ]; then
|
|
145
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
146
|
+
log "dry-run on non-macOS host; skipping platform enforcement"
|
|
147
|
+
else
|
|
148
|
+
die "This bootstrap script is for macOS. Use npm install -g ${PACKAGE_NAME}@latest on other platforms."
|
|
149
|
+
fi
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
|
153
|
+
|
|
154
|
+
load_nvm() {
|
|
155
|
+
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
|
156
|
+
# shellcheck disable=SC1090
|
|
157
|
+
. "$NVM_DIR/nvm.sh"
|
|
158
|
+
elif [ -s "$HOME/.nvm/nvm.sh" ]; then
|
|
159
|
+
export NVM_DIR="$HOME/.nvm"
|
|
160
|
+
# shellcheck disable=SC1091
|
|
161
|
+
. "$HOME/.nvm/nvm.sh"
|
|
162
|
+
fi
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
install_nvm() {
|
|
166
|
+
if command -v nvm >/dev/null 2>&1; then
|
|
167
|
+
return
|
|
168
|
+
fi
|
|
169
|
+
log "nvm was not found; installing ${NVM_VERSION} into ${NVM_DIR}"
|
|
170
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
171
|
+
log "+ curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh | bash"
|
|
172
|
+
return
|
|
173
|
+
fi
|
|
174
|
+
curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" | bash
|
|
175
|
+
load_nvm
|
|
176
|
+
command -v nvm >/dev/null 2>&1 || die "nvm installed but is not available in this shell. Open a new terminal and rerun this script."
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
load_nvm
|
|
180
|
+
install_nvm
|
|
181
|
+
|
|
182
|
+
run nvm install "$NODE_VERSION"
|
|
183
|
+
run nvm alias default "$NODE_VERSION"
|
|
184
|
+
run nvm use "$NODE_VERSION"
|
|
185
|
+
|
|
186
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
187
|
+
NODE_PATH="$(command -v node || true)"
|
|
188
|
+
NPM_PATH="$(command -v npm || true)"
|
|
189
|
+
NPM_PREFIX="$(npm config get prefix || true)"
|
|
190
|
+
log "node: ${NODE_PATH}"
|
|
191
|
+
log "npm: ${NPM_PATH}"
|
|
192
|
+
log "npm prefix: ${NPM_PREFIX}"
|
|
193
|
+
|
|
194
|
+
case "$NPM_PREFIX" in
|
|
195
|
+
/usr/local|/usr/local/*)
|
|
196
|
+
die "npm is still using /usr/local. Reload nvm and rerun this script; do not use sudo npm install -g."
|
|
197
|
+
;;
|
|
198
|
+
esac
|
|
199
|
+
case "$NPM_PREFIX" in
|
|
200
|
+
"$NVM_DIR"/*)
|
|
201
|
+
;;
|
|
202
|
+
*)
|
|
203
|
+
die "npm prefix is not under ${NVM_DIR}. Current prefix: ${NPM_PREFIX}"
|
|
204
|
+
;;
|
|
205
|
+
esac
|
|
206
|
+
else
|
|
207
|
+
log "+ verify: npm config get prefix must be under ${NVM_DIR}, not /usr/local"
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
run npm -g i "${PACKAGE_NAME}@latest"
|
|
211
|
+
run boss-recommend-mcp where
|
|
212
|
+
|
|
213
|
+
INSTALL_ARGS=(install --mcp-launch global-wrapper)
|
|
214
|
+
DOCTOR_ARGS=(doctor)
|
|
215
|
+
if [ -n "$AGENT" ]; then
|
|
216
|
+
INSTALL_ARGS+=(--agent "$AGENT")
|
|
217
|
+
DOCTOR_ARGS+=(--agent "$AGENT")
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
run boss-recommend-mcp "${INSTALL_ARGS[@]}"
|
|
221
|
+
|
|
222
|
+
if [ "$SKIP_LLM_CONFIG" -eq 0 ]; then
|
|
223
|
+
log "LLM screening config is required before running candidate screening."
|
|
224
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
225
|
+
if [ -r /dev/tty ]; then
|
|
226
|
+
BASE_URL="$(prompt_text "LLM base URL" "$BASE_URL")"
|
|
227
|
+
MODEL="$(prompt_text "LLM model" "$MODEL")"
|
|
228
|
+
THINKING_LEVEL="$(prompt_text "LLM thinking level" "$THINKING_LEVEL")"
|
|
229
|
+
GREETING_MESSAGE="$(prompt_text "Greeting message for candidates" "$GREETING_MESSAGE")"
|
|
230
|
+
if [ -z "$API_KEY" ]; then
|
|
231
|
+
API_KEY="$(prompt_text "LLM API key (input hidden)" "" 1)"
|
|
232
|
+
fi
|
|
233
|
+
fi
|
|
234
|
+
if [ -z "$BASE_URL" ] || [ -z "$API_KEY" ] || [ -z "$MODEL" ]; then
|
|
235
|
+
cat >&2 <<EOF
|
|
236
|
+
[boss-recommend-mcp] LLM config is still required.
|
|
237
|
+
[boss-recommend-mcp] Ask the user for:
|
|
238
|
+
1. LLM base URL
|
|
239
|
+
2. LLM API key
|
|
240
|
+
3. LLM model name
|
|
241
|
+
[boss-recommend-mcp] Then run:
|
|
242
|
+
boss-recommend-mcp config set --base-url <baseUrl> --api-key <apiKey> --model <model> --thinking-level ${THINKING_LEVEL}
|
|
243
|
+
EOF
|
|
244
|
+
exit 2
|
|
245
|
+
fi
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
CONFIG_ARGS=(config set --base-url "$BASE_URL" --api-key "$API_KEY" --model "$MODEL")
|
|
249
|
+
if [ -n "$THINKING_LEVEL" ]; then
|
|
250
|
+
CONFIG_ARGS+=(--thinking-level "$THINKING_LEVEL")
|
|
251
|
+
fi
|
|
252
|
+
if [ -n "$GREETING_MESSAGE" ]; then
|
|
253
|
+
CONFIG_ARGS+=(--greeting-message "$GREETING_MESSAGE")
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
257
|
+
log "+ boss-recommend-mcp config set --base-url ${BASE_URL:-<baseUrl>} --api-key <hidden> --model ${MODEL:-<model>} --thinking-level ${THINKING_LEVEL:-<level>}"
|
|
258
|
+
else
|
|
259
|
+
log "+ boss-recommend-mcp config set --base-url ${BASE_URL} --api-key <hidden> --model ${MODEL}"
|
|
260
|
+
boss-recommend-mcp "${CONFIG_ARGS[@]}"
|
|
261
|
+
fi
|
|
262
|
+
else
|
|
263
|
+
log "Skipping LLM config because --skip-llm-config was provided."
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
if [ "$SKIP_DOCTOR" -eq 0 ]; then
|
|
267
|
+
log "+ boss-recommend-mcp ${DOCTOR_ARGS[*]}"
|
|
268
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
269
|
+
if ! boss-recommend-mcp "${DOCTOR_ARGS[@]}"; then
|
|
270
|
+
log "doctor reported follow-up work. The npm package install still completed."
|
|
271
|
+
fi
|
|
272
|
+
fi
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
cat <<EOF
|
|
276
|
+
[boss-recommend-mcp] install complete.
|
|
277
|
+
[boss-recommend-mcp] Future upgrades:
|
|
278
|
+
npm -g i ${PACKAGE_NAME}@latest
|
|
279
|
+
boss-recommend-mcp where
|
|
280
|
+
EOF
|
|
@@ -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
|
-
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
26
|
+
import {
|
|
27
|
+
listRecommendJobsTool,
|
|
28
|
+
prepareRecommendPipelineRunTool,
|
|
29
|
+
startRecommendPipelineRunTool
|
|
30
|
+
} from "./recommend-mcp.js";
|
|
30
31
|
import {
|
|
31
32
|
getBossScreenConfigResolution,
|
|
32
33
|
resolveBossChatRuntimeLayout as resolveCdpBossChatRuntimeLayout,
|
|
@@ -49,11 +50,13 @@ const chromeOnboardingUrlPattern = /^chrome:\/\/(welcome|intro|newtab|signin|his
|
|
|
49
50
|
const bossLoginUrlPattern = /(?:zhipin\.com\/web\/user(?:\/|\?|$)|passport\.zhipin\.com)/i;
|
|
50
51
|
const bossLoginTitlePattern = /登录|signin|扫码登录|BOSS直聘登录/i;
|
|
51
52
|
const supportedMcpClients = ["generic", "cursor", "trae", "claudecode", "openclaw", "qclaw"];
|
|
52
|
-
const defaultMcpServerName = "boss-recommend";
|
|
53
|
-
const defaultMcpCommand = "npx";
|
|
54
|
-
const recommendMcpPackageName = "@reconcrap/boss-recommend-mcp";
|
|
55
|
-
const recommendMcpBinaryName = "boss-recommend-mcp";
|
|
56
|
-
const
|
|
53
|
+
const defaultMcpServerName = "boss-recommend";
|
|
54
|
+
const defaultMcpCommand = "npx";
|
|
55
|
+
const recommendMcpPackageName = "@reconcrap/boss-recommend-mcp";
|
|
56
|
+
const recommendMcpBinaryName = "boss-recommend-mcp";
|
|
57
|
+
const globalMcpWrapperFileName = "boss-recommend-mcp-mcp-server";
|
|
58
|
+
const supportedMcpLaunchModes = ["npx", "global-wrapper"];
|
|
59
|
+
const autoSyncSkipCommands = new Set(["install", "install-skill", "where", "help", "--help", "-h", "list-jobs", "jobs", "recommend-jobs"]);
|
|
57
60
|
const externalMcpTargetsEnv = "BOSS_RECOMMEND_MCP_CONFIG_TARGETS";
|
|
58
61
|
const externalSkillDirsEnv = "BOSS_RECOMMEND_EXTERNAL_SKILL_DIRS";
|
|
59
62
|
const installConfigDefaults = Object.freeze({
|
|
@@ -123,15 +126,22 @@ function getCodexHome() {
|
|
|
123
126
|
: path.join(os.homedir(), ".codex");
|
|
124
127
|
}
|
|
125
128
|
|
|
126
|
-
function getStateHome() {
|
|
127
|
-
return process.env.BOSS_RECOMMEND_HOME
|
|
128
|
-
? path.resolve(process.env.BOSS_RECOMMEND_HOME)
|
|
129
|
-
: path.join(os.homedir(), ".boss-recommend-mcp");
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
function getStateHome() {
|
|
130
|
+
return process.env.BOSS_RECOMMEND_HOME
|
|
131
|
+
? path.resolve(process.env.BOSS_RECOMMEND_HOME)
|
|
132
|
+
: path.join(os.homedir(), ".boss-recommend-mcp");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getGlobalMcpWrapperPath(options = {}) {
|
|
136
|
+
if (typeof options["mcp-wrapper-path"] === "string" && options["mcp-wrapper-path"].trim()) {
|
|
137
|
+
return path.resolve(options["mcp-wrapper-path"].trim());
|
|
138
|
+
}
|
|
139
|
+
return path.join(getStateHome(), "bin", globalMcpWrapperFileName);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function ensureDir(targetPath) {
|
|
143
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
144
|
+
}
|
|
135
145
|
|
|
136
146
|
function pathExists(targetPath) {
|
|
137
147
|
try {
|
|
@@ -737,13 +747,55 @@ function parseMcpClientTargets(rawValue) {
|
|
|
737
747
|
return unique;
|
|
738
748
|
}
|
|
739
749
|
|
|
740
|
-
function isPlainObject(value) {
|
|
741
|
-
return value && typeof value === "object" && !Array.isArray(value);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
function
|
|
745
|
-
const
|
|
746
|
-
|
|
750
|
+
function isPlainObject(value) {
|
|
751
|
+
return value && typeof value === "object" && !Array.isArray(value);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function normalizeMcpLaunchMode(options = {}) {
|
|
755
|
+
const raw = String(options["mcp-launch"] || options.mcpLaunch || "").trim().toLowerCase().replace(/_/g, "-");
|
|
756
|
+
if (!raw) return "npx";
|
|
757
|
+
if (raw === "default") return "npx";
|
|
758
|
+
if (raw === "global" || raw === "wrapper") return "global-wrapper";
|
|
759
|
+
if (supportedMcpLaunchModes.includes(raw)) return raw;
|
|
760
|
+
throw new Error(`Unsupported --mcp-launch value: ${raw}. Supported: ${supportedMcpLaunchModes.join(", ")}`);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function buildGlobalMcpWrapperScript() {
|
|
764
|
+
return `#!/usr/bin/env bash
|
|
765
|
+
set -euo pipefail
|
|
766
|
+
|
|
767
|
+
export NVM_DIR="\${NVM_DIR:-$HOME/.nvm}"
|
|
768
|
+
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
|
769
|
+
. "$NVM_DIR/nvm.sh"
|
|
770
|
+
elif [ -s "$HOME/.nvm/nvm.sh" ]; then
|
|
771
|
+
export NVM_DIR="$HOME/.nvm"
|
|
772
|
+
. "$NVM_DIR/nvm.sh"
|
|
773
|
+
fi
|
|
774
|
+
|
|
775
|
+
if ! command -v ${recommendMcpBinaryName} >/dev/null 2>&1; then
|
|
776
|
+
echo "${recommendMcpBinaryName} not found on PATH. Install or reload nvm, then run: npm -g i ${recommendMcpPackageName}@latest" >&2
|
|
777
|
+
exit 127
|
|
778
|
+
fi
|
|
779
|
+
|
|
780
|
+
exec ${recommendMcpBinaryName} start "$@"
|
|
781
|
+
`;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function ensureGlobalMcpWrapper(options = {}) {
|
|
785
|
+
const wrapperPath = getGlobalMcpWrapperPath(options);
|
|
786
|
+
ensureDir(path.dirname(wrapperPath));
|
|
787
|
+
fs.writeFileSync(wrapperPath, buildGlobalMcpWrapperScript(), "utf8");
|
|
788
|
+
try {
|
|
789
|
+
fs.chmodSync(wrapperPath, 0o755);
|
|
790
|
+
} catch {
|
|
791
|
+
// Some filesystems ignore POSIX executable bits; the path is still valid for POSIX hosts.
|
|
792
|
+
}
|
|
793
|
+
return wrapperPath;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function shouldDefaultRecommendDetachedMcpEnv(options = {}) {
|
|
797
|
+
const client = normalizeMcpClientName(options.client);
|
|
798
|
+
const agent = normalizeAgentName(options.agent);
|
|
747
799
|
return client === "openclaw"
|
|
748
800
|
|| client === "qclaw"
|
|
749
801
|
|| agent === "openclaw"
|
|
@@ -763,10 +815,28 @@ function getAgentConfigOutputDir(options = {}) {
|
|
|
763
815
|
return path.join(getStateHome(), "agent-mcp-configs");
|
|
764
816
|
}
|
|
765
817
|
|
|
766
|
-
function buildMcpLaunchConfig(options = {}) {
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
818
|
+
function buildMcpLaunchConfig(options = {}) {
|
|
819
|
+
const mcpLaunchMode = normalizeMcpLaunchMode(options);
|
|
820
|
+
if (mcpLaunchMode === "global-wrapper") {
|
|
821
|
+
const args = parseJsonOption(options["args-json"], "args-json");
|
|
822
|
+
const env = parseJsonOption(options["env-json"], "env-json");
|
|
823
|
+
const launchConfig = {
|
|
824
|
+
command: ensureGlobalMcpWrapper(options),
|
|
825
|
+
args: Array.isArray(args) ? args : []
|
|
826
|
+
};
|
|
827
|
+
const mergedEnv = {
|
|
828
|
+
...getDefaultMcpEnv(options),
|
|
829
|
+
...(isPlainObject(env) ? env : {})
|
|
830
|
+
};
|
|
831
|
+
if (Object.keys(mergedEnv).length > 0) {
|
|
832
|
+
launchConfig.env = mergedEnv;
|
|
833
|
+
}
|
|
834
|
+
return launchConfig;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const command = typeof options.command === "string" && options.command.trim()
|
|
838
|
+
? options.command.trim()
|
|
839
|
+
: defaultMcpCommand;
|
|
770
840
|
const args = parseJsonOption(options["args-json"], "args-json");
|
|
771
841
|
const env = parseJsonOption(options["env-json"], "env-json");
|
|
772
842
|
const launchArgs = Array.isArray(args) && args.length > 0
|
|
@@ -2621,25 +2691,28 @@ function printHelp() {
|
|
|
2621
2691
|
console.log("boss-recommend-mcp");
|
|
2622
2692
|
console.log("");
|
|
2623
2693
|
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
|
|
2627
|
-
console.log(" boss-recommend-mcp
|
|
2694
|
+
console.log(" boss-recommend-mcp Start the MCP server");
|
|
2695
|
+
console.log(" boss-recommend-mcp start Start the MCP server");
|
|
2696
|
+
console.log(" boss-recommend-mcp prepare-run Validate a cron-ready recommend run payload without starting screening");
|
|
2697
|
+
console.log(" boss-recommend-mcp run Start a CDP-only recommend run through the shared run service");
|
|
2698
|
+
console.log(" boss-recommend-mcp list-jobs CDP-only list of exact recommend job names for cron/one-shot inputs");
|
|
2628
2699
|
console.log(" boss-recommend-mcp chat <subcommand> Run CDP-only boss-chat health/prepare/status commands");
|
|
2629
2700
|
console.log(" boss-recommend-mcp install Install/migrate skills and MCP configs; replaces legacy Boss MCP routes (supports --agent trae-cn/openclaw/qclaw/...)");
|
|
2630
2701
|
console.log(" boss-recommend-mcp install-skill Install bundled Codex skills (recommend/recruit/chat)");
|
|
2631
2702
|
console.log(" boss-recommend-mcp init-config Create screening-config.json if missing (prefer workspace config/, fallback ~/.boss-recommend-mcp)");
|
|
2632
2703
|
console.log(" boss-recommend-mcp config set Write baseUrl/apiKey/model (prefer workspace config/, fallback ~/.boss-recommend-mcp)");
|
|
2633
2704
|
console.log(" boss-recommend-mcp set-port Persist preferred Chrome debug port to screening-config.json");
|
|
2634
|
-
console.log(" boss-recommend-mcp mcp-config Generate MCP config JSON for Cursor/Trae(含 trae-cn)/Claude Code/OpenClaw/QClaw");
|
|
2635
|
-
console.log(" boss-recommend-mcp doctor Check config/runtime/calibration prerequisites (supports --agent trae-cn/qclaw/cursor/...)");
|
|
2636
|
-
console.log(" boss-recommend-mcp calibrate Disabled until CDP-only featured calibration is live-verified");
|
|
2637
|
-
console.log(" boss-recommend-mcp launch-chrome Launch or reuse Chrome debug instance and open Boss recommend page");
|
|
2638
|
-
console.log(" boss-recommend-mcp where Print installed package, skill, and config paths");
|
|
2639
|
-
console.log("");
|
|
2640
|
-
console.log("
|
|
2641
|
-
console.log("
|
|
2642
|
-
console.log(" boss-recommend-mcp run --
|
|
2705
|
+
console.log(" boss-recommend-mcp mcp-config Generate MCP config JSON for Cursor/Trae(含 trae-cn)/Claude Code/OpenClaw/QClaw");
|
|
2706
|
+
console.log(" boss-recommend-mcp doctor Check config/runtime/calibration prerequisites (supports --agent trae-cn/qclaw/cursor/...)");
|
|
2707
|
+
console.log(" boss-recommend-mcp calibrate Disabled until CDP-only featured calibration is live-verified");
|
|
2708
|
+
console.log(" boss-recommend-mcp launch-chrome Launch or reuse Chrome debug instance and open Boss recommend page");
|
|
2709
|
+
console.log(" boss-recommend-mcp where Print installed package, skill, and config paths");
|
|
2710
|
+
console.log(" boss-recommend-mcp install --mcp-launch global-wrapper Use ~/.boss-recommend-mcp/bin wrapper so npm global upgrades affect MCP hosts");
|
|
2711
|
+
console.log("");
|
|
2712
|
+
console.log("Run command:");
|
|
2713
|
+
console.log(" boss-recommend-mcp prepare-run --instruction \"...\" --overrides-file overrides.json --confirmation-file confirmation.json");
|
|
2714
|
+
console.log(" boss-recommend-mcp run --instruction \"推荐页上筛选211男生,近14天没有,有大模型平台经验\" --overrides-file overrides.json --confirmation-file confirmation.json");
|
|
2715
|
+
console.log(" boss-recommend-mcp run --detached --instruction \"...\" --overrides-file overrides.json --confirmation-file confirmation.json");
|
|
2643
2716
|
console.log(" boss-recommend-mcp list-jobs --slow-live --port 9222");
|
|
2644
2717
|
console.log(" boss-recommend-mcp chat prepare-run --slow-live --port 9222 # CDP-only preflight; start runs through MCP start_boss_chat_run");
|
|
2645
2718
|
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>]");
|
|
@@ -2662,14 +2735,15 @@ function printMcpConfig(options = {}) {
|
|
|
2662
2735
|
}
|
|
2663
2736
|
}
|
|
2664
2737
|
|
|
2665
|
-
async function installAll(options = {}) {
|
|
2666
|
-
const runtimeDirsResult = await ensureRuntimeDirectories(options);
|
|
2667
|
-
const skillResults = installSkill();
|
|
2668
|
-
const configResult = await ensureUserConfig(options);
|
|
2669
|
-
const
|
|
2670
|
-
const
|
|
2671
|
-
const
|
|
2672
|
-
|
|
2738
|
+
async function installAll(options = {}) {
|
|
2739
|
+
const runtimeDirsResult = await ensureRuntimeDirectories(options);
|
|
2740
|
+
const skillResults = installSkill();
|
|
2741
|
+
const configResult = await ensureUserConfig(options);
|
|
2742
|
+
const mcpLaunchMode = normalizeMcpLaunchMode(options);
|
|
2743
|
+
const mcpTemplateResult = writeMcpConfigFiles({ ...options, agent: undefined, client: "all" });
|
|
2744
|
+
const externalMcpResult = installExternalMcpConfigs(options);
|
|
2745
|
+
const externalSkillResult = mirrorSkillToExternalDirs(options);
|
|
2746
|
+
console.log(
|
|
2673
2747
|
`Runtime directories prepared: created=${runtimeDirsResult.created.length}, existing=${runtimeDirsResult.existed.length}, failed=${runtimeDirsResult.failed.length}`
|
|
2674
2748
|
);
|
|
2675
2749
|
console.log(`- recommend runtime: ${runtimeDirsResult.stateHome}`);
|
|
@@ -2697,10 +2771,13 @@ async function installAll(options = {}) {
|
|
|
2697
2771
|
console.warn(`screening-config.json skip default patch: ${configResult.patch_error}`);
|
|
2698
2772
|
}
|
|
2699
2773
|
console.log(`请在该目录修改 baseUrl/apiKey/model 并替换占位词后再运行:${path.dirname(configResult.path)}`);
|
|
2700
|
-
console.log(`MCP config templates exported to: ${mcpTemplateResult.outputDir}`);
|
|
2701
|
-
for (const item of mcpTemplateResult.files) {
|
|
2702
|
-
console.log(`- ${item.client}: ${item.file}`);
|
|
2703
|
-
}
|
|
2774
|
+
console.log(`MCP config templates exported to: ${mcpTemplateResult.outputDir}`);
|
|
2775
|
+
for (const item of mcpTemplateResult.files) {
|
|
2776
|
+
console.log(`- ${item.client}: ${item.file}`);
|
|
2777
|
+
}
|
|
2778
|
+
if (mcpLaunchMode === "global-wrapper") {
|
|
2779
|
+
console.log(`Upgrade-stable MCP wrapper: ${getGlobalMcpWrapperPath(options)}`);
|
|
2780
|
+
}
|
|
2704
2781
|
if (externalMcpResult.targets.length > 0) {
|
|
2705
2782
|
console.log(`Auto-configured external MCP files: ${externalMcpResult.applied.length}`);
|
|
2706
2783
|
for (const item of externalMcpResult.applied) {
|
|
@@ -2733,14 +2810,13 @@ async function installAll(options = {}) {
|
|
|
2733
2810
|
}
|
|
2734
2811
|
}
|
|
2735
2812
|
|
|
2736
|
-
|
|
2737
|
-
const instruction = getRunInstruction(options);
|
|
2738
|
-
const confirmation = getRunConfirmation(options);
|
|
2739
|
-
const overrides = getRunOverrides(options);
|
|
2740
|
-
const followUp = getRunFollowUp(options);
|
|
2741
|
-
const
|
|
2742
|
-
|
|
2743
|
-
|
|
2813
|
+
function buildRecommendRunCliInput(options = {}) {
|
|
2814
|
+
const instruction = getRunInstruction(options);
|
|
2815
|
+
const confirmation = getRunConfirmation(options);
|
|
2816
|
+
const overrides = getRunOverrides(options);
|
|
2817
|
+
const followUp = getRunFollowUp(options);
|
|
2818
|
+
const port = parsePositivePort(options.port) || parsePositivePort(process.env.BOSS_RECOMMEND_CHROME_PORT) || 9222;
|
|
2819
|
+
|
|
2744
2820
|
const args = {
|
|
2745
2821
|
instruction,
|
|
2746
2822
|
confirmation: confirmation ?? undefined,
|
|
@@ -2797,15 +2873,46 @@ async function runPipelineOnce(options = {}) {
|
|
|
2797
2873
|
"llm_image_limit",
|
|
2798
2874
|
"llm_image_detail"
|
|
2799
2875
|
];
|
|
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
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2876
|
+
for (const key of optionalPassthrough) {
|
|
2877
|
+
const kebab = key.replace(/_/g, "-");
|
|
2878
|
+
if (options[key] !== undefined) args[key] = options[key];
|
|
2879
|
+
else if (options[kebab] !== undefined) args[key] = options[kebab];
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
return {
|
|
2883
|
+
args,
|
|
2884
|
+
port
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
async function preparePipelineOnce(options = {}) {
|
|
2889
|
+
const workspaceRoot = getWorkspaceRoot(options);
|
|
2890
|
+
const { args, port } = buildRecommendRunCliInput(options);
|
|
2891
|
+
const result = prepareRecommendPipelineRunTool({
|
|
2892
|
+
workspaceRoot,
|
|
2893
|
+
args
|
|
2894
|
+
});
|
|
2895
|
+
printJson({
|
|
2896
|
+
...result,
|
|
2897
|
+
cli: {
|
|
2898
|
+
command: "prepare-run",
|
|
2899
|
+
cdp_only: true,
|
|
2900
|
+
shared_run_service: true,
|
|
2901
|
+
workspace_root: workspaceRoot,
|
|
2902
|
+
port
|
|
2903
|
+
}
|
|
2904
|
+
});
|
|
2905
|
+
if (result.status !== "READY" || result.cron_ready !== true) {
|
|
2906
|
+
process.exitCode = 1;
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
async function runPipelineOnce(options = {}) {
|
|
2911
|
+
const workspaceRoot = getWorkspaceRoot(options);
|
|
2912
|
+
const { args, port } = buildRecommendRunCliInput(options);
|
|
2913
|
+
const result = await startRecommendPipelineRunTool({
|
|
2914
|
+
workspaceRoot,
|
|
2915
|
+
args
|
|
2809
2916
|
});
|
|
2810
2917
|
printJson({
|
|
2811
2918
|
...result,
|
|
@@ -2974,11 +3081,11 @@ export async function runCli(argv = process.argv) {
|
|
|
2974
3081
|
case "mcp":
|
|
2975
3082
|
startServer();
|
|
2976
3083
|
break;
|
|
2977
|
-
case "run":
|
|
2978
|
-
try {
|
|
2979
|
-
if (
|
|
2980
|
-
(options.detached === true || options.background === true)
|
|
2981
|
-
&& process.env[detachedRecommendRunChildEnv] !== "1"
|
|
3084
|
+
case "run":
|
|
3085
|
+
try {
|
|
3086
|
+
if (
|
|
3087
|
+
(options.detached === true || options.background === true)
|
|
3088
|
+
&& process.env[detachedRecommendRunChildEnv] !== "1"
|
|
2982
3089
|
) {
|
|
2983
3090
|
await runPipelineDetached(argv.slice(3), options);
|
|
2984
3091
|
} else {
|
|
@@ -2993,9 +3100,26 @@ export async function runCli(argv = process.argv) {
|
|
|
2993
3100
|
retryable: false
|
|
2994
3101
|
}
|
|
2995
3102
|
});
|
|
2996
|
-
process.exitCode = 1;
|
|
2997
|
-
}
|
|
2998
|
-
break;
|
|
3103
|
+
process.exitCode = 1;
|
|
3104
|
+
}
|
|
3105
|
+
break;
|
|
3106
|
+
case "prepare-run":
|
|
3107
|
+
case "prepare":
|
|
3108
|
+
try {
|
|
3109
|
+
await preparePipelineOnce(options);
|
|
3110
|
+
} catch (error) {
|
|
3111
|
+
printJson({
|
|
3112
|
+
status: "FAILED",
|
|
3113
|
+
cron_ready: false,
|
|
3114
|
+
error: {
|
|
3115
|
+
code: "INVALID_CLI_INPUT",
|
|
3116
|
+
message: error.message || "Invalid CLI input",
|
|
3117
|
+
retryable: false
|
|
3118
|
+
}
|
|
3119
|
+
});
|
|
3120
|
+
process.exitCode = 1;
|
|
3121
|
+
}
|
|
3122
|
+
break;
|
|
2999
3123
|
case "list-jobs":
|
|
3000
3124
|
case "jobs":
|
|
3001
3125
|
case "recommend-jobs":
|
|
@@ -3139,11 +3263,13 @@ export async function runCli(argv = process.argv) {
|
|
|
3139
3263
|
}
|
|
3140
3264
|
|
|
3141
3265
|
export const __testables = {
|
|
3142
|
-
buildRecommendJobListCliInput,
|
|
3143
|
-
buildBossChatCliInput,
|
|
3144
|
-
buildDefaultMcpArgs,
|
|
3145
|
-
buildMcpLaunchConfig,
|
|
3146
|
-
|
|
3266
|
+
buildRecommendJobListCliInput,
|
|
3267
|
+
buildBossChatCliInput,
|
|
3268
|
+
buildDefaultMcpArgs,
|
|
3269
|
+
buildMcpLaunchConfig,
|
|
3270
|
+
ensureGlobalMcpWrapper,
|
|
3271
|
+
getGlobalMcpWrapperPath,
|
|
3272
|
+
collectRuntimeDirectories,
|
|
3147
3273
|
ensureBossChatRuntimeReady: ensureBossChatRuntimeReadyLocal,
|
|
3148
3274
|
ensureRuntimeDirectories,
|
|
3149
3275
|
getBossChatCliRunTarget,
|
|
@@ -3153,10 +3279,11 @@ export const __testables = {
|
|
|
3153
3279
|
installSkill,
|
|
3154
3280
|
isInstalledPackageRoot,
|
|
3155
3281
|
mergeMcpServerConfigFile,
|
|
3156
|
-
resolveBossChatRuntimeLayout: resolveCdpBossChatRuntimeLayout,
|
|
3157
|
-
runBossChatCliCommand,
|
|
3158
|
-
|
|
3159
|
-
|
|
3282
|
+
resolveBossChatRuntimeLayout: resolveCdpBossChatRuntimeLayout,
|
|
3283
|
+
runBossChatCliCommand,
|
|
3284
|
+
preparePipelineOnce,
|
|
3285
|
+
runPipelineOnce
|
|
3286
|
+
};
|
|
3160
3287
|
|
|
3161
3288
|
if (process.argv[1] && path.resolve(process.argv[1]) === currentFilePath) {
|
|
3162
3289
|
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) {
|
package/src/recommend-mcp.js
CHANGED
|
@@ -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)
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
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
|
},
|