@reconcrap/boss-recommend-mcp 2.0.50 → 2.0.52

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 CHANGED
@@ -134,8 +134,8 @@ node src/cli.js start
134
134
 
135
135
  全局 npm 安装会自动运行 `boss-recommend-mcp install`。该安装器会在 Windows 和 macOS 上自动检测 Trae / Trae CN / OpenClaw / QClaw 的常见配置目录:
136
136
 
137
- - Windows: `%APPDATA%\Trae*\User\mcp.json`、`%USERPROFILE%\.trae*\mcp.json`、`%USERPROFILE%\.openclaw\mcp.json`、`%APPDATA%\OpenClaw\User\mcp.json`、`%USERPROFILE%\.qclaw\openclaw.json`
138
- - macOS: `~/Library/Application Support/Trae*/User/mcp.json`、`~/.trae*/mcp.json`、`~/.openclaw/mcp.json`、`~/Library/Application Support/OpenClaw/User/mcp.json`
137
+ - Windows: `%APPDATA%\Trae*\User\mcp.json`、`%USERPROFILE%\.trae*\mcp.json`、`%USERPROFILE%\.openclaw\mcp.json`、`%USERPROFILE%\.openclaw\openclaw.json`、`%APPDATA%\OpenClaw\User\mcp.json`、`%USERPROFILE%\.qclaw\openclaw.json`
138
+ - macOS: `~/Library/Application Support/Trae*/User/mcp.json`、`~/.trae*/mcp.json`、`~/.openclaw/mcp.json`、`~/.openclaw/openclaw.json`、`~/Library/Application Support/OpenClaw/User/mcp.json`
139
139
 
140
140
  如果检测到 legacy Boss server entries,installer 会:
141
141
 
File without changes
@@ -1,4 +1,4 @@
1
- {
1
+ {
2
2
  "baseUrl": "https://api.openai.com/v1",
3
3
  "apiKey": "replace-with-openai-api-key",
4
4
  "model": "gpt-4.1-mini",
package/package.json CHANGED
@@ -1,120 +1,120 @@
1
- {
2
- "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "2.0.50",
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:recommend-recovery": "node scripts/live-recommend-recovery-smoke.js",
64
- "live:self-heal": "node scripts/live-self-heal-smoke.js",
65
- "live:recommend-actions": "node scripts/live-recommend-actions-smoke.js",
66
- "live:recommend-phase10-full": "node scripts/live-recommend-phase10-full.js",
67
- "live:recommend-domain": "node scripts/live-recommend-domain-smoke.js",
68
- "live:recommend-run-service": "node scripts/live-recommend-run-service-smoke.js",
69
- "live:recommend-mcp": "node scripts/live-recommend-mcp-smoke.js",
70
- "live:search-phase10-full": "node scripts/live-search-phase10-full.js",
71
- "live:recruit-domain": "node scripts/live-recruit-domain-smoke.js",
72
- "live:recruit-run-service": "node scripts/live-recruit-run-service-smoke.js",
73
- "live:recruit-mcp": "node scripts/live-recruit-mcp-smoke.js",
74
- "live:chat-domain": "node scripts/live-chat-domain-smoke.js",
75
- "live:chat-run-service": "node scripts/live-chat-run-service-smoke.js",
76
- "live:chat-mcp": "node scripts/live-chat-mcp-smoke.js",
77
- "live:cv-capture-target": "node scripts/live-cv-capture-target-smoke.js",
78
- "live:chat-phase10-full": "node scripts/live-chat-phase10-full.js",
79
- "live:chat-image-screening": "node scripts/live-chat-image-screening-smoke.js"
80
- },
81
- "files": [
82
- "bin",
83
- "config/screening-config.example.json",
84
- "skills",
85
- "scripts/postinstall.cjs",
86
- "src/core",
87
- "src/domains",
88
- "src/chat-mcp.js",
89
- "src/chat-runtime-config.js",
90
- "src/cli.js",
91
- "src/index.js",
92
- "src/parser.js",
93
- "src/recommend-mcp.js",
94
- "src/recruit-mcp.js",
95
- "src/run-state.js",
96
- "README.md"
97
- ],
98
- "dependencies": {
99
- "chrome-remote-interface": "^0.33.3",
100
- "sharp": "^0.34.4",
101
- "ws": "^8.19.0"
102
- },
103
- "engines": {
104
- "node": ">=18"
105
- },
106
- "publishConfig": {
107
- "access": "public"
108
- },
109
- "license": "MIT",
110
- "devDependencies": {},
111
- "repository": {
112
- "type": "git",
113
- "url": "git+https://github.com/reconcrap-cpu/boss-recommend-mcp.git"
114
- },
115
- "author": "",
116
- "bugs": {
117
- "url": "https://github.com/reconcrap-cpu/boss-recommend-mcp/issues"
118
- },
119
- "homepage": "https://github.com/reconcrap-cpu/boss-recommend-mcp#readme"
120
- }
1
+ {
2
+ "name": "@reconcrap/boss-recommend-mcp",
3
+ "version": "2.0.52",
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:recommend-recovery": "node scripts/live-recommend-recovery-smoke.js",
64
+ "live:self-heal": "node scripts/live-self-heal-smoke.js",
65
+ "live:recommend-actions": "node scripts/live-recommend-actions-smoke.js",
66
+ "live:recommend-phase10-full": "node scripts/live-recommend-phase10-full.js",
67
+ "live:recommend-domain": "node scripts/live-recommend-domain-smoke.js",
68
+ "live:recommend-run-service": "node scripts/live-recommend-run-service-smoke.js",
69
+ "live:recommend-mcp": "node scripts/live-recommend-mcp-smoke.js",
70
+ "live:search-phase10-full": "node scripts/live-search-phase10-full.js",
71
+ "live:recruit-domain": "node scripts/live-recruit-domain-smoke.js",
72
+ "live:recruit-run-service": "node scripts/live-recruit-run-service-smoke.js",
73
+ "live:recruit-mcp": "node scripts/live-recruit-mcp-smoke.js",
74
+ "live:chat-domain": "node scripts/live-chat-domain-smoke.js",
75
+ "live:chat-run-service": "node scripts/live-chat-run-service-smoke.js",
76
+ "live:chat-mcp": "node scripts/live-chat-mcp-smoke.js",
77
+ "live:cv-capture-target": "node scripts/live-cv-capture-target-smoke.js",
78
+ "live:chat-phase10-full": "node scripts/live-chat-phase10-full.js",
79
+ "live:chat-image-screening": "node scripts/live-chat-image-screening-smoke.js"
80
+ },
81
+ "files": [
82
+ "bin",
83
+ "config/screening-config.example.json",
84
+ "skills",
85
+ "scripts/postinstall.cjs",
86
+ "src/core",
87
+ "src/domains",
88
+ "src/chat-mcp.js",
89
+ "src/chat-runtime-config.js",
90
+ "src/cli.js",
91
+ "src/index.js",
92
+ "src/parser.js",
93
+ "src/recommend-mcp.js",
94
+ "src/recruit-mcp.js",
95
+ "src/run-state.js",
96
+ "README.md"
97
+ ],
98
+ "dependencies": {
99
+ "chrome-remote-interface": "^0.33.3",
100
+ "sharp": "^0.34.4",
101
+ "ws": "^8.19.0"
102
+ },
103
+ "engines": {
104
+ "node": ">=18"
105
+ },
106
+ "publishConfig": {
107
+ "access": "public"
108
+ },
109
+ "license": "MIT",
110
+ "devDependencies": {},
111
+ "repository": {
112
+ "type": "git",
113
+ "url": "git+https://github.com/reconcrap-cpu/boss-recommend-mcp.git"
114
+ },
115
+ "author": "",
116
+ "bugs": {
117
+ "url": "https://github.com/reconcrap-cpu/boss-recommend-mcp/issues"
118
+ },
119
+ "homepage": "https://github.com/reconcrap-cpu/boss-recommend-mcp#readme"
120
+ }
package/src/cli.js CHANGED
@@ -7,10 +7,10 @@ import { createRequire } from "node:module";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import {
9
9
  assertNoForbiddenCdpCalls,
10
- buildBossChromeLaunchArgs,
11
10
  bringPageToFront,
12
11
  connectToChromeTarget,
13
12
  enableDomains,
13
+ ensureChromeDebugPort,
14
14
  getDocumentRoot,
15
15
  querySelector,
16
16
  sleep as sleepMs
@@ -88,6 +88,10 @@ function getPackageVersion() {
88
88
  }
89
89
 
90
90
  const packageVersion = getPackageVersion();
91
+ const detachedRecommendMcpEnv = {
92
+ BOSS_RECOMMEND_CDP_DETACHED: "1",
93
+ BOSS_RECOMMEND_RUN_HEARTBEAT_MS: "10000"
94
+ };
91
95
 
92
96
  function isInstalledPackageRoot(rootPath = packageRoot) {
93
97
  const normalized = path.resolve(String(rootPath || ""))
@@ -726,6 +730,25 @@ function parseMcpClientTargets(rawValue) {
726
730
  return unique;
727
731
  }
728
732
 
733
+ function isPlainObject(value) {
734
+ return value && typeof value === "object" && !Array.isArray(value);
735
+ }
736
+
737
+ function shouldDefaultRecommendDetachedMcpEnv(options = {}) {
738
+ const client = normalizeMcpClientName(options.client);
739
+ const agent = normalizeAgentName(options.agent);
740
+ return client === "openclaw"
741
+ || client === "qclaw"
742
+ || agent === "openclaw"
743
+ || agent === "qclaw";
744
+ }
745
+
746
+ function getDefaultMcpEnv(options = {}) {
747
+ return shouldDefaultRecommendDetachedMcpEnv(options)
748
+ ? { ...detachedRecommendMcpEnv }
749
+ : {};
750
+ }
751
+
729
752
  function getAgentConfigOutputDir(options = {}) {
730
753
  if (typeof options["output-dir"] === "string" && options["output-dir"].trim()) {
731
754
  return path.resolve(options["output-dir"]);
@@ -745,12 +768,29 @@ function buildMcpLaunchConfig(options = {}) {
745
768
  ? ["start"]
746
769
  : buildDefaultMcpArgs(options);
747
770
  const launchConfig = { command, args: launchArgs };
748
- if (env && typeof env === "object" && !Array.isArray(env) && Object.keys(env).length > 0) {
749
- launchConfig.env = env;
771
+ const mergedEnv = {
772
+ ...getDefaultMcpEnv(options),
773
+ ...(isPlainObject(env) ? env : {})
774
+ };
775
+ if (Object.keys(mergedEnv).length > 0) {
776
+ launchConfig.env = mergedEnv;
750
777
  }
751
778
  return launchConfig;
752
779
  }
753
780
 
781
+ function mergeExistingMcpEntryEnv(existingEntry, launchConfig) {
782
+ if (!isPlainObject(existingEntry?.env) || !isPlainObject(launchConfig)) {
783
+ return launchConfig;
784
+ }
785
+ return {
786
+ ...launchConfig,
787
+ env: {
788
+ ...existingEntry.env,
789
+ ...(isPlainObject(launchConfig.env) ? launchConfig.env : {})
790
+ }
791
+ };
792
+ }
793
+
754
794
  function buildMcpConfigFileContent(options = {}) {
755
795
  const serverName = typeof options["server-name"] === "string" && options["server-name"].trim()
756
796
  ? options["server-name"].trim()
@@ -872,7 +912,7 @@ function getKnownExternalMcpConfigPathsByAgent() {
872
912
  trae: [...traeConfigPaths, path.join(home, ".trae", "mcp.json"), path.join(home, ".trae-cn", "mcp.json")],
873
913
  "trae-cn": [...traeConfigPaths, path.join(home, ".trae-cn", "mcp.json"), path.join(home, ".trae", "mcp.json")],
874
914
  claude: [path.join(home, ".claude", "mcp.json")],
875
- openclaw: [path.join(home, ".openclaw", "mcp.json"), ...openClawConfigPaths],
915
+ openclaw: [path.join(home, ".openclaw", "mcp.json"), path.join(home, ".openclaw", "openclaw.json"), ...openClawConfigPaths],
876
916
  qclaw: [path.join(home, ".qclaw", "openclaw.json")]
877
917
  };
878
918
  }
@@ -920,9 +960,12 @@ function mergeMcpServerConfigFile(filePath, options = {}) {
920
960
  const nextConfig = buildMcpConfigFileContent({ ...options, client: useQClawShape ? "qclaw" : options.client });
921
961
  const nextServers = useQClawShape ? nextConfig.mcp?.servers : nextConfig.mcpServers;
922
962
  const serverName = Object.keys(nextServers || {})[0] || defaultMcpServerName;
923
- const launchConfig = nextServers?.[serverName] || buildMcpLaunchConfig(options);
924
963
  const existingServers = getMcpServersFromConfig(current, useQClawShape);
925
964
  const existingEntry = existingServers[serverName];
965
+ const launchConfig = mergeExistingMcpEntryEnv(
966
+ existingEntry,
967
+ nextServers?.[serverName] || buildMcpLaunchConfig(options)
968
+ );
926
969
  const retainedServers = {};
927
970
  const migratedLegacyServers = [];
928
971
  for (const [name, config] of Object.entries(existingServers)) {
@@ -2209,51 +2252,38 @@ async function launchChrome(options = {}) {
2209
2252
  const port = parsePositivePort(options.port) || parsePositivePort(process.env.BOSS_RECOMMEND_CHROME_PORT) || 9222;
2210
2253
  process.env.BOSS_RECOMMEND_CHROME_PORT = String(port);
2211
2254
  const timing = getLaunchChromeTiming(options);
2212
-
2213
- const initialState = await inspectBossRecommendPageStateCdp(port, {
2214
- timeoutMs: timing.initialTimeoutMs,
2215
- pollMs: timing.pollMs
2216
- });
2217
- if (initialState.state !== "DEBUG_PORT_UNREACHABLE") {
2218
- console.log(`Reusing existing Chrome debug instance on port ${port}`);
2219
- const pageState = await ensureBossRecommendPageReadyCdp(port, {
2220
- attempts: 2,
2221
- inspectTimeoutMs: timing.inspectTimeoutMs,
2222
- pollMs: timing.pollMs,
2223
- settleMs: timing.settleMs
2255
+ const userDataDir = getChromeUserDataDir(port);
2256
+ let chromeGuard = null;
2257
+ try {
2258
+ chromeGuard = await ensureChromeDebugPort({
2259
+ port,
2260
+ url: bossUrl,
2261
+ slowLive: Boolean(options["slow-live"] || options.slowLive),
2262
+ launchIfMissing: true,
2263
+ userDataDir
2224
2264
  });
2225
- if (pageState.ok) {
2226
- console.log("Boss recommend page is ready.");
2227
- const frontResult = await bringBossRecommendTabToFrontCdp(port);
2228
- if (frontResult.ok) {
2229
- console.log(`CDP methods: ${frontResult.method_log.join(", ") || "none"}`);
2230
- }
2231
- } else {
2232
- console.log(pageState.page_state?.message || "Boss recommend page is not ready.");
2265
+ } catch (error) {
2266
+ console.error(error?.message || String(error || "Chrome launch failed"));
2267
+ if (error?.chrome_guard) {
2268
+ console.error(JSON.stringify(error.chrome_guard, null, 2));
2233
2269
  }
2234
- return;
2235
- }
2236
-
2237
- const chromePath = getChromeExecutable();
2238
- if (!chromePath) {
2239
- console.error("Chrome executable not found. Set BOSS_RECOMMEND_CHROME_PATH or install Google Chrome.");
2240
2270
  process.exitCode = 1;
2241
2271
  return;
2242
2272
  }
2243
2273
 
2244
- const userDataDir = getChromeUserDataDir(port);
2245
- const args = buildBossChromeLaunchArgs({ port, userDataDir, url: bossUrl });
2246
- const child = spawn(chromePath, args, {
2247
- detached: true,
2248
- stdio: "ignore",
2249
- windowsHide: false
2250
- });
2251
- child.unref();
2252
- console.log(`Chrome launched with remote debugging port ${port}`);
2253
- console.log(`User data dir: ${userDataDir}`);
2254
- await sleepMs(timing.settleMs + 1200);
2274
+ if (chromeGuard.replaced) {
2275
+ console.log(`Replaced Chrome debug instance on port ${port} because required flags were missing: ${chromeGuard.missing_flags.join(", ")}`);
2276
+ } else if (chromeGuard.launched) {
2277
+ console.log(`Chrome launched with remote debugging port ${port}`);
2278
+ } else {
2279
+ console.log(`Reusing existing Chrome debug instance on port ${port} with required flags`);
2280
+ }
2281
+ console.log(`User data dir: ${chromeGuard.user_data_dir || userDataDir}`);
2282
+ if (chromeGuard.launched || chromeGuard.replaced) {
2283
+ await sleepMs(timing.settleMs + 1200);
2284
+ }
2255
2285
  const pageState = await ensureBossRecommendPageReadyCdp(port, {
2256
- attempts: 6,
2286
+ attempts: chromeGuard.launched || chromeGuard.replaced ? 6 : 2,
2257
2287
  inspectTimeoutMs: timing.inspectTimeoutMs,
2258
2288
  pollMs: timing.pollMs,
2259
2289
  settleMs: timing.settleMs
@@ -2382,15 +2412,30 @@ async function printDoctor(options = {}) {
2382
2412
  const configResolution = getBossScreenConfigResolution(workspaceRoot);
2383
2413
  const calibrationResolution = getFeaturedCalibrationResolutionLocal(workspaceRoot);
2384
2414
  const timing = getLaunchChromeTiming(options);
2415
+ const slowLive = Boolean(options["slow-live"] || options.slowLive);
2416
+ let chromeGuard = null;
2417
+ let chromeGuardError = null;
2418
+ try {
2419
+ chromeGuard = await ensureChromeDebugPort({
2420
+ port,
2421
+ url: bossUrl,
2422
+ slowLive,
2423
+ launchIfMissing: true,
2424
+ userDataDir: getChromeUserDataDir(port)
2425
+ });
2426
+ } catch (error) {
2427
+ chromeGuardError = error;
2428
+ chromeGuard = error?.chrome_guard || null;
2429
+ }
2385
2430
  let pageState = await inspectBossRecommendPageStateCdp(port, {
2386
- timeoutMs: options["slow-live"] || options.slowLive ? timing.initialTimeoutMs : 2000,
2387
- pollMs: options["slow-live"] || options.slowLive ? timing.pollMs : 500
2431
+ timeoutMs: slowLive ? timing.initialTimeoutMs : 2000,
2432
+ pollMs: slowLive ? timing.pollMs : 500
2388
2433
  });
2389
2434
  if (pageState.state === "RECOMMEND_READY") {
2390
2435
  pageState = await verifyRecommendPageStableCdp(port, {
2391
- settleMs: options["slow-live"] || options.slowLive ? timing.settleMs : 800,
2392
- recheckTimeoutMs: options["slow-live"] || options.slowLive ? timing.inspectTimeoutMs : 3000,
2393
- pollMs: options["slow-live"] || options.slowLive ? timing.pollMs : 500
2436
+ settleMs: slowLive ? timing.settleMs : 800,
2437
+ recheckTimeoutMs: slowLive ? timing.inspectTimeoutMs : 3000,
2438
+ pollMs: slowLive ? timing.pollMs : 500
2394
2439
  });
2395
2440
  }
2396
2441
  const resolvedConfigPath = configResolution.resolved_path || configResolution.writable_path;
@@ -2407,6 +2452,28 @@ async function printDoctor(options = {}) {
2407
2452
  ? `检测到配置文件(resolved_path):${resolvedConfigPath}`
2408
2453
  : "用户配置不存在(可通过 `boss-recommend-mcp init-config` 创建模板,或 `boss-recommend-mcp config set` 写入真实值)"
2409
2454
  });
2455
+ const requiredFlags = chromeGuard?.required_flags || [];
2456
+ const missingFlags = chromeGuard?.missing_flags || [];
2457
+ const chromeFlagsOk = Boolean(chromeGuard && !chromeGuardError && chromeGuard.required_flags_ok);
2458
+ checks.push({
2459
+ key: "chrome_required_flags",
2460
+ ok: chromeFlagsOk,
2461
+ path: `http://localhost:${port}`,
2462
+ required_flags: requiredFlags,
2463
+ missing_flags: missingFlags,
2464
+ replaced: Boolean(chromeGuard?.replaced),
2465
+ close_method: chromeGuard?.close_method || null,
2466
+ relaunch: chromeGuard?.relaunch || null,
2467
+ message: chromeFlagsOk
2468
+ ? chromeGuard?.replaced
2469
+ ? `Chrome 调试端口 ${port} 原实例缺少必需 flags,已自动关闭并用正确 flags 重新启动。`
2470
+ : chromeGuard?.launched
2471
+ ? `Chrome 调试端口 ${port} 已用必需 flags 启动。`
2472
+ : `Chrome 调试端口 ${port} 已确认包含必需 flags。`
2473
+ : chromeGuardError
2474
+ ? `Chrome 必需 flags 检查失败:${chromeGuardError.message}`
2475
+ : `Chrome 调试端口 ${port} 未确认包含必需 flags。`
2476
+ });
2410
2477
  checks.push({
2411
2478
  key: "chrome_debug_port",
2412
2479
  ok: pageState.state !== "DEBUG_PORT_UNREACHABLE",