@xbrowser/cli 1.24.0 → 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.
- package/dist/{browser-Q67BJTI2.js → browser-DFM2SQDM.js} +2 -2
- package/dist/{browser-BVRFSUXU.js → browser-I3EZY3FL.js} +1 -1
- package/dist/{browser-DSYUAE7H.js → browser-WCHMUN6H.js} +2 -2
- package/dist/{cdp-driver-WWO2UZDR.js → cdp-driver-57QV7C5O.js} +1 -1
- package/dist/{cdp-driver-DC7EIUG5.js → cdp-driver-BFGFAVAN.js} +1 -1
- package/dist/{cdp-driver-T7D6H2RW.js → cdp-driver-HGWM5NRF.js} +90 -3
- package/dist/{chunk-4OU37CXP.js → chunk-BH3GIMWE.js} +90 -3
- package/dist/{chunk-BROEUPL3.js → chunk-LTLMDKWG.js} +35 -6
- package/dist/{chunk-A3MORKYR.js → chunk-OSZNJT4U.js} +29 -3
- package/dist/{chunk-2Q3GQRUP.js → chunk-PKXJST62.js} +1 -1
- package/dist/{chunk-T4WTKBWI.js → chunk-ROAXLDV7.js} +1 -1
- package/dist/{chunk-A7RZYSWM.js → chunk-SUHS2EWV.js} +35 -6
- package/dist/{chunk-7OP2ISEY.js → chunk-UR3R5LWF.js} +90 -3
- package/dist/{chunk-IFKJO2UR.js → chunk-X735U7G7.js} +90 -3
- package/dist/cli.js +35 -26
- package/dist/daemon-main.js +11 -8
- package/dist/index.d.ts +12 -0
- package/dist/index.js +36 -27
- package/dist/{plugin-singleton-GJO7EZXB.js → plugin-singleton-GGSBI3EG.js} +1 -1
- package/dist/{server-YVMWMNHD.js → server-A5R4OIGX.js} +2 -2
- package/dist/{server-NPINOVJS.js → server-Y5VVPRC6.js} +3 -3
- package/dist/{session-replayer-N665T3G5.js → session-replayer-PSAPQBZA.js} +1 -1
- package/package.json +1 -1
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
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,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
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";
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
12
|
+
} from "./chunk-PKXJST62.js";
|
|
13
13
|
import {
|
|
14
14
|
detectAntiBot,
|
|
15
15
|
formatDetectionMessage
|
|
@@ -7146,7 +7146,7 @@ function setWSServerCache(browserContext, server) {
|
|
|
7146
7146
|
import {
|
|
7147
7147
|
Core
|
|
7148
7148
|
} from "@dyyz1993/xcli-core";
|
|
7149
|
-
import { resolve as resolve2 } from "path";
|
|
7149
|
+
import { resolve as resolve2, basename, dirname as dirname3 } from "path";
|
|
7150
7150
|
import { existsSync as existsSync6, readdirSync } from "fs";
|
|
7151
7151
|
import { homedir as homedir4 } from "os";
|
|
7152
7152
|
|
|
@@ -7450,6 +7450,8 @@ var XBrowserPluginLoader = class {
|
|
|
7450
7450
|
core;
|
|
7451
7451
|
loader;
|
|
7452
7452
|
options;
|
|
7453
|
+
/** 目录名 → 该插件注册的 site 名(site.name ≠ 目录名时的命令别名兜底) */
|
|
7454
|
+
dirSiteAliases = /* @__PURE__ */ new Map();
|
|
7453
7455
|
constructor(options) {
|
|
7454
7456
|
patchLoginRequired();
|
|
7455
7457
|
this.options = options ?? {};
|
|
@@ -7487,15 +7489,39 @@ var XBrowserPluginLoader = class {
|
|
|
7487
7489
|
getLoadedPlugins() {
|
|
7488
7490
|
return this.loader.getLoadedPlugins();
|
|
7489
7491
|
}
|
|
7492
|
+
/**
|
|
7493
|
+
* 目录名别名兜底(小白用户实测踩坑):`plugin list` 显示目录名,但命令路由
|
|
7494
|
+
* 按 site.name 匹配——两者不一致时(如目录 alibaba-1688、site 名 1688),
|
|
7495
|
+
* 用户按 list 显示的名字敲命令会 "Unknown command"。映射在 scanAndLoad/
|
|
7496
|
+
* loadPlugin 时通过加载前后 site 快照 diff 记录(instance.siteName 返回的是
|
|
7497
|
+
* 插件 id 而非 site 名,不可用)。
|
|
7498
|
+
*/
|
|
7499
|
+
resolveSiteName(name) {
|
|
7500
|
+
if (this.core.loader.getSite(name)) return name;
|
|
7501
|
+
const aliased = this.dirSiteAliases.get(name);
|
|
7502
|
+
if (aliased && this.core.loader.getSite(aliased)) return aliased;
|
|
7503
|
+
return void 0;
|
|
7504
|
+
}
|
|
7505
|
+
/** 记录一次插件加载引入的 site:加载前后快照 diff(同插件可注册多个 site) */
|
|
7506
|
+
recordSiteAliases(pluginDirName, before) {
|
|
7507
|
+
for (const site of this.core.loader.getSites()) {
|
|
7508
|
+
if (!before.has(site.name)) this.dirSiteAliases.set(pluginDirName, site.name);
|
|
7509
|
+
}
|
|
7510
|
+
}
|
|
7490
7511
|
getPluginContract(siteName, commandName) {
|
|
7491
|
-
const
|
|
7512
|
+
const resolved = this.resolveSiteName(siteName);
|
|
7513
|
+
if (!resolved) return void 0;
|
|
7514
|
+
const site = this.core.loader.getSite(resolved);
|
|
7492
7515
|
if (!site) return void 0;
|
|
7493
7516
|
const contract = buildPluginContract(site);
|
|
7494
7517
|
if (!commandName) return contract;
|
|
7495
7518
|
return contract.commands.find((command) => command.name === commandName);
|
|
7496
7519
|
}
|
|
7497
7520
|
async loadPlugin(pluginPath, id) {
|
|
7498
|
-
|
|
7521
|
+
const before = new Set(this.core.loader.getSites().map((s) => s.name));
|
|
7522
|
+
const instance = await this.loader.loadPlugin(pluginPath, id);
|
|
7523
|
+
this.recordSiteAliases(basename(dirname3(pluginPath)), before);
|
|
7524
|
+
return instance;
|
|
7499
7525
|
}
|
|
7500
7526
|
async unloadPlugin(id) {
|
|
7501
7527
|
return this.loader.unloadPlugin(id);
|
|
@@ -7563,6 +7589,7 @@ var FAIL_CODES = {
|
|
|
7563
7589
|
selectorNotFound: "RETRYABLE: selector-not-found",
|
|
7564
7590
|
pageNotReady: "RETRYABLE: page-not-ready",
|
|
7565
7591
|
elementGone: "RETRYABLE: element-gone",
|
|
7592
|
+
inputNotRetained: "RETRYABLE: input-not-retained",
|
|
7566
7593
|
captcha: "HUMAN: captcha",
|
|
7567
7594
|
loginWall: "HUMAN: login-wall",
|
|
7568
7595
|
permissionDenied: "FATAL: permission-denied",
|
|
@@ -7582,6 +7609,7 @@ function classifyFailure(message) {
|
|
|
7582
7609
|
if (/selector|选择器|element not found|未找到元素|no such element|queryselector returned null/.test(msg))
|
|
7583
7610
|
return pick(FAIL_CODES.selectorNotFound);
|
|
7584
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);
|
|
7585
7613
|
if (/net::err|404|navigation failed|页面不存在|target closed|element-gone/.test(msg))
|
|
7586
7614
|
return pick(FAIL_CODES.targetGone);
|
|
7587
7615
|
return pick(FAIL_CODES.unknown);
|
|
@@ -8536,7 +8564,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
8536
8564
|
}
|
|
8537
8565
|
let targetPageOverride = null;
|
|
8538
8566
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
8539
|
-
const { findTargetPage } = await import("./browser-
|
|
8567
|
+
const { findTargetPage } = await import("./browser-DFM2SQDM.js");
|
|
8540
8568
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
8541
8569
|
if (!targetPageOverride) {
|
|
8542
8570
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8837,7 +8865,8 @@ async function executeChain(input, options) {
|
|
|
8837
8865
|
const cmdArgs = parts.slice(1);
|
|
8838
8866
|
const loader = await getPluginLoader();
|
|
8839
8867
|
const internalLoader = loader.getCore().loader;
|
|
8840
|
-
const
|
|
8868
|
+
const resolvedName = loader.resolveSiteName(cmdName);
|
|
8869
|
+
const site = resolvedName ? internalLoader.getSite(resolvedName) : void 0;
|
|
8841
8870
|
if (site) {
|
|
8842
8871
|
const subCommand = cmdArgs[0];
|
|
8843
8872
|
if (!subCommand) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Core
|
|
4
4
|
} from "@dyyz1993/xcli-core";
|
|
5
|
-
import { resolve as resolve2 } from "path";
|
|
5
|
+
import { resolve as resolve2, basename, dirname } from "path";
|
|
6
6
|
import { existsSync as existsSync3, readdirSync } from "fs";
|
|
7
7
|
import { homedir } from "os";
|
|
8
8
|
|
|
@@ -306,6 +306,8 @@ var XBrowserPluginLoader = class {
|
|
|
306
306
|
core;
|
|
307
307
|
loader;
|
|
308
308
|
options;
|
|
309
|
+
/** 目录名 → 该插件注册的 site 名(site.name ≠ 目录名时的命令别名兜底) */
|
|
310
|
+
dirSiteAliases = /* @__PURE__ */ new Map();
|
|
309
311
|
constructor(options) {
|
|
310
312
|
patchLoginRequired();
|
|
311
313
|
this.options = options ?? {};
|
|
@@ -343,15 +345,39 @@ var XBrowserPluginLoader = class {
|
|
|
343
345
|
getLoadedPlugins() {
|
|
344
346
|
return this.loader.getLoadedPlugins();
|
|
345
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* 目录名别名兜底(小白用户实测踩坑):`plugin list` 显示目录名,但命令路由
|
|
350
|
+
* 按 site.name 匹配——两者不一致时(如目录 alibaba-1688、site 名 1688),
|
|
351
|
+
* 用户按 list 显示的名字敲命令会 "Unknown command"。映射在 scanAndLoad/
|
|
352
|
+
* loadPlugin 时通过加载前后 site 快照 diff 记录(instance.siteName 返回的是
|
|
353
|
+
* 插件 id 而非 site 名,不可用)。
|
|
354
|
+
*/
|
|
355
|
+
resolveSiteName(name) {
|
|
356
|
+
if (this.core.loader.getSite(name)) return name;
|
|
357
|
+
const aliased = this.dirSiteAliases.get(name);
|
|
358
|
+
if (aliased && this.core.loader.getSite(aliased)) return aliased;
|
|
359
|
+
return void 0;
|
|
360
|
+
}
|
|
361
|
+
/** 记录一次插件加载引入的 site:加载前后快照 diff(同插件可注册多个 site) */
|
|
362
|
+
recordSiteAliases(pluginDirName, before) {
|
|
363
|
+
for (const site of this.core.loader.getSites()) {
|
|
364
|
+
if (!before.has(site.name)) this.dirSiteAliases.set(pluginDirName, site.name);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
346
367
|
getPluginContract(siteName, commandName) {
|
|
347
|
-
const
|
|
368
|
+
const resolved = this.resolveSiteName(siteName);
|
|
369
|
+
if (!resolved) return void 0;
|
|
370
|
+
const site = this.core.loader.getSite(resolved);
|
|
348
371
|
if (!site) return void 0;
|
|
349
372
|
const contract = buildPluginContract(site);
|
|
350
373
|
if (!commandName) return contract;
|
|
351
374
|
return contract.commands.find((command) => command.name === commandName);
|
|
352
375
|
}
|
|
353
376
|
async loadPlugin(pluginPath, id) {
|
|
354
|
-
|
|
377
|
+
const before = new Set(this.core.loader.getSites().map((s) => s.name));
|
|
378
|
+
const instance = await this.loader.loadPlugin(pluginPath, id);
|
|
379
|
+
this.recordSiteAliases(basename(dirname(pluginPath)), before);
|
|
380
|
+
return instance;
|
|
355
381
|
}
|
|
356
382
|
async unloadPlugin(id) {
|
|
357
383
|
return this.loader.unloadPlugin(id);
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
resolveLaunchOpts,
|
|
21
21
|
saveSessionDiskMeta,
|
|
22
22
|
setActivePage
|
|
23
|
-
} from "./chunk-
|
|
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);
|
|
@@ -7045,7 +7047,7 @@ function attachDetectAntiBot(ctx) {
|
|
|
7045
7047
|
import {
|
|
7046
7048
|
Core
|
|
7047
7049
|
} from "@dyyz1993/xcli-core";
|
|
7048
|
-
import { resolve as resolve2 } from "path";
|
|
7050
|
+
import { resolve as resolve2, basename, dirname as dirname3 } from "path";
|
|
7049
7051
|
import { existsSync as existsSync6, readdirSync } from "fs";
|
|
7050
7052
|
import { homedir as homedir5 } from "os";
|
|
7051
7053
|
|
|
@@ -7349,6 +7351,8 @@ var XBrowserPluginLoader = class {
|
|
|
7349
7351
|
core;
|
|
7350
7352
|
loader;
|
|
7351
7353
|
options;
|
|
7354
|
+
/** 目录名 → 该插件注册的 site 名(site.name ≠ 目录名时的命令别名兜底) */
|
|
7355
|
+
dirSiteAliases = /* @__PURE__ */ new Map();
|
|
7352
7356
|
constructor(options) {
|
|
7353
7357
|
patchLoginRequired();
|
|
7354
7358
|
this.options = options ?? {};
|
|
@@ -7386,15 +7390,39 @@ var XBrowserPluginLoader = class {
|
|
|
7386
7390
|
getLoadedPlugins() {
|
|
7387
7391
|
return this.loader.getLoadedPlugins();
|
|
7388
7392
|
}
|
|
7393
|
+
/**
|
|
7394
|
+
* 目录名别名兜底(小白用户实测踩坑):`plugin list` 显示目录名,但命令路由
|
|
7395
|
+
* 按 site.name 匹配——两者不一致时(如目录 alibaba-1688、site 名 1688),
|
|
7396
|
+
* 用户按 list 显示的名字敲命令会 "Unknown command"。映射在 scanAndLoad/
|
|
7397
|
+
* loadPlugin 时通过加载前后 site 快照 diff 记录(instance.siteName 返回的是
|
|
7398
|
+
* 插件 id 而非 site 名,不可用)。
|
|
7399
|
+
*/
|
|
7400
|
+
resolveSiteName(name) {
|
|
7401
|
+
if (this.core.loader.getSite(name)) return name;
|
|
7402
|
+
const aliased = this.dirSiteAliases.get(name);
|
|
7403
|
+
if (aliased && this.core.loader.getSite(aliased)) return aliased;
|
|
7404
|
+
return void 0;
|
|
7405
|
+
}
|
|
7406
|
+
/** 记录一次插件加载引入的 site:加载前后快照 diff(同插件可注册多个 site) */
|
|
7407
|
+
recordSiteAliases(pluginDirName, before) {
|
|
7408
|
+
for (const site of this.core.loader.getSites()) {
|
|
7409
|
+
if (!before.has(site.name)) this.dirSiteAliases.set(pluginDirName, site.name);
|
|
7410
|
+
}
|
|
7411
|
+
}
|
|
7389
7412
|
getPluginContract(siteName, commandName) {
|
|
7390
|
-
const
|
|
7413
|
+
const resolved = this.resolveSiteName(siteName);
|
|
7414
|
+
if (!resolved) return void 0;
|
|
7415
|
+
const site = this.core.loader.getSite(resolved);
|
|
7391
7416
|
if (!site) return void 0;
|
|
7392
7417
|
const contract = buildPluginContract(site);
|
|
7393
7418
|
if (!commandName) return contract;
|
|
7394
7419
|
return contract.commands.find((command) => command.name === commandName);
|
|
7395
7420
|
}
|
|
7396
7421
|
async loadPlugin(pluginPath, id) {
|
|
7397
|
-
|
|
7422
|
+
const before = new Set(this.core.loader.getSites().map((s) => s.name));
|
|
7423
|
+
const instance = await this.loader.loadPlugin(pluginPath, id);
|
|
7424
|
+
this.recordSiteAliases(basename(dirname3(pluginPath)), before);
|
|
7425
|
+
return instance;
|
|
7398
7426
|
}
|
|
7399
7427
|
async unloadPlugin(id) {
|
|
7400
7428
|
return this.loader.unloadPlugin(id);
|
|
@@ -8216,7 +8244,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
8216
8244
|
}
|
|
8217
8245
|
let targetPageOverride = null;
|
|
8218
8246
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
8219
|
-
const { findTargetPage } = await import("./browser-
|
|
8247
|
+
const { findTargetPage } = await import("./browser-I3EZY3FL.js");
|
|
8220
8248
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
8221
8249
|
if (!targetPageOverride) {
|
|
8222
8250
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -8517,7 +8545,8 @@ async function executeChain(input, options) {
|
|
|
8517
8545
|
const cmdArgs = parts.slice(1);
|
|
8518
8546
|
const loader = await getPluginLoader();
|
|
8519
8547
|
const internalLoader = loader.getCore().loader;
|
|
8520
|
-
const
|
|
8548
|
+
const resolvedName = loader.resolveSiteName(cmdName);
|
|
8549
|
+
const site = resolvedName ? internalLoader.getSite(resolvedName) : void 0;
|
|
8521
8550
|
if (site) {
|
|
8522
8551
|
const subCommand = cmdArgs[0];
|
|
8523
8552
|
if (!subCommand) {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
83
|
+
} from "./chunk-X735U7G7.js";
|
|
84
84
|
import "./chunk-GYB5BOSP.js";
|
|
85
85
|
import "./chunk-MYH6FIBZ.js";
|
|
86
86
|
import {
|
|
@@ -629,6 +629,9 @@ async function installFromMarketplace(pluginsDir, slug, options) {
|
|
|
629
629
|
const realSlug = String(plugin.slug || slug);
|
|
630
630
|
try {
|
|
631
631
|
await downloadAndExtractMarketplaceTarball(baseUrl, realSlug, tmpDir, targetDir);
|
|
632
|
+
} catch (err) {
|
|
633
|
+
safeCleanup5(targetDir);
|
|
634
|
+
throw err;
|
|
632
635
|
} finally {
|
|
633
636
|
safeCleanup5(tmpDir);
|
|
634
637
|
}
|
|
@@ -802,6 +805,7 @@ function ensureIndexFile(plugin, name, targetDir) {
|
|
|
802
805
|
}
|
|
803
806
|
|
|
804
807
|
// src/plugin/installer.ts
|
|
808
|
+
import { safeCleanup as safeCleanup6 } from "@dyyz1993/xcli-core";
|
|
805
809
|
var PluginInstaller = class {
|
|
806
810
|
pluginsDir;
|
|
807
811
|
constructor(pluginsDir) {
|
|
@@ -827,31 +831,29 @@ var PluginInstaller = class {
|
|
|
827
831
|
}
|
|
828
832
|
mkdirSync4(targetDir, { recursive: true });
|
|
829
833
|
const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
|
|
834
|
+
const withCleanup = async (fn) => {
|
|
835
|
+
try {
|
|
836
|
+
const r = await fn();
|
|
837
|
+
await this.fixSharedDeps(targetDir);
|
|
838
|
+
ensurePluginDependencies(this.pluginsDir);
|
|
839
|
+
if (!existsSync6(resolve6(targetDir, "index.ts")) && !existsSync6(resolve6(targetDir, "index.js"))) {
|
|
840
|
+
throw new Error("installed package has no index.ts/js entry");
|
|
841
|
+
}
|
|
842
|
+
return r;
|
|
843
|
+
} catch (err) {
|
|
844
|
+
safeCleanup6(targetDir);
|
|
845
|
+
throw err;
|
|
846
|
+
}
|
|
847
|
+
};
|
|
830
848
|
switch (type) {
|
|
831
849
|
case "local":
|
|
832
|
-
return await installFromLocal(source, name, targetDir)
|
|
833
|
-
await this.fixSharedDeps(targetDir);
|
|
834
|
-
ensurePluginDependencies(this.pluginsDir);
|
|
835
|
-
return r;
|
|
836
|
-
});
|
|
850
|
+
return await withCleanup(() => installFromLocal(source, name, targetDir));
|
|
837
851
|
case "npm":
|
|
838
|
-
return await installFromNpm(resolvedSource, name, targetDir)
|
|
839
|
-
await this.fixSharedDeps(targetDir);
|
|
840
|
-
ensurePluginDependencies(this.pluginsDir);
|
|
841
|
-
return r;
|
|
842
|
-
});
|
|
852
|
+
return await withCleanup(() => installFromNpm(resolvedSource, name, targetDir));
|
|
843
853
|
case "git":
|
|
844
|
-
return await installFromGit(source, name, targetDir)
|
|
845
|
-
await this.fixSharedDeps(targetDir);
|
|
846
|
-
ensurePluginDependencies(this.pluginsDir);
|
|
847
|
-
return r;
|
|
848
|
-
});
|
|
854
|
+
return await withCleanup(() => installFromGit(source, name, targetDir));
|
|
849
855
|
case "url":
|
|
850
|
-
return await installFromUrl(source, name, targetDir)
|
|
851
|
-
await this.fixSharedDeps(targetDir);
|
|
852
|
-
ensurePluginDependencies(this.pluginsDir);
|
|
853
|
-
return r;
|
|
854
|
-
});
|
|
856
|
+
return await withCleanup(() => installFromUrl(source, name, targetDir));
|
|
855
857
|
}
|
|
856
858
|
}
|
|
857
859
|
/**
|
|
@@ -5816,7 +5818,14 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
5816
5818
|
default: {
|
|
5817
5819
|
const loader = await getPluginLoader();
|
|
5818
5820
|
const internalLoader = loader.getCore().loader;
|
|
5819
|
-
const
|
|
5821
|
+
const resolvedSiteName = loader.resolveSiteName(command);
|
|
5822
|
+
const site = resolvedSiteName ? internalLoader.getSite(resolvedSiteName) : void 0;
|
|
5823
|
+
if (site && resolvedSiteName !== command) {
|
|
5824
|
+
if (mode !== "json" && mode !== "yaml") {
|
|
5825
|
+
process.stderr.write(`\u2139\uFE0F "${command}" \u7684\u547D\u4EE4\u540D\u662F "${resolvedSiteName}"\uFF08\u63D2\u4EF6\u76EE\u5F55\u540D\u4E0E\u7AD9\u70B9\u540D\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u81EA\u52A8\u6620\u5C04\uFF09
|
|
5826
|
+
`);
|
|
5827
|
+
}
|
|
5828
|
+
}
|
|
5820
5829
|
if (!site) {
|
|
5821
5830
|
const globalFlagSet = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--help", "-h", "--version", "--output", "-o"]);
|
|
5822
5831
|
const cleanParts = [];
|
|
@@ -6007,7 +6016,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
6007
6016
|
const targetPage = pages[cmdTabIndex];
|
|
6008
6017
|
await targetPage.bringToFront().catch(() => {
|
|
6009
6018
|
});
|
|
6010
|
-
const { setActivePage } = await import("./browser-
|
|
6019
|
+
const { setActivePage } = await import("./browser-I3EZY3FL.js");
|
|
6011
6020
|
setActivePage(session, targetPage);
|
|
6012
6021
|
}
|
|
6013
6022
|
}
|
|
@@ -6272,7 +6281,7 @@ async function handleRemote(args, options, mode) {
|
|
|
6272
6281
|
}
|
|
6273
6282
|
}
|
|
6274
6283
|
async function startMcpServer() {
|
|
6275
|
-
const { startMcpStdio } = await import("./server-
|
|
6284
|
+
const { startMcpStdio } = await import("./server-A5R4OIGX.js");
|
|
6276
6285
|
startMcpStdio();
|
|
6277
6286
|
}
|
|
6278
6287
|
|
|
@@ -6291,7 +6300,7 @@ async function main() {
|
|
|
6291
6300
|
const command = process.argv[2];
|
|
6292
6301
|
const isLongRunning = command === "preview" || command === "serve" || command === "mcp";
|
|
6293
6302
|
if (!isLongRunning) {
|
|
6294
|
-
const { ensureProcessCanExit } = await import("./browser-
|
|
6303
|
+
const { ensureProcessCanExit } = await import("./browser-I3EZY3FL.js");
|
|
6295
6304
|
await ensureProcessCanExit().catch(() => {
|
|
6296
6305
|
});
|
|
6297
6306
|
process.exit(process.exitCode || exitCode);
|
package/dist/daemon-main.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-JKVUFP3G.js";
|
|
5
5
|
import {
|
|
6
6
|
getPluginLoader
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OSZNJT4U.js";
|
|
8
8
|
import {
|
|
9
9
|
version
|
|
10
10
|
} from "./chunk-43TEMG4U.js";
|
|
@@ -38,10 +38,10 @@ import {
|
|
|
38
38
|
resolveLaunchOpts,
|
|
39
39
|
saveSessionDiskMeta,
|
|
40
40
|
setActivePage
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-ROAXLDV7.js";
|
|
42
42
|
import {
|
|
43
43
|
createRuleEngine
|
|
44
|
-
} from "./chunk-
|
|
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-
|
|
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.`);
|
|
@@ -8098,7 +8100,8 @@ async function executeChain(input, options) {
|
|
|
8098
8100
|
const cmdArgs = parts.slice(1);
|
|
8099
8101
|
const loader = await getPluginLoader();
|
|
8100
8102
|
const internalLoader = loader.getCore().loader;
|
|
8101
|
-
const
|
|
8103
|
+
const resolvedName = loader.resolveSiteName(cmdName);
|
|
8104
|
+
const site = resolvedName ? internalLoader.getSite(resolvedName) : void 0;
|
|
8102
8105
|
if (site) {
|
|
8103
8106
|
const subCommand = cmdArgs[0];
|
|
8104
8107
|
if (!subCommand) {
|
|
@@ -9238,9 +9241,9 @@ function createRPCHandler() {
|
|
|
9238
9241
|
return result;
|
|
9239
9242
|
}
|
|
9240
9243
|
async function handlePluginsReload() {
|
|
9241
|
-
const { resetPluginLoader } = await import("./plugin-singleton-
|
|
9244
|
+
const { resetPluginLoader } = await import("./plugin-singleton-GGSBI3EG.js");
|
|
9242
9245
|
resetPluginLoader();
|
|
9243
|
-
const loader = await import("./plugin-singleton-
|
|
9246
|
+
const loader = await import("./plugin-singleton-GGSBI3EG.js").then((m) => m.getPluginLoader());
|
|
9244
9247
|
const sites = loader.getCore().loader.getSites();
|
|
9245
9248
|
return { ok: true, plugins: sites.length };
|
|
9246
9249
|
}
|
|
@@ -9636,7 +9639,7 @@ function createRPCHandler() {
|
|
|
9636
9639
|
if (isNewFormat) {
|
|
9637
9640
|
try {
|
|
9638
9641
|
const replayErrors = [];
|
|
9639
|
-
const { SessionReplayer } = await import("./session-replayer-
|
|
9642
|
+
const { SessionReplayer } = await import("./session-replayer-PSAPQBZA.js");
|
|
9640
9643
|
const replayer = new SessionReplayer({
|
|
9641
9644
|
page: session.page,
|
|
9642
9645
|
stepDelay: slowMo * 500,
|
package/dist/index.d.ts
CHANGED
|
@@ -1634,6 +1634,8 @@ declare class XBrowserPluginLoader {
|
|
|
1634
1634
|
private core;
|
|
1635
1635
|
private loader;
|
|
1636
1636
|
private options;
|
|
1637
|
+
/** 目录名 → 该插件注册的 site 名(site.name ≠ 目录名时的命令别名兜底) */
|
|
1638
|
+
private dirSiteAliases;
|
|
1637
1639
|
constructor(options?: PluginLoaderOptions);
|
|
1638
1640
|
getAPI(): XCLIAPI;
|
|
1639
1641
|
/**
|
|
@@ -1644,6 +1646,16 @@ declare class XBrowserPluginLoader {
|
|
|
1644
1646
|
getPlugin(id: string): PluginInstance | undefined;
|
|
1645
1647
|
getPluginStatus(id: string): PluginStatus;
|
|
1646
1648
|
getLoadedPlugins(): PluginInstance[];
|
|
1649
|
+
/**
|
|
1650
|
+
* 目录名别名兜底(小白用户实测踩坑):`plugin list` 显示目录名,但命令路由
|
|
1651
|
+
* 按 site.name 匹配——两者不一致时(如目录 alibaba-1688、site 名 1688),
|
|
1652
|
+
* 用户按 list 显示的名字敲命令会 "Unknown command"。映射在 scanAndLoad/
|
|
1653
|
+
* loadPlugin 时通过加载前后 site 快照 diff 记录(instance.siteName 返回的是
|
|
1654
|
+
* 插件 id 而非 site 名,不可用)。
|
|
1655
|
+
*/
|
|
1656
|
+
resolveSiteName(name: string): string | undefined;
|
|
1657
|
+
/** 记录一次插件加载引入的 site:加载前后快照 diff(同插件可注册多个 site) */
|
|
1658
|
+
private recordSiteAliases;
|
|
1647
1659
|
getPluginContract(siteName: string, commandName?: string): PluginContract | PluginCommandContract | undefined;
|
|
1648
1660
|
loadPlugin(pluginPath: string, id?: string): Promise<PluginInstance>;
|
|
1649
1661
|
unloadPlugin(id: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
splitCommand,
|
|
49
49
|
waitForAIResponse,
|
|
50
50
|
waitForPage
|
|
51
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
123
|
+
import "./chunk-UR3R5LWF.js";
|
|
124
124
|
import "./chunk-GYB5BOSP.js";
|
|
125
125
|
import "./chunk-MYH6FIBZ.js";
|
|
126
126
|
import {
|
|
@@ -720,6 +720,9 @@ async function installFromMarketplace(pluginsDir, slug, options) {
|
|
|
720
720
|
const realSlug = String(plugin.slug || slug);
|
|
721
721
|
try {
|
|
722
722
|
await downloadAndExtractMarketplaceTarball(baseUrl, realSlug, tmpDir, targetDir);
|
|
723
|
+
} catch (err) {
|
|
724
|
+
safeCleanup5(targetDir);
|
|
725
|
+
throw err;
|
|
723
726
|
} finally {
|
|
724
727
|
safeCleanup5(tmpDir);
|
|
725
728
|
}
|
|
@@ -893,6 +896,7 @@ function ensureIndexFile(plugin, name, targetDir) {
|
|
|
893
896
|
}
|
|
894
897
|
|
|
895
898
|
// src/plugin/installer.ts
|
|
899
|
+
import { safeCleanup as safeCleanup6 } from "@dyyz1993/xcli-core";
|
|
896
900
|
var PluginInstaller = class {
|
|
897
901
|
pluginsDir;
|
|
898
902
|
constructor(pluginsDir) {
|
|
@@ -918,31 +922,29 @@ var PluginInstaller = class {
|
|
|
918
922
|
}
|
|
919
923
|
mkdirSync4(targetDir, { recursive: true });
|
|
920
924
|
const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
|
|
925
|
+
const withCleanup = async (fn) => {
|
|
926
|
+
try {
|
|
927
|
+
const r = await fn();
|
|
928
|
+
await this.fixSharedDeps(targetDir);
|
|
929
|
+
ensurePluginDependencies(this.pluginsDir);
|
|
930
|
+
if (!existsSync6(resolve6(targetDir, "index.ts")) && !existsSync6(resolve6(targetDir, "index.js"))) {
|
|
931
|
+
throw new Error("installed package has no index.ts/js entry");
|
|
932
|
+
}
|
|
933
|
+
return r;
|
|
934
|
+
} catch (err) {
|
|
935
|
+
safeCleanup6(targetDir);
|
|
936
|
+
throw err;
|
|
937
|
+
}
|
|
938
|
+
};
|
|
921
939
|
switch (type) {
|
|
922
940
|
case "local":
|
|
923
|
-
return await installFromLocal(source, name, targetDir)
|
|
924
|
-
await this.fixSharedDeps(targetDir);
|
|
925
|
-
ensurePluginDependencies(this.pluginsDir);
|
|
926
|
-
return r;
|
|
927
|
-
});
|
|
941
|
+
return await withCleanup(() => installFromLocal(source, name, targetDir));
|
|
928
942
|
case "npm":
|
|
929
|
-
return await installFromNpm(resolvedSource, name, targetDir)
|
|
930
|
-
await this.fixSharedDeps(targetDir);
|
|
931
|
-
ensurePluginDependencies(this.pluginsDir);
|
|
932
|
-
return r;
|
|
933
|
-
});
|
|
943
|
+
return await withCleanup(() => installFromNpm(resolvedSource, name, targetDir));
|
|
934
944
|
case "git":
|
|
935
|
-
return await installFromGit(source, name, targetDir)
|
|
936
|
-
await this.fixSharedDeps(targetDir);
|
|
937
|
-
ensurePluginDependencies(this.pluginsDir);
|
|
938
|
-
return r;
|
|
939
|
-
});
|
|
945
|
+
return await withCleanup(() => installFromGit(source, name, targetDir));
|
|
940
946
|
case "url":
|
|
941
|
-
return await installFromUrl(source, name, targetDir)
|
|
942
|
-
await this.fixSharedDeps(targetDir);
|
|
943
|
-
ensurePluginDependencies(this.pluginsDir);
|
|
944
|
-
return r;
|
|
945
|
-
});
|
|
947
|
+
return await withCleanup(() => installFromUrl(source, name, targetDir));
|
|
946
948
|
}
|
|
947
949
|
}
|
|
948
950
|
/**
|
|
@@ -5912,7 +5914,14 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
5912
5914
|
default: {
|
|
5913
5915
|
const loader = await getPluginLoader();
|
|
5914
5916
|
const internalLoader = loader.getCore().loader;
|
|
5915
|
-
const
|
|
5917
|
+
const resolvedSiteName = loader.resolveSiteName(command);
|
|
5918
|
+
const site = resolvedSiteName ? internalLoader.getSite(resolvedSiteName) : void 0;
|
|
5919
|
+
if (site && resolvedSiteName !== command) {
|
|
5920
|
+
if (mode !== "json" && mode !== "yaml") {
|
|
5921
|
+
process.stderr.write(`\u2139\uFE0F "${command}" \u7684\u547D\u4EE4\u540D\u662F "${resolvedSiteName}"\uFF08\u63D2\u4EF6\u76EE\u5F55\u540D\u4E0E\u7AD9\u70B9\u540D\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u81EA\u52A8\u6620\u5C04\uFF09
|
|
5922
|
+
`);
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5916
5925
|
if (!site) {
|
|
5917
5926
|
const globalFlagSet = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--help", "-h", "--version", "--output", "-o"]);
|
|
5918
5927
|
const cleanParts = [];
|
|
@@ -6103,7 +6112,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
6103
6112
|
const targetPage = pages[cmdTabIndex];
|
|
6104
6113
|
await targetPage.bringToFront().catch(() => {
|
|
6105
6114
|
});
|
|
6106
|
-
const { setActivePage } = await import("./browser-
|
|
6115
|
+
const { setActivePage } = await import("./browser-DFM2SQDM.js");
|
|
6107
6116
|
setActivePage(session, targetPage);
|
|
6108
6117
|
}
|
|
6109
6118
|
}
|
|
@@ -6368,7 +6377,7 @@ async function handleRemote(args, options, mode) {
|
|
|
6368
6377
|
}
|
|
6369
6378
|
}
|
|
6370
6379
|
async function startMcpServer() {
|
|
6371
|
-
const { startMcpStdio } = await import("./server-
|
|
6380
|
+
const { startMcpStdio } = await import("./server-Y5VVPRC6.js");
|
|
6372
6381
|
startMcpStdio();
|
|
6373
6382
|
}
|
|
6374
6383
|
|
|
@@ -9283,7 +9292,7 @@ var DataCollector = class {
|
|
|
9283
9292
|
return results;
|
|
9284
9293
|
}
|
|
9285
9294
|
async createBrowserContext() {
|
|
9286
|
-
const { launch } = await import("./cdp-driver-
|
|
9295
|
+
const { launch } = await import("./cdp-driver-57QV7C5O.js");
|
|
9287
9296
|
const { browser } = await launch({
|
|
9288
9297
|
headless: true,
|
|
9289
9298
|
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
executeChain,
|
|
3
3
|
executeCommand
|
|
4
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
5
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
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.
|
|
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": {
|