@xbrowser/cli 1.24.1 → 1.24.2

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.
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-2Q3GQRUP.js";
24
- import "./chunk-7OP2ISEY.js";
23
+ } from "./chunk-PKXJST62.js";
24
+ import "./chunk-UR3R5LWF.js";
25
25
  import "./chunk-GYB5BOSP.js";
26
26
  import "./chunk-MYH6FIBZ.js";
27
27
  import "./chunk-GDKLH7ZY.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-IFKJO2UR.js";
23
+ } from "./chunk-X735U7G7.js";
24
24
  import "./chunk-GYB5BOSP.js";
25
25
  import "./chunk-MYH6FIBZ.js";
26
26
  import "./chunk-GDKLH7ZY.js";
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-T4WTKBWI.js";
24
- import "./chunk-4OU37CXP.js";
23
+ } from "./chunk-ROAXLDV7.js";
24
+ import "./chunk-BH3GIMWE.js";
25
25
  import "./chunk-A7NEA4PA.js";
26
26
  import "./chunk-GYB5BOSP.js";
27
27
  import "./chunk-MYH6FIBZ.js";
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-7OP2ISEY.js";
17
+ } from "./chunk-UR3R5LWF.js";
18
18
  import "./chunk-GYB5BOSP.js";
19
19
  import {
20
20
  connectToCDP,
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-4OU37CXP.js";
17
+ } from "./chunk-BH3GIMWE.js";
18
18
  import "./chunk-A7NEA4PA.js";
19
19
  import "./chunk-GYB5BOSP.js";
20
20
  import {
@@ -710,6 +710,61 @@ async function scrollIntoView(page, selector) {
710
710
  `);
711
711
  }
712
712
 
713
+ // src/cdp-driver/editor-profile.ts
714
+ function buildEditorProbe(selector) {
715
+ return `(function() {
716
+ const el = ${selector};
717
+ if (!el) return { react: false, richFamily: null, contenteditable: false, controlled: false };
718
+ const editable = el.closest('[contenteditable="true"]');
719
+ const host = editable || el;
720
+ // React fiber\uFF1A\u5143\u7D20\u81EA\u6709\u952E\u626B\u63CF\uFF08interaction.ts \u9875\u9762\u7EA7\u63A2\u6D4B\u7684\u5143\u7D20\u7EA7\u6536\u7A84\uFF09
721
+ let react = false;
722
+ try {
723
+ for (const k of Object.keys(host)) {
724
+ if (k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')) { react = true; break; }
725
+ }
726
+ if (!react && host.parentElement) {
727
+ for (const k of Object.keys(host.parentElement)) {
728
+ if (k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')) { react = true; break; }
729
+ }
730
+ }
731
+ } catch (e) { /* \u8DE8\u57DF/\u6743\u9650\u515C\u5E95 */ }
732
+ // \u5DF2\u77E5\u7F16\u8F91\u5668\u5BB6\u65CF\uFF08\u81EA\u7814 Draft \u7CFB\u6700\u5371\u9669\u2014\u2014EditorState \u5F3A\u53D7\u63A7\uFF09
733
+ const families = [
734
+ ['.public-DraftEditor-content', 'draft'],
735
+ ['.DraftEditor-editorContainer', 'draft'],
736
+ ['.ProseMirror', 'prosemirror'],
737
+ ['.ql-editor', 'quill'],
738
+ ['.CodeMirror', 'codemirror'],
739
+ ['.cm-editor', 'codemirror'],
740
+ ['[data-slate-editor]', 'slate'],
741
+ ['.jsx-lexical', 'lexical'],
742
+ ['.contenteditable-editor', 'generic-rich'],
743
+ ];
744
+ let richFamily = null;
745
+ for (const [sel, name] of families) {
746
+ if (host.closest(sel)) { richFamily = name; break; }
747
+ }
748
+ const contenteditable = !!editable && el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA';
749
+ return {
750
+ react,
751
+ richFamily,
752
+ contenteditable,
753
+ controlled: react || !!richFamily || contenteditable,
754
+ };
755
+ })()`;
756
+ }
757
+ async function probeEditor(evaluate, selector) {
758
+ const fallback = { react: false, richFamily: null, contenteditable: false, controlled: false };
759
+ try {
760
+ const r = await evaluate(buildEditorProbe(selector));
761
+ if (r && typeof r === "object") return r;
762
+ return fallback;
763
+ } catch {
764
+ return fallback;
765
+ }
766
+ }
767
+
713
768
  // src/cdp-driver/locator.ts
714
769
  var XBLocatorImpl = class _XBLocatorImpl {
715
770
  page;
@@ -783,19 +838,51 @@ var XBLocatorImpl = class _XBLocatorImpl {
783
838
  if ((el.value || el.textContent || '') !== '') document.execCommand('selectAll');
784
839
  })()
785
840
  `);
786
- if (process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type") {
841
+ const profile = await probeEditor(
842
+ (expr) => this.page.evaluate(expr),
843
+ this._q(this.selector)
844
+ );
845
+ const verifyMs = parseInt(process.env.XBROWSER_FILL_VERIFY_MS ?? "600", 10) || 600;
846
+ const readValue = () => this.page.evaluate(
847
+ `(function(){ const el = ${this._q(this.selector)}; if (!el) return ''; const v = el.value !== undefined ? el.value : el.textContent; return v || ''; })()`
848
+ );
849
+ const survived = async (expected) => {
850
+ await this.page.waitForTimeout(verifyMs);
851
+ const got = await readValue();
852
+ return !(got === "" || got.length < expected.length * 0.5);
853
+ };
854
+ const canFastPath = process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type" && !profile.controlled;
855
+ if (canFastPath) {
787
856
  try {
788
857
  const { pasteViaClipboard, syntheticPaste } = await import("./clipboard-XRP54ENE.js");
789
858
  await pasteViaClipboard(this.page, value);
790
859
  const got = await this.page.evaluate(
791
860
  `(function(){ const el = ${this._q(this.selector)}; return el ? (el.value || '') : ''; })()`
792
861
  );
793
- if (got === value) return;
794
- if (await syntheticPaste(this.page, this._q(this.selector), value)) return;
862
+ if (got === value && await survived(value)) return;
863
+ const syn = await syntheticPaste(this.page, this._q(this.selector), value);
864
+ if (syn && await survived(value)) return;
795
865
  } catch {
796
866
  }
797
867
  }
798
868
  await this.page.keyboard.type(value, { stealth: true });
869
+ if (profile.controlled && verifyMs > 0 && !await survived(value)) {
870
+ await this.click({ ...opts });
871
+ await this.page.evaluate(`
872
+ (function() {
873
+ const el = ${this._q(this.selector)};
874
+ if (!el) return;
875
+ if (document.activeElement !== el) el.focus();
876
+ document.execCommand('selectAll');
877
+ })()
878
+ `);
879
+ await this.page.keyboard.type(value, { stealth: true });
880
+ if (!await survived(value)) {
881
+ throw new Error(
882
+ `input not retained: \u53D7\u63A7\u7F16\u8F91\u5668\u5728\u952E\u76D8\u6D41\u540E\u4ECD\u6E05\u7A7A\u6CE8\u5165\u503C\uFF08\u5347\u7EA7\u4EBA\u5DE5\u6216\u68C0\u67E5\u7AD9\u70B9\u88C1\u51B3\u903B\u8F91\uFF1B\u53EF\u8C03 XBROWSER_FILL_VERIFY_MS\uFF09`
883
+ );
884
+ }
885
+ }
799
886
  return;
800
887
  await this.page.evaluate(`
801
888
  (function() {
@@ -611,6 +611,61 @@ async function scrollIntoView(page, selector) {
611
611
  `);
612
612
  }
613
613
 
614
+ // src/cdp-driver/editor-profile.ts
615
+ function buildEditorProbe(selector) {
616
+ return `(function() {
617
+ const el = ${selector};
618
+ if (!el) return { react: false, richFamily: null, contenteditable: false, controlled: false };
619
+ const editable = el.closest('[contenteditable="true"]');
620
+ const host = editable || el;
621
+ // React fiber\uFF1A\u5143\u7D20\u81EA\u6709\u952E\u626B\u63CF\uFF08interaction.ts \u9875\u9762\u7EA7\u63A2\u6D4B\u7684\u5143\u7D20\u7EA7\u6536\u7A84\uFF09
622
+ let react = false;
623
+ try {
624
+ for (const k of Object.keys(host)) {
625
+ if (k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')) { react = true; break; }
626
+ }
627
+ if (!react && host.parentElement) {
628
+ for (const k of Object.keys(host.parentElement)) {
629
+ if (k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')) { react = true; break; }
630
+ }
631
+ }
632
+ } catch (e) { /* \u8DE8\u57DF/\u6743\u9650\u515C\u5E95 */ }
633
+ // \u5DF2\u77E5\u7F16\u8F91\u5668\u5BB6\u65CF\uFF08\u81EA\u7814 Draft \u7CFB\u6700\u5371\u9669\u2014\u2014EditorState \u5F3A\u53D7\u63A7\uFF09
634
+ const families = [
635
+ ['.public-DraftEditor-content', 'draft'],
636
+ ['.DraftEditor-editorContainer', 'draft'],
637
+ ['.ProseMirror', 'prosemirror'],
638
+ ['.ql-editor', 'quill'],
639
+ ['.CodeMirror', 'codemirror'],
640
+ ['.cm-editor', 'codemirror'],
641
+ ['[data-slate-editor]', 'slate'],
642
+ ['.jsx-lexical', 'lexical'],
643
+ ['.contenteditable-editor', 'generic-rich'],
644
+ ];
645
+ let richFamily = null;
646
+ for (const [sel, name] of families) {
647
+ if (host.closest(sel)) { richFamily = name; break; }
648
+ }
649
+ const contenteditable = !!editable && el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA';
650
+ return {
651
+ react,
652
+ richFamily,
653
+ contenteditable,
654
+ controlled: react || !!richFamily || contenteditable,
655
+ };
656
+ })()`;
657
+ }
658
+ async function probeEditor(evaluate, selector) {
659
+ const fallback = { react: false, richFamily: null, contenteditable: false, controlled: false };
660
+ try {
661
+ const r = await evaluate(buildEditorProbe(selector));
662
+ if (r && typeof r === "object") return r;
663
+ return fallback;
664
+ } catch {
665
+ return fallback;
666
+ }
667
+ }
668
+
614
669
  // src/cdp-driver/locator.ts
615
670
  var XBLocatorImpl = class _XBLocatorImpl {
616
671
  page;
@@ -684,19 +739,51 @@ var XBLocatorImpl = class _XBLocatorImpl {
684
739
  if ((el.value || el.textContent || '') !== '') document.execCommand('selectAll');
685
740
  })()
686
741
  `);
687
- if (process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type") {
742
+ const profile = await probeEditor(
743
+ (expr) => this.page.evaluate(expr),
744
+ this._q(this.selector)
745
+ );
746
+ const verifyMs = parseInt(process.env.XBROWSER_FILL_VERIFY_MS ?? "600", 10) || 600;
747
+ const readValue = () => this.page.evaluate(
748
+ `(function(){ const el = ${this._q(this.selector)}; if (!el) return ''; const v = el.value !== undefined ? el.value : el.textContent; return v || ''; })()`
749
+ );
750
+ const survived = async (expected) => {
751
+ await this.page.waitForTimeout(verifyMs);
752
+ const got = await readValue();
753
+ return !(got === "" || got.length < expected.length * 0.5);
754
+ };
755
+ const canFastPath = process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type" && !profile.controlled;
756
+ if (canFastPath) {
688
757
  try {
689
758
  const { pasteViaClipboard, syntheticPaste } = await import("./clipboard-BOL2GP2E.js");
690
759
  await pasteViaClipboard(this.page, value);
691
760
  const got = await this.page.evaluate(
692
761
  `(function(){ const el = ${this._q(this.selector)}; return el ? (el.value || '') : ''; })()`
693
762
  );
694
- if (got === value) return;
695
- if (await syntheticPaste(this.page, this._q(this.selector), value)) return;
763
+ if (got === value && await survived(value)) return;
764
+ const syn = await syntheticPaste(this.page, this._q(this.selector), value);
765
+ if (syn && await survived(value)) return;
696
766
  } catch {
697
767
  }
698
768
  }
699
769
  await this.page.keyboard.type(value, { stealth: true });
770
+ if (profile.controlled && verifyMs > 0 && !await survived(value)) {
771
+ await this.click({ ...opts });
772
+ await this.page.evaluate(`
773
+ (function() {
774
+ const el = ${this._q(this.selector)};
775
+ if (!el) return;
776
+ if (document.activeElement !== el) el.focus();
777
+ document.execCommand('selectAll');
778
+ })()
779
+ `);
780
+ await this.page.keyboard.type(value, { stealth: true });
781
+ if (!await survived(value)) {
782
+ throw new Error(
783
+ `input not retained: \u53D7\u63A7\u7F16\u8F91\u5668\u5728\u952E\u76D8\u6D41\u540E\u4ECD\u6E05\u7A7A\u6CE8\u5165\u503C\uFF08\u5347\u7EA7\u4EBA\u5DE5\u6216\u68C0\u67E5\u7AD9\u70B9\u88C1\u51B3\u903B\u8F91\uFF1B\u53EF\u8C03 XBROWSER_FILL_VERIFY_MS\uFF09`
784
+ );
785
+ }
786
+ }
700
787
  return;
701
788
  await this.page.evaluate(`
702
789
  (function() {
@@ -9,7 +9,7 @@ import {
9
9
  resolveLaunchOpts,
10
10
  saveSessionDiskMeta,
11
11
  setActivePage
12
- } from "./chunk-2Q3GQRUP.js";
12
+ } from "./chunk-PKXJST62.js";
13
13
  import {
14
14
  detectAntiBot,
15
15
  formatDetectionMessage
@@ -7589,6 +7589,7 @@ var FAIL_CODES = {
7589
7589
  selectorNotFound: "RETRYABLE: selector-not-found",
7590
7590
  pageNotReady: "RETRYABLE: page-not-ready",
7591
7591
  elementGone: "RETRYABLE: element-gone",
7592
+ inputNotRetained: "RETRYABLE: input-not-retained",
7592
7593
  captcha: "HUMAN: captcha",
7593
7594
  loginWall: "HUMAN: login-wall",
7594
7595
  permissionDenied: "FATAL: permission-denied",
@@ -7608,6 +7609,7 @@ function classifyFailure(message) {
7608
7609
  if (/selector|选择器|element not found|未找到元素|no such element|queryselector returned null/.test(msg))
7609
7610
  return pick(FAIL_CODES.selectorNotFound);
7610
7611
  if (/timeout|超时|timed out|waiting for/.test(msg)) return pick(FAIL_CODES.pageNotReady);
7612
+ if (/not retained|清空了注入值|input-not-retained/.test(msg)) return pick(FAIL_CODES.inputNotRetained);
7611
7613
  if (/net::err|404|navigation failed|页面不存在|target closed|element-gone/.test(msg))
7612
7614
  return pick(FAIL_CODES.targetGone);
7613
7615
  return pick(FAIL_CODES.unknown);
@@ -8562,7 +8564,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
8562
8564
  }
8563
8565
  let targetPageOverride = null;
8564
8566
  if (_target && extraOpts?.cdpEndpoint) {
8565
- const { findTargetPage } = await import("./browser-Q67BJTI2.js");
8567
+ const { findTargetPage } = await import("./browser-DFM2SQDM.js");
8566
8568
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
8567
8569
  if (!targetPageOverride) {
8568
8570
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-7OP2ISEY.js";
3
+ } from "./chunk-UR3R5LWF.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createRuleEngine,
3
3
  launch
4
- } from "./chunk-4OU37CXP.js";
4
+ } from "./chunk-BH3GIMWE.js";
5
5
  import {
6
6
  errMsg
7
7
  } from "./chunk-GDKLH7ZY.js";
@@ -20,7 +20,7 @@ import {
20
20
  resolveLaunchOpts,
21
21
  saveSessionDiskMeta,
22
22
  setActivePage
23
- } from "./chunk-IFKJO2UR.js";
23
+ } from "./chunk-X735U7G7.js";
24
24
  import {
25
25
  rand,
26
26
  sleep,
@@ -35,6 +35,7 @@ var FAIL_CODES = {
35
35
  selectorNotFound: "RETRYABLE: selector-not-found",
36
36
  pageNotReady: "RETRYABLE: page-not-ready",
37
37
  elementGone: "RETRYABLE: element-gone",
38
+ inputNotRetained: "RETRYABLE: input-not-retained",
38
39
  captcha: "HUMAN: captcha",
39
40
  loginWall: "HUMAN: login-wall",
40
41
  permissionDenied: "FATAL: permission-denied",
@@ -54,6 +55,7 @@ function classifyFailure(message) {
54
55
  if (/selector|选择器|element not found|未找到元素|no such element|queryselector returned null/.test(msg))
55
56
  return pick(FAIL_CODES.selectorNotFound);
56
57
  if (/timeout|超时|timed out|waiting for/.test(msg)) return pick(FAIL_CODES.pageNotReady);
58
+ if (/not retained|清空了注入值|input-not-retained/.test(msg)) return pick(FAIL_CODES.inputNotRetained);
57
59
  if (/net::err|404|navigation failed|页面不存在|target closed|element-gone/.test(msg))
58
60
  return pick(FAIL_CODES.targetGone);
59
61
  return pick(FAIL_CODES.unknown);
@@ -8242,7 +8244,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
8242
8244
  }
8243
8245
  let targetPageOverride = null;
8244
8246
  if (_target && extraOpts?.cdpEndpoint) {
8245
- const { findTargetPage } = await import("./browser-BVRFSUXU.js");
8247
+ const { findTargetPage } = await import("./browser-I3EZY3FL.js");
8246
8248
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
8247
8249
  if (!targetPageOverride) {
8248
8250
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -712,6 +712,61 @@ async function scrollIntoView(page, selector) {
712
712
  `);
713
713
  }
714
714
 
715
+ // src/cdp-driver/editor-profile.ts
716
+ function buildEditorProbe(selector) {
717
+ return `(function() {
718
+ const el = ${selector};
719
+ if (!el) return { react: false, richFamily: null, contenteditable: false, controlled: false };
720
+ const editable = el.closest('[contenteditable="true"]');
721
+ const host = editable || el;
722
+ // React fiber\uFF1A\u5143\u7D20\u81EA\u6709\u952E\u626B\u63CF\uFF08interaction.ts \u9875\u9762\u7EA7\u63A2\u6D4B\u7684\u5143\u7D20\u7EA7\u6536\u7A84\uFF09
723
+ let react = false;
724
+ try {
725
+ for (const k of Object.keys(host)) {
726
+ if (k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')) { react = true; break; }
727
+ }
728
+ if (!react && host.parentElement) {
729
+ for (const k of Object.keys(host.parentElement)) {
730
+ if (k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')) { react = true; break; }
731
+ }
732
+ }
733
+ } catch (e) { /* \u8DE8\u57DF/\u6743\u9650\u515C\u5E95 */ }
734
+ // \u5DF2\u77E5\u7F16\u8F91\u5668\u5BB6\u65CF\uFF08\u81EA\u7814 Draft \u7CFB\u6700\u5371\u9669\u2014\u2014EditorState \u5F3A\u53D7\u63A7\uFF09
735
+ const families = [
736
+ ['.public-DraftEditor-content', 'draft'],
737
+ ['.DraftEditor-editorContainer', 'draft'],
738
+ ['.ProseMirror', 'prosemirror'],
739
+ ['.ql-editor', 'quill'],
740
+ ['.CodeMirror', 'codemirror'],
741
+ ['.cm-editor', 'codemirror'],
742
+ ['[data-slate-editor]', 'slate'],
743
+ ['.jsx-lexical', 'lexical'],
744
+ ['.contenteditable-editor', 'generic-rich'],
745
+ ];
746
+ let richFamily = null;
747
+ for (const [sel, name] of families) {
748
+ if (host.closest(sel)) { richFamily = name; break; }
749
+ }
750
+ const contenteditable = !!editable && el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA';
751
+ return {
752
+ react,
753
+ richFamily,
754
+ contenteditable,
755
+ controlled: react || !!richFamily || contenteditable,
756
+ };
757
+ })()`;
758
+ }
759
+ async function probeEditor(evaluate, selector) {
760
+ const fallback = { react: false, richFamily: null, contenteditable: false, controlled: false };
761
+ try {
762
+ const r = await evaluate(buildEditorProbe(selector));
763
+ if (r && typeof r === "object") return r;
764
+ return fallback;
765
+ } catch {
766
+ return fallback;
767
+ }
768
+ }
769
+
715
770
  // src/cdp-driver/locator.ts
716
771
  var XBLocatorImpl = class _XBLocatorImpl {
717
772
  page;
@@ -785,19 +840,51 @@ var XBLocatorImpl = class _XBLocatorImpl {
785
840
  if ((el.value || el.textContent || '') !== '') document.execCommand('selectAll');
786
841
  })()
787
842
  `);
788
- if (process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type") {
843
+ const profile = await probeEditor(
844
+ (expr) => this.page.evaluate(expr),
845
+ this._q(this.selector)
846
+ );
847
+ const verifyMs = parseInt(process.env.XBROWSER_FILL_VERIFY_MS ?? "600", 10) || 600;
848
+ const readValue = () => this.page.evaluate(
849
+ `(function(){ const el = ${this._q(this.selector)}; if (!el) return ''; const v = el.value !== undefined ? el.value : el.textContent; return v || ''; })()`
850
+ );
851
+ const survived = async (expected) => {
852
+ await this.page.waitForTimeout(verifyMs);
853
+ const got = await readValue();
854
+ return !(got === "" || got.length < expected.length * 0.5);
855
+ };
856
+ const canFastPath = process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type" && !profile.controlled;
857
+ if (canFastPath) {
789
858
  try {
790
859
  const { pasteViaClipboard, syntheticPaste } = await import("./clipboard-BOL2GP2E.js");
791
860
  await pasteViaClipboard(this.page, value);
792
861
  const got = await this.page.evaluate(
793
862
  `(function(){ const el = ${this._q(this.selector)}; return el ? (el.value || '') : ''; })()`
794
863
  );
795
- if (got === value) return;
796
- if (await syntheticPaste(this.page, this._q(this.selector), value)) return;
864
+ if (got === value && await survived(value)) return;
865
+ const syn = await syntheticPaste(this.page, this._q(this.selector), value);
866
+ if (syn && await survived(value)) return;
797
867
  } catch {
798
868
  }
799
869
  }
800
870
  await this.page.keyboard.type(value, { stealth: true });
871
+ if (profile.controlled && verifyMs > 0 && !await survived(value)) {
872
+ await this.click({ ...opts });
873
+ await this.page.evaluate(`
874
+ (function() {
875
+ const el = ${this._q(this.selector)};
876
+ if (!el) return;
877
+ if (document.activeElement !== el) el.focus();
878
+ document.execCommand('selectAll');
879
+ })()
880
+ `);
881
+ await this.page.keyboard.type(value, { stealth: true });
882
+ if (!await survived(value)) {
883
+ throw new Error(
884
+ `input not retained: \u53D7\u63A7\u7F16\u8F91\u5668\u5728\u952E\u76D8\u6D41\u540E\u4ECD\u6E05\u7A7A\u6CE8\u5165\u503C\uFF08\u5347\u7EA7\u4EBA\u5DE5\u6216\u68C0\u67E5\u7AD9\u70B9\u88C1\u51B3\u903B\u8F91\uFF1B\u53EF\u8C03 XBROWSER_FILL_VERIFY_MS\uFF09`
885
+ );
886
+ }
887
+ }
801
888
  return;
802
889
  await this.page.evaluate(`
803
890
  (function() {
@@ -715,6 +715,61 @@ async function scrollIntoView(page, selector) {
715
715
  `);
716
716
  }
717
717
 
718
+ // src/cdp-driver/editor-profile.ts
719
+ function buildEditorProbe(selector) {
720
+ return `(function() {
721
+ const el = ${selector};
722
+ if (!el) return { react: false, richFamily: null, contenteditable: false, controlled: false };
723
+ const editable = el.closest('[contenteditable="true"]');
724
+ const host = editable || el;
725
+ // React fiber\uFF1A\u5143\u7D20\u81EA\u6709\u952E\u626B\u63CF\uFF08interaction.ts \u9875\u9762\u7EA7\u63A2\u6D4B\u7684\u5143\u7D20\u7EA7\u6536\u7A84\uFF09
726
+ let react = false;
727
+ try {
728
+ for (const k of Object.keys(host)) {
729
+ if (k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')) { react = true; break; }
730
+ }
731
+ if (!react && host.parentElement) {
732
+ for (const k of Object.keys(host.parentElement)) {
733
+ if (k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')) { react = true; break; }
734
+ }
735
+ }
736
+ } catch (e) { /* \u8DE8\u57DF/\u6743\u9650\u515C\u5E95 */ }
737
+ // \u5DF2\u77E5\u7F16\u8F91\u5668\u5BB6\u65CF\uFF08\u81EA\u7814 Draft \u7CFB\u6700\u5371\u9669\u2014\u2014EditorState \u5F3A\u53D7\u63A7\uFF09
738
+ const families = [
739
+ ['.public-DraftEditor-content', 'draft'],
740
+ ['.DraftEditor-editorContainer', 'draft'],
741
+ ['.ProseMirror', 'prosemirror'],
742
+ ['.ql-editor', 'quill'],
743
+ ['.CodeMirror', 'codemirror'],
744
+ ['.cm-editor', 'codemirror'],
745
+ ['[data-slate-editor]', 'slate'],
746
+ ['.jsx-lexical', 'lexical'],
747
+ ['.contenteditable-editor', 'generic-rich'],
748
+ ];
749
+ let richFamily = null;
750
+ for (const [sel, name] of families) {
751
+ if (host.closest(sel)) { richFamily = name; break; }
752
+ }
753
+ const contenteditable = !!editable && el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA';
754
+ return {
755
+ react,
756
+ richFamily,
757
+ contenteditable,
758
+ controlled: react || !!richFamily || contenteditable,
759
+ };
760
+ })()`;
761
+ }
762
+ async function probeEditor(evaluate, selector) {
763
+ const fallback = { react: false, richFamily: null, contenteditable: false, controlled: false };
764
+ try {
765
+ const r = await evaluate(buildEditorProbe(selector));
766
+ if (r && typeof r === "object") return r;
767
+ return fallback;
768
+ } catch {
769
+ return fallback;
770
+ }
771
+ }
772
+
718
773
  // src/cdp-driver/locator.ts
719
774
  var XBLocatorImpl = class _XBLocatorImpl {
720
775
  page;
@@ -788,19 +843,51 @@ var XBLocatorImpl = class _XBLocatorImpl {
788
843
  if ((el.value || el.textContent || '') !== '') document.execCommand('selectAll');
789
844
  })()
790
845
  `);
791
- if (process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type") {
846
+ const profile = await probeEditor(
847
+ (expr) => this.page.evaluate(expr),
848
+ this._q(this.selector)
849
+ );
850
+ const verifyMs = parseInt(process.env.XBROWSER_FILL_VERIFY_MS ?? "600", 10) || 600;
851
+ const readValue = () => this.page.evaluate(
852
+ `(function(){ const el = ${this._q(this.selector)}; if (!el) return ''; const v = el.value !== undefined ? el.value : el.textContent; return v || ''; })()`
853
+ );
854
+ const survived = async (expected) => {
855
+ await this.page.waitForTimeout(verifyMs);
856
+ const got = await readValue();
857
+ return !(got === "" || got.length < expected.length * 0.5);
858
+ };
859
+ const canFastPath = process.env.XBROWSER_STEALTH !== "off" && value.length >= 40 && process.env.XBROWSER_FILL_TYPE !== "type" && !profile.controlled;
860
+ if (canFastPath) {
792
861
  try {
793
862
  const { pasteViaClipboard, syntheticPaste } = await import("./clipboard-BOL2GP2E.js");
794
863
  await pasteViaClipboard(this.page, value);
795
864
  const got = await this.page.evaluate(
796
865
  `(function(){ const el = ${this._q(this.selector)}; return el ? (el.value || '') : ''; })()`
797
866
  );
798
- if (got === value) return;
799
- if (await syntheticPaste(this.page, this._q(this.selector), value)) return;
867
+ if (got === value && await survived(value)) return;
868
+ const syn = await syntheticPaste(this.page, this._q(this.selector), value);
869
+ if (syn && await survived(value)) return;
800
870
  } catch {
801
871
  }
802
872
  }
803
873
  await this.page.keyboard.type(value, { stealth: true });
874
+ if (profile.controlled && verifyMs > 0 && !await survived(value)) {
875
+ await this.click({ ...opts });
876
+ await this.page.evaluate(`
877
+ (function() {
878
+ const el = ${this._q(this.selector)};
879
+ if (!el) return;
880
+ if (document.activeElement !== el) el.focus();
881
+ document.execCommand('selectAll');
882
+ })()
883
+ `);
884
+ await this.page.keyboard.type(value, { stealth: true });
885
+ if (!await survived(value)) {
886
+ throw new Error(
887
+ `input not retained: \u53D7\u63A7\u7F16\u8F91\u5668\u5728\u952E\u76D8\u6D41\u540E\u4ECD\u6E05\u7A7A\u6CE8\u5165\u503C\uFF08\u5347\u7EA7\u4EBA\u5DE5\u6216\u68C0\u67E5\u7AD9\u70B9\u88C1\u51B3\u903B\u8F91\uFF1B\u53EF\u8C03 XBROWSER_FILL_VERIFY_MS\uFF09`
888
+ );
889
+ }
890
+ }
804
891
  return;
805
892
  await this.page.evaluate(`
806
893
  (function() {
package/dist/cli.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  loadHooks,
17
17
  parsePluginParams,
18
18
  readJsonFile
19
- } from "./chunk-AYNFXG7V.js";
19
+ } from "./chunk-SUHS2EWV.js";
20
20
  import "./chunk-JKVUFP3G.js";
21
21
  import {
22
22
  forwardCommandLog,
@@ -80,7 +80,7 @@ import {
80
80
  findSession,
81
81
  getAllSessions,
82
82
  saveSessionDiskMeta
83
- } from "./chunk-IFKJO2UR.js";
83
+ } from "./chunk-X735U7G7.js";
84
84
  import "./chunk-GYB5BOSP.js";
85
85
  import "./chunk-MYH6FIBZ.js";
86
86
  import {
@@ -6016,7 +6016,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
6016
6016
  const targetPage = pages[cmdTabIndex];
6017
6017
  await targetPage.bringToFront().catch(() => {
6018
6018
  });
6019
- const { setActivePage } = await import("./browser-BVRFSUXU.js");
6019
+ const { setActivePage } = await import("./browser-I3EZY3FL.js");
6020
6020
  setActivePage(session, targetPage);
6021
6021
  }
6022
6022
  }
@@ -6281,7 +6281,7 @@ async function handleRemote(args, options, mode) {
6281
6281
  }
6282
6282
  }
6283
6283
  async function startMcpServer() {
6284
- const { startMcpStdio } = await import("./server-KSHSG4TY.js");
6284
+ const { startMcpStdio } = await import("./server-A5R4OIGX.js");
6285
6285
  startMcpStdio();
6286
6286
  }
6287
6287
 
@@ -6300,7 +6300,7 @@ async function main() {
6300
6300
  const command = process.argv[2];
6301
6301
  const isLongRunning = command === "preview" || command === "serve" || command === "mcp";
6302
6302
  if (!isLongRunning) {
6303
- const { ensureProcessCanExit } = await import("./browser-BVRFSUXU.js");
6303
+ const { ensureProcessCanExit } = await import("./browser-I3EZY3FL.js");
6304
6304
  await ensureProcessCanExit().catch(() => {
6305
6305
  });
6306
6306
  process.exit(process.exitCode || exitCode);
@@ -38,10 +38,10 @@ import {
38
38
  resolveLaunchOpts,
39
39
  saveSessionDiskMeta,
40
40
  setActivePage
41
- } from "./chunk-T4WTKBWI.js";
41
+ } from "./chunk-ROAXLDV7.js";
42
42
  import {
43
43
  createRuleEngine
44
- } from "./chunk-4OU37CXP.js";
44
+ } from "./chunk-BH3GIMWE.js";
45
45
  import {
46
46
  queryJS
47
47
  } from "./chunk-A7NEA4PA.js";
@@ -74,6 +74,7 @@ var FAIL_CODES = {
74
74
  selectorNotFound: "RETRYABLE: selector-not-found",
75
75
  pageNotReady: "RETRYABLE: page-not-ready",
76
76
  elementGone: "RETRYABLE: element-gone",
77
+ inputNotRetained: "RETRYABLE: input-not-retained",
77
78
  captcha: "HUMAN: captcha",
78
79
  loginWall: "HUMAN: login-wall",
79
80
  permissionDenied: "FATAL: permission-denied",
@@ -93,6 +94,7 @@ function classifyFailure(message) {
93
94
  if (/selector|选择器|element not found|未找到元素|no such element|queryselector returned null/.test(msg))
94
95
  return pick(FAIL_CODES.selectorNotFound);
95
96
  if (/timeout|超时|timed out|waiting for/.test(msg)) return pick(FAIL_CODES.pageNotReady);
97
+ if (/not retained|清空了注入值|input-not-retained/.test(msg)) return pick(FAIL_CODES.inputNotRetained);
96
98
  if (/net::err|404|navigation failed|页面不存在|target closed|element-gone/.test(msg))
97
99
  return pick(FAIL_CODES.targetGone);
98
100
  return pick(FAIL_CODES.unknown);
@@ -7797,7 +7799,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7797
7799
  }
7798
7800
  let targetPageOverride = null;
7799
7801
  if (_target && extraOpts?.cdpEndpoint) {
7800
- const { findTargetPage } = await import("./browser-DSYUAE7H.js");
7802
+ const { findTargetPage } = await import("./browser-WCHMUN6H.js");
7801
7803
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7802
7804
  if (!targetPageOverride) {
7803
7805
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -9637,7 +9639,7 @@ function createRPCHandler() {
9637
9639
  if (isNewFormat) {
9638
9640
  try {
9639
9641
  const replayErrors = [];
9640
- const { SessionReplayer } = await import("./session-replayer-N665T3G5.js");
9642
+ const { SessionReplayer } = await import("./session-replayer-PSAPQBZA.js");
9641
9643
  const replayer = new SessionReplayer({
9642
9644
  page: session.page,
9643
9645
  stepDelay: slowMo * 500,
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ import {
48
48
  splitCommand,
49
49
  waitForAIResponse,
50
50
  waitForPage
51
- } from "./chunk-5PZXCIDR.js";
51
+ } from "./chunk-LTLMDKWG.js";
52
52
  import {
53
53
  SessionRecorder
54
54
  } from "./chunk-IR6C24P7.js";
@@ -63,7 +63,7 @@ import {
63
63
  getBrowser,
64
64
  resetForTesting,
65
65
  saveSessionDiskMeta
66
- } from "./chunk-2Q3GQRUP.js";
66
+ } from "./chunk-PKXJST62.js";
67
67
  import "./chunk-JKVUFP3G.js";
68
68
  import {
69
69
  forwardCommandLog,
@@ -120,7 +120,7 @@ import {
120
120
  generateJSScript,
121
121
  generatePythonScript
122
122
  } from "./chunk-ETCO4SNK.js";
123
- import "./chunk-7OP2ISEY.js";
123
+ import "./chunk-UR3R5LWF.js";
124
124
  import "./chunk-GYB5BOSP.js";
125
125
  import "./chunk-MYH6FIBZ.js";
126
126
  import {
@@ -6112,7 +6112,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
6112
6112
  const targetPage = pages[cmdTabIndex];
6113
6113
  await targetPage.bringToFront().catch(() => {
6114
6114
  });
6115
- const { setActivePage } = await import("./browser-Q67BJTI2.js");
6115
+ const { setActivePage } = await import("./browser-DFM2SQDM.js");
6116
6116
  setActivePage(session, targetPage);
6117
6117
  }
6118
6118
  }
@@ -6377,7 +6377,7 @@ async function handleRemote(args, options, mode) {
6377
6377
  }
6378
6378
  }
6379
6379
  async function startMcpServer() {
6380
- const { startMcpStdio } = await import("./server-NXXU3UGI.js");
6380
+ const { startMcpStdio } = await import("./server-Y5VVPRC6.js");
6381
6381
  startMcpStdio();
6382
6382
  }
6383
6383
 
@@ -9292,7 +9292,7 @@ var DataCollector = class {
9292
9292
  return results;
9293
9293
  }
9294
9294
  async createBrowserContext() {
9295
- const { launch } = await import("./cdp-driver-WWO2UZDR.js");
9295
+ const { launch } = await import("./cdp-driver-57QV7C5O.js");
9296
9296
  const { browser } = await launch({
9297
9297
  headless: true,
9298
9298
  args: ["--no-sandbox", "--disable-setuid-sandbox"]
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  executeChain,
3
3
  executeCommand
4
- } from "./chunk-AYNFXG7V.js";
4
+ } from "./chunk-SUHS2EWV.js";
5
5
  import "./chunk-JKVUFP3G.js";
6
6
  import "./chunk-3OD76SUE.js";
7
7
  import "./chunk-ZTHE5RBZ.js";
8
8
  import "./chunk-Q2DFJQGS.js";
9
- import "./chunk-IFKJO2UR.js";
9
+ import "./chunk-X735U7G7.js";
10
10
  import "./chunk-GYB5BOSP.js";
11
11
  import "./chunk-MYH6FIBZ.js";
12
12
  import "./chunk-GDKLH7ZY.js";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  executeChain,
3
3
  executeCommand
4
- } from "./chunk-5PZXCIDR.js";
5
- import "./chunk-2Q3GQRUP.js";
4
+ } from "./chunk-LTLMDKWG.js";
5
+ import "./chunk-PKXJST62.js";
6
6
  import "./chunk-JKVUFP3G.js";
7
7
  import "./chunk-3OD76SUE.js";
8
8
  import "./chunk-ZTHE5RBZ.js";
9
9
  import "./chunk-Q2DFJQGS.js";
10
- import "./chunk-7OP2ISEY.js";
10
+ import "./chunk-UR3R5LWF.js";
11
11
  import "./chunk-GYB5BOSP.js";
12
12
  import "./chunk-MYH6FIBZ.js";
13
13
  import "./chunk-GDKLH7ZY.js";
@@ -61,7 +61,7 @@ var SessionReplayer = class {
61
61
  if (this.opts.page) {
62
62
  this.page = this.opts.page;
63
63
  } else if (this.opts.cdpUrl) {
64
- const { launch } = await import("./cdp-driver-DC7EIUG5.js");
64
+ const { launch } = await import("./cdp-driver-BFGFAVAN.js");
65
65
  const { browser } = await launch({ cdpEndpoint: this.opts.cdpUrl });
66
66
  let contexts = browser.contexts();
67
67
  for (let i = 0; i < 10 && contexts.length === 0; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "1.24.1",
3
+ "version": "1.24.2",
4
4
  "description": "Browser automation CLI for web scraping, headless browsing, SEO analysis, and AI agent workflows. A command-line alternative to Playwright, Puppeteer, and Selenium.",
5
5
  "type": "module",
6
6
  "bin": {