@reconcrap/boss-recommend-mcp 2.0.4 → 2.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
package/src/chat-mcp.js CHANGED
@@ -494,7 +494,8 @@ async function waitForHealthyChat(client, config, {
494
494
  domain: "chat",
495
495
  roots: roots.roots,
496
496
  selectorProbes: config.selectorProbes,
497
- accessibilityProbes: config.accessibilityProbes
497
+ accessibilityProbes: config.accessibilityProbes,
498
+ viewportProbes: config.viewportProbes
498
499
  });
499
500
  if (lastCheck.status === HEALTH_STATUS.HEALTHY) return lastCheck;
500
501
  await sleep(intervalMs);
@@ -10,6 +10,7 @@ export const BOSS_LOGIN_URL = "https://www.zhipin.com/web/user/?ka=bticket";
10
10
 
11
11
  export const ALLOWED_CDP_DOMAINS = new Set([
12
12
  "Accessibility",
13
+ "Browser",
13
14
  "DOM",
14
15
  "Input",
15
16
  "Network",
@@ -5,6 +5,26 @@ import {
5
5
  querySelectorAll,
6
6
  sleep
7
7
  } from "../browser/index.js";
8
+ import {
9
+ compactViewportHealthResult,
10
+ ensureHealthyViewport
11
+ } from "./viewport.js";
12
+
13
+ export {
14
+ buildViewportHealthDiagnostics,
15
+ compactViewportHealthResult,
16
+ compactViewportState,
17
+ createViewportRunGuard,
18
+ ensureHealthyViewport,
19
+ getCurrentWindowInfo,
20
+ isListViewportCollapsed,
21
+ readViewportState,
22
+ setWindowStateIfPossible,
23
+ toggleWindowStateForViewportRecovery,
24
+ VIEWPORT_COLLAPSE_MIN_EXPECTED_WIDTH,
25
+ VIEWPORT_COLLAPSE_NEAR_FULLSCREEN_RATIO,
26
+ VIEWPORT_COLLAPSE_RATIO_THRESHOLD
27
+ } from "./viewport.js";
8
28
 
9
29
  export const PROBE_STATUS = Object.freeze({
10
30
  PASS: "pass",
@@ -245,6 +265,26 @@ export function createNetworkProbe({
245
265
  };
246
266
  }
247
267
 
268
+ export function createViewportCollapseProbe({
269
+ id = "viewport_collapse",
270
+ root = "frame",
271
+ frameOwnerRoot = "frameOwner",
272
+ required = true,
273
+ repair = true,
274
+ description = ""
275
+ } = {}) {
276
+ if (!id) throw new Error("Viewport collapse probe requires an id");
277
+ return {
278
+ type: "viewport",
279
+ id,
280
+ root,
281
+ frameOwnerRoot,
282
+ required: Boolean(required),
283
+ repair: Boolean(repair),
284
+ description
285
+ };
286
+ }
287
+
248
288
  export async function runSelectorProbe(client, roots, probe) {
249
289
  const nodeId = rootNodeId(roots, probe.root);
250
290
  if (!nodeId) {
@@ -338,6 +378,52 @@ export function runNetworkProbe(networkEvents = [], probe) {
338
378
  };
339
379
  }
340
380
 
381
+ export async function runViewportCollapseProbe(client, roots, probe) {
382
+ const nodeId = rootNodeId(roots, probe.root);
383
+ if (!nodeId) {
384
+ return {
385
+ ...probe,
386
+ ok: !probe.required,
387
+ status: probe.required ? PROBE_STATUS.BLOCKED : PROBE_STATUS.OPTIONAL_ABSENT,
388
+ count: 0,
389
+ collapsed: false,
390
+ recovered: false,
391
+ error: `Root not found: ${probe.root}`
392
+ };
393
+ }
394
+
395
+ try {
396
+ const health = await ensureHealthyViewport(client, {
397
+ roots,
398
+ root: probe.root,
399
+ frameOwnerRoot: probe.frameOwnerRoot,
400
+ reason: probe.id,
401
+ repair: probe.repair
402
+ });
403
+ const ok = Boolean(health.ok);
404
+ return {
405
+ ...probe,
406
+ ok: probe.required ? ok : true,
407
+ status: ok ? PROBE_STATUS.PASS : probe.required ? PROBE_STATUS.FAIL : PROBE_STATUS.OPTIONAL_ABSENT,
408
+ count: ok ? 1 : 0,
409
+ collapsed: Boolean(health.collapsed),
410
+ recovered: Boolean(health.recovered),
411
+ viewport_health: compactViewportHealthResult(health),
412
+ error: health.error || null
413
+ };
414
+ } catch (error) {
415
+ return {
416
+ ...probe,
417
+ ok: !probe.required,
418
+ status: probe.required ? PROBE_STATUS.ERROR : PROBE_STATUS.OPTIONAL_ABSENT,
419
+ count: 0,
420
+ collapsed: false,
421
+ recovered: false,
422
+ error: error?.message || String(error)
423
+ };
424
+ }
425
+ }
426
+
341
427
  export function summarizeProbeResults(probes = []) {
342
428
  const required = probes.filter((probe) => probe.required);
343
429
  const blocked = required.filter((probe) => probe.status === PROBE_STATUS.BLOCKED);
@@ -370,6 +456,7 @@ export function buildDriftReport(probes = []) {
370
456
  expected_min_count: probe.minCount,
371
457
  observed_count: probe.count || 0,
372
458
  selectors: probe.selectors || [],
459
+ viewport_health: probe.viewport_health || undefined,
373
460
  error: probe.error || null
374
461
  }));
375
462
  }
@@ -380,6 +467,7 @@ export async function runSelfHealCheck({
380
467
  roots = {},
381
468
  selectorProbes = [],
382
469
  accessibilityProbes = [],
470
+ viewportProbes = [],
383
471
  networkProbes = [],
384
472
  networkEvents = []
385
473
  } = {}) {
@@ -393,8 +481,13 @@ export async function runSelfHealCheck({
393
481
  accessibilityResults.push(await runAccessibilityProbe(client, probe));
394
482
  }
395
483
 
484
+ const viewportResults = [];
485
+ for (const probe of viewportProbes) {
486
+ viewportResults.push(await runViewportCollapseProbe(client, roots, probe));
487
+ }
488
+
396
489
  const networkResults = networkProbes.map((probe) => runNetworkProbe(networkEvents, probe));
397
- const probes = [...selectorResults, ...accessibilityResults, ...networkResults];
490
+ const probes = [...selectorResults, ...accessibilityResults, ...viewportResults, ...networkResults];
398
491
  const summary = summarizeProbeResults(probes);
399
492
 
400
493
  return {
@@ -507,6 +600,16 @@ export function buildRecommendSelfHealConfig(rules = {}) {
507
600
  description: "Candidate detail popup may mount inside the recommend frame"
508
601
  })
509
602
  ],
603
+ viewportProbes: [
604
+ createViewportCollapseProbe({
605
+ id: "recommend_viewport_collapse",
606
+ root: "frame",
607
+ frameOwnerRoot: "frameOwner",
608
+ required: true,
609
+ repair: true,
610
+ description: "Recommend frame/list viewport has not collapsed relative to the Chrome window"
611
+ })
612
+ ],
510
613
  accessibilityProbes: [
511
614
  createAccessibilityProbe({
512
615
  id: "accessibility_tree",
@@ -610,6 +713,16 @@ export function buildRecruitSelfHealConfig(rules = {}) {
610
713
  description: "Candidate detail popup may mount inside the search frame"
611
714
  })
612
715
  ],
716
+ viewportProbes: [
717
+ createViewportCollapseProbe({
718
+ id: "recruit_viewport_collapse",
719
+ root: "frame",
720
+ frameOwnerRoot: "frameOwner",
721
+ required: true,
722
+ repair: true,
723
+ description: "Search frame/list viewport has not collapsed relative to the Chrome window"
724
+ })
725
+ ],
613
726
  accessibilityProbes: [
614
727
  createAccessibilityProbe({
615
728
  id: "accessibility_tree",
@@ -711,6 +824,16 @@ export function buildChatSelfHealConfig(rules = {}) {
711
824
  description: "Resume iframe appears after the online resume is opened"
712
825
  })
713
826
  ],
827
+ viewportProbes: [
828
+ createViewportCollapseProbe({
829
+ id: "chat_viewport_collapse",
830
+ root: "top",
831
+ frameOwnerRoot: "top",
832
+ required: true,
833
+ repair: true,
834
+ description: "Chat list viewport has not collapsed relative to the Chrome window"
835
+ })
836
+ ],
714
837
  accessibilityProbes: [
715
838
  createAccessibilityProbe({
716
839
  id: "accessibility_tree",
@@ -756,7 +879,8 @@ export async function resolveRecommendSelfHealRoots(client, config = buildRecomm
756
879
  return {
757
880
  roots: {
758
881
  top: topRoot.nodeId,
759
- frame: iframe.documentNodeId
882
+ frame: iframe.documentNodeId,
883
+ frameOwner: iframe.nodeId
760
884
  },
761
885
  topRoot,
762
886
  iframe
@@ -779,7 +903,8 @@ export async function resolveRecruitSelfHealRoots(client, config = buildRecruitS
779
903
  return {
780
904
  roots: {
781
905
  top: topRoot.nodeId,
782
- frame: iframe.documentNodeId
906
+ frame: iframe.documentNodeId,
907
+ frameOwner: iframe.nodeId
783
908
  },
784
909
  topRoot,
785
910
  iframe