@reconcrap/boss-recommend-mcp 2.0.39 → 2.0.41
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
|
@@ -92,10 +92,28 @@ export async function findRecommendJobTrigger(client, frameNodeId) {
|
|
|
92
92
|
return null;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
export async function waitForRecommendJobTrigger(client, frameNodeId, {
|
|
96
|
+
timeoutMs = 8000,
|
|
97
|
+
intervalMs = 250
|
|
98
|
+
} = {}) {
|
|
99
|
+
const started = Date.now();
|
|
100
|
+
while (Date.now() - started <= timeoutMs) {
|
|
101
|
+
const trigger = await findRecommendJobTrigger(client, frameNodeId);
|
|
102
|
+
if (trigger) return trigger;
|
|
103
|
+
await sleep(intervalMs);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
95
108
|
export async function openRecommendJobDropdown(client, frameNodeId, {
|
|
96
|
-
timeoutMs = 4000
|
|
109
|
+
timeoutMs = 4000,
|
|
110
|
+
triggerTimeoutMs = Math.max(8000, timeoutMs),
|
|
111
|
+
triggerIntervalMs = 250
|
|
97
112
|
} = {}) {
|
|
98
|
-
const trigger = await
|
|
113
|
+
const trigger = await waitForRecommendJobTrigger(client, frameNodeId, {
|
|
114
|
+
timeoutMs: triggerTimeoutMs,
|
|
115
|
+
intervalMs: triggerIntervalMs
|
|
116
|
+
});
|
|
99
117
|
if (!trigger) {
|
|
100
118
|
throw new Error("Recommend job trigger was not found");
|
|
101
119
|
}
|
|
@@ -166,7 +184,8 @@ export async function closeRecommendJobDropdown(client) {
|
|
|
166
184
|
|
|
167
185
|
export async function selectRecommendJob(client, frameNodeId, {
|
|
168
186
|
jobLabel = "",
|
|
169
|
-
settleMs = 6000
|
|
187
|
+
settleMs = 6000,
|
|
188
|
+
dropdownTimeoutMs = Math.max(8000, settleMs)
|
|
170
189
|
} = {}) {
|
|
171
190
|
const target = normalizeText(jobLabel);
|
|
172
191
|
if (!target) {
|
|
@@ -178,7 +197,10 @@ export async function selectRecommendJob(client, frameNodeId, {
|
|
|
178
197
|
};
|
|
179
198
|
}
|
|
180
199
|
|
|
181
|
-
const opened = await openRecommendJobDropdown(client, frameNodeId
|
|
200
|
+
const opened = await openRecommendJobDropdown(client, frameNodeId, {
|
|
201
|
+
timeoutMs: dropdownTimeoutMs,
|
|
202
|
+
triggerTimeoutMs: dropdownTimeoutMs
|
|
203
|
+
});
|
|
182
204
|
const options = opened.options.length
|
|
183
205
|
? opened.options
|
|
184
206
|
: await listRecommendJobOptions(client, frameNodeId, { openDropdown: false });
|
|
@@ -101,6 +101,91 @@ function compactFilterReapplyError(error) {
|
|
|
101
101
|
return error?.message || String(error || "Recommend filter reapply failed");
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
export function isRetryableRecommendJobSelectionError(error) {
|
|
105
|
+
const message = String(error?.message || error || "");
|
|
106
|
+
return /Recommend job trigger was not found|Recommend job dropdown did not mount options/i.test(message);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function compactJobSelectionAttempt({
|
|
110
|
+
ok = false,
|
|
111
|
+
attempt = 0,
|
|
112
|
+
iframeDocumentNodeId = 0,
|
|
113
|
+
error = null,
|
|
114
|
+
selection = null
|
|
115
|
+
} = {}) {
|
|
116
|
+
return {
|
|
117
|
+
ok: Boolean(ok),
|
|
118
|
+
method: "job_select",
|
|
119
|
+
reason: error ? "job_select_failed" : null,
|
|
120
|
+
error: error ? (error?.message || String(error)) : null,
|
|
121
|
+
attempt,
|
|
122
|
+
iframe_document_node_id: iframeDocumentNodeId || 0,
|
|
123
|
+
selected: Boolean(selection?.selected),
|
|
124
|
+
selection_reason: selection?.reason || null
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function selectRecommendJobWithRootRefresh(client, rootState, {
|
|
129
|
+
jobLabel = "",
|
|
130
|
+
settleMs = 6000,
|
|
131
|
+
dropdownTimeoutMs = 4000,
|
|
132
|
+
totalTimeoutMs = 30000,
|
|
133
|
+
retryDelayMs = 1000
|
|
134
|
+
} = {}) {
|
|
135
|
+
const started = Date.now();
|
|
136
|
+
const attempts = [];
|
|
137
|
+
let currentRootState = rootState || null;
|
|
138
|
+
let lastError = null;
|
|
139
|
+
let attempt = 0;
|
|
140
|
+
|
|
141
|
+
while (Date.now() - started <= totalTimeoutMs) {
|
|
142
|
+
attempt += 1;
|
|
143
|
+
if (!currentRootState?.iframe?.documentNodeId) {
|
|
144
|
+
currentRootState = await getRecommendRoots(client);
|
|
145
|
+
}
|
|
146
|
+
const iframeDocumentNodeId = currentRootState?.iframe?.documentNodeId || 0;
|
|
147
|
+
try {
|
|
148
|
+
const selection = await selectRecommendJob(client, iframeDocumentNodeId, {
|
|
149
|
+
jobLabel,
|
|
150
|
+
settleMs,
|
|
151
|
+
dropdownTimeoutMs
|
|
152
|
+
});
|
|
153
|
+
attempts.push(compactJobSelectionAttempt({
|
|
154
|
+
ok: true,
|
|
155
|
+
attempt,
|
|
156
|
+
iframeDocumentNodeId,
|
|
157
|
+
selection
|
|
158
|
+
}));
|
|
159
|
+
return {
|
|
160
|
+
job_selection: {
|
|
161
|
+
...selection,
|
|
162
|
+
refresh_attempts: attempts
|
|
163
|
+
},
|
|
164
|
+
root_state: currentRootState,
|
|
165
|
+
attempts
|
|
166
|
+
};
|
|
167
|
+
} catch (error) {
|
|
168
|
+
lastError = error;
|
|
169
|
+
attempts.push(compactJobSelectionAttempt({
|
|
170
|
+
ok: false,
|
|
171
|
+
attempt,
|
|
172
|
+
iframeDocumentNodeId,
|
|
173
|
+
error
|
|
174
|
+
}));
|
|
175
|
+
if (!isRetryableRecommendJobSelectionError(error) || Date.now() - started >= totalTimeoutMs) {
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
if (retryDelayMs > 0) await sleep(retryDelayMs);
|
|
179
|
+
currentRootState = await getRecommendRoots(client);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const wrapped = new Error(lastError?.message || "Recommend job selection failed after refresh reload");
|
|
184
|
+
wrapped.cause = lastError;
|
|
185
|
+
wrapped.job_selection_attempts = attempts;
|
|
186
|
+
throw wrapped;
|
|
187
|
+
}
|
|
188
|
+
|
|
104
189
|
async function selectAndConfirmRefreshFilter(client, rootState, filterOptions, {
|
|
105
190
|
maxAttempts = 3,
|
|
106
191
|
retryDelayMs = 1500
|
|
@@ -162,6 +247,7 @@ async function applyRefreshMethod(client, method, {
|
|
|
162
247
|
const started = Date.now();
|
|
163
248
|
let currentRootState = null;
|
|
164
249
|
let jobSelection = null;
|
|
250
|
+
let jobSelectionAttempts = [];
|
|
165
251
|
let pageScopeResult = null;
|
|
166
252
|
let filterResult = null;
|
|
167
253
|
let filterReapplyAttempts = [];
|
|
@@ -180,14 +266,19 @@ async function applyRefreshMethod(client, method, {
|
|
|
180
266
|
throw new Error("Recommend iframe was not ready after refresh reload");
|
|
181
267
|
}
|
|
182
268
|
if (jobLabel) {
|
|
183
|
-
|
|
269
|
+
const jobSelectionResult = await selectRecommendJobWithRootRefresh(client, currentRootState, {
|
|
184
270
|
jobLabel,
|
|
185
|
-
settleMs: reloadSettleMs > 10000 ? 12000 : 6000
|
|
271
|
+
settleMs: reloadSettleMs > 10000 ? 12000 : 6000,
|
|
272
|
+
dropdownTimeoutMs: 4000,
|
|
273
|
+
totalTimeoutMs: reloadSettleMs > 10000 ? 45000 : 30000,
|
|
274
|
+
retryDelayMs: 1200
|
|
186
275
|
});
|
|
276
|
+
jobSelection = jobSelectionResult.job_selection;
|
|
277
|
+
jobSelectionAttempts = jobSelectionResult.attempts;
|
|
187
278
|
if (!jobSelection.selected) {
|
|
188
279
|
throw new Error(`Requested recommend job was not selected after refresh reload: ${jobSelection.reason}`);
|
|
189
280
|
}
|
|
190
|
-
currentRootState = await getRecommendRoots(client);
|
|
281
|
+
currentRootState = jobSelectionResult.root_state || await getRecommendRoots(client);
|
|
191
282
|
}
|
|
192
283
|
pageScopeResult = await selectRecommendPageScope(
|
|
193
284
|
client,
|
|
@@ -226,6 +317,7 @@ async function applyRefreshMethod(client, method, {
|
|
|
226
317
|
method,
|
|
227
318
|
target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
|
|
228
319
|
job_selection: jobSelection,
|
|
320
|
+
job_selection_attempts: jobSelectionAttempts,
|
|
229
321
|
page_scope: pageScopeResult,
|
|
230
322
|
filter: filterResult,
|
|
231
323
|
filter_reapply_attempts: filterReapplyAttempts,
|
|
@@ -242,6 +334,7 @@ async function applyRefreshMethod(client, method, {
|
|
|
242
334
|
error: error?.message || String(error),
|
|
243
335
|
target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
|
|
244
336
|
job_selection: jobSelection,
|
|
337
|
+
job_selection_attempts: error?.job_selection_attempts || jobSelectionAttempts,
|
|
245
338
|
page_scope: pageScopeResult,
|
|
246
339
|
filter: filterResult,
|
|
247
340
|
filter_reapply_attempts: error?.filter_reapply_attempts || filterReapplyAttempts,
|
|
@@ -379,6 +379,16 @@ function compactRefreshAttempt(refreshAttempt) {
|
|
|
379
379
|
error: attempt.error || null,
|
|
380
380
|
attempt: attempt.attempt || 0
|
|
381
381
|
})),
|
|
382
|
+
job_selection_attempts: (refreshAttempt.job_selection_attempts || []).map((attempt) => ({
|
|
383
|
+
ok: Boolean(attempt.ok),
|
|
384
|
+
method: attempt.method || "job_select",
|
|
385
|
+
reason: attempt.reason || null,
|
|
386
|
+
error: attempt.error || null,
|
|
387
|
+
attempt: attempt.attempt || 0,
|
|
388
|
+
iframe_document_node_id: attempt.iframe_document_node_id || 0,
|
|
389
|
+
selected: Boolean(attempt.selected),
|
|
390
|
+
selection_reason: attempt.selection_reason || null
|
|
391
|
+
})),
|
|
382
392
|
job_selection: compactJobSelection(refreshAttempt.job_selection),
|
|
383
393
|
page_scope: compactPageScopeSelection(refreshAttempt.page_scope),
|
|
384
394
|
filter: compactFilterResult(refreshAttempt.filter)
|