@reconcrap/boss-recommend-mcp 2.0.36 → 2.0.38
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 +0 -0
- package/config/screening-config.example.json +1 -1
- package/package.json +119 -119
- package/src/core/run/index.js +7 -2
- package/src/domains/recommend/detail.js +61 -18
- package/src/domains/recommend/run-service.js +4 -0
- package/src/index.js +429 -42
- package/src/recommend-mcp.js +216 -49
- package/src/run-state.js +2 -0
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@reconcrap/boss-recommend-mcp",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"boss",
|
|
7
|
-
"mcp",
|
|
8
|
-
"codex",
|
|
9
|
-
"recruiting",
|
|
10
|
-
"boss-zhipin",
|
|
11
|
-
"recommend"
|
|
12
|
-
],
|
|
13
|
-
"type": "module",
|
|
14
|
-
"main": "src/index.js",
|
|
15
|
-
"bin": {
|
|
16
|
-
"boss-recommend-mcp": "bin/boss-recommend-mcp.js"
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"start": "node src/index.js",
|
|
20
|
-
"cli": "node src/cli.js",
|
|
21
|
-
"install:local": "node src/cli.js install",
|
|
22
|
-
"postinstall": "node scripts/postinstall.cjs",
|
|
23
|
-
"test:parser": "node src/test-parser.js",
|
|
24
|
-
"test:run-state": "node src/test-run-state.js",
|
|
25
|
-
"test:cdp-browser": "node src/test-cdp-browser.js",
|
|
26
|
-
"test:core-capture": "node src/test-core-capture.js",
|
|
27
|
-
"test:core-cv-capture-target": "node src/test-core-cv-capture-target.js",
|
|
28
|
-
"test:core-cv-acquisition": "node src/test-core-cv-acquisition.js",
|
|
29
|
-
"test:core-greet-quota": "node src/test-core-greet-quota.js",
|
|
30
|
-
"test:core-infinite-list": "node src/test-core-infinite-list.js",
|
|
31
|
-
"test:core-reporting": "node src/test-core-reporting.js",
|
|
32
|
-
"test:core-run": "node src/test-core-run.js",
|
|
33
|
-
"test:core-screening": "node src/test-core-screening.js",
|
|
34
|
-
"test:core-self-heal": "node src/test-core-self-heal.js",
|
|
35
|
-
"test:installer-migration": "node src/test-installer-migration.js",
|
|
36
|
-
"test:recommend-actions": "node src/test-recommend-actions.js",
|
|
37
|
-
"test:recommend-domain": "node src/test-recommend-domain.js",
|
|
38
|
-
"test:recommend-run-service": "node src/test-recommend-run-service.js",
|
|
39
|
-
"test:recommend-mcp": "node src/test-recommend-mcp.js",
|
|
40
|
-
"test:recruit-domain": "node src/test-recruit-domain.js",
|
|
41
|
-
"test:recruit-mcp": "node src/test-recruit-mcp.js",
|
|
42
|
-
"test:chat-domain": "node src/test-chat-domain.js",
|
|
43
|
-
"test:chat-run-service": "node src/test-chat-run-service.js",
|
|
44
|
-
"test:chat-mcp": "node src/test-chat-mcp.js",
|
|
45
|
-
"test:async": "node src/test-index-async.js",
|
|
46
|
-
"test:runtime-scan": "node src/test-runtime-scan.js",
|
|
47
|
-
"scan:runtime": "node scripts/scan-forbidden-runtime.js",
|
|
48
|
-
"scan:runtime:json": "node scripts/scan-forbidden-runtime.js --json",
|
|
49
|
-
"scan:runtime:strict": "node scripts/scan-forbidden-runtime.js --fail-on-findings",
|
|
50
|
-
"scan:runtime:package": "node scripts/scan-forbidden-runtime.js --package-surface",
|
|
51
|
-
"scan:runtime:package:strict": "node scripts/scan-forbidden-runtime.js --package-surface --fail-on-legacy",
|
|
52
|
-
"scan:legacy-boundary": "node scripts/scan-legacy-boundary.js",
|
|
53
|
-
"scan:package-boundary": "node scripts/scan-package-boundary.js",
|
|
54
|
-
"gate:phase9-static": "node scripts/phase9-static-gate.js",
|
|
55
|
-
"gate:phase10-complete": "node scripts/phase10-completion-gate.js",
|
|
56
|
-
"live:cdp-smoke": "node scripts/live-cdp-smoke.js",
|
|
57
|
-
"live:run-lifecycle": "node scripts/live-run-lifecycle-smoke.js",
|
|
58
|
-
"live:screening": "node scripts/live-screening-smoke.js",
|
|
59
|
-
"live:detail": "node scripts/live-detail-smoke.js",
|
|
60
|
-
"live:infinite-list": "node scripts/live-infinite-list-smoke.js",
|
|
61
|
-
"live:scroll-end": "node scripts/live-scroll-end-screenshot.js",
|
|
62
|
-
"live:refresh-round": "node scripts/live-refresh-round-smoke.js",
|
|
63
|
-
"live:self-heal": "node scripts/live-self-heal-smoke.js",
|
|
64
|
-
"live:recommend-actions": "node scripts/live-recommend-actions-smoke.js",
|
|
65
|
-
"live:recommend-phase10-full": "node scripts/live-recommend-phase10-full.js",
|
|
66
|
-
"live:recommend-domain": "node scripts/live-recommend-domain-smoke.js",
|
|
67
|
-
"live:recommend-run-service": "node scripts/live-recommend-run-service-smoke.js",
|
|
68
|
-
"live:recommend-mcp": "node scripts/live-recommend-mcp-smoke.js",
|
|
69
|
-
"live:search-phase10-full": "node scripts/live-search-phase10-full.js",
|
|
70
|
-
"live:recruit-domain": "node scripts/live-recruit-domain-smoke.js",
|
|
71
|
-
"live:recruit-run-service": "node scripts/live-recruit-run-service-smoke.js",
|
|
72
|
-
"live:recruit-mcp": "node scripts/live-recruit-mcp-smoke.js",
|
|
73
|
-
"live:chat-domain": "node scripts/live-chat-domain-smoke.js",
|
|
74
|
-
"live:chat-run-service": "node scripts/live-chat-run-service-smoke.js",
|
|
75
|
-
"live:chat-mcp": "node scripts/live-chat-mcp-smoke.js",
|
|
76
|
-
"live:cv-capture-target": "node scripts/live-cv-capture-target-smoke.js",
|
|
77
|
-
"live:chat-phase10-full": "node scripts/live-chat-phase10-full.js",
|
|
78
|
-
"live:chat-image-screening": "node scripts/live-chat-image-screening-smoke.js"
|
|
79
|
-
},
|
|
80
|
-
"files": [
|
|
81
|
-
"bin",
|
|
82
|
-
"config/screening-config.example.json",
|
|
83
|
-
"skills",
|
|
84
|
-
"scripts/postinstall.cjs",
|
|
85
|
-
"src/core",
|
|
86
|
-
"src/domains",
|
|
87
|
-
"src/chat-mcp.js",
|
|
88
|
-
"src/chat-runtime-config.js",
|
|
89
|
-
"src/cli.js",
|
|
90
|
-
"src/index.js",
|
|
91
|
-
"src/parser.js",
|
|
92
|
-
"src/recommend-mcp.js",
|
|
93
|
-
"src/recruit-mcp.js",
|
|
94
|
-
"src/run-state.js",
|
|
95
|
-
"README.md"
|
|
96
|
-
],
|
|
97
|
-
"dependencies": {
|
|
98
|
-
"chrome-remote-interface": "^0.33.3",
|
|
99
|
-
"sharp": "^0.34.4",
|
|
100
|
-
"ws": "^8.19.0"
|
|
101
|
-
},
|
|
102
|
-
"engines": {
|
|
103
|
-
"node": ">=18"
|
|
104
|
-
},
|
|
105
|
-
"publishConfig": {
|
|
106
|
-
"access": "public"
|
|
107
|
-
},
|
|
108
|
-
"license": "MIT",
|
|
109
|
-
"devDependencies": {},
|
|
110
|
-
"repository": {
|
|
111
|
-
"type": "git",
|
|
112
|
-
"url": "git+https://github.com/reconcrap-cpu/boss-recommend-mcp.git"
|
|
113
|
-
},
|
|
114
|
-
"author": "",
|
|
115
|
-
"bugs": {
|
|
116
|
-
"url": "https://github.com/reconcrap-cpu/boss-recommend-mcp/issues"
|
|
117
|
-
},
|
|
118
|
-
"homepage": "https://github.com/reconcrap-cpu/boss-recommend-mcp#readme"
|
|
119
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@reconcrap/boss-recommend-mcp",
|
|
3
|
+
"version": "2.0.38",
|
|
4
|
+
"description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"boss",
|
|
7
|
+
"mcp",
|
|
8
|
+
"codex",
|
|
9
|
+
"recruiting",
|
|
10
|
+
"boss-zhipin",
|
|
11
|
+
"recommend"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "src/index.js",
|
|
15
|
+
"bin": {
|
|
16
|
+
"boss-recommend-mcp": "bin/boss-recommend-mcp.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node src/index.js",
|
|
20
|
+
"cli": "node src/cli.js",
|
|
21
|
+
"install:local": "node src/cli.js install",
|
|
22
|
+
"postinstall": "node scripts/postinstall.cjs",
|
|
23
|
+
"test:parser": "node src/test-parser.js",
|
|
24
|
+
"test:run-state": "node src/test-run-state.js",
|
|
25
|
+
"test:cdp-browser": "node src/test-cdp-browser.js",
|
|
26
|
+
"test:core-capture": "node src/test-core-capture.js",
|
|
27
|
+
"test:core-cv-capture-target": "node src/test-core-cv-capture-target.js",
|
|
28
|
+
"test:core-cv-acquisition": "node src/test-core-cv-acquisition.js",
|
|
29
|
+
"test:core-greet-quota": "node src/test-core-greet-quota.js",
|
|
30
|
+
"test:core-infinite-list": "node src/test-core-infinite-list.js",
|
|
31
|
+
"test:core-reporting": "node src/test-core-reporting.js",
|
|
32
|
+
"test:core-run": "node src/test-core-run.js",
|
|
33
|
+
"test:core-screening": "node src/test-core-screening.js",
|
|
34
|
+
"test:core-self-heal": "node src/test-core-self-heal.js",
|
|
35
|
+
"test:installer-migration": "node src/test-installer-migration.js",
|
|
36
|
+
"test:recommend-actions": "node src/test-recommend-actions.js",
|
|
37
|
+
"test:recommend-domain": "node src/test-recommend-domain.js",
|
|
38
|
+
"test:recommend-run-service": "node src/test-recommend-run-service.js",
|
|
39
|
+
"test:recommend-mcp": "node src/test-recommend-mcp.js",
|
|
40
|
+
"test:recruit-domain": "node src/test-recruit-domain.js",
|
|
41
|
+
"test:recruit-mcp": "node src/test-recruit-mcp.js",
|
|
42
|
+
"test:chat-domain": "node src/test-chat-domain.js",
|
|
43
|
+
"test:chat-run-service": "node src/test-chat-run-service.js",
|
|
44
|
+
"test:chat-mcp": "node src/test-chat-mcp.js",
|
|
45
|
+
"test:async": "node src/test-index-async.js",
|
|
46
|
+
"test:runtime-scan": "node src/test-runtime-scan.js",
|
|
47
|
+
"scan:runtime": "node scripts/scan-forbidden-runtime.js",
|
|
48
|
+
"scan:runtime:json": "node scripts/scan-forbidden-runtime.js --json",
|
|
49
|
+
"scan:runtime:strict": "node scripts/scan-forbidden-runtime.js --fail-on-findings",
|
|
50
|
+
"scan:runtime:package": "node scripts/scan-forbidden-runtime.js --package-surface",
|
|
51
|
+
"scan:runtime:package:strict": "node scripts/scan-forbidden-runtime.js --package-surface --fail-on-legacy",
|
|
52
|
+
"scan:legacy-boundary": "node scripts/scan-legacy-boundary.js",
|
|
53
|
+
"scan:package-boundary": "node scripts/scan-package-boundary.js",
|
|
54
|
+
"gate:phase9-static": "node scripts/phase9-static-gate.js",
|
|
55
|
+
"gate:phase10-complete": "node scripts/phase10-completion-gate.js",
|
|
56
|
+
"live:cdp-smoke": "node scripts/live-cdp-smoke.js",
|
|
57
|
+
"live:run-lifecycle": "node scripts/live-run-lifecycle-smoke.js",
|
|
58
|
+
"live:screening": "node scripts/live-screening-smoke.js",
|
|
59
|
+
"live:detail": "node scripts/live-detail-smoke.js",
|
|
60
|
+
"live:infinite-list": "node scripts/live-infinite-list-smoke.js",
|
|
61
|
+
"live:scroll-end": "node scripts/live-scroll-end-screenshot.js",
|
|
62
|
+
"live:refresh-round": "node scripts/live-refresh-round-smoke.js",
|
|
63
|
+
"live:self-heal": "node scripts/live-self-heal-smoke.js",
|
|
64
|
+
"live:recommend-actions": "node scripts/live-recommend-actions-smoke.js",
|
|
65
|
+
"live:recommend-phase10-full": "node scripts/live-recommend-phase10-full.js",
|
|
66
|
+
"live:recommend-domain": "node scripts/live-recommend-domain-smoke.js",
|
|
67
|
+
"live:recommend-run-service": "node scripts/live-recommend-run-service-smoke.js",
|
|
68
|
+
"live:recommend-mcp": "node scripts/live-recommend-mcp-smoke.js",
|
|
69
|
+
"live:search-phase10-full": "node scripts/live-search-phase10-full.js",
|
|
70
|
+
"live:recruit-domain": "node scripts/live-recruit-domain-smoke.js",
|
|
71
|
+
"live:recruit-run-service": "node scripts/live-recruit-run-service-smoke.js",
|
|
72
|
+
"live:recruit-mcp": "node scripts/live-recruit-mcp-smoke.js",
|
|
73
|
+
"live:chat-domain": "node scripts/live-chat-domain-smoke.js",
|
|
74
|
+
"live:chat-run-service": "node scripts/live-chat-run-service-smoke.js",
|
|
75
|
+
"live:chat-mcp": "node scripts/live-chat-mcp-smoke.js",
|
|
76
|
+
"live:cv-capture-target": "node scripts/live-cv-capture-target-smoke.js",
|
|
77
|
+
"live:chat-phase10-full": "node scripts/live-chat-phase10-full.js",
|
|
78
|
+
"live:chat-image-screening": "node scripts/live-chat-image-screening-smoke.js"
|
|
79
|
+
},
|
|
80
|
+
"files": [
|
|
81
|
+
"bin",
|
|
82
|
+
"config/screening-config.example.json",
|
|
83
|
+
"skills",
|
|
84
|
+
"scripts/postinstall.cjs",
|
|
85
|
+
"src/core",
|
|
86
|
+
"src/domains",
|
|
87
|
+
"src/chat-mcp.js",
|
|
88
|
+
"src/chat-runtime-config.js",
|
|
89
|
+
"src/cli.js",
|
|
90
|
+
"src/index.js",
|
|
91
|
+
"src/parser.js",
|
|
92
|
+
"src/recommend-mcp.js",
|
|
93
|
+
"src/recruit-mcp.js",
|
|
94
|
+
"src/run-state.js",
|
|
95
|
+
"README.md"
|
|
96
|
+
],
|
|
97
|
+
"dependencies": {
|
|
98
|
+
"chrome-remote-interface": "^0.33.3",
|
|
99
|
+
"sharp": "^0.34.4",
|
|
100
|
+
"ws": "^8.19.0"
|
|
101
|
+
},
|
|
102
|
+
"engines": {
|
|
103
|
+
"node": ">=18"
|
|
104
|
+
},
|
|
105
|
+
"publishConfig": {
|
|
106
|
+
"access": "public"
|
|
107
|
+
},
|
|
108
|
+
"license": "MIT",
|
|
109
|
+
"devDependencies": {},
|
|
110
|
+
"repository": {
|
|
111
|
+
"type": "git",
|
|
112
|
+
"url": "git+https://github.com/reconcrap-cpu/boss-recommend-mcp.git"
|
|
113
|
+
},
|
|
114
|
+
"author": "",
|
|
115
|
+
"bugs": {
|
|
116
|
+
"url": "https://github.com/reconcrap-cpu/boss-recommend-mcp/issues"
|
|
117
|
+
},
|
|
118
|
+
"homepage": "https://github.com/reconcrap-cpu/boss-recommend-mcp#readme"
|
|
119
|
+
}
|
package/src/core/run/index.js
CHANGED
|
@@ -47,6 +47,7 @@ function snapshotFromEntry(entry) {
|
|
|
47
47
|
return clone({
|
|
48
48
|
runId: run.runId,
|
|
49
49
|
name: run.name,
|
|
50
|
+
pid: run.pid,
|
|
50
51
|
status: run.status,
|
|
51
52
|
phase: run.phase,
|
|
52
53
|
progress: run.progress,
|
|
@@ -205,11 +206,14 @@ export function createRunLifecycleManager({
|
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
function startRun({ name, context = {}, progress = {}, checkpoint = {}, task }) {
|
|
209
|
+
function startRun({ runId: requestedRunId = "", name, pid = process.pid, context = {}, progress = {}, checkpoint = {}, task }) {
|
|
209
210
|
if (typeof task !== "function") {
|
|
210
211
|
throw new Error("startRun requires a task function");
|
|
211
212
|
}
|
|
212
|
-
const runId = createRunId(idPrefix);
|
|
213
|
+
const runId = String(requestedRunId || "").trim() || createRunId(idPrefix);
|
|
214
|
+
if (runs.has(runId)) {
|
|
215
|
+
throw new Error(`Run already exists: ${runId}`);
|
|
216
|
+
}
|
|
213
217
|
const startedAt = now();
|
|
214
218
|
const entry = {
|
|
215
219
|
controller: new AbortController(),
|
|
@@ -219,6 +223,7 @@ export function createRunLifecycleManager({
|
|
|
219
223
|
run: {
|
|
220
224
|
runId,
|
|
221
225
|
name: name || runId,
|
|
226
|
+
pid: Number.isInteger(pid) && pid > 0 ? pid : process.pid,
|
|
222
227
|
status: RUN_STATUS_QUEUED,
|
|
223
228
|
phase: "queued",
|
|
224
229
|
progress,
|
|
@@ -131,21 +131,49 @@ export async function waitForRecommendDetail(client, {
|
|
|
131
131
|
const started = Date.now();
|
|
132
132
|
let lastState = null;
|
|
133
133
|
while (Date.now() - started <= timeoutMs) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const resumeIframe = await findVisibleDetailTarget(client, rootState.roots, DETAIL_RESUME_IFRAME_SELECTORS);
|
|
137
|
-
lastState = {
|
|
138
|
-
iframe: rootState.iframe,
|
|
139
|
-
roots: rootState.roots,
|
|
140
|
-
popup,
|
|
141
|
-
resumeIframe
|
|
142
|
-
};
|
|
143
|
-
if (popup || resumeIframe) return lastState;
|
|
134
|
+
lastState = await readRecommendDetailState(client);
|
|
135
|
+
if (lastState?.popup || lastState?.resumeIframe) return lastState;
|
|
144
136
|
await sleep(intervalMs);
|
|
145
137
|
}
|
|
146
138
|
return lastState;
|
|
147
139
|
}
|
|
148
140
|
|
|
141
|
+
async function readRecommendDetailState(client) {
|
|
142
|
+
const rootState = await getRecommendRoots(client);
|
|
143
|
+
const popup = await findVisibleDetailTarget(client, rootState.roots, DETAIL_POPUP_SELECTORS);
|
|
144
|
+
const resumeIframe = await findVisibleDetailTarget(client, rootState.roots, DETAIL_RESUME_IFRAME_SELECTORS);
|
|
145
|
+
return {
|
|
146
|
+
iframe: rootState.iframe,
|
|
147
|
+
roots: rootState.roots,
|
|
148
|
+
popup,
|
|
149
|
+
resumeIframe
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function waitForRecommendDetailClosed(client, {
|
|
154
|
+
timeoutMs = 4000,
|
|
155
|
+
intervalMs = 250
|
|
156
|
+
} = {}) {
|
|
157
|
+
const started = Date.now();
|
|
158
|
+
let lastState = null;
|
|
159
|
+
while (Date.now() - started <= timeoutMs) {
|
|
160
|
+
lastState = await readRecommendDetailState(client);
|
|
161
|
+
if (!lastState?.popup && !lastState?.resumeIframe) {
|
|
162
|
+
return {
|
|
163
|
+
closed: true,
|
|
164
|
+
elapsed_ms: Date.now() - started,
|
|
165
|
+
state: lastState
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
await sleep(intervalMs);
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
closed: false,
|
|
172
|
+
elapsed_ms: Date.now() - started,
|
|
173
|
+
state: lastState
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
149
177
|
async function findVisibleDetailTarget(client, roots, selectors) {
|
|
150
178
|
for (const root of roots) {
|
|
151
179
|
if (!root?.nodeId) continue;
|
|
@@ -397,7 +425,9 @@ export async function openRecommendCardDetailWithFreshRetry(client, {
|
|
|
397
425
|
}
|
|
398
426
|
|
|
399
427
|
export async function closeRecommendDetail(client, {
|
|
400
|
-
attemptsLimit =
|
|
428
|
+
attemptsLimit = 4,
|
|
429
|
+
closeWaitMs = 5000,
|
|
430
|
+
escapeWaitMs = 3500
|
|
401
431
|
} = {}) {
|
|
402
432
|
const attempts = [];
|
|
403
433
|
for (let index = 0; index < attemptsLimit; index += 1) {
|
|
@@ -433,15 +463,21 @@ export async function closeRecommendDetail(client, {
|
|
|
433
463
|
await pressEscape(client);
|
|
434
464
|
attempts.push({ mode: "Escape-after-close-selector-error" });
|
|
435
465
|
}
|
|
436
|
-
await sleep(700);
|
|
437
466
|
} else {
|
|
438
467
|
await pressEscape(client);
|
|
439
468
|
attempts.push({ mode: "Escape" });
|
|
440
|
-
await sleep(700);
|
|
441
469
|
}
|
|
442
470
|
|
|
443
|
-
|
|
444
|
-
|
|
471
|
+
const closedAfterClick = await waitForRecommendDetailClosed(client, {
|
|
472
|
+
timeoutMs: closeWaitMs,
|
|
473
|
+
intervalMs: 250
|
|
474
|
+
});
|
|
475
|
+
attempts.push({
|
|
476
|
+
mode: "wait-closed-after-primary",
|
|
477
|
+
closed: closedAfterClick.closed,
|
|
478
|
+
elapsed_ms: closedAfterClick.elapsed_ms
|
|
479
|
+
});
|
|
480
|
+
if (closedAfterClick.closed) {
|
|
445
481
|
return {
|
|
446
482
|
closed: true,
|
|
447
483
|
attempts
|
|
@@ -450,10 +486,17 @@ export async function closeRecommendDetail(client, {
|
|
|
450
486
|
|
|
451
487
|
await pressEscape(client);
|
|
452
488
|
attempts.push({ mode: "Escape-fallback" });
|
|
453
|
-
await sleep(700);
|
|
454
489
|
|
|
455
|
-
|
|
456
|
-
|
|
490
|
+
const closedAfterEscape = await waitForRecommendDetailClosed(client, {
|
|
491
|
+
timeoutMs: escapeWaitMs,
|
|
492
|
+
intervalMs: 250
|
|
493
|
+
});
|
|
494
|
+
attempts.push({
|
|
495
|
+
mode: "wait-closed-after-escape",
|
|
496
|
+
closed: closedAfterEscape.closed,
|
|
497
|
+
elapsed_ms: closedAfterEscape.elapsed_ms
|
|
498
|
+
});
|
|
499
|
+
if (closedAfterEscape.closed) {
|
|
457
500
|
return {
|
|
458
501
|
closed: true,
|
|
459
502
|
attempts
|
|
@@ -1266,6 +1266,8 @@ export function createRecommendRunService({
|
|
|
1266
1266
|
const manager = lifecycle || createRunLifecycleManager({ idPrefix, onSnapshot });
|
|
1267
1267
|
|
|
1268
1268
|
function startRecommendRun({
|
|
1269
|
+
runId = "",
|
|
1270
|
+
pid = process.pid,
|
|
1269
1271
|
client,
|
|
1270
1272
|
targetUrl = "",
|
|
1271
1273
|
criteria = "",
|
|
@@ -1313,7 +1315,9 @@ export function createRecommendRunService({
|
|
|
1313
1315
|
const candidateLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
1314
1316
|
const normalizedDetailLimit = detailLimit == null ? null : Math.max(0, Number(detailLimit) || 0);
|
|
1315
1317
|
return manager.startRun({
|
|
1318
|
+
runId,
|
|
1316
1319
|
name,
|
|
1320
|
+
pid,
|
|
1317
1321
|
context: {
|
|
1318
1322
|
domain: "recommend",
|
|
1319
1323
|
target_url: targetUrl,
|