@reconcrap/boss-recommend-mcp 2.0.36 → 2.0.38

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.
File without changes
@@ -1,4 +1,4 @@
1
- {
1
+ {
2
2
  "baseUrl": "https://api.openai.com/v1",
3
3
  "apiKey": "replace-with-openai-api-key",
4
4
  "model": "gpt-4.1-mini",
package/package.json CHANGED
@@ -1,119 +1,119 @@
1
- {
2
- "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "2.0.36",
4
- "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
- "keywords": [
6
- "boss",
7
- "mcp",
8
- "codex",
9
- "recruiting",
10
- "boss-zhipin",
11
- "recommend"
12
- ],
13
- "type": "module",
14
- "main": "src/index.js",
15
- "bin": {
16
- "boss-recommend-mcp": "bin/boss-recommend-mcp.js"
17
- },
18
- "scripts": {
19
- "start": "node src/index.js",
20
- "cli": "node src/cli.js",
21
- "install:local": "node src/cli.js install",
22
- "postinstall": "node scripts/postinstall.cjs",
23
- "test:parser": "node src/test-parser.js",
24
- "test:run-state": "node src/test-run-state.js",
25
- "test:cdp-browser": "node src/test-cdp-browser.js",
26
- "test:core-capture": "node src/test-core-capture.js",
27
- "test:core-cv-capture-target": "node src/test-core-cv-capture-target.js",
28
- "test:core-cv-acquisition": "node src/test-core-cv-acquisition.js",
29
- "test:core-greet-quota": "node src/test-core-greet-quota.js",
30
- "test:core-infinite-list": "node src/test-core-infinite-list.js",
31
- "test:core-reporting": "node src/test-core-reporting.js",
32
- "test:core-run": "node src/test-core-run.js",
33
- "test:core-screening": "node src/test-core-screening.js",
34
- "test:core-self-heal": "node src/test-core-self-heal.js",
35
- "test:installer-migration": "node src/test-installer-migration.js",
36
- "test:recommend-actions": "node src/test-recommend-actions.js",
37
- "test:recommend-domain": "node src/test-recommend-domain.js",
38
- "test:recommend-run-service": "node src/test-recommend-run-service.js",
39
- "test:recommend-mcp": "node src/test-recommend-mcp.js",
40
- "test:recruit-domain": "node src/test-recruit-domain.js",
41
- "test:recruit-mcp": "node src/test-recruit-mcp.js",
42
- "test:chat-domain": "node src/test-chat-domain.js",
43
- "test:chat-run-service": "node src/test-chat-run-service.js",
44
- "test:chat-mcp": "node src/test-chat-mcp.js",
45
- "test:async": "node src/test-index-async.js",
46
- "test:runtime-scan": "node src/test-runtime-scan.js",
47
- "scan:runtime": "node scripts/scan-forbidden-runtime.js",
48
- "scan:runtime:json": "node scripts/scan-forbidden-runtime.js --json",
49
- "scan:runtime:strict": "node scripts/scan-forbidden-runtime.js --fail-on-findings",
50
- "scan:runtime:package": "node scripts/scan-forbidden-runtime.js --package-surface",
51
- "scan:runtime:package:strict": "node scripts/scan-forbidden-runtime.js --package-surface --fail-on-legacy",
52
- "scan:legacy-boundary": "node scripts/scan-legacy-boundary.js",
53
- "scan:package-boundary": "node scripts/scan-package-boundary.js",
54
- "gate:phase9-static": "node scripts/phase9-static-gate.js",
55
- "gate:phase10-complete": "node scripts/phase10-completion-gate.js",
56
- "live:cdp-smoke": "node scripts/live-cdp-smoke.js",
57
- "live:run-lifecycle": "node scripts/live-run-lifecycle-smoke.js",
58
- "live:screening": "node scripts/live-screening-smoke.js",
59
- "live:detail": "node scripts/live-detail-smoke.js",
60
- "live:infinite-list": "node scripts/live-infinite-list-smoke.js",
61
- "live:scroll-end": "node scripts/live-scroll-end-screenshot.js",
62
- "live:refresh-round": "node scripts/live-refresh-round-smoke.js",
63
- "live:self-heal": "node scripts/live-self-heal-smoke.js",
64
- "live:recommend-actions": "node scripts/live-recommend-actions-smoke.js",
65
- "live:recommend-phase10-full": "node scripts/live-recommend-phase10-full.js",
66
- "live:recommend-domain": "node scripts/live-recommend-domain-smoke.js",
67
- "live:recommend-run-service": "node scripts/live-recommend-run-service-smoke.js",
68
- "live:recommend-mcp": "node scripts/live-recommend-mcp-smoke.js",
69
- "live:search-phase10-full": "node scripts/live-search-phase10-full.js",
70
- "live:recruit-domain": "node scripts/live-recruit-domain-smoke.js",
71
- "live:recruit-run-service": "node scripts/live-recruit-run-service-smoke.js",
72
- "live:recruit-mcp": "node scripts/live-recruit-mcp-smoke.js",
73
- "live:chat-domain": "node scripts/live-chat-domain-smoke.js",
74
- "live:chat-run-service": "node scripts/live-chat-run-service-smoke.js",
75
- "live:chat-mcp": "node scripts/live-chat-mcp-smoke.js",
76
- "live:cv-capture-target": "node scripts/live-cv-capture-target-smoke.js",
77
- "live:chat-phase10-full": "node scripts/live-chat-phase10-full.js",
78
- "live:chat-image-screening": "node scripts/live-chat-image-screening-smoke.js"
79
- },
80
- "files": [
81
- "bin",
82
- "config/screening-config.example.json",
83
- "skills",
84
- "scripts/postinstall.cjs",
85
- "src/core",
86
- "src/domains",
87
- "src/chat-mcp.js",
88
- "src/chat-runtime-config.js",
89
- "src/cli.js",
90
- "src/index.js",
91
- "src/parser.js",
92
- "src/recommend-mcp.js",
93
- "src/recruit-mcp.js",
94
- "src/run-state.js",
95
- "README.md"
96
- ],
97
- "dependencies": {
98
- "chrome-remote-interface": "^0.33.3",
99
- "sharp": "^0.34.4",
100
- "ws": "^8.19.0"
101
- },
102
- "engines": {
103
- "node": ">=18"
104
- },
105
- "publishConfig": {
106
- "access": "public"
107
- },
108
- "license": "MIT",
109
- "devDependencies": {},
110
- "repository": {
111
- "type": "git",
112
- "url": "git+https://github.com/reconcrap-cpu/boss-recommend-mcp.git"
113
- },
114
- "author": "",
115
- "bugs": {
116
- "url": "https://github.com/reconcrap-cpu/boss-recommend-mcp/issues"
117
- },
118
- "homepage": "https://github.com/reconcrap-cpu/boss-recommend-mcp#readme"
119
- }
1
+ {
2
+ "name": "@reconcrap/boss-recommend-mcp",
3
+ "version": "2.0.38",
4
+ "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
+ "keywords": [
6
+ "boss",
7
+ "mcp",
8
+ "codex",
9
+ "recruiting",
10
+ "boss-zhipin",
11
+ "recommend"
12
+ ],
13
+ "type": "module",
14
+ "main": "src/index.js",
15
+ "bin": {
16
+ "boss-recommend-mcp": "bin/boss-recommend-mcp.js"
17
+ },
18
+ "scripts": {
19
+ "start": "node src/index.js",
20
+ "cli": "node src/cli.js",
21
+ "install:local": "node src/cli.js install",
22
+ "postinstall": "node scripts/postinstall.cjs",
23
+ "test:parser": "node src/test-parser.js",
24
+ "test:run-state": "node src/test-run-state.js",
25
+ "test:cdp-browser": "node src/test-cdp-browser.js",
26
+ "test:core-capture": "node src/test-core-capture.js",
27
+ "test:core-cv-capture-target": "node src/test-core-cv-capture-target.js",
28
+ "test:core-cv-acquisition": "node src/test-core-cv-acquisition.js",
29
+ "test:core-greet-quota": "node src/test-core-greet-quota.js",
30
+ "test:core-infinite-list": "node src/test-core-infinite-list.js",
31
+ "test:core-reporting": "node src/test-core-reporting.js",
32
+ "test:core-run": "node src/test-core-run.js",
33
+ "test:core-screening": "node src/test-core-screening.js",
34
+ "test:core-self-heal": "node src/test-core-self-heal.js",
35
+ "test:installer-migration": "node src/test-installer-migration.js",
36
+ "test:recommend-actions": "node src/test-recommend-actions.js",
37
+ "test:recommend-domain": "node src/test-recommend-domain.js",
38
+ "test:recommend-run-service": "node src/test-recommend-run-service.js",
39
+ "test:recommend-mcp": "node src/test-recommend-mcp.js",
40
+ "test:recruit-domain": "node src/test-recruit-domain.js",
41
+ "test:recruit-mcp": "node src/test-recruit-mcp.js",
42
+ "test:chat-domain": "node src/test-chat-domain.js",
43
+ "test:chat-run-service": "node src/test-chat-run-service.js",
44
+ "test:chat-mcp": "node src/test-chat-mcp.js",
45
+ "test:async": "node src/test-index-async.js",
46
+ "test:runtime-scan": "node src/test-runtime-scan.js",
47
+ "scan:runtime": "node scripts/scan-forbidden-runtime.js",
48
+ "scan:runtime:json": "node scripts/scan-forbidden-runtime.js --json",
49
+ "scan:runtime:strict": "node scripts/scan-forbidden-runtime.js --fail-on-findings",
50
+ "scan:runtime:package": "node scripts/scan-forbidden-runtime.js --package-surface",
51
+ "scan:runtime:package:strict": "node scripts/scan-forbidden-runtime.js --package-surface --fail-on-legacy",
52
+ "scan:legacy-boundary": "node scripts/scan-legacy-boundary.js",
53
+ "scan:package-boundary": "node scripts/scan-package-boundary.js",
54
+ "gate:phase9-static": "node scripts/phase9-static-gate.js",
55
+ "gate:phase10-complete": "node scripts/phase10-completion-gate.js",
56
+ "live:cdp-smoke": "node scripts/live-cdp-smoke.js",
57
+ "live:run-lifecycle": "node scripts/live-run-lifecycle-smoke.js",
58
+ "live:screening": "node scripts/live-screening-smoke.js",
59
+ "live:detail": "node scripts/live-detail-smoke.js",
60
+ "live:infinite-list": "node scripts/live-infinite-list-smoke.js",
61
+ "live:scroll-end": "node scripts/live-scroll-end-screenshot.js",
62
+ "live:refresh-round": "node scripts/live-refresh-round-smoke.js",
63
+ "live:self-heal": "node scripts/live-self-heal-smoke.js",
64
+ "live:recommend-actions": "node scripts/live-recommend-actions-smoke.js",
65
+ "live:recommend-phase10-full": "node scripts/live-recommend-phase10-full.js",
66
+ "live:recommend-domain": "node scripts/live-recommend-domain-smoke.js",
67
+ "live:recommend-run-service": "node scripts/live-recommend-run-service-smoke.js",
68
+ "live:recommend-mcp": "node scripts/live-recommend-mcp-smoke.js",
69
+ "live:search-phase10-full": "node scripts/live-search-phase10-full.js",
70
+ "live:recruit-domain": "node scripts/live-recruit-domain-smoke.js",
71
+ "live:recruit-run-service": "node scripts/live-recruit-run-service-smoke.js",
72
+ "live:recruit-mcp": "node scripts/live-recruit-mcp-smoke.js",
73
+ "live:chat-domain": "node scripts/live-chat-domain-smoke.js",
74
+ "live:chat-run-service": "node scripts/live-chat-run-service-smoke.js",
75
+ "live:chat-mcp": "node scripts/live-chat-mcp-smoke.js",
76
+ "live:cv-capture-target": "node scripts/live-cv-capture-target-smoke.js",
77
+ "live:chat-phase10-full": "node scripts/live-chat-phase10-full.js",
78
+ "live:chat-image-screening": "node scripts/live-chat-image-screening-smoke.js"
79
+ },
80
+ "files": [
81
+ "bin",
82
+ "config/screening-config.example.json",
83
+ "skills",
84
+ "scripts/postinstall.cjs",
85
+ "src/core",
86
+ "src/domains",
87
+ "src/chat-mcp.js",
88
+ "src/chat-runtime-config.js",
89
+ "src/cli.js",
90
+ "src/index.js",
91
+ "src/parser.js",
92
+ "src/recommend-mcp.js",
93
+ "src/recruit-mcp.js",
94
+ "src/run-state.js",
95
+ "README.md"
96
+ ],
97
+ "dependencies": {
98
+ "chrome-remote-interface": "^0.33.3",
99
+ "sharp": "^0.34.4",
100
+ "ws": "^8.19.0"
101
+ },
102
+ "engines": {
103
+ "node": ">=18"
104
+ },
105
+ "publishConfig": {
106
+ "access": "public"
107
+ },
108
+ "license": "MIT",
109
+ "devDependencies": {},
110
+ "repository": {
111
+ "type": "git",
112
+ "url": "git+https://github.com/reconcrap-cpu/boss-recommend-mcp.git"
113
+ },
114
+ "author": "",
115
+ "bugs": {
116
+ "url": "https://github.com/reconcrap-cpu/boss-recommend-mcp/issues"
117
+ },
118
+ "homepage": "https://github.com/reconcrap-cpu/boss-recommend-mcp#readme"
119
+ }
@@ -47,6 +47,7 @@ function snapshotFromEntry(entry) {
47
47
  return clone({
48
48
  runId: run.runId,
49
49
  name: run.name,
50
+ pid: run.pid,
50
51
  status: run.status,
51
52
  phase: run.phase,
52
53
  progress: run.progress,
@@ -205,11 +206,14 @@ export function createRunLifecycleManager({
205
206
  }
206
207
  }
207
208
 
208
- function startRun({ name, context = {}, progress = {}, checkpoint = {}, task }) {
209
+ function startRun({ runId: requestedRunId = "", name, pid = process.pid, context = {}, progress = {}, checkpoint = {}, task }) {
209
210
  if (typeof task !== "function") {
210
211
  throw new Error("startRun requires a task function");
211
212
  }
212
- const runId = createRunId(idPrefix);
213
+ const runId = String(requestedRunId || "").trim() || createRunId(idPrefix);
214
+ if (runs.has(runId)) {
215
+ throw new Error(`Run already exists: ${runId}`);
216
+ }
213
217
  const startedAt = now();
214
218
  const entry = {
215
219
  controller: new AbortController(),
@@ -219,6 +223,7 @@ export function createRunLifecycleManager({
219
223
  run: {
220
224
  runId,
221
225
  name: name || runId,
226
+ pid: Number.isInteger(pid) && pid > 0 ? pid : process.pid,
222
227
  status: RUN_STATUS_QUEUED,
223
228
  phase: "queued",
224
229
  progress,
@@ -131,21 +131,49 @@ export async function waitForRecommendDetail(client, {
131
131
  const started = Date.now();
132
132
  let lastState = null;
133
133
  while (Date.now() - started <= timeoutMs) {
134
- const rootState = await getRecommendRoots(client);
135
- const popup = await findVisibleDetailTarget(client, rootState.roots, DETAIL_POPUP_SELECTORS);
136
- const resumeIframe = await findVisibleDetailTarget(client, rootState.roots, DETAIL_RESUME_IFRAME_SELECTORS);
137
- lastState = {
138
- iframe: rootState.iframe,
139
- roots: rootState.roots,
140
- popup,
141
- resumeIframe
142
- };
143
- if (popup || resumeIframe) return lastState;
134
+ lastState = await readRecommendDetailState(client);
135
+ if (lastState?.popup || lastState?.resumeIframe) return lastState;
144
136
  await sleep(intervalMs);
145
137
  }
146
138
  return lastState;
147
139
  }
148
140
 
141
+ async function readRecommendDetailState(client) {
142
+ const rootState = await getRecommendRoots(client);
143
+ const popup = await findVisibleDetailTarget(client, rootState.roots, DETAIL_POPUP_SELECTORS);
144
+ const resumeIframe = await findVisibleDetailTarget(client, rootState.roots, DETAIL_RESUME_IFRAME_SELECTORS);
145
+ return {
146
+ iframe: rootState.iframe,
147
+ roots: rootState.roots,
148
+ popup,
149
+ resumeIframe
150
+ };
151
+ }
152
+
153
+ export async function waitForRecommendDetailClosed(client, {
154
+ timeoutMs = 4000,
155
+ intervalMs = 250
156
+ } = {}) {
157
+ const started = Date.now();
158
+ let lastState = null;
159
+ while (Date.now() - started <= timeoutMs) {
160
+ lastState = await readRecommendDetailState(client);
161
+ if (!lastState?.popup && !lastState?.resumeIframe) {
162
+ return {
163
+ closed: true,
164
+ elapsed_ms: Date.now() - started,
165
+ state: lastState
166
+ };
167
+ }
168
+ await sleep(intervalMs);
169
+ }
170
+ return {
171
+ closed: false,
172
+ elapsed_ms: Date.now() - started,
173
+ state: lastState
174
+ };
175
+ }
176
+
149
177
  async function findVisibleDetailTarget(client, roots, selectors) {
150
178
  for (const root of roots) {
151
179
  if (!root?.nodeId) continue;
@@ -397,7 +425,9 @@ export async function openRecommendCardDetailWithFreshRetry(client, {
397
425
  }
398
426
 
399
427
  export async function closeRecommendDetail(client, {
400
- attemptsLimit = 3
428
+ attemptsLimit = 4,
429
+ closeWaitMs = 5000,
430
+ escapeWaitMs = 3500
401
431
  } = {}) {
402
432
  const attempts = [];
403
433
  for (let index = 0; index < attemptsLimit; index += 1) {
@@ -433,15 +463,21 @@ export async function closeRecommendDetail(client, {
433
463
  await pressEscape(client);
434
464
  attempts.push({ mode: "Escape-after-close-selector-error" });
435
465
  }
436
- await sleep(700);
437
466
  } else {
438
467
  await pressEscape(client);
439
468
  attempts.push({ mode: "Escape" });
440
- await sleep(700);
441
469
  }
442
470
 
443
- let state = await waitForRecommendDetail(client, { timeoutMs: 1000 });
444
- if (!state?.popup && !state?.resumeIframe) {
471
+ const closedAfterClick = await waitForRecommendDetailClosed(client, {
472
+ timeoutMs: closeWaitMs,
473
+ intervalMs: 250
474
+ });
475
+ attempts.push({
476
+ mode: "wait-closed-after-primary",
477
+ closed: closedAfterClick.closed,
478
+ elapsed_ms: closedAfterClick.elapsed_ms
479
+ });
480
+ if (closedAfterClick.closed) {
445
481
  return {
446
482
  closed: true,
447
483
  attempts
@@ -450,10 +486,17 @@ export async function closeRecommendDetail(client, {
450
486
 
451
487
  await pressEscape(client);
452
488
  attempts.push({ mode: "Escape-fallback" });
453
- await sleep(700);
454
489
 
455
- state = await waitForRecommendDetail(client, { timeoutMs: 1000 });
456
- if (!state?.popup && !state?.resumeIframe) {
490
+ const closedAfterEscape = await waitForRecommendDetailClosed(client, {
491
+ timeoutMs: escapeWaitMs,
492
+ intervalMs: 250
493
+ });
494
+ attempts.push({
495
+ mode: "wait-closed-after-escape",
496
+ closed: closedAfterEscape.closed,
497
+ elapsed_ms: closedAfterEscape.elapsed_ms
498
+ });
499
+ if (closedAfterEscape.closed) {
457
500
  return {
458
501
  closed: true,
459
502
  attempts
@@ -1266,6 +1266,8 @@ export function createRecommendRunService({
1266
1266
  const manager = lifecycle || createRunLifecycleManager({ idPrefix, onSnapshot });
1267
1267
 
1268
1268
  function startRecommendRun({
1269
+ runId = "",
1270
+ pid = process.pid,
1269
1271
  client,
1270
1272
  targetUrl = "",
1271
1273
  criteria = "",
@@ -1313,7 +1315,9 @@ export function createRecommendRunService({
1313
1315
  const candidateLimit = Math.max(1, Number(maxCandidates) || 1);
1314
1316
  const normalizedDetailLimit = detailLimit == null ? null : Math.max(0, Number(detailLimit) || 0);
1315
1317
  return manager.startRun({
1318
+ runId,
1316
1319
  name,
1320
+ pid,
1317
1321
  context: {
1318
1322
  domain: "recommend",
1319
1323
  target_url: targetUrl,