@reconcrap/boss-recommend-mcp 2.0.35 → 2.0.36

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.35",
3
+ "version": "2.0.36",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -88,6 +88,105 @@ export function buildRecommendFilterSelectionOptions(filter = {}, {
88
88
  };
89
89
  }
90
90
 
91
+ function refreshFailureReason(method = "") {
92
+ return method === "page_navigate" ? "page_navigate_failed" : "page_reload_failed";
93
+ }
94
+
95
+ async function applyRefreshMethod(client, method, {
96
+ jobLabel = "",
97
+ pageScope = "recommend",
98
+ fallbackPageScope = "recommend",
99
+ filter = {},
100
+ targetUrl = RECOMMEND_TARGET_URL,
101
+ forceRecentNotView = true,
102
+ cardTimeoutMs = 30000,
103
+ reloadSettleMs = 8000
104
+ } = {}) {
105
+ const started = Date.now();
106
+ let currentRootState = null;
107
+ let jobSelection = null;
108
+ let pageScopeResult = null;
109
+ let filterResult = null;
110
+ try {
111
+ if (method === "page_navigate") {
112
+ await client.Page.navigate({ url: targetUrl || RECOMMEND_TARGET_URL });
113
+ } else {
114
+ await client.Page.reload({ ignoreCache: true });
115
+ }
116
+ if (reloadSettleMs > 0) await sleep(reloadSettleMs);
117
+ currentRootState = await waitForRecommendRoots(client, {
118
+ timeoutMs: Math.max(45000, reloadSettleMs * 6),
119
+ intervalMs: 500
120
+ });
121
+ if (!currentRootState?.iframe?.documentNodeId) {
122
+ throw new Error("Recommend iframe was not ready after refresh reload");
123
+ }
124
+ if (jobLabel) {
125
+ jobSelection = await selectRecommendJob(client, currentRootState.iframe.documentNodeId, {
126
+ jobLabel,
127
+ settleMs: reloadSettleMs > 10000 ? 12000 : 6000
128
+ });
129
+ if (!jobSelection.selected) {
130
+ throw new Error(`Requested recommend job was not selected after refresh reload: ${jobSelection.reason}`);
131
+ }
132
+ currentRootState = await getRecommendRoots(client);
133
+ }
134
+ pageScopeResult = await selectRecommendPageScope(
135
+ client,
136
+ currentRootState.iframe.documentNodeId,
137
+ {
138
+ pageScope,
139
+ fallbackScope: fallbackPageScope,
140
+ settleMs: reloadSettleMs > 10000 ? 3000 : 1200,
141
+ timeoutMs: Math.max(10000, Math.min(cardTimeoutMs, 60000))
142
+ }
143
+ );
144
+ if (!pageScopeResult.selected) {
145
+ throw new Error(`Recommend page scope was not selected after refresh reload: ${pageScopeResult.reason || pageScope}`);
146
+ }
147
+ currentRootState = await getRecommendRoots(client);
148
+ filterResult = await selectAndConfirmFirstSafeFilter(
149
+ client,
150
+ currentRootState.iframe.documentNodeId,
151
+ buildRecommendFilterSelectionOptions(filter, { forceRecentNotView })
152
+ );
153
+ const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
154
+ timeoutMs: cardTimeoutMs,
155
+ intervalMs: 500
156
+ });
157
+ if (!cardNodeIds.length) {
158
+ throw new Error("No recommend candidate cards were found after refresh reload");
159
+ }
160
+ return {
161
+ ok: true,
162
+ method,
163
+ target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
164
+ job_selection: jobSelection,
165
+ page_scope: pageScopeResult,
166
+ filter: filterResult,
167
+ card_count: cardNodeIds.length,
168
+ root_state: currentRootState,
169
+ forced_recent_not_view: forceRecentNotView,
170
+ elapsed_ms: Date.now() - started
171
+ };
172
+ } catch (error) {
173
+ return {
174
+ ok: false,
175
+ method,
176
+ reason: refreshFailureReason(method),
177
+ error: error?.message || String(error),
178
+ target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
179
+ job_selection: jobSelection,
180
+ page_scope: pageScopeResult,
181
+ filter: filterResult,
182
+ card_count: 0,
183
+ root_state: currentRootState,
184
+ forced_recent_not_view: forceRecentNotView,
185
+ elapsed_ms: Date.now() - started
186
+ };
187
+ }
188
+ }
189
+
91
190
  export async function refreshRecommendListAtEnd(client, {
92
191
  rootState = null,
93
192
  jobLabel = "",
@@ -103,9 +202,10 @@ export async function refreshRecommendListAtEnd(client, {
103
202
  reloadSettleMs = 8000
104
203
  } = {}) {
105
204
  const attempts = [];
106
- let currentRootState = rootState || await getRecommendRoots(client);
205
+ let currentRootState = rootState || null;
107
206
 
108
207
  if (preferEndRefreshButton) {
208
+ currentRootState = currentRootState || await getRecommendRoots(client);
109
209
  const buttonResult = await clickRecommendEndRefreshButton(
110
210
  client,
111
211
  currentRootState.iframe.documentNodeId,
@@ -159,82 +259,49 @@ export async function refreshRecommendListAtEnd(client, {
159
259
  }
160
260
  }
161
261
 
162
- let fallbackMethod = "page_reload";
163
- try {
164
- let method = "page_reload";
165
- if (forceNavigate && typeof client?.Page?.navigate === "function") {
166
- await client.Page.navigate({ url: targetUrl || RECOMMEND_TARGET_URL });
167
- method = "page_navigate";
168
- fallbackMethod = method;
169
- } else {
170
- await client.Page.reload({ ignoreCache: true });
171
- fallbackMethod = method;
172
- }
173
- if (reloadSettleMs > 0) await sleep(reloadSettleMs);
174
- currentRootState = await waitForRecommendRoots(client, {
175
- timeoutMs: Math.max(30000, reloadSettleMs * 4),
176
- intervalMs: 500
262
+ const fallbackMethods = [];
263
+ if (forceNavigate && typeof client?.Page?.navigate === "function") {
264
+ fallbackMethods.push("page_navigate");
265
+ }
266
+ if (typeof client?.Page?.reload === "function") {
267
+ fallbackMethods.push("page_reload");
268
+ }
269
+ if (!fallbackMethods.length) {
270
+ fallbackMethods.push("page_reload");
271
+ }
272
+
273
+ let lastRefreshResult = null;
274
+ for (const method of fallbackMethods) {
275
+ const refreshResult = await applyRefreshMethod(client, method, {
276
+ jobLabel,
277
+ pageScope,
278
+ fallbackPageScope,
279
+ filter,
280
+ targetUrl,
281
+ forceRecentNotView,
282
+ cardTimeoutMs,
283
+ reloadSettleMs
177
284
  });
178
- if (!currentRootState?.iframe?.documentNodeId) {
179
- throw new Error("Recommend iframe was not ready after refresh reload");
180
- }
181
- let jobSelection = null;
182
- if (jobLabel) {
183
- jobSelection = await selectRecommendJob(client, currentRootState.iframe.documentNodeId, {
184
- jobLabel,
185
- settleMs: reloadSettleMs > 10000 ? 12000 : 6000
186
- });
187
- if (!jobSelection.selected) {
188
- throw new Error(`Requested recommend job was not selected after refresh reload: ${jobSelection.reason}`);
189
- }
190
- currentRootState = await getRecommendRoots(client);
285
+ if (refreshResult.ok) {
286
+ return {
287
+ ...refreshResult,
288
+ attempts
289
+ };
191
290
  }
192
- const pageScopeResult = await selectRecommendPageScope(
193
- client,
194
- currentRootState.iframe.documentNodeId,
195
- {
196
- pageScope,
197
- fallbackScope: fallbackPageScope,
198
- settleMs: reloadSettleMs > 10000 ? 3000 : 1200,
199
- timeoutMs: Math.max(10000, Math.min(cardTimeoutMs, 60000))
200
- }
201
- );
202
- if (!pageScopeResult.selected) {
203
- throw new Error(`Recommend page scope was not selected after refresh reload: ${pageScopeResult.reason || pageScope}`);
204
- }
205
- currentRootState = await getRecommendRoots(client);
206
- const filterResult = await selectAndConfirmFirstSafeFilter(
207
- client,
208
- currentRootState.iframe.documentNodeId,
209
- buildRecommendFilterSelectionOptions(filter, { forceRecentNotView })
210
- );
211
- const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
212
- timeoutMs: cardTimeoutMs,
213
- intervalMs: 500
214
- });
215
- return {
216
- ok: cardNodeIds.length > 0,
217
- method,
218
- attempts,
219
- target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
220
- job_selection: jobSelection,
221
- page_scope: pageScopeResult,
222
- filter: filterResult,
223
- card_count: cardNodeIds.length,
224
- root_state: currentRootState,
225
- forced_recent_not_view: forceRecentNotView
226
- };
227
- } catch (error) {
228
- return {
291
+ attempts.push(refreshResult);
292
+ lastRefreshResult = refreshResult;
293
+ }
294
+
295
+ return {
296
+ ...(lastRefreshResult || {
229
297
  ok: false,
230
- method: fallbackMethod,
231
- reason: fallbackMethod === "page_navigate" ? "page_navigate_failed" : "page_reload_failed",
232
- error: error?.message || String(error),
233
- attempts,
234
- target_url: fallbackMethod === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
298
+ method: fallbackMethods[fallbackMethods.length - 1] || "page_reload",
299
+ reason: "refresh_failed",
300
+ error: "Recommend refresh did not run",
235
301
  card_count: 0,
236
302
  root_state: currentRootState,
237
303
  forced_recent_not_view: forceRecentNotView
238
- };
239
- }
304
+ }),
305
+ attempts
306
+ };
240
307
  }
@@ -355,16 +355,22 @@ function compactRefreshAttempt(refreshAttempt) {
355
355
  return {
356
356
  ok: Boolean(refreshAttempt.ok),
357
357
  method: refreshAttempt.method || "",
358
+ reason: refreshAttempt.reason || null,
359
+ error: refreshAttempt.error || null,
358
360
  forced_recent_not_view: Boolean(refreshAttempt.forced_recent_not_view),
359
361
  target_url: refreshAttempt.target_url || null,
360
362
  card_count: refreshAttempt.card_count || 0,
363
+ elapsed_ms: refreshAttempt.elapsed_ms || 0,
361
364
  attempts: (refreshAttempt.attempts || []).map((attempt) => ({
362
365
  ok: Boolean(attempt.ok),
363
366
  method: attempt.method || "",
364
367
  reason: attempt.reason || null,
368
+ error: attempt.error || null,
365
369
  label: attempt.label || null,
366
370
  before_card_count: attempt.before_card_count || 0,
367
- after_card_count: attempt.after_card_count || 0
371
+ after_card_count: attempt.after_card_count || 0,
372
+ card_count: attempt.card_count || 0,
373
+ elapsed_ms: attempt.elapsed_ms || 0
368
374
  })),
369
375
  job_selection: compactJobSelection(refreshAttempt.job_selection),
370
376
  page_scope: compactPageScopeSelection(refreshAttempt.page_scope),