@reconcrap/boss-recommend-mcp 2.0.45 → 2.0.47
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/bin/boss-recommend-mcp.js +4 -4
- package/config/screening-config.example.json +27 -27
- package/package.json +1 -1
- package/scripts/postinstall.cjs +44 -44
- package/skills/boss-chat/README.md +39 -39
- package/skills/boss-chat/SKILL.md +93 -93
- package/skills/boss-recommend-pipeline/README.md +12 -12
- package/skills/boss-recommend-pipeline/SKILL.md +180 -180
- package/skills/boss-recruit-pipeline/README.md +17 -17
- package/skills/boss-recruit-pipeline/SKILL.md +58 -58
- package/src/chat-mcp.js +1780 -1780
- package/src/chat-runtime-config.js +749 -749
- package/src/cli.js +3054 -3054
- package/src/core/boss-cards/index.js +199 -199
- package/src/core/browser/index.js +1453 -1446
- package/src/core/capture/index.js +1201 -1201
- package/src/core/cv-acquisition/index.js +238 -238
- package/src/core/cv-capture-target/index.js +299 -299
- package/src/core/greet-quota/index.js +54 -54
- package/src/core/infinite-list/index.js +1326 -1326
- package/src/core/reporting/legacy-csv.js +341 -341
- package/src/core/run/timing.js +33 -33
- package/src/core/screening/index.js +50 -3
- package/src/core/self-heal/index.js +973 -973
- package/src/core/self-heal/viewport.js +564 -564
- package/src/domains/chat/cards.js +137 -137
- package/src/domains/chat/constants.js +221 -221
- package/src/domains/chat/detail.js +1668 -1661
- package/src/domains/chat/index.js +7 -7
- package/src/domains/chat/jobs.js +592 -588
- package/src/domains/chat/page-guard.js +98 -98
- package/src/domains/chat/roots.js +56 -56
- package/src/domains/chat/run-service.js +1977 -1955
- package/src/domains/recommend/actions.js +457 -457
- package/src/domains/recommend/cards.js +243 -243
- package/src/domains/recommend/constants.js +165 -165
- package/src/domains/recommend/detail.js +36 -28
- package/src/domains/recommend/filters.js +610 -581
- package/src/domains/recommend/index.js +10 -10
- package/src/domains/recommend/jobs.js +316 -263
- package/src/domains/recommend/refresh.js +472 -472
- package/src/domains/recommend/roots.js +80 -80
- package/src/domains/recommend/run-service.js +75 -35
- package/src/domains/recommend/scopes.js +246 -245
- package/src/domains/recruit/actions.js +277 -277
- package/src/domains/recruit/cards.js +74 -74
- package/src/domains/recruit/constants.js +167 -167
- package/src/domains/recruit/detail.js +461 -460
- package/src/domains/recruit/index.js +9 -9
- package/src/domains/recruit/instruction-parser.js +451 -451
- package/src/domains/recruit/refresh.js +44 -44
- package/src/domains/recruit/roots.js +68 -68
- package/src/domains/recruit/run-service.js +1207 -1161
- package/src/domains/recruit/search.js +1202 -1149
- package/src/recommend-mcp.js +22 -22
- package/src/recruit-mcp.js +1338 -1338
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
import {
|
|
2
|
-
findIframeDocument,
|
|
3
|
-
getDocumentRoot,
|
|
4
|
-
querySelector,
|
|
5
|
-
sleep
|
|
6
|
-
} from "../../core/browser/index.js";
|
|
7
|
-
import { RECOMMEND_IFRAME_SELECTORS } from "./constants.js";
|
|
8
|
-
|
|
9
|
-
export async function getRecommendRoots(client, {
|
|
10
|
-
iframeSelectors = RECOMMEND_IFRAME_SELECTORS,
|
|
11
|
-
requireFrame = true
|
|
12
|
-
} = {}) {
|
|
13
|
-
const topRoot = await getDocumentRoot(client);
|
|
14
|
-
const iframe = await findIframeDocument(client, topRoot.nodeId, iframeSelectors);
|
|
15
|
-
if (!iframe && requireFrame) {
|
|
16
|
-
throw new Error("recommendFrame iframe was not found");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
topRoot,
|
|
21
|
-
iframe,
|
|
22
|
-
roots: [
|
|
23
|
-
{ name: "top", nodeId: topRoot.nodeId },
|
|
24
|
-
iframe ? { name: "recommend-frame", nodeId: iframe.documentNodeId } : null
|
|
25
|
-
].filter(Boolean),
|
|
26
|
-
rootNodes: {
|
|
27
|
-
top: topRoot.nodeId,
|
|
28
|
-
frame: iframe?.documentNodeId || 0,
|
|
29
|
-
frameOwner: iframe?.nodeId || 0
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function waitForRecommendRoots(client, {
|
|
35
|
-
timeoutMs = 10000,
|
|
36
|
-
intervalMs = 250,
|
|
37
|
-
iframeSelectors = RECOMMEND_IFRAME_SELECTORS
|
|
38
|
-
} = {}) {
|
|
39
|
-
const started = Date.now();
|
|
40
|
-
let lastState = null;
|
|
41
|
-
while (Date.now() - started <= timeoutMs) {
|
|
42
|
-
try {
|
|
43
|
-
lastState = await getRecommendRoots(client, {
|
|
44
|
-
iframeSelectors,
|
|
45
|
-
requireFrame: false
|
|
46
|
-
});
|
|
47
|
-
} catch (error) {
|
|
48
|
-
lastState = {
|
|
49
|
-
error: error?.message || String(error),
|
|
50
|
-
roots: [],
|
|
51
|
-
rootNodes: {
|
|
52
|
-
top: 0,
|
|
53
|
-
frame: 0,
|
|
54
|
-
frameOwner: 0
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
if (lastState.iframe?.documentNodeId) return lastState;
|
|
59
|
-
await sleep(intervalMs);
|
|
60
|
-
}
|
|
61
|
-
return lastState;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export async function queryFirstAcrossRoots(client, roots, selectors) {
|
|
65
|
-
for (const root of roots) {
|
|
66
|
-
if (!root?.nodeId) continue;
|
|
67
|
-
for (const selector of selectors) {
|
|
68
|
-
const nodeId = await querySelector(client, root.nodeId, selector);
|
|
69
|
-
if (nodeId) {
|
|
70
|
-
return {
|
|
71
|
-
root: root.name,
|
|
72
|
-
root_node_id: root.nodeId,
|
|
73
|
-
selector,
|
|
74
|
-
node_id: nodeId
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
findIframeDocument,
|
|
3
|
+
getDocumentRoot,
|
|
4
|
+
querySelector,
|
|
5
|
+
sleep
|
|
6
|
+
} from "../../core/browser/index.js";
|
|
7
|
+
import { RECOMMEND_IFRAME_SELECTORS } from "./constants.js";
|
|
8
|
+
|
|
9
|
+
export async function getRecommendRoots(client, {
|
|
10
|
+
iframeSelectors = RECOMMEND_IFRAME_SELECTORS,
|
|
11
|
+
requireFrame = true
|
|
12
|
+
} = {}) {
|
|
13
|
+
const topRoot = await getDocumentRoot(client);
|
|
14
|
+
const iframe = await findIframeDocument(client, topRoot.nodeId, iframeSelectors);
|
|
15
|
+
if (!iframe && requireFrame) {
|
|
16
|
+
throw new Error("recommendFrame iframe was not found");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
topRoot,
|
|
21
|
+
iframe,
|
|
22
|
+
roots: [
|
|
23
|
+
{ name: "top", nodeId: topRoot.nodeId },
|
|
24
|
+
iframe ? { name: "recommend-frame", nodeId: iframe.documentNodeId } : null
|
|
25
|
+
].filter(Boolean),
|
|
26
|
+
rootNodes: {
|
|
27
|
+
top: topRoot.nodeId,
|
|
28
|
+
frame: iframe?.documentNodeId || 0,
|
|
29
|
+
frameOwner: iframe?.nodeId || 0
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function waitForRecommendRoots(client, {
|
|
35
|
+
timeoutMs = 10000,
|
|
36
|
+
intervalMs = 250,
|
|
37
|
+
iframeSelectors = RECOMMEND_IFRAME_SELECTORS
|
|
38
|
+
} = {}) {
|
|
39
|
+
const started = Date.now();
|
|
40
|
+
let lastState = null;
|
|
41
|
+
while (Date.now() - started <= timeoutMs) {
|
|
42
|
+
try {
|
|
43
|
+
lastState = await getRecommendRoots(client, {
|
|
44
|
+
iframeSelectors,
|
|
45
|
+
requireFrame: false
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
lastState = {
|
|
49
|
+
error: error?.message || String(error),
|
|
50
|
+
roots: [],
|
|
51
|
+
rootNodes: {
|
|
52
|
+
top: 0,
|
|
53
|
+
frame: 0,
|
|
54
|
+
frameOwner: 0
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (lastState.iframe?.documentNodeId) return lastState;
|
|
59
|
+
await sleep(intervalMs);
|
|
60
|
+
}
|
|
61
|
+
return lastState;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function queryFirstAcrossRoots(client, roots, selectors) {
|
|
65
|
+
for (const root of roots) {
|
|
66
|
+
if (!root?.nodeId) continue;
|
|
67
|
+
for (const selector of selectors) {
|
|
68
|
+
const nodeId = await querySelector(client, root.nodeId, selector);
|
|
69
|
+
if (nodeId) {
|
|
70
|
+
return {
|
|
71
|
+
root: root.name,
|
|
72
|
+
root_node_id: root.nodeId,
|
|
73
|
+
selector,
|
|
74
|
+
node_id: nodeId
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
@@ -46,13 +46,14 @@ import {
|
|
|
46
46
|
screenCandidate
|
|
47
47
|
} from "../../core/screening/index.js";
|
|
48
48
|
import {
|
|
49
|
-
closeRecommendDetail,
|
|
50
|
-
createRecommendDetailNetworkRecorder,
|
|
51
|
-
extractRecommendDetailCandidate,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
closeRecommendDetail,
|
|
50
|
+
createRecommendDetailNetworkRecorder,
|
|
51
|
+
extractRecommendDetailCandidate,
|
|
52
|
+
isRecommendDetailOpenMissError,
|
|
53
|
+
isStaleRecommendNodeError,
|
|
54
|
+
openRecommendCardDetailWithFreshRetry,
|
|
55
|
+
waitForRecommendDetailNetworkEvents
|
|
56
|
+
} from "./detail.js";
|
|
56
57
|
import {
|
|
57
58
|
readRecommendCardCandidate,
|
|
58
59
|
waitForRecommendCardNodeIds
|
|
@@ -416,12 +417,13 @@ export function countRecommendResultStatuses(results = [], {
|
|
|
416
417
|
detail_open_failed: results.filter((item) => (
|
|
417
418
|
item.error?.code === "DETAIL_STALE_NODE"
|
|
418
419
|
|| item.error?.code === "DETAIL_OPEN_FAILED"
|
|
419
|
-
)).length,
|
|
420
|
-
transient_recovered: results.filter((item) => (
|
|
421
|
-
item.error?.code === "DETAIL_STALE_NODE"
|
|
422
|
-
|| item.error?.code === "
|
|
423
|
-
|| item.error?.code === "
|
|
424
|
-
|| item.error?.code === "
|
|
420
|
+
)).length,
|
|
421
|
+
transient_recovered: results.filter((item) => (
|
|
422
|
+
item.error?.code === "DETAIL_STALE_NODE"
|
|
423
|
+
|| item.error?.code === "DETAIL_OPEN_FAILED"
|
|
424
|
+
|| item.error?.code === "IMAGE_CAPTURE_STALE_NODE"
|
|
425
|
+
|| item.error?.code === "IMAGE_CAPTURE_TIMEOUT"
|
|
426
|
+
|| item.error?.code === "IMAGE_CAPTURE_TOTAL_TIMEOUT"
|
|
425
427
|
)).length
|
|
426
428
|
};
|
|
427
429
|
}
|
|
@@ -449,15 +451,46 @@ function compactError(error, fallbackCode = "RECOMMEND_RUN_ERROR") {
|
|
|
449
451
|
if (error.close_result) {
|
|
450
452
|
result.close_result = compactCloseResult(error.close_result);
|
|
451
453
|
}
|
|
454
|
+
if (error.refresh_attempt) {
|
|
455
|
+
result.refresh_attempt = error.refresh_attempt;
|
|
456
|
+
}
|
|
457
|
+
if (error.list_end_reason) {
|
|
458
|
+
result.list_end_reason = error.list_end_reason;
|
|
459
|
+
}
|
|
460
|
+
if (error.target_count != null) {
|
|
461
|
+
result.target_count = error.target_count;
|
|
462
|
+
}
|
|
463
|
+
if (error.passed_count != null) {
|
|
464
|
+
result.passed_count = error.passed_count;
|
|
465
|
+
}
|
|
466
|
+
if (Array.isArray(error.recommend_detail_open_attempts)) {
|
|
467
|
+
result.recommend_detail_open_attempts = error.recommend_detail_open_attempts;
|
|
468
|
+
}
|
|
452
469
|
return result;
|
|
453
470
|
}
|
|
454
471
|
|
|
455
|
-
function createRecommendCloseFailureError(closeResult) {
|
|
456
|
-
const error = new Error(closeResult?.reason || "Recommend detail did not close before recovery");
|
|
457
|
-
error.code = "DETAIL_CLOSE_FAILED";
|
|
458
|
-
error.close_result = closeResult || null;
|
|
459
|
-
return error;
|
|
460
|
-
}
|
|
472
|
+
function createRecommendCloseFailureError(closeResult) {
|
|
473
|
+
const error = new Error(closeResult?.reason || "Recommend detail did not close before recovery");
|
|
474
|
+
error.code = "DETAIL_CLOSE_FAILED";
|
|
475
|
+
error.close_result = closeResult || null;
|
|
476
|
+
return error;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function createRecommendRefreshFailureError(refreshAttempt, {
|
|
480
|
+
listEndReason = "",
|
|
481
|
+
targetCount = 0,
|
|
482
|
+
passedCount = 0
|
|
483
|
+
} = {}) {
|
|
484
|
+
const reason = refreshAttempt?.reason || "refresh_failed";
|
|
485
|
+
const detail = refreshAttempt?.error ? `: ${refreshAttempt.error}` : "";
|
|
486
|
+
const error = new Error(`Recommend refresh failed before target was reached (${reason}${detail})`);
|
|
487
|
+
error.code = "RECOMMEND_END_REFRESH_FAILED";
|
|
488
|
+
error.refresh_attempt = refreshAttempt || null;
|
|
489
|
+
error.list_end_reason = listEndReason || null;
|
|
490
|
+
error.target_count = targetCount;
|
|
491
|
+
error.passed_count = passedCount;
|
|
492
|
+
return error;
|
|
493
|
+
}
|
|
461
494
|
|
|
462
495
|
export function isRecoverableImageCaptureError(error) {
|
|
463
496
|
const code = String(error?.code || "");
|
|
@@ -520,9 +553,9 @@ function createImageCaptureFailureScreening(candidate, error) {
|
|
|
520
553
|
};
|
|
521
554
|
}
|
|
522
555
|
|
|
523
|
-
export function isRecoverableRecommendDetailError(error) {
|
|
524
|
-
return isStaleRecommendNodeError(error);
|
|
525
|
-
}
|
|
556
|
+
export function isRecoverableRecommendDetailError(error) {
|
|
557
|
+
return isStaleRecommendNodeError(error) || isRecommendDetailOpenMissError(error);
|
|
558
|
+
}
|
|
526
559
|
|
|
527
560
|
function compactRecoverableDetailError(error) {
|
|
528
561
|
return compactError(error, isStaleRecommendNodeError(error) ? "DETAIL_STALE_NODE" : "DETAIL_OPEN_FAILED");
|
|
@@ -532,10 +565,12 @@ function createRecoverableDetailFailureScreening(candidate, error) {
|
|
|
532
565
|
return {
|
|
533
566
|
status: "fail",
|
|
534
567
|
passed: false,
|
|
535
|
-
score: 0,
|
|
536
|
-
reasons: isStaleRecommendNodeError(error)
|
|
537
|
-
? ["detail_open_failed", "stale_node"]
|
|
538
|
-
:
|
|
568
|
+
score: 0,
|
|
569
|
+
reasons: isStaleRecommendNodeError(error)
|
|
570
|
+
? ["detail_open_failed", "stale_node"]
|
|
571
|
+
: isRecommendDetailOpenMissError(error)
|
|
572
|
+
? ["detail_open_failed", "detail_open_miss"]
|
|
573
|
+
: ["detail_open_failed"],
|
|
539
574
|
error: compactRecoverableDetailError(error),
|
|
540
575
|
candidate
|
|
541
576
|
};
|
|
@@ -960,9 +995,9 @@ export async function runRecommendWorkflow({
|
|
|
960
995
|
refresh_forced_recent_not_view: true,
|
|
961
996
|
list_end_reason: listEndReason
|
|
962
997
|
});
|
|
963
|
-
if (refreshResult.ok) {
|
|
964
|
-
rootState = refreshResult.root_state || await getRecommendRoots(client);
|
|
965
|
-
rootState = await ensureRecommendViewport(rootState, "refresh_after");
|
|
998
|
+
if (refreshResult.ok) {
|
|
999
|
+
rootState = refreshResult.root_state || await getRecommendRoots(client);
|
|
1000
|
+
rootState = await ensureRecommendViewport(rootState, "refresh_after");
|
|
966
1001
|
cardNodeIds = await waitForRecommendCardNodeIds(client, rootState.iframe.documentNodeId, {
|
|
967
1002
|
timeoutMs: cardTimeoutMs,
|
|
968
1003
|
intervalMs: 300
|
|
@@ -976,12 +1011,17 @@ export async function runRecommendWorkflow({
|
|
|
976
1011
|
forced_recent_not_view: true
|
|
977
1012
|
}
|
|
978
1013
|
});
|
|
979
|
-
listEndReason = "";
|
|
980
|
-
continue;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1014
|
+
listEndReason = "";
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
throw createRecommendRefreshFailureError(compactRefresh, {
|
|
1018
|
+
listEndReason,
|
|
1019
|
+
targetCount: targetPassCount,
|
|
1020
|
+
passedCount: countPassedResults(results)
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
break;
|
|
1024
|
+
}
|
|
985
1025
|
|
|
986
1026
|
const index = results.length;
|
|
987
1027
|
let cardNodeId = nextCandidateResult.item.node_id;
|