@reconcrap/boss-recommend-mcp 2.1.23 → 2.1.24
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 +5 -0
- package/bin/boss-recommend-mcp.js +4 -4
- package/package.json +6 -1
- package/scripts/install-macos.sh +280 -280
- package/scripts/postinstall.cjs +44 -44
- package/skills/boss-chat/README.md +43 -42
- package/skills/boss-chat/SKILL.md +107 -106
- package/skills/boss-recommend-pipeline/README.md +13 -13
- package/skills/boss-recommend-pipeline/SKILL.md +47 -47
- package/skills/boss-recruit-pipeline/README.md +19 -19
- package/skills/boss-recruit-pipeline/SKILL.md +89 -89
- package/src/chat-mcp.js +301 -127
- package/src/core/boss-cards/index.js +199 -199
- package/src/core/browser/index.js +286 -136
- package/src/core/capture/index.js +2930 -1201
- package/src/core/cv-acquisition/index.js +512 -238
- package/src/core/cv-capture-target/index.js +513 -299
- package/src/core/greet-quota/index.js +71 -71
- package/src/core/infinite-list/index.js +31 -31
- package/src/core/reporting/legacy-csv.js +12 -12
- package/src/core/run/detached-launcher.js +305 -0
- package/src/core/run/index.js +105 -52
- package/src/core/run/timing.js +33 -33
- package/src/core/run/windows-detached-worker.ps1 +106 -0
- package/src/core/screening/index.js +2135 -2135
- package/src/core/self-heal/index.js +989 -973
- package/src/core/self-heal/viewport.js +1505 -564
- package/src/detached-worker.js +99 -99
- package/src/domains/chat/action-journal.js +443 -0
- package/src/domains/chat/cards.js +137 -137
- package/src/domains/chat/constants.js +9 -9
- package/src/domains/chat/detail.js +1684 -401
- package/src/domains/chat/index.js +8 -7
- package/src/domains/chat/jobs.js +620 -620
- package/src/domains/chat/page-guard.js +157 -122
- package/src/domains/chat/roots.js +56 -56
- package/src/domains/chat/run-service.js +1801 -760
- package/src/domains/common/account-rights-panel.js +314 -314
- package/src/domains/common/recovery-settle.js +159 -159
- package/src/domains/recommend/actions.js +515 -472
- package/src/domains/recommend/cards.js +243 -243
- package/src/domains/recommend/colleague-contact.js +333 -333
- package/src/domains/recommend/constants.js +92 -92
- package/src/domains/recommend/detail.js +12 -12
- package/src/domains/recommend/filters.js +611 -611
- package/src/domains/recommend/index.js +9 -9
- package/src/domains/recommend/jobs.js +542 -542
- package/src/domains/recommend/location.js +736 -736
- package/src/domains/recommend/refresh.js +422 -422
- package/src/domains/recommend/roots.js +80 -80
- package/src/domains/recommend/run-service.js +1791 -1205
- package/src/domains/recommend/scopes.js +246 -246
- package/src/domains/recruit/actions.js +277 -277
- package/src/domains/recruit/cards.js +74 -74
- package/src/domains/recruit/constants.js +236 -236
- package/src/domains/recruit/detail.js +588 -588
- package/src/domains/recruit/index.js +9 -9
- package/src/domains/recruit/instruction-parser.js +866 -866
- package/src/domains/recruit/refresh.js +45 -45
- package/src/domains/recruit/roots.js +68 -68
- package/src/domains/recruit/run-service.js +1817 -1620
- package/src/domains/recruit/search.js +3229 -3229
- package/src/index.js +16 -1
- package/src/parser.js +1296 -1296
- package/src/recommend-mcp.js +551 -362
- package/src/recommend-scheduler.js +75 -75
package/README.md
CHANGED
|
@@ -177,6 +177,8 @@ boss-recommend-mcp schedule-status --schedule-id <schedule_id>
|
|
|
177
177
|
- 不会对每位候选人重复确认
|
|
178
178
|
- 推荐页详情处理完成后,会强制关闭详情页并确认已关闭
|
|
179
179
|
- 简历提取优先使用 Network 响应;没有可解析 Network CV 时,回退到完整滚动截图序列再交给多模态模型判断
|
|
180
|
+
- recommend / search / chat 共用同一条截图安全路径:浏览器只截取可见视口(`captureBeyondViewport:false`、`fromSurface:true`、不传 `clip`),再由现有 Sharp 依赖在本地裁剪 CV 区域;滚动序列至少保留 20% 重叠,只有在连续两次滚动且图像与可用 DOM 锚点均无进展后才判定到底。达到页数上限但没有终止证据时会 fail closed,不会把可能截断的简历交给模型。
|
|
181
|
+
- `Page.captureScreenshot` 不会在 CDP 断线后自动重放;超时或连接关闭会放弃原 session、重新获取 document / iframe / CV target,并且每位候选人最多做一次工作流级恢复。视口健康度始终相对最初稳定基线比较宽度和高度,恢复后需要连续两次稳定读数。
|
|
180
182
|
- recommend / search / 带 `criteria` 的 chat 正式运行默认全部使用 `screening-config.json` 配置的 LLM 筛选;chat 的 `criteria` 留空时进入收集简历模式,不需要 LLM 配置。deterministic/local scorer 只保留给明确测试场景,必须显式传 `debug_test_mode=true` 且 `screening_mode=deterministic` 或 `use_llm=false`。
|
|
181
183
|
- `detail_limit=0`、`no_filter`、`filter_enabled=false`、后置动作 dry-run、chat 求简历 dry-run 等调试路径不会在正式 live run 默认启用;需要测试时必须显式传 `debug_test_mode=true`。
|
|
182
184
|
- 提供显式运维自愈工具:只在手动调用 `run_recommend_self_heal` 时运行,不会接入正常 run / doctor / preflight 自动链路
|
|
@@ -464,6 +466,8 @@ node src/cli.js chat prepare-run --slow-live --port 9222
|
|
|
464
466
|
说明:
|
|
465
467
|
|
|
466
468
|
- `start_from` / `target_count` 为必填;`criteria` 可选,留空时 chat run 进入收集简历模式:不触发 LLM 筛选,针对没有在线/附件简历的人选发送求简历消息并点击求简历按钮
|
|
469
|
+
- 收集简历模式会为每位已识别的人选随机设定 `10-15` 秒的最低处理时长;已有在线/附件简历、已发送过简历请求或需要新发请求都适用。候选人处理本身已达到目标时不再额外补时,否则只等待剩余差额。
|
|
470
|
+
- 该最低处理时长独立于 `human_behavior.restLevel`:补时结束后仍按用户选择的 `low/medium/high` 执行原有休息预算;显式 `delay_ms` 也继续作为后续的额外延时。
|
|
467
471
|
- `greeting_text` 可选(兼容 `greetingText`)
|
|
468
472
|
- `profile` 可选,默认 `default`
|
|
469
473
|
- `job` 与 `port` 继承 recommend run 已选岗位和调试端口
|
|
@@ -501,6 +505,7 @@ chat-only 交互建议:
|
|
|
501
505
|
- 然后基于 `job_options` 让用户选择 `job`,并补齐 `start_from`、`target_count` 后调用 `start_boss_chat_run` 启动任务;若要筛选再求简历,提供 `criteria`,若只想收集缺失简历则留空。
|
|
502
506
|
- `greeting_text` 可选;未传时使用 `screening-config.json.greetingMessage`,若未配置则使用默认招呼语(`Hi同学,能麻烦发下简历吗?`)。
|
|
503
507
|
- `target_count` 支持正整数、`all`、`-1`;若用户给出 `全部候选人` / `所有候选人`,会自动按不限(扫到底)处理。
|
|
508
|
+
- `criteria` 留空的收集简历模式固定保证每位人选从识别到提交结果至少停留随机 `10-15` 秒;该产品级下限无需新增参数,且不会替代所选休息强度。
|
|
504
509
|
|
|
505
510
|
Trae-CN / 长对话防循环建议:
|
|
506
511
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { runCli } from "../src/cli.js";
|
|
3
|
-
|
|
4
|
-
await runCli(process.argv);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runCli } from "../src/cli.js";
|
|
3
|
+
|
|
4
|
+
await runCli(process.argv);
|
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.24",
|
|
4
4
|
"description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"boss",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"test:core-infinite-list": "node src/test-core-infinite-list.js",
|
|
31
31
|
"test:core-reporting": "node src/test-core-reporting.js",
|
|
32
32
|
"test:core-run": "node src/test-core-run.js",
|
|
33
|
+
"test:detached-launcher": "node src/test-detached-launcher.js",
|
|
33
34
|
"test:core-screening": "node src/test-core-screening.js",
|
|
34
35
|
"test:core-self-heal": "node src/test-core-self-heal.js",
|
|
35
36
|
"test:installer-migration": "node src/test-installer-migration.js",
|
|
@@ -40,11 +41,13 @@
|
|
|
40
41
|
"test:live-recommend-mcp-smoke": "node src/test-live-recommend-mcp-smoke.js",
|
|
41
42
|
"test:recruit-domain": "node src/test-recruit-domain.js",
|
|
42
43
|
"test:recruit-mcp": "node src/test-recruit-mcp.js",
|
|
44
|
+
"test:chat-action-journal": "node src/test-chat-action-journal.js",
|
|
43
45
|
"test:chat-domain": "node src/test-chat-domain.js",
|
|
44
46
|
"test:chat-run-service": "node src/test-chat-run-service.js",
|
|
45
47
|
"test:chat-mcp": "node src/test-chat-mcp.js",
|
|
46
48
|
"test:async": "node src/test-index-async.js",
|
|
47
49
|
"test:runtime-scan": "node src/test-runtime-scan.js",
|
|
50
|
+
"test:live-capture-acceptance": "node src/test-live-capture-acceptance.js",
|
|
48
51
|
"scan:runtime": "node scripts/scan-forbidden-runtime.js",
|
|
49
52
|
"scan:runtime:json": "node scripts/scan-forbidden-runtime.js --json",
|
|
50
53
|
"scan:runtime:strict": "node scripts/scan-forbidden-runtime.js --fail-on-findings",
|
|
@@ -52,6 +55,7 @@
|
|
|
52
55
|
"scan:runtime:package:strict": "node scripts/scan-forbidden-runtime.js --package-surface --fail-on-legacy",
|
|
53
56
|
"scan:legacy-boundary": "node scripts/scan-legacy-boundary.js",
|
|
54
57
|
"scan:package-boundary": "node scripts/scan-package-boundary.js",
|
|
58
|
+
"scan:screenshot-safety": "node scripts/scan-screenshot-safety.js",
|
|
55
59
|
"gate:phase9-static": "node scripts/phase9-static-gate.js",
|
|
56
60
|
"gate:phase10-complete": "node scripts/phase10-completion-gate.js",
|
|
57
61
|
"live:cdp-smoke": "node scripts/live-cdp-smoke.js",
|
|
@@ -68,6 +72,7 @@
|
|
|
68
72
|
"live:recommend-domain": "node scripts/live-recommend-domain-smoke.js",
|
|
69
73
|
"live:recommend-run-service": "node scripts/live-recommend-run-service-smoke.js",
|
|
70
74
|
"live:recommend-mcp": "node scripts/live-recommend-mcp-smoke.js",
|
|
75
|
+
"live:recommend-viewport-collapse": "node scripts/live-recommend-viewport-collapse-acceptance.js",
|
|
71
76
|
"live:search-phase10-full": "node scripts/live-search-phase10-full.js",
|
|
72
77
|
"live:recruit-domain": "node scripts/live-recruit-domain-smoke.js",
|
|
73
78
|
"live:recruit-run-service": "node scripts/live-recruit-run-service-smoke.js",
|
package/scripts/install-macos.sh
CHANGED
|
@@ -1,280 +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
|
|
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
|