@reconcrap/boss-recommend-mcp 2.0.2 → 2.0.4

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.
@@ -4,8 +4,13 @@ import process from "node:process";
4
4
  import {
5
5
  assertNoForbiddenCdpCalls,
6
6
  bringPageToFront,
7
- connectToChromeTarget,
7
+ connectToChromeTargetOrOpen,
8
+ createBossLoginRequiredError,
9
+ detectBossLoginState,
8
10
  enableDomains,
11
+ getMainFrameUrl,
12
+ isBossLoginUrl,
13
+ waitForMainFrameUrl,
9
14
  sleep
10
15
  } from "./core/browser/index.js";
11
16
  import {
@@ -39,6 +44,10 @@ import {
39
44
  parseRecommendInstruction
40
45
  } from "./parser.js";
41
46
  import { getRunsDir } from "./run-state.js";
47
+ import {
48
+ resolveBossConfiguredOutputDir,
49
+ resolveBossScreeningConfig
50
+ } from "./chat-runtime-config.js";
42
51
 
43
52
  const DEFAULT_RECOMMEND_HOST = "127.0.0.1";
44
53
  const DEFAULT_RECOMMEND_PORT = 9222;
@@ -110,12 +119,14 @@ function getRecommendRunArtifacts(runId) {
110
119
  const normalized = normalizeRunId(runId);
111
120
  if (!normalized) return null;
112
121
  const runsDir = getRunsDir();
122
+ const outputDir = resolveBossConfiguredOutputDir("", runsDir);
113
123
  return {
114
124
  runs_dir: runsDir,
125
+ output_dir: outputDir,
115
126
  run_state_path: path.join(runsDir, `${normalized}.json`),
116
127
  checkpoint_path: path.join(runsDir, `${normalized}.checkpoint.json`),
117
- output_csv: path.join(runsDir, `${normalized}.results.csv`),
118
- report_json: path.join(runsDir, `${normalized}.report.json`)
128
+ output_csv: path.join(outputDir, `${normalized}.results.csv`),
129
+ report_json: path.join(outputDir, `${normalized}.report.json`)
119
130
  };
120
131
  }
121
132
 
@@ -479,8 +490,12 @@ async function readRecommendJobOptionsFromSession(session) {
479
490
  }
480
491
 
481
492
  export async function listRecommendJobsTool({ workspaceRoot = "", args = {} } = {}) {
493
+ const configResolution = resolveBossScreeningConfig(workspaceRoot);
482
494
  const host = normalizeText(args.host) || DEFAULT_RECOMMEND_HOST;
483
- const port = parsePositiveInteger(args.port, DEFAULT_RECOMMEND_PORT);
495
+ const port = parsePositiveInteger(
496
+ args.port,
497
+ configResolution.ok ? configResolution.config.debugPort : DEFAULT_RECOMMEND_PORT
498
+ );
484
499
  const targetUrlIncludes = normalizeText(args.target_url_includes) || RECOMMEND_TARGET_URL;
485
500
  const allowNavigate = args.allow_navigate !== false;
486
501
  const slowLive = args.slow_live === true;
@@ -526,27 +541,36 @@ export async function listRecommendJobsTool({ workspaceRoot = "", args = {} } =
526
541
  host,
527
542
  port,
528
543
  target_url: session.navigation?.url || session.target?.url || RECOMMEND_TARGET_URL,
529
- target_id: session.target?.id || null
544
+ target_id: session.target?.id || null,
545
+ auto_launch: session.chrome || null
530
546
  },
531
547
  method_summary: methodSummary(session.methodLog || []),
532
548
  method_log: session.methodLog || []
533
549
  };
534
550
  } catch (error) {
535
551
  const methodLog = session?.methodLog || [];
552
+ const loginRequired = error?.code === "BOSS_LOGIN_REQUIRED";
536
553
  return {
537
554
  status: "FAILED",
538
555
  stage: "recommend_job_list",
539
556
  cdp_only: true,
540
557
  runtime_evaluate_used: methodLog.some((entry) => String(entry?.method || entry).startsWith("Runtime.")),
541
558
  error: {
542
- code: "RECOMMEND_JOB_LIST_FAILED",
559
+ code: loginRequired ? "BOSS_LOGIN_REQUIRED" : "RECOMMEND_JOB_LIST_FAILED",
543
560
  message: error?.message || "Failed to read recommend job list",
561
+ requires_login: Boolean(error?.requires_login),
562
+ login_url: error?.login_url || null,
563
+ login_detection: error?.login_detection || null,
564
+ current_url: error?.current_url || null,
565
+ target_url: error?.target_url || RECOMMEND_TARGET_URL,
566
+ chrome: error?.chrome || null,
544
567
  retryable: true
545
568
  },
546
569
  chrome: {
547
570
  host,
548
571
  port,
549
- target_url: targetUrlIncludes
572
+ target_url: targetUrlIncludes,
573
+ auto_launch: error?.chrome || session?.chrome || null
550
574
  },
551
575
  method_summary: methodSummary(methodLog),
552
576
  method_log: methodLog
@@ -585,6 +609,14 @@ async function waitForHealthyRecommend(client, config, {
585
609
  const started = Date.now();
586
610
  let lastCheck = null;
587
611
  while (Date.now() - started <= timeoutMs) {
612
+ const loginDetection = await detectBossLoginState(client).catch(() => null);
613
+ if (loginDetection?.requires_login) {
614
+ return {
615
+ status: "login_required",
616
+ summary: "Boss login is required",
617
+ loginDetection
618
+ };
619
+ }
588
620
  const roots = await resolveRecommendSelfHealRoots(client, config);
589
621
  lastCheck = await runSelfHealCheck({
590
622
  client,
@@ -610,24 +642,18 @@ async function connectRecommendChromeSession({
610
642
  allowNavigate = true,
611
643
  slowLive = false
612
644
  } = {}) {
613
- let session;
614
- try {
615
- session = await connectToChromeTarget({
616
- host,
617
- port,
618
- targetUrlIncludes
619
- });
620
- } catch (error) {
621
- if (!allowNavigate) throw error;
622
- session = await connectToChromeTarget({
623
- host,
624
- port,
625
- targetPredicate: (target) => (
626
- target?.type === "page"
627
- && String(target?.url || "").includes("zhipin.com/web/chat")
628
- )
629
- });
630
- }
645
+ const session = await connectToChromeTargetOrOpen({
646
+ host,
647
+ port,
648
+ targetUrlIncludes,
649
+ targetUrl: RECOMMEND_TARGET_URL,
650
+ allowNavigate,
651
+ slowLive,
652
+ fallbackTargetPredicate: (target) => (
653
+ target?.type === "page"
654
+ && String(target?.url || "").includes("zhipin.com")
655
+ )
656
+ });
631
657
 
632
658
  const { client, target } = session;
633
659
  await enableDomains(client, ["Page", "DOM", "Input", "Network", "Accessibility"]);
@@ -644,12 +670,56 @@ async function connectRecommendChromeSession({
644
670
  if (allowNavigate && shouldNavigateToRecommend(targetUrl)) {
645
671
  await client.Page.navigate({ url: RECOMMEND_TARGET_URL });
646
672
  const settleMs = slowLive ? 12000 : 5000;
647
- await sleep(settleMs);
673
+ const waited = await waitForMainFrameUrl(
674
+ client,
675
+ (url) => isBossLoginUrl(url) || !shouldNavigateToRecommend(url),
676
+ { timeoutMs: settleMs, intervalMs: 500 }
677
+ );
678
+ navigation = {
679
+ navigated: true,
680
+ url: RECOMMEND_TARGET_URL,
681
+ settle_ms: settleMs,
682
+ observed_url: waited.url || null,
683
+ observed_url_ok: waited.ok
684
+ };
685
+ }
686
+ let currentUrl = await getMainFrameUrl(client).catch(() => navigation.url || targetUrl);
687
+ if (allowNavigate && shouldNavigateToRecommend(currentUrl) && !isBossLoginUrl(currentUrl)) {
688
+ await client.Page.navigate({ url: RECOMMEND_TARGET_URL });
689
+ const settleMs = slowLive ? 12000 : 5000;
690
+ const waited = await waitForMainFrameUrl(
691
+ client,
692
+ (url) => isBossLoginUrl(url) || !shouldNavigateToRecommend(url),
693
+ { timeoutMs: settleMs, intervalMs: 500 }
694
+ );
648
695
  navigation = {
649
696
  navigated: true,
650
697
  url: RECOMMEND_TARGET_URL,
651
- settle_ms: settleMs
698
+ settle_ms: settleMs,
699
+ observed_url: waited.url || null,
700
+ observed_url_ok: waited.ok,
701
+ reason: "observed_url_mismatch"
652
702
  };
703
+ currentUrl = await getMainFrameUrl(client).catch(() => waited.url || currentUrl);
704
+ }
705
+ const loginDetection = await detectBossLoginState(client, { currentUrl }).catch(() => ({
706
+ requires_login: isBossLoginUrl(currentUrl),
707
+ reason: "login_detection_failed",
708
+ current_url: currentUrl
709
+ }));
710
+ if (loginDetection.requires_login) {
711
+ await session.close?.();
712
+ throw createBossLoginRequiredError({
713
+ domain: "recommend",
714
+ currentUrl: loginDetection.current_url || currentUrl,
715
+ targetUrl: RECOMMEND_TARGET_URL,
716
+ loginDetection,
717
+ chrome: session.chrome || null
718
+ });
719
+ }
720
+ if (shouldNavigateToRecommend(currentUrl)) {
721
+ await session.close?.();
722
+ throw new Error(`Boss recommend page did not navigate to ${RECOMMEND_TARGET_URL}; current URL: ${currentUrl || "unknown"}`);
653
723
  }
654
724
 
655
725
  const selfHealConfig = buildRecommendSelfHealConfig();
@@ -657,7 +727,33 @@ async function connectRecommendChromeSession({
657
727
  timeoutMs: slowLive ? 180000 : 90000,
658
728
  intervalMs: slowLive ? 1200 : 800
659
729
  });
730
+ if (health?.loginDetection?.requires_login) {
731
+ await session.close?.();
732
+ throw createBossLoginRequiredError({
733
+ domain: "recommend",
734
+ currentUrl: health.loginDetection.current_url || currentUrl,
735
+ targetUrl: RECOMMEND_TARGET_URL,
736
+ loginDetection: health.loginDetection,
737
+ chrome: session.chrome || null
738
+ });
739
+ }
660
740
  if (!health || health.status !== HEALTH_STATUS.HEALTHY) {
741
+ const latestUrl = await getMainFrameUrl(client).catch(() => currentUrl);
742
+ const latestLoginDetection = await detectBossLoginState(client, { currentUrl: latestUrl }).catch(() => ({
743
+ requires_login: isBossLoginUrl(latestUrl),
744
+ reason: "login_detection_failed",
745
+ current_url: latestUrl
746
+ }));
747
+ if (latestLoginDetection.requires_login) {
748
+ await session.close?.();
749
+ throw createBossLoginRequiredError({
750
+ domain: "recommend",
751
+ currentUrl: latestLoginDetection.current_url || latestUrl,
752
+ targetUrl: RECOMMEND_TARGET_URL,
753
+ loginDetection: latestLoginDetection,
754
+ chrome: session.chrome || null
755
+ });
756
+ }
661
757
  throw new Error(`Boss recommend page is not healthy: ${health?.status || "missing"}`);
662
758
  }
663
759
 
@@ -836,7 +932,7 @@ function buildRecommendFilter(parsed, args = {}) {
836
932
  return groups.length ? { filterGroups: groups } : { enabled: false };
837
933
  }
838
934
 
839
- function normalizeRecommendStartInput(args = {}, parsed) {
935
+ function normalizeRecommendStartInput(args = {}, parsed, configResolution = null) {
840
936
  const confirmation = args.confirmation || {};
841
937
  const overrides = args.overrides || {};
842
938
  const slowLive = args.slow_live === true;
@@ -846,7 +942,10 @@ function normalizeRecommendStartInput(args = {}, parsed) {
846
942
  );
847
943
  return {
848
944
  host: normalizeText(args.host) || DEFAULT_RECOMMEND_HOST,
849
- port: parsePositiveInteger(args.port, DEFAULT_RECOMMEND_PORT),
945
+ port: parsePositiveInteger(
946
+ args.port,
947
+ configResolution?.ok ? configResolution.config.debugPort : DEFAULT_RECOMMEND_PORT
948
+ ),
850
949
  targetUrlIncludes: normalizeText(args.target_url_includes) || RECOMMEND_TARGET_URL,
851
950
  allowNavigate: args.allow_navigate !== false,
852
951
  slowLive,
@@ -952,7 +1051,8 @@ async function startRecommendPipelineRunInternal(args = {}, { workspaceRoot = ""
952
1051
  const parsed = parseRecommendPipelineRequest(args);
953
1052
  const gate = evaluateRecommendPipelineGate(parsed, args);
954
1053
  if (gate) return gate;
955
- const normalized = normalizeRecommendStartInput(args, parsed);
1054
+ const configResolution = resolveBossScreeningConfig(workspaceRoot);
1055
+ const normalized = normalizeRecommendStartInput(args, parsed, configResolution);
956
1056
 
957
1057
  let session;
958
1058
  try {
@@ -964,13 +1064,21 @@ async function startRecommendPipelineRunInternal(args = {}, { workspaceRoot = ""
964
1064
  slowLive: normalized.slowLive
965
1065
  });
966
1066
  } catch (error) {
1067
+ const loginRequired = error?.code === "BOSS_LOGIN_REQUIRED";
967
1068
  return {
968
1069
  status: "FAILED",
969
1070
  error: {
970
- code: "BOSS_RECOMMEND_PAGE_NOT_READY",
1071
+ code: loginRequired ? "BOSS_LOGIN_REQUIRED" : "BOSS_RECOMMEND_PAGE_NOT_READY",
971
1072
  message: error?.message || "Boss recommend page is not ready",
1073
+ requires_login: Boolean(error?.requires_login),
1074
+ login_url: error?.login_url || null,
1075
+ login_detection: error?.login_detection || null,
1076
+ chrome: error?.chrome || null,
1077
+ current_url: error?.current_url || null,
1078
+ target_url: error?.target_url || RECOMMEND_TARGET_URL,
972
1079
  retryable: true
973
- }
1080
+ },
1081
+ chrome: error?.chrome || null
974
1082
  };
975
1083
  }
976
1084
 
@@ -1000,7 +1108,8 @@ async function startRecommendPipelineRunInternal(args = {}, { workspaceRoot = ""
1000
1108
  host: normalized.host,
1001
1109
  port: normalized.port,
1002
1110
  target_url: session.navigation?.url || session.target?.url || RECOMMEND_TARGET_URL,
1003
- target_id: session.target?.id || null
1111
+ target_id: session.target?.id || null,
1112
+ auto_launch: session.chrome || null
1004
1113
  },
1005
1114
  health: session.health || null
1006
1115
  });
@@ -4,8 +4,13 @@ import path from "node:path";
4
4
  import {
5
5
  assertNoForbiddenCdpCalls,
6
6
  bringPageToFront,
7
- connectToChromeTarget,
7
+ connectToChromeTargetOrOpen,
8
+ createBossLoginRequiredError,
9
+ detectBossLoginState,
8
10
  enableDomains,
11
+ getMainFrameUrl,
12
+ isBossLoginUrl,
13
+ waitForMainFrameUrl,
9
14
  sleep
10
15
  } from "./core/browser/index.js";
11
16
  import {
@@ -27,6 +32,10 @@ import {
27
32
  runRecruitWorkflow,
28
33
  waitForRecruitSearchControls
29
34
  } from "./domains/recruit/index.js";
35
+ import {
36
+ resolveBossConfiguredOutputDir,
37
+ resolveBossScreeningConfig
38
+ } from "./chat-runtime-config.js";
30
39
 
31
40
  const RUN_MODE_ASYNC = "async";
32
41
  const RUN_MODE_SYNC = "sync";
@@ -103,12 +112,14 @@ function getRecruitRunArtifacts(runId) {
103
112
  const normalized = normalizeRunId(runId);
104
113
  if (!normalized) return null;
105
114
  const runsDir = getRecruitRunsDir();
115
+ const outputDir = resolveBossConfiguredOutputDir("", runsDir);
106
116
  return {
107
117
  runs_dir: runsDir,
118
+ output_dir: outputDir,
108
119
  run_state_path: path.join(runsDir, `${normalized}.json`),
109
120
  checkpoint_path: path.join(runsDir, `${normalized}.checkpoint.json`),
110
- output_csv: path.join(runsDir, `${normalized}.results.csv`),
111
- report_json: path.join(runsDir, `${normalized}.report.json`)
121
+ output_csv: path.join(outputDir, `${normalized}.results.csv`),
122
+ report_json: path.join(outputDir, `${normalized}.report.json`)
112
123
  };
113
124
  }
114
125
 
@@ -586,6 +597,32 @@ function attachMethodEvidence(payload, runId) {
586
597
  };
587
598
  }
588
599
 
600
+ async function waitForRecruitSearchControlsOrLogin(client, {
601
+ timeoutMs = 90000,
602
+ intervalMs = 300
603
+ } = {}) {
604
+ const started = Date.now();
605
+ let lastControls = null;
606
+ while (Date.now() - started <= timeoutMs) {
607
+ const loginDetection = await detectBossLoginState(client).catch(() => null);
608
+ if (loginDetection?.requires_login) {
609
+ return {
610
+ ok: false,
611
+ reason: "login_required",
612
+ loginDetection
613
+ };
614
+ }
615
+ const remainingMs = Math.max(1, timeoutMs - (Date.now() - started));
616
+ lastControls = await waitForRecruitSearchControls(client, {
617
+ timeoutMs: Math.min(remainingMs, 1500),
618
+ intervalMs
619
+ });
620
+ if (lastControls.ok) return lastControls;
621
+ await sleep(intervalMs);
622
+ }
623
+ return lastControls || { ok: false, reason: "timeout" };
624
+ }
625
+
589
626
  async function connectRecruitChromeSession({
590
627
  host = DEFAULT_RECRUIT_HOST,
591
628
  port = DEFAULT_RECRUIT_PORT,
@@ -593,24 +630,18 @@ async function connectRecruitChromeSession({
593
630
  allowNavigate = true,
594
631
  slowLive = false
595
632
  } = {}) {
596
- let session;
597
- try {
598
- session = await connectToChromeTarget({
599
- host,
600
- port,
601
- targetUrlIncludes
602
- });
603
- } catch (error) {
604
- if (!allowNavigate) throw error;
605
- session = await connectToChromeTarget({
606
- host,
607
- port,
608
- targetPredicate: (target) => (
609
- target?.type === "page"
610
- && String(target?.url || "").includes("zhipin.com/web/chat")
611
- )
612
- });
613
- }
633
+ const session = await connectToChromeTargetOrOpen({
634
+ host,
635
+ port,
636
+ targetUrlIncludes,
637
+ targetUrl: RECRUIT_TARGET_URL,
638
+ allowNavigate,
639
+ slowLive,
640
+ fallbackTargetPredicate: (target) => (
641
+ target?.type === "page"
642
+ && String(target?.url || "").includes("zhipin.com")
643
+ )
644
+ });
614
645
 
615
646
  const { client, target } = session;
616
647
  await enableDomains(client, ["Page", "DOM", "Input", "Network", "Accessibility"]);
@@ -620,21 +651,102 @@ async function connectRecruitChromeSession({
620
651
  await bringPageToFront(client);
621
652
 
622
653
  const targetUrl = String(target?.url || "");
654
+ let navigation = {
655
+ navigated: false,
656
+ url: targetUrl
657
+ };
623
658
  if (allowNavigate && !targetUrl.includes(targetUrlIncludes)) {
624
659
  await client.Page.navigate({ url: RECRUIT_TARGET_URL });
625
- await sleep(slowLive ? 8000 : 3000);
660
+ const settleMs = slowLive ? 8000 : 3000;
661
+ const waited = await waitForMainFrameUrl(
662
+ client,
663
+ (url) => isBossLoginUrl(url) || String(url || "").includes(RECRUIT_TARGET_URL),
664
+ { timeoutMs: settleMs, intervalMs: 500 }
665
+ );
666
+ navigation = {
667
+ navigated: true,
668
+ url: RECRUIT_TARGET_URL,
669
+ settle_ms: settleMs,
670
+ observed_url: waited.url || null,
671
+ observed_url_ok: waited.ok
672
+ };
673
+ }
674
+ let currentUrl = await getMainFrameUrl(client).catch(() => targetUrl);
675
+ if (allowNavigate && !String(currentUrl || "").includes(RECRUIT_TARGET_URL) && !isBossLoginUrl(currentUrl)) {
676
+ await client.Page.navigate({ url: RECRUIT_TARGET_URL });
677
+ const settleMs = slowLive ? 8000 : 3000;
678
+ const waited = await waitForMainFrameUrl(
679
+ client,
680
+ (url) => isBossLoginUrl(url) || String(url || "").includes(RECRUIT_TARGET_URL),
681
+ { timeoutMs: settleMs, intervalMs: 500 }
682
+ );
683
+ navigation = {
684
+ navigated: true,
685
+ url: RECRUIT_TARGET_URL,
686
+ settle_ms: settleMs,
687
+ observed_url: waited.url || null,
688
+ observed_url_ok: waited.ok,
689
+ reason: "observed_url_mismatch"
690
+ };
691
+ currentUrl = await getMainFrameUrl(client).catch(() => waited.url || currentUrl);
692
+ }
693
+ const loginDetection = await detectBossLoginState(client, { currentUrl }).catch(() => ({
694
+ requires_login: isBossLoginUrl(currentUrl),
695
+ reason: "login_detection_failed",
696
+ current_url: currentUrl
697
+ }));
698
+ if (loginDetection.requires_login) {
699
+ await session.close?.();
700
+ throw createBossLoginRequiredError({
701
+ domain: "search",
702
+ currentUrl: loginDetection.current_url || currentUrl,
703
+ targetUrl: RECRUIT_TARGET_URL,
704
+ loginDetection,
705
+ chrome: session.chrome || null
706
+ });
707
+ }
708
+ if (!String(currentUrl || "").includes(RECRUIT_TARGET_URL)) {
709
+ await session.close?.();
710
+ throw new Error(`Boss search page did not navigate to ${RECRUIT_TARGET_URL}; current URL: ${currentUrl || "unknown"}`);
626
711
  }
627
712
 
628
- const controls = await waitForRecruitSearchControls(client, {
713
+ const controls = await waitForRecruitSearchControlsOrLogin(client, {
629
714
  timeoutMs: slowLive ? 180000 : 90000,
630
715
  intervalMs: 300
631
716
  });
717
+ if (controls.loginDetection?.requires_login) {
718
+ await session.close?.();
719
+ throw createBossLoginRequiredError({
720
+ domain: "search",
721
+ currentUrl: controls.loginDetection.current_url || currentUrl,
722
+ targetUrl: RECRUIT_TARGET_URL,
723
+ loginDetection: controls.loginDetection,
724
+ chrome: session.chrome || null
725
+ });
726
+ }
632
727
  if (!controls.ok) {
728
+ const latestUrl = await getMainFrameUrl(client).catch(() => currentUrl);
729
+ const latestLoginDetection = await detectBossLoginState(client, { currentUrl: latestUrl }).catch(() => ({
730
+ requires_login: isBossLoginUrl(latestUrl),
731
+ reason: "login_detection_failed",
732
+ current_url: latestUrl
733
+ }));
734
+ if (latestLoginDetection.requires_login) {
735
+ await session.close?.();
736
+ throw createBossLoginRequiredError({
737
+ domain: "search",
738
+ currentUrl: latestLoginDetection.current_url || latestUrl,
739
+ targetUrl: RECRUIT_TARGET_URL,
740
+ loginDetection: latestLoginDetection,
741
+ chrome: session.chrome || null
742
+ });
743
+ }
633
744
  throw new Error("Boss recruit search page did not expose ready search controls");
634
745
  }
635
746
 
636
747
  return {
637
748
  ...session,
749
+ navigation,
638
750
  controls
639
751
  };
640
752
  }
@@ -701,24 +813,38 @@ async function startRecruitPipelineRunInternal(args = {}, { workspaceRoot = "" }
701
813
  const parsed = parseRecruitPipelineRequest(args);
702
814
  const gate = evaluateRecruitPipelineGate(parsed);
703
815
  if (gate) return gate;
816
+ const configResolution = resolveBossScreeningConfig(workspaceRoot);
817
+ const host = normalizeText(args.host) || DEFAULT_RECRUIT_HOST;
818
+ const port = parsePositiveInteger(
819
+ args.port,
820
+ configResolution.ok ? configResolution.config.debugPort : DEFAULT_RECRUIT_PORT
821
+ );
704
822
 
705
823
  let session;
706
824
  try {
707
825
  session = await recruitConnectorImpl({
708
- host: normalizeText(args.host) || DEFAULT_RECRUIT_HOST,
709
- port: parsePositiveInteger(args.port, DEFAULT_RECRUIT_PORT),
826
+ host,
827
+ port,
710
828
  targetUrlIncludes: normalizeText(args.target_url_includes) || RECRUIT_TARGET_URL,
711
829
  allowNavigate: args.allow_navigate !== false,
712
830
  slowLive: args.slow_live === true
713
831
  });
714
832
  } catch (error) {
833
+ const loginRequired = error?.code === "BOSS_LOGIN_REQUIRED";
715
834
  return {
716
835
  status: "FAILED",
717
836
  error: {
718
- code: "BOSS_SEARCH_PAGE_NOT_READY",
837
+ code: loginRequired ? "BOSS_LOGIN_REQUIRED" : "BOSS_SEARCH_PAGE_NOT_READY",
719
838
  message: error?.message || "Boss recruit search page is not ready",
839
+ requires_login: Boolean(error?.requires_login),
840
+ login_url: error?.login_url || null,
841
+ login_detection: error?.login_detection || null,
842
+ chrome: error?.chrome || null,
843
+ current_url: error?.current_url || null,
844
+ target_url: error?.target_url || RECRUIT_TARGET_URL,
720
845
  retryable: true
721
- }
846
+ },
847
+ chrome: error?.chrome || null
722
848
  };
723
849
  }
724
850
 
@@ -744,10 +870,11 @@ async function startRecruitPipelineRunInternal(args = {}, { workspaceRoot = "" }
744
870
  workspaceRoot: normalizeText(workspaceRoot) || globalThis.process?.cwd?.() || "",
745
871
  args: clonePlain(args, {}),
746
872
  chrome: {
747
- host: normalizeText(args.host) || DEFAULT_RECRUIT_HOST,
748
- port: parsePositiveInteger(args.port, DEFAULT_RECRUIT_PORT),
873
+ host,
874
+ port,
749
875
  target_url: session.target?.url || RECRUIT_TARGET_URL,
750
- target_id: session.target?.id || null
876
+ target_id: session.target?.id || null,
877
+ auto_launch: session.chrome || null
751
878
  },
752
879
  parsed
753
880
  });