@reconcrap/boss-recommend-mcp 2.1.23 → 2.1.24
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/README.md +5 -0
- package/bin/boss-recommend-mcp.js +4 -4
- package/package.json +6 -1
- package/scripts/install-macos.sh +280 -280
- package/scripts/postinstall.cjs +44 -44
- package/skills/boss-chat/README.md +43 -42
- package/skills/boss-chat/SKILL.md +107 -106
- package/skills/boss-recommend-pipeline/README.md +13 -13
- package/skills/boss-recommend-pipeline/SKILL.md +47 -47
- package/skills/boss-recruit-pipeline/README.md +19 -19
- package/skills/boss-recruit-pipeline/SKILL.md +89 -89
- package/src/chat-mcp.js +301 -127
- package/src/core/boss-cards/index.js +199 -199
- package/src/core/browser/index.js +286 -136
- package/src/core/capture/index.js +2930 -1201
- package/src/core/cv-acquisition/index.js +512 -238
- package/src/core/cv-capture-target/index.js +513 -299
- package/src/core/greet-quota/index.js +71 -71
- package/src/core/infinite-list/index.js +31 -31
- package/src/core/reporting/legacy-csv.js +12 -12
- package/src/core/run/detached-launcher.js +305 -0
- package/src/core/run/index.js +105 -52
- package/src/core/run/timing.js +33 -33
- package/src/core/run/windows-detached-worker.ps1 +106 -0
- package/src/core/screening/index.js +2135 -2135
- package/src/core/self-heal/index.js +989 -973
- package/src/core/self-heal/viewport.js +1505 -564
- package/src/detached-worker.js +99 -99
- package/src/domains/chat/action-journal.js +443 -0
- package/src/domains/chat/cards.js +137 -137
- package/src/domains/chat/constants.js +9 -9
- package/src/domains/chat/detail.js +1684 -401
- package/src/domains/chat/index.js +8 -7
- package/src/domains/chat/jobs.js +620 -620
- package/src/domains/chat/page-guard.js +157 -122
- package/src/domains/chat/roots.js +56 -56
- package/src/domains/chat/run-service.js +1801 -760
- package/src/domains/common/account-rights-panel.js +314 -314
- package/src/domains/common/recovery-settle.js +159 -159
- package/src/domains/recommend/actions.js +515 -472
- package/src/domains/recommend/cards.js +243 -243
- package/src/domains/recommend/colleague-contact.js +333 -333
- package/src/domains/recommend/constants.js +92 -92
- package/src/domains/recommend/detail.js +12 -12
- package/src/domains/recommend/filters.js +611 -611
- package/src/domains/recommend/index.js +9 -9
- package/src/domains/recommend/jobs.js +542 -542
- package/src/domains/recommend/location.js +736 -736
- package/src/domains/recommend/refresh.js +422 -422
- package/src/domains/recommend/roots.js +80 -80
- package/src/domains/recommend/run-service.js +1791 -1205
- package/src/domains/recommend/scopes.js +246 -246
- package/src/domains/recruit/actions.js +277 -277
- package/src/domains/recruit/cards.js +74 -74
- package/src/domains/recruit/constants.js +236 -236
- package/src/domains/recruit/detail.js +588 -588
- package/src/domains/recruit/index.js +9 -9
- package/src/domains/recruit/instruction-parser.js +866 -866
- package/src/domains/recruit/refresh.js +45 -45
- package/src/domains/recruit/roots.js +68 -68
- package/src/domains/recruit/run-service.js +1817 -1620
- package/src/domains/recruit/search.js +3229 -3229
- package/src/index.js +16 -1
- package/src/parser.js +1296 -1296
- package/src/recommend-mcp.js +551 -362
- package/src/recommend-scheduler.js +75 -75
|
@@ -11,17 +11,17 @@ import {
|
|
|
11
11
|
clickRecommendEndRefreshButton,
|
|
12
12
|
waitForRecommendCardNodeIds
|
|
13
13
|
} from "./cards.js";
|
|
14
|
-
import {
|
|
15
|
-
RECOMMEND_RECENT_NOT_VIEW_LABEL,
|
|
16
|
-
RECOMMEND_TARGET_URL
|
|
17
|
-
} from "./constants.js";
|
|
18
|
-
import { selectAndConfirmFirstSafeFilter } from "./filters.js";
|
|
19
|
-
import { ensureRecommendCurrentCityOnly } from "./location.js";
|
|
20
|
-
import {
|
|
21
|
-
selectRecommendJob,
|
|
22
|
-
verifyRecommendJobSelection
|
|
23
|
-
} from "./jobs.js";
|
|
24
|
-
import { selectRecommendPageScope } from "./scopes.js";
|
|
14
|
+
import {
|
|
15
|
+
RECOMMEND_RECENT_NOT_VIEW_LABEL,
|
|
16
|
+
RECOMMEND_TARGET_URL
|
|
17
|
+
} from "./constants.js";
|
|
18
|
+
import { selectAndConfirmFirstSafeFilter } from "./filters.js";
|
|
19
|
+
import { ensureRecommendCurrentCityOnly } from "./location.js";
|
|
20
|
+
import {
|
|
21
|
+
selectRecommendJob,
|
|
22
|
+
verifyRecommendJobSelection
|
|
23
|
+
} from "./jobs.js";
|
|
24
|
+
import { selectRecommendPageScope } from "./scopes.js";
|
|
25
25
|
import {
|
|
26
26
|
getRecommendRoots,
|
|
27
27
|
waitForRecommendRoots
|
|
@@ -31,19 +31,19 @@ import { isStaleRecommendNodeError } from "./detail.js";
|
|
|
31
31
|
function normalizeLabels(labels = []) {
|
|
32
32
|
return labels.map((label) => String(label || "").trim()).filter(Boolean);
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
function normalizeFilterGroup(spec = {}) {
|
|
36
|
-
return {
|
|
37
|
-
group: String(spec.group || "").trim(),
|
|
38
|
-
labels: normalizeLabels(spec.labels || spec.filterLabels || []),
|
|
39
|
-
selectAllLabels: spec.selectAllLabels !== false,
|
|
40
|
-
allowUnlimited: spec.allowUnlimited === true,
|
|
41
|
-
verifySticky: spec.verifySticky === true
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function buildRecommendFilterGroups(filter = {}, {
|
|
46
|
-
forceRecentNotView = false
|
|
34
|
+
|
|
35
|
+
function normalizeFilterGroup(spec = {}) {
|
|
36
|
+
return {
|
|
37
|
+
group: String(spec.group || "").trim(),
|
|
38
|
+
labels: normalizeLabels(spec.labels || spec.filterLabels || []),
|
|
39
|
+
selectAllLabels: spec.selectAllLabels !== false,
|
|
40
|
+
allowUnlimited: spec.allowUnlimited === true,
|
|
41
|
+
verifySticky: spec.verifySticky === true
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function buildRecommendFilterGroups(filter = {}, {
|
|
46
|
+
forceRecentNotView = false
|
|
47
47
|
} = {}) {
|
|
48
48
|
const groups = [];
|
|
49
49
|
const sourceGroups = Array.isArray(filter.filterGroups)
|
|
@@ -68,19 +68,19 @@ export function buildRecommendFilterGroups(filter = {}, {
|
|
|
68
68
|
if (!recentGroup.labels.some((label) => label.replace(/\s+/g, "") === RECOMMEND_RECENT_NOT_VIEW_LABEL)) {
|
|
69
69
|
recentGroup.labels.push(RECOMMEND_RECENT_NOT_VIEW_LABEL);
|
|
70
70
|
}
|
|
71
|
-
recentGroup.selectAllLabels = true;
|
|
72
|
-
} else {
|
|
73
|
-
groups.unshift({
|
|
74
|
-
group: "recentNotView",
|
|
75
|
-
labels: [RECOMMEND_RECENT_NOT_VIEW_LABEL],
|
|
76
|
-
selectAllLabels: true,
|
|
77
|
-
allowUnlimited: false,
|
|
78
|
-
verifySticky: false
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return groups;
|
|
71
|
+
recentGroup.selectAllLabels = true;
|
|
72
|
+
} else {
|
|
73
|
+
groups.unshift({
|
|
74
|
+
group: "recentNotView",
|
|
75
|
+
labels: [RECOMMEND_RECENT_NOT_VIEW_LABEL],
|
|
76
|
+
selectAllLabels: true,
|
|
77
|
+
allowUnlimited: false,
|
|
78
|
+
verifySticky: false
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return groups;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export function buildRecommendFilterSelectionOptions(filter = {}, {
|
|
@@ -90,134 +90,134 @@ export function buildRecommendFilterSelectionOptions(filter = {}, {
|
|
|
90
90
|
if (filterGroups.length > 1 || forceRecentNotView || Array.isArray(filter.filterGroups) || Array.isArray(filter.groups)) {
|
|
91
91
|
return { filterGroups };
|
|
92
92
|
}
|
|
93
|
-
const [singleGroup] = filterGroups;
|
|
94
|
-
if (singleGroup) {
|
|
95
|
-
return {
|
|
96
|
-
group: singleGroup.group,
|
|
97
|
-
labels: singleGroup.labels,
|
|
98
|
-
selectAllLabels: singleGroup.selectAllLabels,
|
|
99
|
-
allowUnlimited: singleGroup.allowUnlimited,
|
|
100
|
-
verifySticky: singleGroup.verifySticky
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
return {
|
|
104
|
-
group: filter.group || "",
|
|
105
|
-
labels: normalizeLabels(filter.labels || filter.filterLabels || []),
|
|
106
|
-
selectAllLabels: filter.selectAllLabels !== false,
|
|
107
|
-
allowUnlimited: filter.allowUnlimited === true,
|
|
108
|
-
verifySticky: filter.verifySticky === true
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export async function applyRecommendFilterEnvelopeStages(filter = {}, {
|
|
113
|
-
applyCurrentCityOnly,
|
|
114
|
-
applyFilterPanel
|
|
115
|
-
} = {}) {
|
|
116
|
-
if (filter.enabled === false) {
|
|
117
|
-
return {
|
|
118
|
-
applied: false,
|
|
119
|
-
skipped: true,
|
|
120
|
-
current_city_only: null,
|
|
121
|
-
filter: null
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
if (typeof applyCurrentCityOnly !== "function" || typeof applyFilterPanel !== "function") {
|
|
125
|
-
throw new Error("Recommend native filter stages require location and filter-panel callbacks");
|
|
126
|
-
}
|
|
127
|
-
const currentCityOnlyResult = await applyCurrentCityOnly();
|
|
128
|
-
const filterResult = await applyFilterPanel();
|
|
129
|
-
return {
|
|
130
|
-
applied: true,
|
|
131
|
-
skipped: false,
|
|
132
|
-
current_city_only: currentCityOnlyResult,
|
|
133
|
-
filter: filterResult
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function refreshFailureReason(method = "") {
|
|
138
|
-
return method === "page_navigate" ? "page_navigate_failed" : "page_reload_failed";
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function safeDiagnosticText(value, maxLength = 4000) {
|
|
142
|
-
const text = String(value ?? "");
|
|
143
|
-
return text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function safeDiagnosticStack(stack) {
|
|
147
|
-
if (!stack) return null;
|
|
148
|
-
return safeDiagnosticText(stack, 8000)
|
|
149
|
-
.split(/\r?\n/)
|
|
150
|
-
.slice(0, 12)
|
|
151
|
-
.join("\n");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function buildErrorDiagnostic(error, {
|
|
155
|
-
depth = 0,
|
|
156
|
-
seen = new Set()
|
|
157
|
-
} = {}) {
|
|
158
|
-
if (!error) return null;
|
|
159
|
-
const isRecord = typeof error === "object" || typeof error === "function";
|
|
160
|
-
if (isRecord && seen.has(error)) {
|
|
161
|
-
return {
|
|
162
|
-
name: error?.name || "Error",
|
|
163
|
-
message: safeDiagnosticText(error?.message || String(error)),
|
|
164
|
-
circular: true
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
if (isRecord) seen.add(error);
|
|
168
|
-
const diagnostic = {
|
|
169
|
-
name: error?.name || "Error",
|
|
170
|
-
message: safeDiagnosticText(error?.message || String(error))
|
|
171
|
-
};
|
|
172
|
-
if (error?.code !== undefined && error?.code !== null && error.code !== "") {
|
|
173
|
-
diagnostic.code = typeof error.code === "number"
|
|
174
|
-
? error.code
|
|
175
|
-
: safeDiagnosticText(error.code, 300);
|
|
176
|
-
}
|
|
177
|
-
if (error?.phase) diagnostic.phase = safeDiagnosticText(error.phase, 300);
|
|
178
|
-
if (error?.cdp_method) diagnostic.cdp_method = safeDiagnosticText(error.cdp_method, 300);
|
|
179
|
-
if (error?.cdp_at) diagnostic.cdp_at = safeDiagnosticText(error.cdp_at, 100);
|
|
180
|
-
if (Number.isInteger(error?.cdp_node_id)) diagnostic.cdp_node_id = error.cdp_node_id;
|
|
181
|
-
if (Number.isInteger(error?.cdp_backend_node_id)) {
|
|
182
|
-
diagnostic.cdp_backend_node_id = error.cdp_backend_node_id;
|
|
183
|
-
}
|
|
184
|
-
if (Number.isInteger(error?.node_id)) diagnostic.node_id = error.node_id;
|
|
185
|
-
if (Number.isInteger(error?.backend_node_id)) diagnostic.backend_node_id = error.backend_node_id;
|
|
186
|
-
if (error?.cdp_search_id) diagnostic.cdp_search_id = safeDiagnosticText(error.cdp_search_id, 300);
|
|
187
|
-
if (Array.isArray(error?.cdp_param_keys)) {
|
|
188
|
-
diagnostic.cdp_param_keys = error.cdp_param_keys
|
|
189
|
-
.slice(0, 20)
|
|
190
|
-
.map((key) => safeDiagnosticText(key, 100));
|
|
191
|
-
}
|
|
192
|
-
const stack = safeDiagnosticStack(error?.stack);
|
|
193
|
-
if (stack) diagnostic.stack = stack;
|
|
194
|
-
if (depth < 2 && error?.cause && error.cause !== error) {
|
|
195
|
-
diagnostic.cause = buildErrorDiagnostic(error.cause, {
|
|
196
|
-
depth: depth + 1,
|
|
197
|
-
seen
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
return diagnostic;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export function compactRecommendRefreshErrorDiagnostic(error) {
|
|
204
|
-
return buildErrorDiagnostic(error);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function isRetryableRecommendFilterReapplyError(error) {
|
|
208
|
-
const messages = [];
|
|
209
|
-
const seen = new Set();
|
|
210
|
-
let current = error;
|
|
211
|
-
for (let depth = 0; current && depth < 4; depth += 1) {
|
|
212
|
-
if ((typeof current === "object" || typeof current === "function") && seen.has(current)) break;
|
|
213
|
-
if (typeof current === "object" || typeof current === "function") seen.add(current);
|
|
214
|
-
if (isStaleRecommendNodeError(current)) return true;
|
|
215
|
-
messages.push(String(current?.message || current || ""));
|
|
216
|
-
current = current?.cause;
|
|
217
|
-
}
|
|
218
|
-
const message = messages.join("\n");
|
|
219
|
-
return /Recommend filter panel did not open|Recommend filter trigger was not found|Recommend filter confirm button was not found|No matching recommend filter option|Invalid (?:backend )?node(?:\s*id)?|Node with given id does not exist|No node found for given backend id/i.test(message);
|
|
220
|
-
}
|
|
93
|
+
const [singleGroup] = filterGroups;
|
|
94
|
+
if (singleGroup) {
|
|
95
|
+
return {
|
|
96
|
+
group: singleGroup.group,
|
|
97
|
+
labels: singleGroup.labels,
|
|
98
|
+
selectAllLabels: singleGroup.selectAllLabels,
|
|
99
|
+
allowUnlimited: singleGroup.allowUnlimited,
|
|
100
|
+
verifySticky: singleGroup.verifySticky
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
group: filter.group || "",
|
|
105
|
+
labels: normalizeLabels(filter.labels || filter.filterLabels || []),
|
|
106
|
+
selectAllLabels: filter.selectAllLabels !== false,
|
|
107
|
+
allowUnlimited: filter.allowUnlimited === true,
|
|
108
|
+
verifySticky: filter.verifySticky === true
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function applyRecommendFilterEnvelopeStages(filter = {}, {
|
|
113
|
+
applyCurrentCityOnly,
|
|
114
|
+
applyFilterPanel
|
|
115
|
+
} = {}) {
|
|
116
|
+
if (filter.enabled === false) {
|
|
117
|
+
return {
|
|
118
|
+
applied: false,
|
|
119
|
+
skipped: true,
|
|
120
|
+
current_city_only: null,
|
|
121
|
+
filter: null
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (typeof applyCurrentCityOnly !== "function" || typeof applyFilterPanel !== "function") {
|
|
125
|
+
throw new Error("Recommend native filter stages require location and filter-panel callbacks");
|
|
126
|
+
}
|
|
127
|
+
const currentCityOnlyResult = await applyCurrentCityOnly();
|
|
128
|
+
const filterResult = await applyFilterPanel();
|
|
129
|
+
return {
|
|
130
|
+
applied: true,
|
|
131
|
+
skipped: false,
|
|
132
|
+
current_city_only: currentCityOnlyResult,
|
|
133
|
+
filter: filterResult
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function refreshFailureReason(method = "") {
|
|
138
|
+
return method === "page_navigate" ? "page_navigate_failed" : "page_reload_failed";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function safeDiagnosticText(value, maxLength = 4000) {
|
|
142
|
+
const text = String(value ?? "");
|
|
143
|
+
return text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function safeDiagnosticStack(stack) {
|
|
147
|
+
if (!stack) return null;
|
|
148
|
+
return safeDiagnosticText(stack, 8000)
|
|
149
|
+
.split(/\r?\n/)
|
|
150
|
+
.slice(0, 12)
|
|
151
|
+
.join("\n");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function buildErrorDiagnostic(error, {
|
|
155
|
+
depth = 0,
|
|
156
|
+
seen = new Set()
|
|
157
|
+
} = {}) {
|
|
158
|
+
if (!error) return null;
|
|
159
|
+
const isRecord = typeof error === "object" || typeof error === "function";
|
|
160
|
+
if (isRecord && seen.has(error)) {
|
|
161
|
+
return {
|
|
162
|
+
name: error?.name || "Error",
|
|
163
|
+
message: safeDiagnosticText(error?.message || String(error)),
|
|
164
|
+
circular: true
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (isRecord) seen.add(error);
|
|
168
|
+
const diagnostic = {
|
|
169
|
+
name: error?.name || "Error",
|
|
170
|
+
message: safeDiagnosticText(error?.message || String(error))
|
|
171
|
+
};
|
|
172
|
+
if (error?.code !== undefined && error?.code !== null && error.code !== "") {
|
|
173
|
+
diagnostic.code = typeof error.code === "number"
|
|
174
|
+
? error.code
|
|
175
|
+
: safeDiagnosticText(error.code, 300);
|
|
176
|
+
}
|
|
177
|
+
if (error?.phase) diagnostic.phase = safeDiagnosticText(error.phase, 300);
|
|
178
|
+
if (error?.cdp_method) diagnostic.cdp_method = safeDiagnosticText(error.cdp_method, 300);
|
|
179
|
+
if (error?.cdp_at) diagnostic.cdp_at = safeDiagnosticText(error.cdp_at, 100);
|
|
180
|
+
if (Number.isInteger(error?.cdp_node_id)) diagnostic.cdp_node_id = error.cdp_node_id;
|
|
181
|
+
if (Number.isInteger(error?.cdp_backend_node_id)) {
|
|
182
|
+
diagnostic.cdp_backend_node_id = error.cdp_backend_node_id;
|
|
183
|
+
}
|
|
184
|
+
if (Number.isInteger(error?.node_id)) diagnostic.node_id = error.node_id;
|
|
185
|
+
if (Number.isInteger(error?.backend_node_id)) diagnostic.backend_node_id = error.backend_node_id;
|
|
186
|
+
if (error?.cdp_search_id) diagnostic.cdp_search_id = safeDiagnosticText(error.cdp_search_id, 300);
|
|
187
|
+
if (Array.isArray(error?.cdp_param_keys)) {
|
|
188
|
+
diagnostic.cdp_param_keys = error.cdp_param_keys
|
|
189
|
+
.slice(0, 20)
|
|
190
|
+
.map((key) => safeDiagnosticText(key, 100));
|
|
191
|
+
}
|
|
192
|
+
const stack = safeDiagnosticStack(error?.stack);
|
|
193
|
+
if (stack) diagnostic.stack = stack;
|
|
194
|
+
if (depth < 2 && error?.cause && error.cause !== error) {
|
|
195
|
+
diagnostic.cause = buildErrorDiagnostic(error.cause, {
|
|
196
|
+
depth: depth + 1,
|
|
197
|
+
seen
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return diagnostic;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function compactRecommendRefreshErrorDiagnostic(error) {
|
|
204
|
+
return buildErrorDiagnostic(error);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function isRetryableRecommendFilterReapplyError(error) {
|
|
208
|
+
const messages = [];
|
|
209
|
+
const seen = new Set();
|
|
210
|
+
let current = error;
|
|
211
|
+
for (let depth = 0; current && depth < 4; depth += 1) {
|
|
212
|
+
if ((typeof current === "object" || typeof current === "function") && seen.has(current)) break;
|
|
213
|
+
if (typeof current === "object" || typeof current === "function") seen.add(current);
|
|
214
|
+
if (isStaleRecommendNodeError(current)) return true;
|
|
215
|
+
messages.push(String(current?.message || current || ""));
|
|
216
|
+
current = current?.cause;
|
|
217
|
+
}
|
|
218
|
+
const message = messages.join("\n");
|
|
219
|
+
return /Recommend filter panel did not open|Recommend filter trigger was not found|Recommend filter confirm button was not found|No matching recommend filter option|Invalid (?:backend )?node(?:\s*id)?|Node with given id does not exist|No node found for given backend id/i.test(message);
|
|
220
|
+
}
|
|
221
221
|
|
|
222
222
|
function compactFilterReapplyError(error) {
|
|
223
223
|
return error?.message || String(error || "Recommend filter reapply failed");
|
|
@@ -239,10 +239,10 @@ function compactJobSelectionAttempt({
|
|
|
239
239
|
return {
|
|
240
240
|
ok: Boolean(ok),
|
|
241
241
|
method: "job_select",
|
|
242
|
-
reason: error ? "job_select_failed" : null,
|
|
243
|
-
error: error ? (error?.message || String(error)) : null,
|
|
244
|
-
error_diagnostic: error ? compactRecommendRefreshErrorDiagnostic(error) : null,
|
|
245
|
-
attempt,
|
|
242
|
+
reason: error ? "job_select_failed" : null,
|
|
243
|
+
error: error ? (error?.message || String(error)) : null,
|
|
244
|
+
error_diagnostic: error ? compactRecommendRefreshErrorDiagnostic(error) : null,
|
|
245
|
+
attempt,
|
|
246
246
|
iframe_document_node_id: iframeDocumentNodeId || 0,
|
|
247
247
|
selected: Boolean(selection?.selected),
|
|
248
248
|
selection_reason: selection?.reason || null,
|
|
@@ -401,10 +401,10 @@ async function selectAndConfirmRefreshFilter(client, rootState, filterOptions, {
|
|
|
401
401
|
attempts.push({
|
|
402
402
|
ok: false,
|
|
403
403
|
method: "filter_reapply",
|
|
404
|
-
reason: "filter_reapply_failed",
|
|
405
|
-
error: compactFilterReapplyError(error),
|
|
406
|
-
error_diagnostic: compactRecommendRefreshErrorDiagnostic(error),
|
|
407
|
-
attempt
|
|
404
|
+
reason: "filter_reapply_failed",
|
|
405
|
+
error: compactFilterReapplyError(error),
|
|
406
|
+
error_diagnostic: compactRecommendRefreshErrorDiagnostic(error),
|
|
407
|
+
attempt
|
|
408
408
|
});
|
|
409
409
|
if (attempt >= maxAttempts || !isRetryableRecommendFilterReapplyError(error)) {
|
|
410
410
|
break;
|
|
@@ -415,95 +415,95 @@ async function selectAndConfirmRefreshFilter(client, rootState, filterOptions, {
|
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
const wrapped = new Error(compactFilterReapplyError(lastError));
|
|
418
|
-
wrapped.cause = lastError;
|
|
419
|
-
wrapped.filter_reapply_attempts = attempts;
|
|
420
|
-
throw wrapped;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function compactCurrentCityOnlyReapplyError(error) {
|
|
424
|
-
return error?.message || String(error || "Recommend current-city-only reapply failed");
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function isRetryableRecommendCurrentCityOnlyReapplyError(error) {
|
|
428
|
-
if (isStaleRecommendNodeError(error)) return true;
|
|
429
|
-
const message = compactCurrentCityOnlyReapplyError(error);
|
|
430
|
-
return /current-city|current city|location|popover|trigger|checkbox|stale|did not (?:open|mount|close)/i.test(message)
|
|
431
|
-
&& !/unavailable/i.test(message);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
async function ensureRefreshCurrentCityOnly(client, rootState, {
|
|
435
|
-
enabled = false,
|
|
436
|
-
maxAttempts = 3,
|
|
437
|
-
retryDelayMs = 1500
|
|
438
|
-
} = {}) {
|
|
439
|
-
const attempts = [];
|
|
440
|
-
let currentRootState = rootState;
|
|
441
|
-
let lastError = null;
|
|
442
|
-
|
|
443
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
444
|
-
try {
|
|
445
|
-
const result = await ensureRecommendCurrentCityOnly(
|
|
446
|
-
client,
|
|
447
|
-
currentRootState.iframe.documentNodeId,
|
|
448
|
-
{ enabled }
|
|
449
|
-
);
|
|
450
|
-
attempts.push({
|
|
451
|
-
ok: true,
|
|
452
|
-
method: "current_city_only_reapply",
|
|
453
|
-
attempt,
|
|
454
|
-
result
|
|
455
|
-
});
|
|
456
|
-
return {
|
|
457
|
-
current_city_only: result,
|
|
458
|
-
root_state: currentRootState,
|
|
459
|
-
attempts
|
|
460
|
-
};
|
|
461
|
-
} catch (error) {
|
|
462
|
-
lastError = error;
|
|
463
|
-
attempts.push({
|
|
464
|
-
ok: false,
|
|
465
|
-
method: "current_city_only_reapply",
|
|
466
|
-
reason: "current_city_only_reapply_failed",
|
|
467
|
-
error: compactCurrentCityOnlyReapplyError(error),
|
|
468
|
-
error_diagnostic: compactRecommendRefreshErrorDiagnostic(error),
|
|
469
|
-
attempt
|
|
470
|
-
});
|
|
471
|
-
if (attempt >= maxAttempts || !isRetryableRecommendCurrentCityOnlyReapplyError(error)) {
|
|
472
|
-
break;
|
|
473
|
-
}
|
|
474
|
-
if (retryDelayMs > 0) await sleep(retryDelayMs);
|
|
475
|
-
currentRootState = await getRecommendRoots(client);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const wrapped = new Error(compactCurrentCityOnlyReapplyError(lastError));
|
|
480
|
-
wrapped.cause = lastError;
|
|
481
|
-
wrapped.current_city_only_attempts = attempts;
|
|
482
|
-
throw wrapped;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
async function applyRefreshMethod(client, method, {
|
|
486
|
-
jobLabel = "",
|
|
487
|
-
pageScope = "recommend",
|
|
488
|
-
fallbackPageScope = "recommend",
|
|
489
|
-
filter = {},
|
|
418
|
+
wrapped.cause = lastError;
|
|
419
|
+
wrapped.filter_reapply_attempts = attempts;
|
|
420
|
+
throw wrapped;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function compactCurrentCityOnlyReapplyError(error) {
|
|
424
|
+
return error?.message || String(error || "Recommend current-city-only reapply failed");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function isRetryableRecommendCurrentCityOnlyReapplyError(error) {
|
|
428
|
+
if (isStaleRecommendNodeError(error)) return true;
|
|
429
|
+
const message = compactCurrentCityOnlyReapplyError(error);
|
|
430
|
+
return /current-city|current city|location|popover|trigger|checkbox|stale|did not (?:open|mount|close)/i.test(message)
|
|
431
|
+
&& !/unavailable/i.test(message);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async function ensureRefreshCurrentCityOnly(client, rootState, {
|
|
435
|
+
enabled = false,
|
|
436
|
+
maxAttempts = 3,
|
|
437
|
+
retryDelayMs = 1500
|
|
438
|
+
} = {}) {
|
|
439
|
+
const attempts = [];
|
|
440
|
+
let currentRootState = rootState;
|
|
441
|
+
let lastError = null;
|
|
442
|
+
|
|
443
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
444
|
+
try {
|
|
445
|
+
const result = await ensureRecommendCurrentCityOnly(
|
|
446
|
+
client,
|
|
447
|
+
currentRootState.iframe.documentNodeId,
|
|
448
|
+
{ enabled }
|
|
449
|
+
);
|
|
450
|
+
attempts.push({
|
|
451
|
+
ok: true,
|
|
452
|
+
method: "current_city_only_reapply",
|
|
453
|
+
attempt,
|
|
454
|
+
result
|
|
455
|
+
});
|
|
456
|
+
return {
|
|
457
|
+
current_city_only: result,
|
|
458
|
+
root_state: currentRootState,
|
|
459
|
+
attempts
|
|
460
|
+
};
|
|
461
|
+
} catch (error) {
|
|
462
|
+
lastError = error;
|
|
463
|
+
attempts.push({
|
|
464
|
+
ok: false,
|
|
465
|
+
method: "current_city_only_reapply",
|
|
466
|
+
reason: "current_city_only_reapply_failed",
|
|
467
|
+
error: compactCurrentCityOnlyReapplyError(error),
|
|
468
|
+
error_diagnostic: compactRecommendRefreshErrorDiagnostic(error),
|
|
469
|
+
attempt
|
|
470
|
+
});
|
|
471
|
+
if (attempt >= maxAttempts || !isRetryableRecommendCurrentCityOnlyReapplyError(error)) {
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
if (retryDelayMs > 0) await sleep(retryDelayMs);
|
|
475
|
+
currentRootState = await getRecommendRoots(client);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const wrapped = new Error(compactCurrentCityOnlyReapplyError(lastError));
|
|
480
|
+
wrapped.cause = lastError;
|
|
481
|
+
wrapped.current_city_only_attempts = attempts;
|
|
482
|
+
throw wrapped;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
async function applyRefreshMethod(client, method, {
|
|
486
|
+
jobLabel = "",
|
|
487
|
+
pageScope = "recommend",
|
|
488
|
+
fallbackPageScope = "recommend",
|
|
489
|
+
filter = {},
|
|
490
490
|
targetUrl = RECOMMEND_TARGET_URL,
|
|
491
491
|
forceRecentNotView = true,
|
|
492
492
|
cardTimeoutMs = 30000,
|
|
493
493
|
reloadSettleMs = 8000
|
|
494
494
|
} = {}) {
|
|
495
|
-
const started = Date.now();
|
|
496
|
-
let currentRootState = null;
|
|
497
|
-
let jobSelection = null;
|
|
498
|
-
let jobSelectionAttempts = [];
|
|
499
|
-
let pageScopeResult = null;
|
|
500
|
-
let currentCityOnlyResult = null;
|
|
501
|
-
let currentCityOnlyAttempts = [];
|
|
502
|
-
let filterResult = null;
|
|
503
|
-
let filterReapplyAttempts = [];
|
|
504
|
-
let recoverySettle = null;
|
|
505
|
-
try {
|
|
506
|
-
if (method === "page_navigate") {
|
|
495
|
+
const started = Date.now();
|
|
496
|
+
let currentRootState = null;
|
|
497
|
+
let jobSelection = null;
|
|
498
|
+
let jobSelectionAttempts = [];
|
|
499
|
+
let pageScopeResult = null;
|
|
500
|
+
let currentCityOnlyResult = null;
|
|
501
|
+
let currentCityOnlyAttempts = [];
|
|
502
|
+
let filterResult = null;
|
|
503
|
+
let filterReapplyAttempts = [];
|
|
504
|
+
let recoverySettle = null;
|
|
505
|
+
try {
|
|
506
|
+
if (method === "page_navigate") {
|
|
507
507
|
await client.Page.navigate({ url: targetUrl || RECOMMEND_TARGET_URL });
|
|
508
508
|
} else {
|
|
509
509
|
await client.Page.reload({ ignoreCache: true });
|
|
@@ -546,84 +546,84 @@ async function applyRefreshMethod(client, method, {
|
|
|
546
546
|
settleMs: reloadSettleMs > 10000 ? 3000 : 1200,
|
|
547
547
|
timeoutMs: Math.max(10000, Math.min(cardTimeoutMs, 60000))
|
|
548
548
|
}
|
|
549
|
-
);
|
|
550
|
-
if (!pageScopeResult.selected) {
|
|
551
|
-
throw new Error(`Recommend page scope was not selected after refresh reload: ${pageScopeResult.reason || pageScope}`);
|
|
552
|
-
}
|
|
553
|
-
currentRootState = await getRecommendRoots(client);
|
|
554
|
-
const retryDelayMs = Math.max(1200, Math.min(5000, Math.floor((reloadSettleMs || 8000) / 2)));
|
|
555
|
-
const filterStages = await applyRecommendFilterEnvelopeStages(filter, {
|
|
556
|
-
applyCurrentCityOnly: async () => {
|
|
557
|
-
const selection = await ensureRefreshCurrentCityOnly(client, currentRootState, {
|
|
558
|
-
enabled: filter.currentCityOnly === true || filter.current_city_only === true,
|
|
559
|
-
retryDelayMs
|
|
560
|
-
});
|
|
561
|
-
currentCityOnlyAttempts = selection.attempts;
|
|
562
|
-
currentRootState = await getRecommendRoots(client);
|
|
563
|
-
return selection.current_city_only;
|
|
564
|
-
},
|
|
565
|
-
applyFilterPanel: async () => {
|
|
566
|
-
const selection = await selectAndConfirmRefreshFilter(
|
|
567
|
-
client,
|
|
568
|
-
currentRootState,
|
|
569
|
-
buildRecommendFilterSelectionOptions(filter, { forceRecentNotView }),
|
|
570
|
-
{ retryDelayMs }
|
|
571
|
-
);
|
|
572
|
-
filterReapplyAttempts = selection.attempts;
|
|
573
|
-
currentRootState = await getRecommendRoots(client);
|
|
574
|
-
return selection.filter;
|
|
575
|
-
}
|
|
576
|
-
});
|
|
577
|
-
currentCityOnlyResult = filterStages.current_city_only;
|
|
578
|
-
filterResult = filterStages.filter;
|
|
579
|
-
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
580
|
-
timeoutMs: cardTimeoutMs,
|
|
581
|
-
intervalMs: 500
|
|
582
|
-
});
|
|
583
|
-
if (!cardNodeIds.length) {
|
|
549
|
+
);
|
|
550
|
+
if (!pageScopeResult.selected) {
|
|
551
|
+
throw new Error(`Recommend page scope was not selected after refresh reload: ${pageScopeResult.reason || pageScope}`);
|
|
552
|
+
}
|
|
553
|
+
currentRootState = await getRecommendRoots(client);
|
|
554
|
+
const retryDelayMs = Math.max(1200, Math.min(5000, Math.floor((reloadSettleMs || 8000) / 2)));
|
|
555
|
+
const filterStages = await applyRecommendFilterEnvelopeStages(filter, {
|
|
556
|
+
applyCurrentCityOnly: async () => {
|
|
557
|
+
const selection = await ensureRefreshCurrentCityOnly(client, currentRootState, {
|
|
558
|
+
enabled: filter.currentCityOnly === true || filter.current_city_only === true,
|
|
559
|
+
retryDelayMs
|
|
560
|
+
});
|
|
561
|
+
currentCityOnlyAttempts = selection.attempts;
|
|
562
|
+
currentRootState = await getRecommendRoots(client);
|
|
563
|
+
return selection.current_city_only;
|
|
564
|
+
},
|
|
565
|
+
applyFilterPanel: async () => {
|
|
566
|
+
const selection = await selectAndConfirmRefreshFilter(
|
|
567
|
+
client,
|
|
568
|
+
currentRootState,
|
|
569
|
+
buildRecommendFilterSelectionOptions(filter, { forceRecentNotView }),
|
|
570
|
+
{ retryDelayMs }
|
|
571
|
+
);
|
|
572
|
+
filterReapplyAttempts = selection.attempts;
|
|
573
|
+
currentRootState = await getRecommendRoots(client);
|
|
574
|
+
return selection.filter;
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
currentCityOnlyResult = filterStages.current_city_only;
|
|
578
|
+
filterResult = filterStages.filter;
|
|
579
|
+
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
580
|
+
timeoutMs: cardTimeoutMs,
|
|
581
|
+
intervalMs: 500
|
|
582
|
+
});
|
|
583
|
+
if (!cardNodeIds.length) {
|
|
584
584
|
throw new Error("No recommend candidate cards were found after refresh reload");
|
|
585
585
|
}
|
|
586
586
|
return {
|
|
587
587
|
ok: true,
|
|
588
588
|
method,
|
|
589
|
-
target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
|
|
590
|
-
job_selection: jobSelection,
|
|
591
|
-
job_selection_attempts: jobSelectionAttempts,
|
|
592
|
-
recovery_settle: recoverySettle,
|
|
593
|
-
page_scope: pageScopeResult,
|
|
594
|
-
current_city_only: currentCityOnlyResult,
|
|
595
|
-
current_city_only_attempts: currentCityOnlyAttempts,
|
|
596
|
-
filter: filterResult,
|
|
597
|
-
filter_reapply_attempts: filterReapplyAttempts,
|
|
598
|
-
card_count: cardNodeIds.length,
|
|
599
|
-
root_state: currentRootState,
|
|
600
|
-
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false),
|
|
601
|
-
elapsed_ms: Date.now() - started
|
|
602
|
-
};
|
|
603
|
-
} catch (error) {
|
|
604
|
-
return {
|
|
605
|
-
ok: false,
|
|
589
|
+
target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
|
|
590
|
+
job_selection: jobSelection,
|
|
591
|
+
job_selection_attempts: jobSelectionAttempts,
|
|
592
|
+
recovery_settle: recoverySettle,
|
|
593
|
+
page_scope: pageScopeResult,
|
|
594
|
+
current_city_only: currentCityOnlyResult,
|
|
595
|
+
current_city_only_attempts: currentCityOnlyAttempts,
|
|
596
|
+
filter: filterResult,
|
|
597
|
+
filter_reapply_attempts: filterReapplyAttempts,
|
|
598
|
+
card_count: cardNodeIds.length,
|
|
599
|
+
root_state: currentRootState,
|
|
600
|
+
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false),
|
|
601
|
+
elapsed_ms: Date.now() - started
|
|
602
|
+
};
|
|
603
|
+
} catch (error) {
|
|
604
|
+
return {
|
|
605
|
+
ok: false,
|
|
606
606
|
method,
|
|
607
|
-
reason: refreshFailureReason(method),
|
|
608
|
-
error: error?.message || String(error),
|
|
609
|
-
error_diagnostic: compactRecommendRefreshErrorDiagnostic(error),
|
|
610
|
-
target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
|
|
611
|
-
job_selection: jobSelection,
|
|
612
|
-
job_selection_attempts: error?.job_selection_attempts || jobSelectionAttempts,
|
|
613
|
-
recovery_settle: error?.recovery_settle || recoverySettle,
|
|
614
|
-
page_scope: pageScopeResult,
|
|
615
|
-
current_city_only: currentCityOnlyResult,
|
|
616
|
-
current_city_only_attempts: error?.current_city_only_attempts || currentCityOnlyAttempts,
|
|
617
|
-
filter: filterResult,
|
|
618
|
-
filter_reapply_attempts: error?.filter_reapply_attempts || filterReapplyAttempts,
|
|
619
|
-
card_count: 0,
|
|
620
|
-
root_state: currentRootState,
|
|
621
|
-
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false),
|
|
622
|
-
elapsed_ms: Date.now() - started
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
607
|
+
reason: refreshFailureReason(method),
|
|
608
|
+
error: error?.message || String(error),
|
|
609
|
+
error_diagnostic: compactRecommendRefreshErrorDiagnostic(error),
|
|
610
|
+
target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
|
|
611
|
+
job_selection: jobSelection,
|
|
612
|
+
job_selection_attempts: error?.job_selection_attempts || jobSelectionAttempts,
|
|
613
|
+
recovery_settle: error?.recovery_settle || recoverySettle,
|
|
614
|
+
page_scope: pageScopeResult,
|
|
615
|
+
current_city_only: currentCityOnlyResult,
|
|
616
|
+
current_city_only_attempts: error?.current_city_only_attempts || currentCityOnlyAttempts,
|
|
617
|
+
filter: filterResult,
|
|
618
|
+
filter_reapply_attempts: error?.filter_reapply_attempts || filterReapplyAttempts,
|
|
619
|
+
card_count: 0,
|
|
620
|
+
root_state: currentRootState,
|
|
621
|
+
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false),
|
|
622
|
+
elapsed_ms: Date.now() - started
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
627
|
export async function refreshRecommendListAtEnd(client, {
|
|
628
628
|
rootState = null,
|
|
629
629
|
jobLabel = "",
|
|
@@ -645,92 +645,92 @@ export async function refreshRecommendListAtEnd(client, {
|
|
|
645
645
|
currentRootState = currentRootState || await getRecommendRoots(client);
|
|
646
646
|
const buttonResult = await clickRecommendEndRefreshButton(
|
|
647
647
|
client,
|
|
648
|
-
currentRootState.iframe.documentNodeId,
|
|
649
|
-
{ settleMs: buttonSettleMs }
|
|
650
|
-
);
|
|
651
|
-
attempts.push(buttonResult);
|
|
652
|
-
if (buttonResult.ok) {
|
|
653
|
-
let pageScopeResult = null;
|
|
654
|
-
let currentCityOnlyResult = null;
|
|
655
|
-
let currentCityOnlyAttempts = [];
|
|
656
|
-
let filterResult = null;
|
|
657
|
-
let filterReapplyAttempts = [];
|
|
658
|
-
try {
|
|
659
|
-
currentRootState = await getRecommendRoots(client);
|
|
660
|
-
pageScopeResult = await selectRecommendPageScope(
|
|
661
|
-
client,
|
|
662
|
-
currentRootState.iframe.documentNodeId,
|
|
663
|
-
{
|
|
664
|
-
pageScope,
|
|
665
|
-
fallbackScope: fallbackPageScope,
|
|
648
|
+
currentRootState.iframe.documentNodeId,
|
|
649
|
+
{ settleMs: buttonSettleMs }
|
|
650
|
+
);
|
|
651
|
+
attempts.push(buttonResult);
|
|
652
|
+
if (buttonResult.ok) {
|
|
653
|
+
let pageScopeResult = null;
|
|
654
|
+
let currentCityOnlyResult = null;
|
|
655
|
+
let currentCityOnlyAttempts = [];
|
|
656
|
+
let filterResult = null;
|
|
657
|
+
let filterReapplyAttempts = [];
|
|
658
|
+
try {
|
|
659
|
+
currentRootState = await getRecommendRoots(client);
|
|
660
|
+
pageScopeResult = await selectRecommendPageScope(
|
|
661
|
+
client,
|
|
662
|
+
currentRootState.iframe.documentNodeId,
|
|
663
|
+
{
|
|
664
|
+
pageScope,
|
|
665
|
+
fallbackScope: fallbackPageScope,
|
|
666
666
|
settleMs: buttonSettleMs > 10000 ? 3000 : 1200,
|
|
667
667
|
timeoutMs: Math.max(10000, Math.min(cardTimeoutMs, 60000))
|
|
668
668
|
}
|
|
669
|
-
);
|
|
670
|
-
if (!pageScopeResult.selected) {
|
|
671
|
-
throw new Error(`Recommend page scope was not selected after end refresh: ${pageScopeResult.reason || pageScope}`);
|
|
672
|
-
}
|
|
673
|
-
currentRootState = await getRecommendRoots(client);
|
|
674
|
-
const retryDelayMs = Math.max(1200, Math.min(5000, Math.floor((buttonSettleMs || 8000) / 2)));
|
|
675
|
-
const filterStages = await applyRecommendFilterEnvelopeStages(filter, {
|
|
676
|
-
applyCurrentCityOnly: async () => {
|
|
677
|
-
const selection = await ensureRefreshCurrentCityOnly(client, currentRootState, {
|
|
678
|
-
enabled: filter.currentCityOnly === true || filter.current_city_only === true,
|
|
679
|
-
retryDelayMs
|
|
680
|
-
});
|
|
681
|
-
currentCityOnlyAttempts = selection.attempts;
|
|
682
|
-
currentRootState = await getRecommendRoots(client);
|
|
683
|
-
return selection.current_city_only;
|
|
684
|
-
},
|
|
685
|
-
applyFilterPanel: async () => {
|
|
686
|
-
const selection = await selectAndConfirmRefreshFilter(
|
|
687
|
-
client,
|
|
688
|
-
currentRootState,
|
|
689
|
-
buildRecommendFilterSelectionOptions(filter, { forceRecentNotView }),
|
|
690
|
-
{ retryDelayMs }
|
|
691
|
-
);
|
|
692
|
-
filterReapplyAttempts = selection.attempts;
|
|
693
|
-
currentRootState = await getRecommendRoots(client);
|
|
694
|
-
return selection.filter;
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
currentCityOnlyResult = filterStages.current_city_only;
|
|
698
|
-
filterResult = filterStages.filter;
|
|
699
|
-
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
700
|
-
timeoutMs: cardTimeoutMs,
|
|
701
|
-
intervalMs: 500
|
|
702
|
-
});
|
|
703
|
-
return {
|
|
704
|
-
ok: cardNodeIds.length > 0,
|
|
705
|
-
method: "end_refresh_button",
|
|
706
|
-
attempts,
|
|
707
|
-
page_scope: pageScopeResult,
|
|
708
|
-
current_city_only: currentCityOnlyResult,
|
|
709
|
-
current_city_only_attempts: currentCityOnlyAttempts,
|
|
710
|
-
filter: filterResult,
|
|
711
|
-
filter_reapply_attempts: filterReapplyAttempts,
|
|
712
|
-
card_count: cardNodeIds.length,
|
|
713
|
-
root_state: currentRootState,
|
|
714
|
-
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false)
|
|
715
|
-
};
|
|
716
|
-
} catch (error) {
|
|
717
|
-
attempts.push({
|
|
718
|
-
ok: false,
|
|
719
|
-
method: "end_refresh_button_after_click",
|
|
720
|
-
reason: "end_refresh_reapply_failed",
|
|
721
|
-
error: error?.message || String(error),
|
|
722
|
-
error_diagnostic: compactRecommendRefreshErrorDiagnostic(error),
|
|
723
|
-
page_scope: pageScopeResult,
|
|
724
|
-
current_city_only: currentCityOnlyResult,
|
|
725
|
-
current_city_only_attempts: error?.current_city_only_attempts || currentCityOnlyAttempts,
|
|
726
|
-
filter: filterResult,
|
|
727
|
-
filter_reapply_attempts: error?.filter_reapply_attempts || filterReapplyAttempts,
|
|
728
|
-
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false)
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
669
|
+
);
|
|
670
|
+
if (!pageScopeResult.selected) {
|
|
671
|
+
throw new Error(`Recommend page scope was not selected after end refresh: ${pageScopeResult.reason || pageScope}`);
|
|
672
|
+
}
|
|
673
|
+
currentRootState = await getRecommendRoots(client);
|
|
674
|
+
const retryDelayMs = Math.max(1200, Math.min(5000, Math.floor((buttonSettleMs || 8000) / 2)));
|
|
675
|
+
const filterStages = await applyRecommendFilterEnvelopeStages(filter, {
|
|
676
|
+
applyCurrentCityOnly: async () => {
|
|
677
|
+
const selection = await ensureRefreshCurrentCityOnly(client, currentRootState, {
|
|
678
|
+
enabled: filter.currentCityOnly === true || filter.current_city_only === true,
|
|
679
|
+
retryDelayMs
|
|
680
|
+
});
|
|
681
|
+
currentCityOnlyAttempts = selection.attempts;
|
|
682
|
+
currentRootState = await getRecommendRoots(client);
|
|
683
|
+
return selection.current_city_only;
|
|
684
|
+
},
|
|
685
|
+
applyFilterPanel: async () => {
|
|
686
|
+
const selection = await selectAndConfirmRefreshFilter(
|
|
687
|
+
client,
|
|
688
|
+
currentRootState,
|
|
689
|
+
buildRecommendFilterSelectionOptions(filter, { forceRecentNotView }),
|
|
690
|
+
{ retryDelayMs }
|
|
691
|
+
);
|
|
692
|
+
filterReapplyAttempts = selection.attempts;
|
|
693
|
+
currentRootState = await getRecommendRoots(client);
|
|
694
|
+
return selection.filter;
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
currentCityOnlyResult = filterStages.current_city_only;
|
|
698
|
+
filterResult = filterStages.filter;
|
|
699
|
+
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
700
|
+
timeoutMs: cardTimeoutMs,
|
|
701
|
+
intervalMs: 500
|
|
702
|
+
});
|
|
703
|
+
return {
|
|
704
|
+
ok: cardNodeIds.length > 0,
|
|
705
|
+
method: "end_refresh_button",
|
|
706
|
+
attempts,
|
|
707
|
+
page_scope: pageScopeResult,
|
|
708
|
+
current_city_only: currentCityOnlyResult,
|
|
709
|
+
current_city_only_attempts: currentCityOnlyAttempts,
|
|
710
|
+
filter: filterResult,
|
|
711
|
+
filter_reapply_attempts: filterReapplyAttempts,
|
|
712
|
+
card_count: cardNodeIds.length,
|
|
713
|
+
root_state: currentRootState,
|
|
714
|
+
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false)
|
|
715
|
+
};
|
|
716
|
+
} catch (error) {
|
|
717
|
+
attempts.push({
|
|
718
|
+
ok: false,
|
|
719
|
+
method: "end_refresh_button_after_click",
|
|
720
|
+
reason: "end_refresh_reapply_failed",
|
|
721
|
+
error: error?.message || String(error),
|
|
722
|
+
error_diagnostic: compactRecommendRefreshErrorDiagnostic(error),
|
|
723
|
+
page_scope: pageScopeResult,
|
|
724
|
+
current_city_only: currentCityOnlyResult,
|
|
725
|
+
current_city_only_attempts: error?.current_city_only_attempts || currentCityOnlyAttempts,
|
|
726
|
+
filter: filterResult,
|
|
727
|
+
filter_reapply_attempts: error?.filter_reapply_attempts || filterReapplyAttempts,
|
|
728
|
+
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false)
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
734
|
const fallbackMethods = [];
|
|
735
735
|
if (forceNavigate && typeof client?.Page?.navigate === "function") {
|
|
736
736
|
fallbackMethods.push("page_navigate");
|
|
@@ -767,13 +767,13 @@ export async function refreshRecommendListAtEnd(client, {
|
|
|
767
767
|
return {
|
|
768
768
|
...(lastRefreshResult || {
|
|
769
769
|
ok: false,
|
|
770
|
-
method: fallbackMethods[fallbackMethods.length - 1] || "page_reload",
|
|
771
|
-
reason: "refresh_failed",
|
|
772
|
-
error: "Recommend refresh did not run",
|
|
773
|
-
card_count: 0,
|
|
774
|
-
root_state: currentRootState,
|
|
775
|
-
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false)
|
|
776
|
-
}),
|
|
777
|
-
attempts
|
|
778
|
-
};
|
|
779
|
-
}
|
|
770
|
+
method: fallbackMethods[fallbackMethods.length - 1] || "page_reload",
|
|
771
|
+
reason: "refresh_failed",
|
|
772
|
+
error: "Recommend refresh did not run",
|
|
773
|
+
card_count: 0,
|
|
774
|
+
root_state: currentRootState,
|
|
775
|
+
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false)
|
|
776
|
+
}),
|
|
777
|
+
attempts
|
|
778
|
+
};
|
|
779
|
+
}
|