@reconcrap/boss-recommend-mcp 2.1.23 → 2.1.25

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.
Files changed (67) hide show
  1. package/README.md +5 -0
  2. package/bin/boss-recommend-mcp.js +4 -4
  3. package/package.json +6 -1
  4. package/scripts/install-macos.sh +280 -280
  5. package/scripts/postinstall.cjs +44 -44
  6. package/skills/boss-chat/README.md +43 -42
  7. package/skills/boss-chat/SKILL.md +107 -106
  8. package/skills/boss-recommend-pipeline/README.md +13 -13
  9. package/skills/boss-recommend-pipeline/SKILL.md +47 -47
  10. package/skills/boss-recruit-pipeline/README.md +19 -19
  11. package/skills/boss-recruit-pipeline/SKILL.md +89 -89
  12. package/src/chat-mcp.js +301 -127
  13. package/src/chat-runtime-config.js +7 -5
  14. package/src/core/boss-cards/index.js +199 -199
  15. package/src/core/browser/index.js +302 -145
  16. package/src/core/capture/index.js +2930 -1201
  17. package/src/core/cv-acquisition/index.js +512 -238
  18. package/src/core/cv-capture-target/index.js +513 -299
  19. package/src/core/greet-quota/index.js +71 -71
  20. package/src/core/infinite-list/index.js +31 -31
  21. package/src/core/reporting/legacy-csv.js +12 -12
  22. package/src/core/run/detached-launcher.js +305 -0
  23. package/src/core/run/index.js +109 -55
  24. package/src/core/run/timing.js +33 -33
  25. package/src/core/run/windows-detached-worker.ps1 +106 -0
  26. package/src/core/screening/index.js +2400 -2135
  27. package/src/core/self-heal/index.js +989 -973
  28. package/src/core/self-heal/viewport.js +1505 -564
  29. package/src/detached-worker.js +99 -99
  30. package/src/domains/chat/action-journal.js +536 -0
  31. package/src/domains/chat/cards.js +137 -137
  32. package/src/domains/chat/constants.js +9 -9
  33. package/src/domains/chat/detail.js +1684 -401
  34. package/src/domains/chat/index.js +8 -7
  35. package/src/domains/chat/jobs.js +620 -620
  36. package/src/domains/chat/page-guard.js +157 -122
  37. package/src/domains/chat/roots.js +56 -56
  38. package/src/domains/chat/run-service.js +1801 -760
  39. package/src/domains/common/account-rights-panel.js +314 -314
  40. package/src/domains/common/recovery-settle.js +159 -159
  41. package/src/domains/recommend/actions.js +1219 -472
  42. package/src/domains/recommend/cards.js +243 -243
  43. package/src/domains/recommend/colleague-contact.js +1079 -333
  44. package/src/domains/recommend/constants.js +92 -92
  45. package/src/domains/recommend/detail.js +4037 -136
  46. package/src/domains/recommend/filters.js +608 -590
  47. package/src/domains/recommend/index.js +9 -9
  48. package/src/domains/recommend/jobs.js +571 -542
  49. package/src/domains/recommend/location.js +754 -707
  50. package/src/domains/recommend/refresh.js +677 -392
  51. package/src/domains/recommend/roots.js +80 -80
  52. package/src/domains/recommend/run-service.js +4048 -1447
  53. package/src/domains/recommend/scopes.js +246 -246
  54. package/src/domains/recruit/actions.js +277 -277
  55. package/src/domains/recruit/cards.js +74 -74
  56. package/src/domains/recruit/constants.js +236 -236
  57. package/src/domains/recruit/detail.js +588 -588
  58. package/src/domains/recruit/index.js +9 -9
  59. package/src/domains/recruit/instruction-parser.js +866 -866
  60. package/src/domains/recruit/refresh.js +45 -45
  61. package/src/domains/recruit/roots.js +68 -68
  62. package/src/domains/recruit/run-service.js +1817 -1620
  63. package/src/domains/recruit/search.js +3229 -3229
  64. package/src/index.js +16 -1
  65. package/src/parser.js +1296 -1296
  66. package/src/recommend-mcp.js +1061 -450
  67. package/src/recommend-scheduler.js +75 -75
@@ -1,243 +1,243 @@
1
- import {
2
- clickNodeCenter,
3
- getAttributesMap,
4
- getNodeBox,
5
- getOuterHTML,
6
- querySelectorAll,
7
- sleep
8
- } from "../../core/browser/index.js";
9
- import {
10
- mergeBossCandidateCardFields,
11
- parseBossCandidateCardFieldsFromHtml
12
- } from "../../core/boss-cards/index.js";
13
- import {
14
- htmlToText,
15
- normalizeCandidateFromHtml,
16
- normalizeText
17
- } from "../../core/screening/index.js";
18
- import {
19
- RECOMMEND_CARD_SELECTOR,
20
- RECOMMEND_END_REFRESH_SELECTOR
21
- } from "./constants.js";
22
-
23
- function uniqueNodeIds(nodeIds = []) {
24
- return Array.from(new Set(nodeIds.filter(Boolean)));
25
- }
26
-
27
- function normalizeRefreshButtonLabel(outerHTML = "") {
28
- return normalizeText(htmlToText(outerHTML)).replace(/\s+/g, "");
29
- }
30
-
31
- export function parseRecommendCardFieldsFromHtml(html = "") {
32
- return parseBossCandidateCardFieldsFromHtml(html);
33
- }
34
-
35
- function enrichRecommendCardCandidate(candidate, outerHTML = "") {
36
- return mergeBossCandidateCardFields(candidate, outerHTML, {
37
- metadataKey: "recommend_card_fields"
38
- });
39
- }
40
-
41
- function isRefreshButtonLabel(label = "") {
42
- const normalized = String(label || "").trim();
43
- if (!normalized || normalized.length > 80) return false;
44
- return /刷新|refresh/i.test(normalized);
45
- }
46
-
47
- function refreshButtonRank(candidate) {
48
- const label = String(candidate.label || "").toLowerCase();
49
- if (label === "刷新" || label === "refresh") return 0;
50
- if (/^刷新$|^refresh$/i.test(label)) return 0;
51
- if (/刷新/.test(label) || /refresh/i.test(label)) return 1;
52
- return 2;
53
- }
54
-
55
- async function searchTextNodeIds(client, query, {
56
- maxResults = 200
57
- } = {}) {
58
- if (typeof client?.DOM?.performSearch !== "function") return [];
59
- const search = await client.DOM.performSearch({
60
- query,
61
- includeUserAgentShadowDOM: false
62
- });
63
- const searchId = search.searchId;
64
- const resultCount = Math.min(search.resultCount || 0, maxResults);
65
- if (!searchId || resultCount <= 0) return [];
66
- try {
67
- const results = await client.DOM.getSearchResults({
68
- searchId,
69
- fromIndex: 0,
70
- toIndex: resultCount
71
- });
72
- return results.nodeIds || [];
73
- } finally {
74
- await client.DOM.discardSearchResults({ searchId });
75
- }
76
- }
77
-
78
- export async function findRecommendCardNodeIds(client, frameNodeId, {
79
- selector = RECOMMEND_CARD_SELECTOR
80
- } = {}) {
81
- return querySelectorAll(client, frameNodeId, selector);
82
- }
83
-
84
- export async function waitForRecommendCardNodeIds(client, frameNodeId, {
85
- selector = RECOMMEND_CARD_SELECTOR,
86
- timeoutMs = 10000,
87
- intervalMs = 300
88
- } = {}) {
89
- const started = Date.now();
90
- let nodeIds = [];
91
- while (Date.now() - started <= timeoutMs) {
92
- nodeIds = await findRecommendCardNodeIds(client, frameNodeId, { selector });
93
- if (nodeIds.length) return nodeIds;
94
- await sleep(intervalMs);
95
- }
96
- return nodeIds;
97
- }
98
-
99
- export async function readRecommendCardCandidate(client, cardNodeId, {
100
- targetUrl = "",
101
- source = "recommend-domain-card",
102
- metadata = {}
103
- } = {}) {
104
- const [attributes, outerHTML] = await Promise.all([
105
- getAttributesMap(client, cardNodeId),
106
- getOuterHTML(client, cardNodeId)
107
- ]);
108
- const candidate = normalizeCandidateFromHtml({
109
- domain: "recommend",
110
- source,
111
- html: outerHTML,
112
- attributes,
113
- metadata: {
114
- target_url: targetUrl,
115
- card_node_id: cardNodeId,
116
- ...metadata
117
- }
118
- });
119
- return enrichRecommendCardCandidate(candidate, outerHTML);
120
- }
121
-
122
- export async function readFirstRecommendCardCandidate(client, frameNodeId, options = {}) {
123
- const cardNodeIds = await findRecommendCardNodeIds(client, frameNodeId, options);
124
- if (!cardNodeIds.length) {
125
- throw new Error("No recommend candidate cards found");
126
- }
127
-
128
- const candidate = await readRecommendCardCandidate(client, cardNodeIds[0], options);
129
- return {
130
- card_count: cardNodeIds.length,
131
- first_card_node_id: cardNodeIds[0],
132
- card_node_ids: cardNodeIds,
133
- candidate
134
- };
135
- }
136
-
137
- export async function findRecommendEndRefreshButtons(client, frameNodeId, {
138
- selector = RECOMMEND_END_REFRESH_SELECTOR,
139
- maxCandidates = 1200
140
- } = {}) {
141
- const textNodeIds = [
142
- ...await searchTextNodeIds(client, "刷新", { maxResults: 200 }),
143
- ...await searchTextNodeIds(client, "refresh", { maxResults: 50 })
144
- ];
145
- const selectorNodeIds = textNodeIds.length
146
- ? await querySelectorAll(client, frameNodeId, selector)
147
- : [];
148
- const nodeIds = uniqueNodeIds([...textNodeIds, ...selectorNodeIds]).slice(0, maxCandidates);
149
- const candidates = [];
150
- for (let index = 0; index < nodeIds.length; index += 1) {
151
- const nodeId = nodeIds[index];
152
- let outerHTML = "";
153
- try {
154
- outerHTML = await getOuterHTML(client, nodeId);
155
- } catch {
156
- continue;
157
- }
158
- const label = normalizeRefreshButtonLabel(outerHTML);
159
- if (!isRefreshButtonLabel(label)) continue;
160
-
161
- let box = null;
162
- try {
163
- box = await getNodeBox(client, nodeId);
164
- } catch {
165
- // Some text matches can be hidden or stale. Keep the label out of the click set.
166
- continue;
167
- }
168
- candidates.push({
169
- node_id: nodeId,
170
- index,
171
- label,
172
- box,
173
- rank: refreshButtonRank({ label })
174
- });
175
- }
176
-
177
- return candidates.sort((left, right) => {
178
- const rankDiff = left.rank - right.rank;
179
- if (rankDiff !== 0) return rankDiff;
180
- return (right.box?.rect?.y || 0) - (left.box?.rect?.y || 0);
181
- });
182
- }
183
-
184
- export async function clickRecommendEndRefreshButton(client, frameNodeId, {
185
- settleMs = 5000
186
- } = {}) {
187
- const beforeCardCount = (await findRecommendCardNodeIds(client, frameNodeId)).length;
188
- const candidates = await findRecommendEndRefreshButtons(client, frameNodeId);
189
- if (!candidates.length) {
190
- return {
191
- ok: false,
192
- method: "end_refresh_button",
193
- reason: "refresh_button_not_found",
194
- before_card_count: beforeCardCount,
195
- candidates: []
196
- };
197
- }
198
-
199
- const attempts = [];
200
- for (const candidate of candidates) {
201
- try {
202
- const box = await clickNodeCenter(client, candidate.node_id, { scrollIntoView: true });
203
- if (settleMs > 0) await sleep(settleMs);
204
- const afterCardCount = (await findRecommendCardNodeIds(client, frameNodeId)).length;
205
- return {
206
- ok: true,
207
- method: "end_refresh_button",
208
- clicked: true,
209
- node_id: candidate.node_id,
210
- label: candidate.label,
211
- box,
212
- before_card_count: beforeCardCount,
213
- after_card_count: afterCardCount,
214
- settle_ms: settleMs,
215
- candidates: candidates.map((item) => ({
216
- node_id: item.node_id,
217
- label: item.label,
218
- y: item.box?.rect?.y || null
219
- })).slice(0, 10),
220
- attempts
221
- };
222
- } catch (error) {
223
- attempts.push({
224
- node_id: candidate.node_id,
225
- label: candidate.label,
226
- error: error?.message || String(error)
227
- });
228
- }
229
- }
230
-
231
- return {
232
- ok: false,
233
- method: "end_refresh_button",
234
- reason: "refresh_button_click_failed",
235
- before_card_count: beforeCardCount,
236
- attempts,
237
- candidates: candidates.map((item) => ({
238
- node_id: item.node_id,
239
- label: item.label,
240
- y: item.box?.rect?.y || null
241
- })).slice(0, 10)
242
- };
243
- }
1
+ import {
2
+ clickNodeCenter,
3
+ getAttributesMap,
4
+ getNodeBox,
5
+ getOuterHTML,
6
+ querySelectorAll,
7
+ sleep
8
+ } from "../../core/browser/index.js";
9
+ import {
10
+ mergeBossCandidateCardFields,
11
+ parseBossCandidateCardFieldsFromHtml
12
+ } from "../../core/boss-cards/index.js";
13
+ import {
14
+ htmlToText,
15
+ normalizeCandidateFromHtml,
16
+ normalizeText
17
+ } from "../../core/screening/index.js";
18
+ import {
19
+ RECOMMEND_CARD_SELECTOR,
20
+ RECOMMEND_END_REFRESH_SELECTOR
21
+ } from "./constants.js";
22
+
23
+ function uniqueNodeIds(nodeIds = []) {
24
+ return Array.from(new Set(nodeIds.filter(Boolean)));
25
+ }
26
+
27
+ function normalizeRefreshButtonLabel(outerHTML = "") {
28
+ return normalizeText(htmlToText(outerHTML)).replace(/\s+/g, "");
29
+ }
30
+
31
+ export function parseRecommendCardFieldsFromHtml(html = "") {
32
+ return parseBossCandidateCardFieldsFromHtml(html);
33
+ }
34
+
35
+ function enrichRecommendCardCandidate(candidate, outerHTML = "") {
36
+ return mergeBossCandidateCardFields(candidate, outerHTML, {
37
+ metadataKey: "recommend_card_fields"
38
+ });
39
+ }
40
+
41
+ function isRefreshButtonLabel(label = "") {
42
+ const normalized = String(label || "").trim();
43
+ if (!normalized || normalized.length > 80) return false;
44
+ return /刷新|refresh/i.test(normalized);
45
+ }
46
+
47
+ function refreshButtonRank(candidate) {
48
+ const label = String(candidate.label || "").toLowerCase();
49
+ if (label === "刷新" || label === "refresh") return 0;
50
+ if (/^刷新$|^refresh$/i.test(label)) return 0;
51
+ if (/刷新/.test(label) || /refresh/i.test(label)) return 1;
52
+ return 2;
53
+ }
54
+
55
+ async function searchTextNodeIds(client, query, {
56
+ maxResults = 200
57
+ } = {}) {
58
+ if (typeof client?.DOM?.performSearch !== "function") return [];
59
+ const search = await client.DOM.performSearch({
60
+ query,
61
+ includeUserAgentShadowDOM: false
62
+ });
63
+ const searchId = search.searchId;
64
+ const resultCount = Math.min(search.resultCount || 0, maxResults);
65
+ if (!searchId || resultCount <= 0) return [];
66
+ try {
67
+ const results = await client.DOM.getSearchResults({
68
+ searchId,
69
+ fromIndex: 0,
70
+ toIndex: resultCount
71
+ });
72
+ return results.nodeIds || [];
73
+ } finally {
74
+ await client.DOM.discardSearchResults({ searchId });
75
+ }
76
+ }
77
+
78
+ export async function findRecommendCardNodeIds(client, frameNodeId, {
79
+ selector = RECOMMEND_CARD_SELECTOR
80
+ } = {}) {
81
+ return querySelectorAll(client, frameNodeId, selector);
82
+ }
83
+
84
+ export async function waitForRecommendCardNodeIds(client, frameNodeId, {
85
+ selector = RECOMMEND_CARD_SELECTOR,
86
+ timeoutMs = 10000,
87
+ intervalMs = 300
88
+ } = {}) {
89
+ const started = Date.now();
90
+ let nodeIds = [];
91
+ while (Date.now() - started <= timeoutMs) {
92
+ nodeIds = await findRecommendCardNodeIds(client, frameNodeId, { selector });
93
+ if (nodeIds.length) return nodeIds;
94
+ await sleep(intervalMs);
95
+ }
96
+ return nodeIds;
97
+ }
98
+
99
+ export async function readRecommendCardCandidate(client, cardNodeId, {
100
+ targetUrl = "",
101
+ source = "recommend-domain-card",
102
+ metadata = {}
103
+ } = {}) {
104
+ const [attributes, outerHTML] = await Promise.all([
105
+ getAttributesMap(client, cardNodeId),
106
+ getOuterHTML(client, cardNodeId)
107
+ ]);
108
+ const candidate = normalizeCandidateFromHtml({
109
+ domain: "recommend",
110
+ source,
111
+ html: outerHTML,
112
+ attributes,
113
+ metadata: {
114
+ target_url: targetUrl,
115
+ card_node_id: cardNodeId,
116
+ ...metadata
117
+ }
118
+ });
119
+ return enrichRecommendCardCandidate(candidate, outerHTML);
120
+ }
121
+
122
+ export async function readFirstRecommendCardCandidate(client, frameNodeId, options = {}) {
123
+ const cardNodeIds = await findRecommendCardNodeIds(client, frameNodeId, options);
124
+ if (!cardNodeIds.length) {
125
+ throw new Error("No recommend candidate cards found");
126
+ }
127
+
128
+ const candidate = await readRecommendCardCandidate(client, cardNodeIds[0], options);
129
+ return {
130
+ card_count: cardNodeIds.length,
131
+ first_card_node_id: cardNodeIds[0],
132
+ card_node_ids: cardNodeIds,
133
+ candidate
134
+ };
135
+ }
136
+
137
+ export async function findRecommendEndRefreshButtons(client, frameNodeId, {
138
+ selector = RECOMMEND_END_REFRESH_SELECTOR,
139
+ maxCandidates = 1200
140
+ } = {}) {
141
+ const textNodeIds = [
142
+ ...await searchTextNodeIds(client, "刷新", { maxResults: 200 }),
143
+ ...await searchTextNodeIds(client, "refresh", { maxResults: 50 })
144
+ ];
145
+ const selectorNodeIds = textNodeIds.length
146
+ ? await querySelectorAll(client, frameNodeId, selector)
147
+ : [];
148
+ const nodeIds = uniqueNodeIds([...textNodeIds, ...selectorNodeIds]).slice(0, maxCandidates);
149
+ const candidates = [];
150
+ for (let index = 0; index < nodeIds.length; index += 1) {
151
+ const nodeId = nodeIds[index];
152
+ let outerHTML = "";
153
+ try {
154
+ outerHTML = await getOuterHTML(client, nodeId);
155
+ } catch {
156
+ continue;
157
+ }
158
+ const label = normalizeRefreshButtonLabel(outerHTML);
159
+ if (!isRefreshButtonLabel(label)) continue;
160
+
161
+ let box = null;
162
+ try {
163
+ box = await getNodeBox(client, nodeId);
164
+ } catch {
165
+ // Some text matches can be hidden or stale. Keep the label out of the click set.
166
+ continue;
167
+ }
168
+ candidates.push({
169
+ node_id: nodeId,
170
+ index,
171
+ label,
172
+ box,
173
+ rank: refreshButtonRank({ label })
174
+ });
175
+ }
176
+
177
+ return candidates.sort((left, right) => {
178
+ const rankDiff = left.rank - right.rank;
179
+ if (rankDiff !== 0) return rankDiff;
180
+ return (right.box?.rect?.y || 0) - (left.box?.rect?.y || 0);
181
+ });
182
+ }
183
+
184
+ export async function clickRecommendEndRefreshButton(client, frameNodeId, {
185
+ settleMs = 5000
186
+ } = {}) {
187
+ const beforeCardCount = (await findRecommendCardNodeIds(client, frameNodeId)).length;
188
+ const candidates = await findRecommendEndRefreshButtons(client, frameNodeId);
189
+ if (!candidates.length) {
190
+ return {
191
+ ok: false,
192
+ method: "end_refresh_button",
193
+ reason: "refresh_button_not_found",
194
+ before_card_count: beforeCardCount,
195
+ candidates: []
196
+ };
197
+ }
198
+
199
+ const attempts = [];
200
+ for (const candidate of candidates) {
201
+ try {
202
+ const box = await clickNodeCenter(client, candidate.node_id, { scrollIntoView: true });
203
+ if (settleMs > 0) await sleep(settleMs);
204
+ const afterCardCount = (await findRecommendCardNodeIds(client, frameNodeId)).length;
205
+ return {
206
+ ok: true,
207
+ method: "end_refresh_button",
208
+ clicked: true,
209
+ node_id: candidate.node_id,
210
+ label: candidate.label,
211
+ box,
212
+ before_card_count: beforeCardCount,
213
+ after_card_count: afterCardCount,
214
+ settle_ms: settleMs,
215
+ candidates: candidates.map((item) => ({
216
+ node_id: item.node_id,
217
+ label: item.label,
218
+ y: item.box?.rect?.y || null
219
+ })).slice(0, 10),
220
+ attempts
221
+ };
222
+ } catch (error) {
223
+ attempts.push({
224
+ node_id: candidate.node_id,
225
+ label: candidate.label,
226
+ error: error?.message || String(error)
227
+ });
228
+ }
229
+ }
230
+
231
+ return {
232
+ ok: false,
233
+ method: "end_refresh_button",
234
+ reason: "refresh_button_click_failed",
235
+ before_card_count: beforeCardCount,
236
+ attempts,
237
+ candidates: candidates.map((item) => ({
238
+ node_id: item.node_id,
239
+ label: item.label,
240
+ y: item.box?.rect?.y || null
241
+ })).slice(0, 10)
242
+ };
243
+ }