@reconcrap/boss-recruit-mcp 1.0.4 → 1.0.5
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 +1 -0
- package/package.json +1 -1
- package/skills/boss-recruit-pipeline/README.md +1 -0
- package/skills/boss-recruit-pipeline/SKILL.md +14 -2
- package/src/cli.js +132 -8
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -27,6 +27,7 @@ npx @reconcrap/boss-recruit-mcp install
|
|
|
27
27
|
- 正式开始前,必须先做一轮参数确认,分开展示已识别参数、待确认参数、缺失参数。
|
|
28
28
|
- 参数确认尽量复用统一模板:`已识别参数` / `待确认或待修正` / `缺失参数` / `默认值提醒` / `请用户回复`。
|
|
29
29
|
- 端口未确认时,必须先询问用户是否使用推荐的 `9222`,或提供一个已有的其他远程调试端口,不能直接默认 `9222`。
|
|
30
|
+
- 新打开 Chrome 实例后,要检查页面是否仍停留在 Boss search;如果跳转到其他页面,必须提示用户先手动登录 Boss,再继续。
|
|
30
31
|
- 如果识别结果里出现明显脏值或可疑字段,例如“杭州筛选做过”,必须要求用户改成标准值后再继续。
|
|
31
32
|
- 如果缺少 `favorite-calibration.json`,必须指导用户在当前环境重新校准,不能搜索或复制历史遗留校准文件来顶替。
|
|
32
33
|
- 若缺失参数仍未补齐,只能在用户明确确认接受默认值和质量风险后继续,不能静默按默认执行。
|
|
@@ -31,7 +31,11 @@
|
|
|
31
31
|
5. 若 Chrome 未以远程调试模式启动,优先帮助用户启动:
|
|
32
32
|
- `boss-recruit-mcp launch-chrome --port <port>`
|
|
33
33
|
- 打开页面:`https://www.zhipin.com/web/chat/search`
|
|
34
|
-
6.
|
|
34
|
+
6. 启动新的 Chrome debugging 实例后,必须检查页面是否仍停留在 `https://www.zhipin.com/web/chat/search`:
|
|
35
|
+
- 若仍在 search 页面,可继续;
|
|
36
|
+
- 若跳转到登录页、首页或其他 Boss 页面,视为“需要重新登录”;
|
|
37
|
+
- 必须明确提示用户手动登录 Boss,并等待用户回复“已登录/可以继续”后,才能继续后续动作。
|
|
38
|
+
7. 只有在以上条件满足后,才继续调用流水线。
|
|
35
39
|
|
|
36
40
|
## Calibration Requirement
|
|
37
41
|
|
|
@@ -124,6 +128,7 @@
|
|
|
124
128
|
- 若 MCP 不可用,CLI fallback 是否可用
|
|
125
129
|
- Chrome 调试端口是否已确认
|
|
126
130
|
- Chrome 是否已启动到 Boss 搜索页面
|
|
131
|
+
- 如果刚启动了新的 Chrome 实例,是否仍停留在 Boss search 页面而未跳转登录
|
|
127
132
|
- `favorite-calibration.json` 是否存在
|
|
128
133
|
2. 若缺少依赖或 MCP 未启动:
|
|
129
134
|
- 自动安装依赖并帮助用户启动 MCP;
|
|
@@ -135,7 +140,12 @@
|
|
|
135
140
|
- 明确给出校准步骤与命令;
|
|
136
141
|
- 明确指出期望生成到哪个路径;
|
|
137
142
|
- 不要在本机搜索并复用其他 `favorite-calibration.json`。
|
|
138
|
-
4.
|
|
143
|
+
4. 若 `launch-chrome` 后发现页面没有停留在 search,而是跳到了登录页、首页或其他页面:
|
|
144
|
+
- 明确告诉用户“当前需要手动登录 Boss”;
|
|
145
|
+
- 明确要求用户在新打开的 Chrome 窗口中完成登录;
|
|
146
|
+
- 等用户回复“已登录,可以继续”后,再继续下一步;
|
|
147
|
+
- 不要在用户未确认登录完成前直接执行搜索、校准或流水线。
|
|
148
|
+
5. 只有当以上条件满足时,才首次进入流水线解析:
|
|
139
149
|
- 若 MCP 可用,调用 `run_recruit_pipeline`(只传 `instruction`)
|
|
140
150
|
- 若 MCP 不可用,调用 `boss-recruit-mcp run --instruction ...`
|
|
141
151
|
- 这一步用于“解析”,不是立刻执行最终搜索结论。
|
|
@@ -246,6 +256,7 @@
|
|
|
246
256
|
- 如果工具返回 `output_csv`,在摘要里给出路径,避免重复解释内部流程。
|
|
247
257
|
- 如果端口还没确认,必须先问用户“是否使用推荐的 `9222`,还是你已经有别的远程调试端口”,不能直接把 `9222` 当成已确认值。
|
|
248
258
|
- 如果需要打开 Chrome,优先帮用户执行而不是只给命令。
|
|
259
|
+
- 如果新打开的 Chrome 页面跳离了 search 页面,必须判断为“需要登录”,提示用户手动登录后再继续。
|
|
249
260
|
|
|
250
261
|
## Example
|
|
251
262
|
|
|
@@ -273,5 +284,6 @@
|
|
|
273
284
|
- 端口未确认时,用“推荐值 + 可选其他端口”的话术,不要直接替用户决定。
|
|
274
285
|
- 校准缺失时,直接指导用户重新校准,不要建议复制或复用任何历史 calibration 文件。
|
|
275
286
|
- 若当前 agent 受 MCP 数量限制,明确告诉用户“本轮改走 CLI fallback”,但用户体验上仍保持同一套确认和状态输出。
|
|
287
|
+
- 新开 Chrome 后若检测到跳转登录,先提示用户手动登录并等待确认,再继续。
|
|
276
288
|
- 若要使用默认值,必须写明“请确认是否按默认值继续”,不能模糊带过。
|
|
277
289
|
- 若运行失败,优先给用户“现在卡在哪一步 + 怎么继续”。
|
package/src/cli.js
CHANGED
|
@@ -135,6 +135,106 @@ function printJson(value) {
|
|
|
135
135
|
console.log(JSON.stringify(value, null, 2));
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
function sleep(ms) {
|
|
139
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function listChromeTabs(port) {
|
|
143
|
+
const response = await fetch(`http://127.0.0.1:${port}/json/list`);
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
throw new Error(`DevTools endpoint returned ${response.status}`);
|
|
146
|
+
}
|
|
147
|
+
const data = await response.json();
|
|
148
|
+
return Array.isArray(data) ? data : [];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function buildBossPageState(payload) {
|
|
152
|
+
return {
|
|
153
|
+
key: "boss_page_state",
|
|
154
|
+
...payload
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function inspectBossPageState(port, options = {}) {
|
|
159
|
+
const timeoutMs = Number.isFinite(options.timeoutMs) ? options.timeoutMs : 8000;
|
|
160
|
+
const pollMs = Number.isFinite(options.pollMs) ? options.pollMs : 1000;
|
|
161
|
+
const expectedUrl = options.expectedUrl || bossUrl;
|
|
162
|
+
const deadline = Date.now() + timeoutMs;
|
|
163
|
+
let lastError = null;
|
|
164
|
+
let lastTabs = [];
|
|
165
|
+
|
|
166
|
+
while (Date.now() < deadline) {
|
|
167
|
+
try {
|
|
168
|
+
const tabs = await listChromeTabs(port);
|
|
169
|
+
lastTabs = tabs;
|
|
170
|
+
|
|
171
|
+
const exactSearchTab = tabs.find(
|
|
172
|
+
(tab) => typeof tab?.url === "string" && tab.url.includes("/web/chat/search")
|
|
173
|
+
);
|
|
174
|
+
if (exactSearchTab) {
|
|
175
|
+
return buildBossPageState({
|
|
176
|
+
ok: true,
|
|
177
|
+
state: "SEARCH_READY",
|
|
178
|
+
path: exactSearchTab.url,
|
|
179
|
+
current_url: exactSearchTab.url,
|
|
180
|
+
title: exactSearchTab.title || null,
|
|
181
|
+
requires_login: false,
|
|
182
|
+
message: "Boss 搜索页已打开,且当前仍停留在 search 页面。"
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const bossTab = tabs.find(
|
|
187
|
+
(tab) => typeof tab?.url === "string" && tab.url.includes("zhipin.com")
|
|
188
|
+
);
|
|
189
|
+
if (bossTab) {
|
|
190
|
+
return buildBossPageState({
|
|
191
|
+
ok: false,
|
|
192
|
+
state: "LOGIN_REQUIRED",
|
|
193
|
+
path: bossTab.url,
|
|
194
|
+
current_url: bossTab.url,
|
|
195
|
+
title: bossTab.title || null,
|
|
196
|
+
requires_login: true,
|
|
197
|
+
expected_url: expectedUrl,
|
|
198
|
+
message: "Boss 页面没有停留在 search 页面,通常表示需要重新登录。请用户手动登录 Boss 后再继续。"
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
lastError = error;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
await sleep(pollMs);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (lastError) {
|
|
209
|
+
return buildBossPageState({
|
|
210
|
+
ok: false,
|
|
211
|
+
state: "DEBUG_PORT_UNREACHABLE",
|
|
212
|
+
path: `http://127.0.0.1:${port}`,
|
|
213
|
+
current_url: null,
|
|
214
|
+
title: null,
|
|
215
|
+
requires_login: false,
|
|
216
|
+
expected_url: expectedUrl,
|
|
217
|
+
message: `无法连接到 Chrome DevTools 端口 ${port}。请确认 Chrome 已以远程调试模式启动。`,
|
|
218
|
+
error: lastError.message
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return buildBossPageState({
|
|
223
|
+
ok: false,
|
|
224
|
+
state: "BOSS_TAB_NOT_FOUND",
|
|
225
|
+
path: expectedUrl,
|
|
226
|
+
current_url: null,
|
|
227
|
+
title: null,
|
|
228
|
+
requires_login: false,
|
|
229
|
+
expected_url: expectedUrl,
|
|
230
|
+
message: "未检测到 Boss 页面标签页。请确认 Chrome 已打开 Boss 搜索页。",
|
|
231
|
+
sample_urls: lastTabs
|
|
232
|
+
.map((tab) => tab?.url)
|
|
233
|
+
.filter(Boolean)
|
|
234
|
+
.slice(0, 5)
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
138
238
|
function hasModule(moduleName) {
|
|
139
239
|
try {
|
|
140
240
|
require.resolve(moduleName);
|
|
@@ -195,9 +295,10 @@ function ensureUserConfig() {
|
|
|
195
295
|
return { path: targetPath, created: false };
|
|
196
296
|
}
|
|
197
297
|
|
|
198
|
-
function printDoctor(options) {
|
|
298
|
+
async function printDoctor(options) {
|
|
199
299
|
const port = getDebugPort(options);
|
|
200
300
|
const checks = runPipelinePreflight(process.cwd()).checks.slice();
|
|
301
|
+
const pageState = await inspectBossPageState(port, { timeoutMs: 2000, pollMs: 500 });
|
|
201
302
|
const userConfigPath = getUserConfigPath();
|
|
202
303
|
checks.push({
|
|
203
304
|
key: "user_config",
|
|
@@ -225,10 +326,14 @@ function printDoctor(options) {
|
|
|
225
326
|
});
|
|
226
327
|
checks.push({
|
|
227
328
|
key: "chrome_debug_port",
|
|
228
|
-
ok:
|
|
329
|
+
ok: pageState.state !== "DEBUG_PORT_UNREACHABLE",
|
|
229
330
|
path: `http://localhost:${port}`,
|
|
230
|
-
message:
|
|
331
|
+
message:
|
|
332
|
+
pageState.state === "DEBUG_PORT_UNREACHABLE"
|
|
333
|
+
? `无法连接 Chrome 调试端口 ${port}`
|
|
334
|
+
: `Chrome 调试端口 ${port} 可连接`
|
|
231
335
|
});
|
|
336
|
+
checks.push(pageState);
|
|
232
337
|
console.log(JSON.stringify({ ok: checks.every((item) => item.ok), port, checks }, null, 2));
|
|
233
338
|
}
|
|
234
339
|
|
|
@@ -251,7 +356,7 @@ async function calibrate(options) {
|
|
|
251
356
|
process.exitCode = code;
|
|
252
357
|
}
|
|
253
358
|
|
|
254
|
-
function launchChrome(options) {
|
|
359
|
+
async function launchChrome(options) {
|
|
255
360
|
const chromePath = getChromeExecutable();
|
|
256
361
|
if (!chromePath) {
|
|
257
362
|
console.error("Chrome executable not found. Set BOSS_RECRUIT_CHROME_PATH or install Google Chrome.");
|
|
@@ -275,6 +380,25 @@ function launchChrome(options) {
|
|
|
275
380
|
console.log(`Chrome launched with remote debugging port ${port}`);
|
|
276
381
|
console.log(`User data dir: ${userDataDir}`);
|
|
277
382
|
console.log(`URL: ${bossUrl}`);
|
|
383
|
+
|
|
384
|
+
const pageState = await inspectBossPageState(port, { timeoutMs: 12000, pollMs: 1000 });
|
|
385
|
+
if (pageState.state === "SEARCH_READY") {
|
|
386
|
+
console.log("Boss search page is ready.");
|
|
387
|
+
console.log(`Current URL: ${pageState.current_url}`);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (pageState.state === "LOGIN_REQUIRED") {
|
|
392
|
+
console.log("Boss page redirected away from search. Manual login is required.");
|
|
393
|
+
console.log(`Current URL: ${pageState.current_url}`);
|
|
394
|
+
console.log("Please log in to Boss manually in the opened Chrome window, then tell the AI agent to continue.");
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
console.log(pageState.message);
|
|
399
|
+
if (pageState.current_url) {
|
|
400
|
+
console.log(`Current URL: ${pageState.current_url}`);
|
|
401
|
+
}
|
|
278
402
|
}
|
|
279
403
|
|
|
280
404
|
function printHelp() {
|
|
@@ -289,7 +413,7 @@ function printHelp() {
|
|
|
289
413
|
console.log(" boss-recruit-mcp init-config Create ~/.codex/boss-recruit-mcp/screening-config.json if missing");
|
|
290
414
|
console.log(" boss-recruit-mcp doctor Check config, calibration, and runtime prerequisites");
|
|
291
415
|
console.log(" boss-recruit-mcp calibrate Run favorite-button calibration and save favorite-calibration.json");
|
|
292
|
-
console.log(" boss-recruit-mcp launch-chrome Launch Chrome in remote-debugging mode
|
|
416
|
+
console.log(" boss-recruit-mcp launch-chrome Launch Chrome in remote-debugging mode, open Boss search, and check login state");
|
|
293
417
|
console.log(" boss-recruit-mcp where Print installed package, skill, and config paths");
|
|
294
418
|
console.log("");
|
|
295
419
|
console.log("Run command:");
|
|
@@ -322,7 +446,7 @@ function installAll() {
|
|
|
322
446
|
console.log("1. Fill in baseUrl/apiKey/model in the config file above.");
|
|
323
447
|
console.log("2. Choose a Chrome remote-debugging port (9222 is recommended, but you can reuse an existing port).");
|
|
324
448
|
console.log("3. Run `boss-recruit-mcp doctor --port <your-port>` to verify config, calibration, and runtime prerequisites.");
|
|
325
|
-
console.log("4. Run `boss-recruit-mcp launch-chrome --port <your-port
|
|
449
|
+
console.log("4. Run `boss-recruit-mcp launch-chrome --port <your-port>`; if it reports the page redirected away from search, log in to Boss manually in that Chrome window.");
|
|
326
450
|
console.log("5. Run `boss-recruit-mcp calibrate --port <your-port>` to generate favorite-calibration.json for this environment.");
|
|
327
451
|
console.log("6. Run `boss-recruit-mcp start` or configure your MCP client to launch `boss-recruit-mcp`.");
|
|
328
452
|
}
|
|
@@ -381,13 +505,13 @@ switch (command) {
|
|
|
381
505
|
break;
|
|
382
506
|
}
|
|
383
507
|
case "doctor":
|
|
384
|
-
printDoctor(options);
|
|
508
|
+
await printDoctor(options);
|
|
385
509
|
break;
|
|
386
510
|
case "calibrate":
|
|
387
511
|
await calibrate(options);
|
|
388
512
|
break;
|
|
389
513
|
case "launch-chrome":
|
|
390
|
-
launchChrome(options);
|
|
514
|
+
await launchChrome(options);
|
|
391
515
|
break;
|
|
392
516
|
case "where":
|
|
393
517
|
printPaths();
|