@duckmind/dm-darwin-x64 0.52.6 → 0.53.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dm +0 -0
- package/extensions/.dm-extensions.json +14 -10
- package/extensions/dm-cua/bin/browser-cua.mjs +6 -6
- package/extensions/dm-cua/index.js +5 -5
- package/extensions/dm-cua/src/browser-cua-lib.mjs +2 -2
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +8 -1
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +1 -1
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +1 -1
- package/extensions/greedysearch-dm/bin/cdp.mjs +29 -20
- package/extensions/greedysearch-dm/bin/gschrome.mjs +1 -8
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +1 -1
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +1 -2
- package/extensions/greedysearch-dm/bin/launch.mjs +9 -11
- package/extensions/greedysearch-dm/bin/mcp.mjs +375 -0
- package/extensions/greedysearch-dm/bin/search.mjs +309 -157
- package/extensions/greedysearch-dm/bin/visible.mjs +1 -1
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +205 -68
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +229 -140
- package/extensions/greedysearch-dm/extractors/common.mjs +160 -50
- package/extensions/greedysearch-dm/extractors/consensus.mjs +226 -93
- package/extensions/greedysearch-dm/extractors/consent.mjs +44 -17
- package/extensions/greedysearch-dm/extractors/gemini.mjs +277 -138
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +172 -64
- package/extensions/greedysearch-dm/extractors/logically.mjs +183 -69
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +331 -65
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +118 -41
- package/extensions/greedysearch-dm/index.js +23 -20
- package/extensions/greedysearch-dm/package.json +8 -3
- package/extensions/greedysearch-dm/skills/greedy-search/skill.md +8 -8
- package/extensions/greedysearch-dm/src/fetcher.mjs +2 -2
- package/extensions/greedysearch-dm/src/formatters/results.js +8 -6
- package/extensions/greedysearch-dm/src/github.mjs +7 -7
- package/extensions/greedysearch-dm/src/reddit.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +10 -8
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +48 -0
- package/extensions/greedysearch-dm/src/search/chrome.mjs +130 -54
- package/extensions/greedysearch-dm/src/search/constants.mjs +6 -6
- package/extensions/greedysearch-dm/src/search/engines.mjs +9 -9
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +160 -83
- package/extensions/greedysearch-dm/src/search/minimize.mjs +1 -0
- package/extensions/greedysearch-dm/src/search/paths.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/pdf.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/port-pid.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/progress.mjs +2 -0
- package/extensions/greedysearch-dm/src/search/recovery.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/research.mjs +262 -167
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +11 -0
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +827 -0
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +9 -9
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +10 -10
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +23 -20
- package/extensions/greedysearch-dm/src/tools/shared.js +10 -9
- package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +1 -1
- package/package.json +1 -1
- package/extensions/greedysearch-dm/src/search/cdp-endpoint.mjs +0 -3
- package/extensions/greedysearch-dm/src/search/launcher-paths.mjs +0 -3
- package/extensions/greedysearch-dm/src/search/partial-output.mjs +0 -1
- package/extensions/greedysearch-dm/src/utils/browser-executable.mjs +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{randomInt as
|
|
3
|
-
`)[0]?.slice(0,8);if(!
|
|
4
|
-
`)}return
|
|
2
|
+
import{randomInt as U}from"node:crypto";import{spawn as Y}from"node:child_process";import{basename as X}from"node:path";import{dirname as G,join as z}from"node:path";import{fileURLToPath as Q}from"node:url";function Z(e=process.env,t=process.execPath){let r=e.GREEDY_SEARCH_NODE||e.NODE_BINARY||e.NODE;if(r?.trim())return r.trim();let n=X(t||"").toLowerCase();if(n==="node"||n==="node.exe")return t;return"node"}var K=G(Q(import.meta.url)),ee=z(K,"..","bin","cdp.mjs"),te=new Set(["list","snap","eval","shot","html","nav","net","click","clickxy","type","loadall","evalraw","browse","stop","--tab"]);function re(e){if(!Array.isArray(e)||e.length===0)throw Error("cdp: args must be a non-empty array");if(e[0]==="test")return e.map((t,r)=>O(t,r));if(!te.has(e[0]))throw Error(`cdp: unknown subcommand '${e[0]}'`);return e.map((t,r)=>O(t,r))}function O(e,t){if(typeof e!=="string")throw Error(`cdp: argv[${t}] must be a string (got ${typeof e})`);if(e.includes("\x00"))throw Error(`cdp: argv[${t}] contains a null byte`);return e}function c(e,t=30000){return ne(e,null,t)}function ne(e,t=null,r=30000){let n=re(e);return new Promise((a,l)=>{let i=Y(Z(),[ee,...n],{stdio:[t==null?"ignore":"pipe","pipe","pipe"]});if(t!=null)i.stdin.write(t),i.stdin.end();let o="",s="";i.stdout.on("data",(u)=>o+=u),i.stderr.on("data",(u)=>s+=u);let f=setTimeout(()=>{i.kill(),l(Error(`cdp timeout: ${e[0]}`))},r);i.on("close",(u)=>{if(clearTimeout(f),u===0)a(o.trim());else l(Error(s.trim()||`cdp exit ${u}`))})})}async function C(e){if(e)return e;let t=(await c(["list"])).split(`
|
|
3
|
+
`)[0]?.slice(0,8);if(!t)throw Error("No Chrome tabs found. Is Chrome running with --remote-debugging-port=9222?");let r=await c(["evalraw",t,"Target.createTarget",'{"url":"about:blank"}']),n;try{n=JSON.parse(r)}catch(i){throw Error(`Target.createTarget returned invalid JSON: ${i.message}`)}let{targetId:a}=n;if(!a)throw Error("Target.createTarget did not return a targetId");await c(["list"]);let l=a.slice(0,8);try{await ie(l)}catch(i){process.stderr.write(`[getOrOpenTab] stealth injection failed: ${i.message}
|
|
4
|
+
`)}return l}async function S(e,t){let r=`
|
|
5
5
|
(() => {
|
|
6
|
-
window.${
|
|
6
|
+
window.${t} = null;
|
|
7
7
|
const _clipboard = navigator.clipboard;
|
|
8
8
|
if (!_clipboard) return;
|
|
9
9
|
const _origWriteText = typeof _clipboard.writeText === 'function'
|
|
@@ -14,7 +14,7 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
14
14
|
: null;
|
|
15
15
|
|
|
16
16
|
_clipboard.writeText = function(text) {
|
|
17
|
-
window.${
|
|
17
|
+
window.${t} = String(text ?? '');
|
|
18
18
|
if (!_origWriteText) return Promise.resolve();
|
|
19
19
|
// The OS/browser clipboard write may be denied in automated Chrome or
|
|
20
20
|
// when the tab is not focused. We only need the captured text; returning
|
|
@@ -28,7 +28,7 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
28
28
|
for (const item of items || []) {
|
|
29
29
|
if (item.types && item.types.includes('text/plain')) {
|
|
30
30
|
const blob = await item.getType('text/plain');
|
|
31
|
-
window.${
|
|
31
|
+
window.${t} = await blob.text();
|
|
32
32
|
break;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -38,7 +38,26 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
38
38
|
catch (_) { return undefined; }
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
|
-
`;await c(["eval",e,
|
|
41
|
+
`;await c(["eval",e,r])}async function D(e,t,r,n={}){let{timeoutMs:a=2600}=n,l=n.retryClick!=null?n.retryClick:Math.floor(a*0.4),i=String.raw`
|
|
42
|
+
new Promise((resolve) => {
|
|
43
|
+
const _deadline = Date.now() + ${a};
|
|
44
|
+
const _retryAt = Date.now() + ${l};
|
|
45
|
+
let _retried = false;
|
|
46
|
+
function _click() { try { ${t}; } catch(_) {} }
|
|
47
|
+
function _poll() {
|
|
48
|
+
const val = window.${r};
|
|
49
|
+
if (val) { resolve(val); return; }
|
|
50
|
+
if (!_retried && Date.now() >= _retryAt) {
|
|
51
|
+
_retried = true;
|
|
52
|
+
_click();
|
|
53
|
+
}
|
|
54
|
+
if (Date.now() < _deadline) { setTimeout(_poll, 100); }
|
|
55
|
+
else { resolve(window.${r} || ''); }
|
|
56
|
+
}
|
|
57
|
+
_click();
|
|
58
|
+
_poll();
|
|
59
|
+
})
|
|
60
|
+
`;return await c(["eval",e,i],a+5000).catch(()=>"")}async function ie(e){await c(["evalraw",e,"Page.addScriptToEvaluateOnNewDocument",JSON.stringify({source:`
|
|
42
61
|
(function() {
|
|
43
62
|
// ── Runtime.enable / CDP detection masking ──────────────
|
|
44
63
|
try { delete window.__REBROWSER_RUNTIME_ENABLE; } catch(_) {}
|
|
@@ -49,40 +68,62 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
49
68
|
try { delete window._phantom; } catch(_) {}
|
|
50
69
|
try { delete window.Buffer; } catch(_) {}
|
|
51
70
|
|
|
52
|
-
// Real Chrome without automation
|
|
53
|
-
// A literal false
|
|
54
|
-
//
|
|
55
|
-
|
|
71
|
+
// Real Chrome without automation should not expose navigator.webdriver at all.
|
|
72
|
+
// A literal false or an own-property getter returning undefined is itself a
|
|
73
|
+
// common stealth tell; remove both instance and prototype properties when the
|
|
74
|
+
// descriptor is configurable (as it is with --disable-blink-features).
|
|
75
|
+
try { delete navigator.webdriver; } catch(_) {}
|
|
76
|
+
try { delete Navigator.prototype.webdriver; } catch(_) {}
|
|
56
77
|
Object.defineProperty(navigator, 'vendor', { get: () => 'Google Inc.', configurable: true });
|
|
57
78
|
Object.defineProperty(navigator, 'platform', { get: () => 'Win32', configurable: true });
|
|
58
79
|
Object.defineProperty(navigator, 'maxTouchPoints', { get: () => 0, configurable: true });
|
|
59
80
|
Object.defineProperty(navigator, 'pdfViewerEnabled', { get: () => true, configurable: true });
|
|
81
|
+
Object.defineProperty(navigator, 'productSub', { get: () => '20030107', configurable: true });
|
|
82
|
+
Object.defineProperty(navigator, 'product', { get: () => 'Gecko', configurable: true });
|
|
83
|
+
var __greedyMimeTypes = null;
|
|
84
|
+
function __makeMimeTypes() {
|
|
85
|
+
var pdf = { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format', enabledPlugin: null };
|
|
86
|
+
var textPdf = { type: 'text/pdf', suffixes: 'pdf', description: 'Portable Document Format', enabledPlugin: null };
|
|
87
|
+
try { Object.setPrototypeOf(pdf, MimeType.prototype); } catch(_) {}
|
|
88
|
+
try { Object.setPrototypeOf(textPdf, MimeType.prototype); } catch(_) {}
|
|
89
|
+
var m = [pdf, textPdf];
|
|
90
|
+
try { Object.setPrototypeOf(m, MimeTypeArray.prototype); } catch(_) {}
|
|
91
|
+
m.item = function item(i) { return this[i] || null; };
|
|
92
|
+
m.namedItem = function namedItem(name) { return Array.prototype.find.call(this, function(x) { return x && x.type === name; }) || null; };
|
|
93
|
+
return m;
|
|
94
|
+
}
|
|
60
95
|
Object.defineProperty(navigator, 'plugins', {
|
|
61
96
|
get: () => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
97
|
+
__greedyMimeTypes = __greedyMimeTypes || __makeMimeTypes();
|
|
98
|
+
var plugin0 = { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' };
|
|
99
|
+
var plugin1 = { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' };
|
|
100
|
+
var plugin2 = { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' };
|
|
101
|
+
try { Object.setPrototypeOf(plugin0, Plugin.prototype); } catch(_) {}
|
|
102
|
+
try { Object.setPrototypeOf(plugin1, Plugin.prototype); } catch(_) {}
|
|
103
|
+
try { Object.setPrototypeOf(plugin2, Plugin.prototype); } catch(_) {}
|
|
104
|
+
var p = [plugin0, plugin1, plugin2];
|
|
105
|
+
p.item = function item(i) { return this[i] || null; };
|
|
106
|
+
p.namedItem = function namedItem(name) { return Array.prototype.find.call(this, function(x) { return x && x.name === name; }) || null; };
|
|
107
|
+
p.refresh = function refresh() {};
|
|
108
|
+
try { Object.setPrototypeOf(p, PluginArray.prototype); } catch(_) {}
|
|
109
|
+
try {
|
|
110
|
+
__greedyMimeTypes[0].enabledPlugin = p[0];
|
|
111
|
+
__greedyMimeTypes[1].enabledPlugin = p[0];
|
|
112
|
+
} catch(_) {}
|
|
68
113
|
return p;
|
|
69
114
|
},
|
|
115
|
+
configurable: true,
|
|
70
116
|
});
|
|
71
117
|
Object.defineProperty(navigator, 'mimeTypes', {
|
|
72
118
|
get: () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
{ type: 'text/pdf', suffixes: 'pdf', description: 'Portable Document Format', enabledPlugin: null },
|
|
76
|
-
];
|
|
77
|
-
m.item = function(i) { return m[i] || null; };
|
|
78
|
-
m.namedItem = function(name) { return m.find(function(x) { return x.type === name; }) || null; };
|
|
79
|
-
return m;
|
|
119
|
+
__greedyMimeTypes = __greedyMimeTypes || __makeMimeTypes();
|
|
120
|
+
return __greedyMimeTypes;
|
|
80
121
|
},
|
|
81
122
|
configurable: true,
|
|
82
123
|
});
|
|
83
124
|
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'], configurable: true });
|
|
84
125
|
try {
|
|
85
|
-
Object.defineProperty(navigator, 'connection', { get: () => ({ effectiveType: '4g', rtt: 50, downlink: 10, saveData: false }), configurable: true });
|
|
126
|
+
Object.defineProperty(navigator, 'connection', { get: () => ({ effectiveType: '4g', rtt: 50, downlink: 10, downlinkMax: Infinity, saveData: false }), configurable: true });
|
|
86
127
|
} catch(_) {}
|
|
87
128
|
if (!navigator.mediaDevices) {
|
|
88
129
|
Object.defineProperty(navigator, 'mediaDevices', {
|
|
@@ -98,6 +139,18 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
98
139
|
configurable: true,
|
|
99
140
|
});
|
|
100
141
|
}
|
|
142
|
+
// ── Missing platform APIs (headless often lacks these) ─
|
|
143
|
+
try {
|
|
144
|
+
if (!navigator.share) {
|
|
145
|
+
navigator.share = function() { return Promise.reject(new Error('NotAllowedError')); };
|
|
146
|
+
}
|
|
147
|
+
} catch(_) {}
|
|
148
|
+
try {
|
|
149
|
+
if (!navigator.contentIndex) {
|
|
150
|
+
Object.defineProperty(navigator, 'contentIndex', { get: () => ({ add: function() {}, delete: function() {}, getAll: function() { return Promise.resolve([]); } }), configurable: true });
|
|
151
|
+
}
|
|
152
|
+
} catch(_) {}
|
|
153
|
+
|
|
101
154
|
if (!window.chrome) {
|
|
102
155
|
window.chrome = {
|
|
103
156
|
app: { isInstalled: false, InstallState: {}, RunningState: {} },
|
|
@@ -105,8 +158,8 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
105
158
|
OnInstalledReason: {}, OnRestartRequiredReason: {}, PlatformArch: {}, PlatformNaclArch: {}, PlatformOs: {}, RequestUpdateCheckStatus: {},
|
|
106
159
|
connect: () => ({}), sendMessage: () => {}, onMessage: { addListener: () => {} }
|
|
107
160
|
},
|
|
108
|
-
loadTimes: ()
|
|
109
|
-
csi: ()
|
|
161
|
+
loadTimes: function() { return { requestTime: 0, startLoadTime: Date.now() - 5000, commitLoadTime: Date.now() - 3000, finishDocumentLoadTime: Date.now() - 2000, finishLoadTime: Date.now() - 1000, firstPaintTime: Date.now() - 800, navigationType: 'Other', wasFetchedViaSpdy: true, wasNpnNegotiated: true, npnNegotiatedProtocol: 'h2', wasAlternateProtocolAvailable: false, connectionInfo: 'http/2' }; },
|
|
162
|
+
csi: function() { var t = Date.now(); return { onloadT: t - 2000, startE: t - 5000, pageT: 'back', tran: 2 }; },
|
|
110
163
|
};
|
|
111
164
|
}
|
|
112
165
|
var __greedyNativeFns = [];
|
|
@@ -127,6 +180,19 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
127
180
|
return getParam.call(this, p);
|
|
128
181
|
});
|
|
129
182
|
} catch(_) {}
|
|
183
|
+
// ── WebGL readPixels noise ──────────────────────────
|
|
184
|
+
// CreepJS and other fingerprinters draw content with WebGL and read back the
|
|
185
|
+
// rendered pixels. Adding subtle noise breaks rendering-based fingerprinting.
|
|
186
|
+
try {
|
|
187
|
+
var origReadPixels = WebGLRenderingContext.prototype.readPixels;
|
|
188
|
+
WebGLRenderingContext.prototype.readPixels = __markNative(function readPixels(x, y, width, height, format, type, pixels) {
|
|
189
|
+
var result = origReadPixels.call(this, x, y, width, height, format, type, pixels);
|
|
190
|
+
if (pixels && pixels.length > 0) {
|
|
191
|
+
pixels[0] ^= 1;
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
});
|
|
195
|
+
} catch(_) {}
|
|
130
196
|
Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8, configurable: true });
|
|
131
197
|
Object.defineProperty(navigator, 'deviceMemory', { get: () => 8, configurable: true });
|
|
132
198
|
|
|
@@ -134,7 +200,7 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
134
200
|
// Headless rendering engines produce slightly different canvas output
|
|
135
201
|
// than headed Chrome. Subtle noise breaks hash-based fingerprinting.
|
|
136
202
|
try {
|
|
137
|
-
var __canvasNoise = ((Date.now()
|
|
203
|
+
var __canvasNoise = ((Date.now() & 0xFF) | 1);
|
|
138
204
|
var origFill = CanvasRenderingContext2D.prototype.fillText;
|
|
139
205
|
CanvasRenderingContext2D.prototype.fillText = __markNative(function fillText() {
|
|
140
206
|
this.globalAlpha = 0.9995;
|
|
@@ -153,15 +219,39 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
153
219
|
HTMLCanvasElement.prototype.toDataURL = __markNative(function toDataURL() {
|
|
154
220
|
var ctx = this.getContext('2d');
|
|
155
221
|
if (ctx) {
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
222
|
+
// Spread noise across canvas to break hash-based fingerprinting.
|
|
223
|
+
// Uses a deterministic pattern so it's consistent per page load
|
|
224
|
+
// but varies between sessions.
|
|
225
|
+
var w = this.width, h = this.height;
|
|
226
|
+
if (w > 0 && h > 0) {
|
|
227
|
+
var imgData = ctx.getImageData(0, 0, Math.min(w, 4), Math.min(h, 4));
|
|
228
|
+
if (imgData && imgData.data) {
|
|
229
|
+
for (var __i = 0; __i < imgData.data.length; __i += 4) {
|
|
230
|
+
imgData.data[__i] ^= (__canvasNoise + __i) & 0xFF;
|
|
231
|
+
}
|
|
232
|
+
ctx.putImageData(imgData, 0, 0);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
160
235
|
}
|
|
161
236
|
return origToDataURL.apply(this, arguments);
|
|
162
237
|
});
|
|
163
238
|
} catch(_) {}
|
|
164
239
|
|
|
240
|
+
// ── AudioContext fingerprint noise ────────────────────
|
|
241
|
+
// Headless Chrome's AudioContext produces slightly different output.
|
|
242
|
+
// Subtle noise breaks audio-based fingerprinting.
|
|
243
|
+
try {
|
|
244
|
+
var __audioSeed = ((Date.now() & 0x1F) | 1);
|
|
245
|
+
var origGetChannelData = AudioBuffer.prototype.getChannelData;
|
|
246
|
+
AudioBuffer.prototype.getChannelData = __markNative(function getChannelData(channel) {
|
|
247
|
+
var data = origGetChannelData.call(this, channel);
|
|
248
|
+
for (var __i = 0; __i < data.length; __i += 64) {
|
|
249
|
+
data[__i] *= 0.99999;
|
|
250
|
+
}
|
|
251
|
+
return data;
|
|
252
|
+
});
|
|
253
|
+
} catch(_) {}
|
|
254
|
+
|
|
165
255
|
// ── window outer dimensions ──────────────────────────
|
|
166
256
|
// outerWidth/Height = 0 in headless — a well-known bot signal.
|
|
167
257
|
// Mirror innerWidth/Height (set by --window-size flag) so the ratio is sane.
|
|
@@ -171,9 +261,15 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
171
261
|
} catch(_) {}
|
|
172
262
|
|
|
173
263
|
// ── screen properties ─────────────────────────────────
|
|
264
|
+
// Headless Chrome often reports an 800x600 screen even when the viewport is
|
|
265
|
+
// 1920x1080. Keep screen metrics internally consistent with our launch flags.
|
|
174
266
|
try {
|
|
175
|
-
|
|
176
|
-
|
|
267
|
+
Object.defineProperty(screen, 'width', { get: () => 1920, configurable: true });
|
|
268
|
+
Object.defineProperty(screen, 'height', { get: () => 1080, configurable: true });
|
|
269
|
+
Object.defineProperty(screen, 'availWidth', { get: () => 1920, configurable: true });
|
|
270
|
+
Object.defineProperty(screen, 'availHeight', { get: () => 1040, configurable: true });
|
|
271
|
+
Object.defineProperty(screen, 'colorDepth', { get: () => 24, configurable: true });
|
|
272
|
+
Object.defineProperty(screen, 'pixelDepth', { get: () => 24, configurable: true });
|
|
177
273
|
} catch(_) {}
|
|
178
274
|
|
|
179
275
|
// ── navigator.userAgentData (UA Client Hints) ─────────
|
|
@@ -246,12 +342,12 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
246
342
|
};
|
|
247
343
|
} catch(_) {}
|
|
248
344
|
})();
|
|
249
|
-
`})])}function
|
|
345
|
+
`})])}function N(e){if(!e)return[];let t=[],r=0;while(r<e.length&&t.length<10){let n=e.indexOf("[",r);if(n===-1)break;let a=e.indexOf("](",n);if(a===-1)break;let l=a+2,i=-1;for(let o=l;o<e.length;o++){let s=e[o];if(s===")"){i=o;break}if(/\s/.test(s))break}if(i!==-1){let o=e.slice(n+1,a),s=e.slice(l,i);if(/^https?:\/\//i.test(s)&&o){if(!t.some((f)=>f.url===s))t.push({title:o,url:s})}r=i+1}else r=n+1}return t}var $={postNav:800,postNavSlow:1200,postClick:300,postType:300,inputPoll:400,copyPoll:600,afterVerify:1500};function P(e){let t=e*0.2,r=U(-Math.floor(t),Math.floor(t)+1);return Math.max(50,Math.round(e+r))}async function M(e,t={}){let{timeout:r=20000,interval:n=600,stableRounds:a=3,selector:l="document.body",minLength:i=0,isStreamingExpr:o="false"}=t,s=String.raw`
|
|
250
346
|
new Promise((resolve, reject) => {
|
|
251
|
-
const _deadline = Date.now() + ${
|
|
347
|
+
const _deadline = Date.now() + ${r};
|
|
252
348
|
const _baseInterval = ${n};
|
|
253
349
|
const _stableRounds = ${a};
|
|
254
|
-
const _minLength = ${
|
|
350
|
+
const _minLength = ${i};
|
|
255
351
|
let _lastLen = -1;
|
|
256
352
|
let _stableCount = 0;
|
|
257
353
|
|
|
@@ -262,9 +358,13 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
262
358
|
function _poll() {
|
|
263
359
|
try {
|
|
264
360
|
// Re-query DOM each tick — element may not exist at eval start
|
|
265
|
-
const el = ${
|
|
266
|
-
const cur = el?.
|
|
267
|
-
|
|
361
|
+
const el = ${l};
|
|
362
|
+
const cur = el?.textContent?.length ?? 0;
|
|
363
|
+
const streaming = ${o};
|
|
364
|
+
if (streaming) {
|
|
365
|
+
if (cur !== _lastLen) _lastLen = cur;
|
|
366
|
+
_stableCount = 0;
|
|
367
|
+
} else if (cur >= _minLength) {
|
|
268
368
|
if (cur === _lastLen) {
|
|
269
369
|
_stableCount++;
|
|
270
370
|
if (_stableCount >= _stableRounds) { resolve(cur); return; }
|
|
@@ -276,17 +376,17 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
276
376
|
if (Date.now() < _deadline) {
|
|
277
377
|
setTimeout(_poll, _jitter(_baseInterval));
|
|
278
378
|
} else {
|
|
279
|
-
if (_lastLen >= _minLength) { resolve(_lastLen); }
|
|
280
|
-
else { reject(new Error('Generation did not stabilise within ${
|
|
379
|
+
if (_lastLen >= _minLength && !streaming) { resolve(_lastLen); }
|
|
380
|
+
else { reject(new Error('Generation did not stabilise within ${r}ms')); }
|
|
281
381
|
}
|
|
282
382
|
} catch(e) { reject(e); }
|
|
283
383
|
}
|
|
284
384
|
|
|
285
385
|
_poll();
|
|
286
386
|
})
|
|
287
|
-
`,
|
|
387
|
+
`,f=await c(["eval",e,s],r+1e4),u=parseInt(f,10)||0;if(u>=i)return u;throw Error(`Generation did not stabilise within ${r}ms`)}async function I(e,t,r=15000,n=500){let a=String.raw`
|
|
288
388
|
new Promise((resolve) => {
|
|
289
|
-
const _deadline = Date.now() + ${
|
|
389
|
+
const _deadline = Date.now() + ${r};
|
|
290
390
|
const _baseInterval = ${n};
|
|
291
391
|
|
|
292
392
|
function _jitter(ms) {
|
|
@@ -295,7 +395,7 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
295
395
|
|
|
296
396
|
function _poll() {
|
|
297
397
|
try {
|
|
298
|
-
if (document.querySelector('${
|
|
398
|
+
if (document.querySelector('${t}')) { resolve(true); return; }
|
|
299
399
|
if (Date.now() < _deadline) { setTimeout(_poll, _jitter(_baseInterval)); }
|
|
300
400
|
else { resolve(false); }
|
|
301
401
|
} catch(_) { resolve(false); }
|
|
@@ -303,10 +403,10 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
303
403
|
|
|
304
404
|
_poll();
|
|
305
405
|
})
|
|
306
|
-
`;return await c(["eval",e,a],
|
|
307
|
-
`)}function
|
|
406
|
+
`;return await c(["eval",e,a],r+5000)==="true"}async function A(e){let t=e.indexOf("--stdin");if(t===-1)return e;let r=await new Promise((a)=>{let l="";process.stdin.setEncoding("utf8"),process.stdin.on("data",(i)=>l+=i),process.stdin.on("end",()=>a(l.trim()))}),n=[...e];return n[t]=r,n}function E(e){let t=e.includes("--short"),r=e.filter((o)=>o!=="--short"),n=r.indexOf("--tab"),a=n===-1?null:r[n+1];if(n!==-1)r=r.filter((o,s)=>s!==n&&s!==n+1);let l=r.indexOf("--locale"),i=l===-1?null:r[l+1];if(l!==-1)r=r.filter((o,s)=>s!==l&&s!==l+1);return{query:r.join(" "),tabPrefix:a,short:t,locale:i}}function j(e,t){if(!e.length||e[0]==="--help")process.stderr.write(t),process.exit(1)}function L(e,t,r=300){if(!t||e.length<=r)return e;let n=e.slice(0,r),a=n.lastIndexOf(" ");return a>0?`${n.slice(0,a)}…`:`${n}…`}function R(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
407
|
+
`)}function k({engine:e,mode:t="headless",clipboardEmpty:r=null,fallbackUsed:n=null,blockedBy:a=null,verificationResult:l=null,inputReady:i=null,durationMs:o=null,lastStage:s=null,stages:f=null}={}){return{engine:e,mode:t,clipboardEmpty:r,fallbackUsed:n,blockedBy:a,verificationResult:l,inputReady:i,durationMs:o,lastStage:s,stages:f}}function F(e,t=null){if(t){let r=JSON.stringify({_envelope:t,error:e.message});process.stdout.write(`${r}
|
|
308
408
|
`)}process.stderr.write(`Error: ${e.message}
|
|
309
|
-
`),process.exit(1)}import{randomInt as
|
|
409
|
+
`),process.exit(1)}import{randomInt as ae}from"node:crypto";import{existsSync as oe,readFileSync as le}from"node:fs";import se from"node:http";var ce=`
|
|
310
410
|
(function() {
|
|
311
411
|
// Google consent page (consent.google.com)
|
|
312
412
|
var g = document.querySelector('#L2AGLb, button[jsname="b3VHJd"], .tHlp8d');
|
|
@@ -323,7 +423,7 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
323
423
|
|
|
324
424
|
return null;
|
|
325
425
|
})()
|
|
326
|
-
`,
|
|
426
|
+
`,ue=`
|
|
327
427
|
(function() {
|
|
328
428
|
var url = document.location.href;
|
|
329
429
|
|
|
@@ -354,12 +454,24 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
354
454
|
|
|
355
455
|
// --- Cloudflare Turnstile widget inside closed shadow DOM (Copilot, etc.) ---
|
|
356
456
|
// The iframe is not queryable from main document, but the host container
|
|
357
|
-
// (#cf-turnstile) and the hidden response input are.
|
|
358
|
-
|
|
457
|
+
// (#cf-turnstile) and the hidden response input are. When only the
|
|
458
|
+
// hidden response input matches (no #cf-turnstile host and no visible
|
|
459
|
+
// iframe), the actual challenge widget is rendered inside a closed
|
|
460
|
+
// shadow DOM and cannot be auto-clicked. Return a sentinel so callers
|
|
461
|
+
// know to surface this as needs-human verification instead of wasting
|
|
462
|
+
// time on a doomed waitForSelector.
|
|
463
|
+
var cfTurnstileHost = document.querySelector('#cf-turnstile');
|
|
359
464
|
if (cfTurnstileHost) {
|
|
360
465
|
var r2 = cfTurnstileHost.getBoundingClientRect();
|
|
361
466
|
return JSON.stringify({t:'xy',x:r2.left+r2.width/2,y:r2.top+r2.height/2});
|
|
362
467
|
}
|
|
468
|
+
// Hidden cf-chl-widget-*_response input present but no visible host:
|
|
469
|
+
// the widget is in closed shadow DOM. Signal this so handleVerification
|
|
470
|
+
// can return 'needs-human' rather than 'clear'.
|
|
471
|
+
var cfResponseInput = document.querySelector('input[name="cf-turnstile-response"], [id^="cf-chl-widget-"][id$="_response"]');
|
|
472
|
+
if (cfResponseInput && cfResponseInput.value === '') {
|
|
473
|
+
return 'cf-closed-shadow-dom';
|
|
474
|
+
}
|
|
363
475
|
|
|
364
476
|
// --- Cloudflare challenge page ---
|
|
365
477
|
var cfCheckbox = document.querySelector('#cf-stage input[type="checkbox"], .ctp-checkbox-container input');
|
|
@@ -374,15 +486,28 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
374
486
|
}
|
|
375
487
|
|
|
376
488
|
// --- Generic verify/continue/proceed buttons (catch-all) ---
|
|
377
|
-
// IMPORTANT: exclude sign-in / OAuth buttons (e.g. "Continue with Google"
|
|
489
|
+
// IMPORTANT: exclude sign-in / OAuth buttons (e.g. "Continue with Google",
|
|
490
|
+
// "Continue with email", "Login or sign up for free"). These appear on
|
|
491
|
+
// many sites (Perplexity, ChatGPT, etc.) when the user isn't logged in,
|
|
492
|
+
// and clicking them triggers a sign-in flow that takes us to a login
|
|
493
|
+
// wall — a much worse outcome than the original search failure we were
|
|
494
|
+
// trying to recover from. The exclusion list must cover both OAuth
|
|
495
|
+
// providers AND generic "sign in / log in / with email" patterns.
|
|
378
496
|
var btns = Array.from(document.querySelectorAll('button, input[type=submit], a[role=button]'));
|
|
379
497
|
var verify = btns.find(b => {
|
|
380
498
|
var t = (b.innerText?.trim() || b.value || '').toLowerCase();
|
|
381
|
-
var isVerifyLike = (t
|
|
499
|
+
var isVerifyLike = (t === 'continue' || t === 'proceed' || t === 'next' ||
|
|
500
|
+
t.startsWith('verify ') || t.startsWith('human ') || t === 'i am human' || t.includes('robot check')) &&
|
|
382
501
|
!t.includes('verified') && !document.querySelector('iframe[src*="recaptcha"]');
|
|
383
502
|
if (!isVerifyLike) return false;
|
|
384
503
|
// Exclude OAuth / sign-in buttons to prevent accidental login flows
|
|
385
|
-
|
|
504
|
+
// — covers "Continue with Google", "Continue with Apple", "Continue
|
|
505
|
+
// with email", "Login or sign up", "Log in", "Sign in", "Sign up",
|
|
506
|
+
// "Single sign-on", and the visible panel "Login or sign up for free"
|
|
507
|
+
// text. The previous list missed "email" and "sso" which let the
|
|
508
|
+
// auto-click land on the email/SSO sign-in buttons on Perplexity's
|
|
509
|
+
// anonymous-mode homepage, navigating us into a login flow.
|
|
510
|
+
var isSignIn = new RegExp("sign.?in|log.?in|sign.?up|with\\s+(google|apple|email|github|facebook|microsoft|sso)|sso|auth", "i").test(t);
|
|
386
511
|
return !isSignIn;
|
|
387
512
|
});
|
|
388
513
|
if (verify) { verify.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:verify.innerText?.trim()||verify.value}); }
|
|
@@ -393,7 +518,7 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
393
518
|
|
|
394
519
|
return null;
|
|
395
520
|
})()
|
|
396
|
-
`,
|
|
521
|
+
`,de=`
|
|
397
522
|
(function() {
|
|
398
523
|
var url = document.location.href;
|
|
399
524
|
var isVerifyPage = url.includes('/sorry/') ||
|
|
@@ -427,16 +552,157 @@ import{randomInt as F}from"node:crypto";import{spawn as J}from"node:child_proces
|
|
|
427
552
|
|
|
428
553
|
return 'still-verifying';
|
|
429
554
|
})()
|
|
430
|
-
`;async function
|
|
431
|
-
`)[0],
|
|
432
|
-
var el = document.querySelector('${
|
|
555
|
+
`;async function T(e,t){let r=await t(["eval",e,ce]).catch(()=>null);if(r&&r!=="null")await new Promise((n)=>setTimeout(n,1500))}function y(e,t){return ae(e*1000,t*1000)/1000}async function fe(e,t){if(!globalThis.WebSocket)return;let r=process.env.CDP_PROFILE_DIR;if(!r)return;let n=`${r.replaceAll("\\","/")}/DevToolsActivePort`;if(!oe(n))return;let a=le(n,"utf8").trim().split(`
|
|
556
|
+
`)[0],l=await new Promise((s,f)=>{let u=se.get(`http://localhost:${a}/json/version`,(m)=>{let g="";m.on("data",(p)=>g+=p),m.on("end",()=>{try{s(JSON.parse(g))}catch{f(Error("bad JSON"))}})});u.on("error",f),u.setTimeout(1000,()=>{u.destroy(),f(Error("timeout"))})}),i=new globalThis.WebSocket(l.webSocketDebuggerUrl),o=0;await new Promise((s)=>{i.onopen=async()=>{let f=(g,p)=>new Promise((_)=>{let v=++o,d=(h)=>{if(JSON.parse(h.data).id===v)i.removeEventListener("message",d),_()};i.addEventListener("message",d),i.send(JSON.stringify({id:v,method:g,params:p}))}),u=e+y(-2,2),m=t+y(-2,2);await f("Input.dispatchMouseEvent",{type:"mouseMoved",x:u,y:m,button:"none"}),await new Promise((g)=>setTimeout(g,y(80,160))),await f("Input.dispatchMouseEvent",{type:"mousePressed",x:u,y:m,button:"left",clickCount:1}),await new Promise((g)=>setTimeout(g,y(30,80))),await f("Input.dispatchMouseEvent",{type:"mouseReleased",x:u+y(-1,1),y:m+y(-1,1),button:"left",clickCount:1}),setTimeout(()=>{i.close(),s()},200)},i.onerror=()=>s(),setTimeout(s,3000)})}async function W(e,t,r,n){let a=Number.parseFloat(r),l=Number.parseFloat(n);if(Number.isNaN(a)||Number.isNaN(l))throw Error(`humanClickXY: invalid coordinates (${r}, ${n})`);let i={button:"left",clickCount:1,modifiers:0},o=a+y(-3,3),s=l+y(-3,3);await t(["evalraw",e,"Input.dispatchMouseEvent",JSON.stringify({...i,type:"mouseMoved",x:o,y:s})]),await new Promise((p)=>setTimeout(p,y(80,180)));let f=a+y(-2,2),u=l+y(-2,2);await t(["evalraw",e,"Input.dispatchMouseEvent",JSON.stringify({...i,type:"mousePressed",x:f,y:u})]),await new Promise((p)=>setTimeout(p,y(30,90)));let m=f+y(-1,1),g=u+y(-1,1);return await t(["evalraw",e,"Input.dispatchMouseEvent",JSON.stringify({...i,type:"mouseReleased",x:m,y:g})]),await fe(a,l).catch(()=>{}),await new Promise((p)=>setTimeout(p,y(100,300))),`human-clicked at (${a.toFixed(0)}, ${l.toFixed(0)})`}async function ge(e,t,r){let n=await t(["eval",e,`(function() {
|
|
557
|
+
var el = document.querySelector('${r.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}');
|
|
433
558
|
if (!el) return 'null';
|
|
434
559
|
var r = el.getBoundingClientRect();
|
|
435
560
|
return JSON.stringify({x: r.left + r.width / 2, y: r.top + r.height / 2, w: r.width, h: r.height});
|
|
436
|
-
})()`]).catch(()=>"null");if(!n||n==="null")return null;let a=JSON.parse(n);if(a.w===0||a.h===0||a.x===0&&a.y===0)return null;let{x:
|
|
437
|
-
`),
|
|
438
|
-
`),
|
|
439
|
-
`)
|
|
440
|
-
`)
|
|
441
|
-
`),"
|
|
442
|
-
|
|
561
|
+
})()`]).catch(()=>"null");if(!n||n==="null")return null;let a=JSON.parse(n);if(a.w===0||a.h===0||a.x===0&&a.y===0)return null;let{x:l,y:i}=a;return W(e,t,l,i)}function J(e,t,r){if(!r||r==="null"||r==="cleared"||r==="still-verifying"||r==="cf-closed-shadow-dom")return Promise.resolve("no-challenge");try{let n=JSON.parse(r);if(n.t==="sel"&&n.s)return process.stderr.write(`[greedysearch] Human-clicking "${n.txt}" via CDP...
|
|
562
|
+
`),ge(e,t,n.s).then((a)=>a!==null?"clicked":"cant-click");if(n.t==="xy"){if(!n.x&&!n.y)return Promise.resolve("cant-click");return process.stderr.write(`[greedysearch] Human-clicking at (${n.x.toFixed(0)}, ${n.y.toFixed(0)})...
|
|
563
|
+
`),W(e,t,n.x,n.y).then(()=>"clicked")}}catch{}return Promise.resolve("no-challenge")}async function me(e,t){let r=await t(["eval",e,ue]).catch(()=>null);if(r==="cf-closed-shadow-dom"){let n=await pe(e,t).catch(()=>null);if(n)return n;return r}if(r&&r!=="null")return r;return null}async function pe(e,t){if(typeof t!=="function")return null;await t(["evalraw",e,"DOM.enable","{}"]).catch(()=>{});let r=await t(["evalraw",e,"DOM.getDocument",JSON.stringify({depth:-1,pierce:!0})]).catch(()=>null);if(!r)return null;let n;try{n=JSON.parse(r)}catch{return null}if(n.error||!n.root)return null;let a=n.root;return await q(a,e,t)}async function q(e,t,r){if(!e)return null;let n=[];if(e.shadowRoots&&e.shadowRoots.length>0)for(let a of e.shadowRoots)n.push(a);if(e.children)for(let a of e.children)n.push(a);for(let a of n){if(a.nodeName==="IFRAME"){let i=a.attributes||[],o=i.indexOf("src"),s=o>=0?i[o+1]:"";if(s&&/challenges\.cloudflare\.com|turnstile/i.test(s)&&a.backendNodeId){let f=await r(["evalraw",t,"DOM.getBoxModel",JSON.stringify({backendNodeId:a.backendNodeId})]).catch(()=>null);if(!f)continue;let u;try{u=JSON.parse(f)}catch{continue}let m=u?.model?.content||u?.result?.model?.content;if(!m||m.length<8)continue;let g=m[0],p=m[1],_=m[4],v=m[5],d=_-g,h=v-p;if(d<50||h<20)continue;let w=g+d*0.25,x=p+h*0.5;return process.stderr.write(`[greedysearch] Found CF iframe via CDP pierce at (${g.toFixed(0)}, ${p.toFixed(0)}) ${d.toFixed(0)}x${h.toFixed(0)}, clicking checkbox at (${w.toFixed(0)}, ${x.toFixed(0)})
|
|
564
|
+
`),JSON.stringify({t:"xy",x:w,y:x})}}let l=await q(a,t,r);if(l)return l}return null}async function H(e,t,r=30000){let n=await me(e,t);if(!n)return"clear";if(n==="sorry-page"){process.stderr.write(`[greedysearch] Google CAPTCHA detected — please solve it in the browser window (waiting up to ${Math.floor(r/1000)}s)...
|
|
565
|
+
`);let l=Date.now()+r;while(Date.now()<l)if(await new Promise((i)=>setTimeout(i,2000)),!(await t(["eval",e,"document.location.href"]).catch(()=>"")).includes("/sorry/"))return"cleared-by-user";return"needs-human"}let a=await J(e,t,n);if(a==="clicked"){await new Promise((i)=>setTimeout(i,2000));let l=Date.now()+r;while(Date.now()<l){let i=await t(["eval",e,de]).catch(()=>null);if(i==="cleared"||!i||i==="null")return process.stderr.write(`[greedysearch] Verification cleared.
|
|
566
|
+
`),"clicked";if(i!=="still-verifying")await J(e,t,i),await new Promise((o)=>setTimeout(o,2000));else await new Promise((o)=>setTimeout(o,1500))}return process.stderr.write(`[greedysearch] Verification may require manual intervention.
|
|
567
|
+
`),"needs-human"}if(a==="cant-click")return process.stderr.write(`[greedysearch] Verification challenge detected but cannot be auto-clicked — please solve it manually in the visible browser window.
|
|
568
|
+
`),"needs-human";return"clear"}var B={perplexity:{input:"#ask-input",copyButton:null,sourceItem:"[data-pplx-citation-url]",sourceLink:"a",consent:"#onetrust-accept-btn-handler"},bing:{input:"#userInput",copyButton:'button[data-testid="copy-ai-message-button"]',sourceLink:'a[href^="http"][target="_blank"]',sourceExclude:"copilot.microsoft.com",consent:"#onetrust-accept-btn-handler"},google:{answerContainer:".pWvJNd",sourceLink:'a[href^="http"]',sourceExclude:["google.","gstatic","googleapis"],sourceHeadingParent:"[data-snhf]",consent:'#L2AGLb, button[jsname="b3VHJd"], .tHlp8d'},gemini:{input:"rich-textarea .ql-editor",copyButton:'button:has(mat-icon[data-mat-icon-name="copy"])',sendButton:'button:has(mat-icon[data-mat-icon-name="arrow_upward"]), [data-test-id="send-button"], .send-button',sourcesSidebarButton:"button.legacy-sources-sidebar-button",sourcesExclude:["gemini.google","gstatic","google.com/search"],citationButtonPattern:'button[aria-label*="citation from"]',citationNameRegex:/from\s{1,20}([^.]{1,200})\.\s/}};var b=B.perplexity,V="__pplxClipboard";function he(){return"Array.from(document.querySelectorAll('button')).filter(b => b.innerHTML.includes('#pplx-icon-copy')).pop()"}async function ye(e,t){function r(l){let i=(l||"").trim();if(i.length>50)return!0;return i.length>=5&&/\s|[.,!?;:]/.test(i)}if(await c(["eval",e,`new Promise((resolve) => {
|
|
569
|
+
const _deadline = Date.now() + 8000;
|
|
570
|
+
function _checkNav() {
|
|
571
|
+
const url = document.location.href;
|
|
572
|
+
if (url.includes('/search/') || url.includes('/thread/') || url.match(/perplexity.ai\\/[^/]+/)) {
|
|
573
|
+
resolve('navigated');
|
|
574
|
+
} else if (Date.now() < _deadline) {
|
|
575
|
+
setTimeout(_checkNav, 300);
|
|
576
|
+
} else {
|
|
577
|
+
resolve('timeout');
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
_checkNav();
|
|
581
|
+
})`],1e4).catch(()=>"timeout")==="timeout")return null;let a=await c(["eval",e,`new Promise((resolve) => {
|
|
582
|
+
const _deadline = Date.now() + 5000;
|
|
583
|
+
function _looksLikeAnswerText(text) {
|
|
584
|
+
const t = (text || '').trim();
|
|
585
|
+
if (t.length > 50) return true;
|
|
586
|
+
return t.length >= 5 && /\\s|[.,!?;:]/.test(t);
|
|
587
|
+
}
|
|
588
|
+
function _tryExtract() {
|
|
589
|
+
try {
|
|
590
|
+
// Strategy 1: Find .prose block that's NOT the question
|
|
591
|
+
// and NOT in the sidebar/nav. The answer is the last .prose
|
|
592
|
+
// that contains substantial text and is in the main content area.
|
|
593
|
+
const proseBlocks = Array.from(document.querySelectorAll('.prose, [class*="prose"]'));
|
|
594
|
+
const candidates = proseBlocks.filter(el => {
|
|
595
|
+
const text = el.innerText?.trim() || '';
|
|
596
|
+
if (!_looksLikeAnswerText(text)) return false;
|
|
597
|
+
// Exclude sidebar/nav (they're usually in <nav> or <aside> or have specific classes)
|
|
598
|
+
if (el.closest('nav, aside, [role="navigation"], [class*="sidebar"], [class*="nav-"]')) return false;
|
|
599
|
+
return true;
|
|
600
|
+
});
|
|
601
|
+
if (candidates.length > 0) {
|
|
602
|
+
const last = candidates[candidates.length - 1];
|
|
603
|
+
return resolve(JSON.stringify({ answer: last.innerText.trim(), method: 'prose' }));
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Strategy 2: Look for the answer container by data attributes
|
|
607
|
+
// Perplexity uses [data-testid*="answer"] or [class*="answer-content"]
|
|
608
|
+
const answerContainer = document.querySelector('[data-testid*="answer"], [class*="answer-content"], [class*="response-content"]');
|
|
609
|
+
if (answerContainer && _looksLikeAnswerText(answerContainer.innerText?.trim())) {
|
|
610
|
+
return resolve(JSON.stringify({ answer: answerContainer.innerText.trim(), method: 'answer-container' }));
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Strategy 3: Find the largest text block in the main content area
|
|
614
|
+
// (not in nav/aside/sidebar), positioned after the input.
|
|
615
|
+
const input = document.querySelector('${b.input}');
|
|
616
|
+
if (!input) return resolve(null);
|
|
617
|
+
const inputRect = input.getBoundingClientRect();
|
|
618
|
+
const main = document.querySelector('main, [role="main"], [class*="main-content"]') || document.body;
|
|
619
|
+
const blocks = Array.from(main.querySelectorAll('div, article, section'))
|
|
620
|
+
.filter(d => {
|
|
621
|
+
const r = d.getBoundingClientRect();
|
|
622
|
+
if (r.top <= inputRect.bottom) return false; // not below input
|
|
623
|
+
if (r.width === 0 || r.height === 0) return false; // not visible
|
|
624
|
+
if (d.closest('nav, aside, [role="navigation"], [class*="sidebar"]')) return false; // not in nav
|
|
625
|
+
const text = d.innerText?.trim() || '';
|
|
626
|
+
return _looksLikeAnswerText(text) && d.children.length < 20;
|
|
627
|
+
})
|
|
628
|
+
.sort((a, b) => (b.innerText?.length || 0) - (a.innerText?.length || 0));
|
|
629
|
+
if (blocks.length > 0) {
|
|
630
|
+
return resolve(JSON.stringify({ answer: blocks[0].innerText.trim(), method: 'main-content' }));
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Retry if we haven't found anything yet
|
|
634
|
+
if (Date.now() < _deadline) {
|
|
635
|
+
setTimeout(_tryExtract, 400);
|
|
636
|
+
} else {
|
|
637
|
+
resolve(null);
|
|
638
|
+
}
|
|
639
|
+
} catch(e) { resolve(null); }
|
|
640
|
+
}
|
|
641
|
+
_tryExtract();
|
|
642
|
+
})`],8000).catch(()=>null);if(!a||a==="null")return null;try{let{answer:l,method:i}=JSON.parse(a);if(l&&r(l)){t.fallbackUsed=`dom:${i}`,t.clipboardEmpty=!0;let o=await c(["eval",e,`(() => {
|
|
643
|
+
const links = Array.from(document.querySelectorAll('a[href^="https://"]'))
|
|
644
|
+
.filter(a => {
|
|
645
|
+
const href = a.href || '';
|
|
646
|
+
return !href.includes('perplexity.ai') && !href.includes('google.com') && !href.includes('gstatic');
|
|
647
|
+
})
|
|
648
|
+
.slice(0, 10)
|
|
649
|
+
.map(a => ({ title: a.innerText?.trim() || a.href, url: a.href }));
|
|
650
|
+
return JSON.stringify(links);
|
|
651
|
+
})()`],3000).catch(()=>"[]"),s=[];try{s=JSON.parse(o||"[]")}catch{}return{answer:l,sources:s}}}catch{}return null}async function we(e,t){let r=he(),n=await D(e,`(${r})?.click()`,V,{timeoutMs:2400,retryClick:400});if(t.clipboardEmpty=!n,t.query&&n){let l=t.query.toLowerCase().trim();if(n.toLowerCase().trim()===l||n.trim().length<Math.max(20,l.length*0.5))console.error(`[perplexity] Clipboard contains query echo or stub (${n.length} chars), retrying with longer wait...`),t.clipboardEmpty=!0,n=""}if(!n){console.error("[perplexity] Clipboard empty — trying DOM fallback...");let l=await ye(e,t);if(l)return console.error(`[perplexity] DOM fallback succeeded (${t.fallbackUsed})`),l;throw Error("Clipboard interceptor returned empty text")}let a=N(n);return{answer:n.trim(),sources:a}}var be=`Usage: node extractors/perplexity.mjs "<query>" [--tab <prefix>]
|
|
652
|
+
`;async function ve(){let e=await A(process.argv.slice(2));j(e,be);let{query:t,tabPrefix:r,short:n}=E(e),a=Date.now(),i={engine:"perplexity",mode:process.env.GREEDY_SEARCH_VISIBLE==="1"?"visible":"headless",clipboardEmpty:null,fallbackUsed:null,blockedBy:null,verificationResult:null,inputReady:null,query:t};try{if(!r)await c(["list"]);let o=await C(r),s=await c(["eval",o,"document.location.href"]).catch(()=>""),f=!1;try{let d=new URL(s).hostname.toLowerCase();f=d==="perplexity.ai"||d.endsWith(".perplexity.ai")}catch{}if(!f)if(await c(["nav",o,"https://www.perplexity.ai/"],20000),await c(["eval",o,`new Promise((resolve) => {
|
|
653
|
+
const _deadline = Date.now() + 15000;
|
|
654
|
+
function _check() {
|
|
655
|
+
const input = document.querySelector('${b.input}');
|
|
656
|
+
if (input) {
|
|
657
|
+
// Force visibility on all parents up to body —
|
|
658
|
+
// Perplexity hides the first 5 wrapper DIVs until
|
|
659
|
+
// the user interacts with the page
|
|
660
|
+
let el = input;
|
|
661
|
+
while (el && el !== document.body) {
|
|
662
|
+
if (window.getComputedStyle(el).visibility === 'hidden') {
|
|
663
|
+
el.style.visibility = 'visible';
|
|
664
|
+
}
|
|
665
|
+
el = el.parentElement;
|
|
666
|
+
}
|
|
667
|
+
input.focus();
|
|
668
|
+
if (document.activeElement === input) return resolve('ready');
|
|
669
|
+
}
|
|
670
|
+
if (Date.now() < _deadline) setTimeout(_check, 500);
|
|
671
|
+
else resolve('timeout');
|
|
672
|
+
}
|
|
673
|
+
_check();
|
|
674
|
+
})`],18000).catch(()=>"timeout")!=="ready"){for(let h=0;h<2;h++)if(await c(["nav",o,"https://www.perplexity.ai/"],20000),await new Promise((x)=>setTimeout(x,2000)),await c(["eval",o,`(() => {
|
|
675
|
+
const input = document.querySelector('${b.input}');
|
|
676
|
+
if (!input) return false;
|
|
677
|
+
let el = input;
|
|
678
|
+
while (el && el !== document.body) {
|
|
679
|
+
if (window.getComputedStyle(el).visibility === 'hidden') {
|
|
680
|
+
el.style.visibility = 'visible';
|
|
681
|
+
}
|
|
682
|
+
el = el.parentElement;
|
|
683
|
+
}
|
|
684
|
+
input.focus();
|
|
685
|
+
return document.activeElement === input;
|
|
686
|
+
})()`],5000).catch(()=>!1)==="true")break}else await new Promise((h)=>setTimeout(h,600));let u=await H(o,c,1e4);if(i.verificationResult=u,u==="needs-human")throw Error("Perplexity verification required — please solve it manually in the browser window");if(await T(o,c),u==="clicked"){await new Promise((w)=>setTimeout(w,$.afterVerify));let d=await c(["eval",o,"document.location.href"]).catch(()=>""),h=!1;try{let w=new URL(d).hostname.toLowerCase();h=w==="perplexity.ai"||w.endsWith(".perplexity.ai")}catch{}if(!h)await c(["nav",o,"https://www.perplexity.ai/"],20000),await new Promise((w)=>setTimeout(w,800)),await T(o,c)}let m=await I(o,b.input,15000,400);if(i.inputReady=m,!m)throw Error("Perplexity input not found — page may not have loaded or is in unexpected state");await new Promise((d)=>setTimeout(d,P(300))),await S(o,V),await c(["click",o,b.input]),await new Promise((d)=>setTimeout(d,P(400)));let g;for(let d=0;d<3;d++){if(g=await c(["eval",o,`(() => {
|
|
687
|
+
try {
|
|
688
|
+
const input = document.querySelector('${b.input}');
|
|
689
|
+
if (!input) return 'no-input';
|
|
690
|
+
input.focus();
|
|
691
|
+
if (document.activeElement !== input) {
|
|
692
|
+
const activeTag = document.activeElement?.tagName || 'none';
|
|
693
|
+
const activeClass = (document.activeElement?.className || '').slice(0, 80);
|
|
694
|
+
return 'not-focused:active=' + activeTag + '.' + activeClass;
|
|
695
|
+
}
|
|
696
|
+
// execCommand('insertText') dispatches the proper input
|
|
697
|
+
// event that React's onChange listens for
|
|
698
|
+
const ok = document.execCommand('insertText', false, ${JSON.stringify(t)});
|
|
699
|
+
return ok ? 'ok' : 'exec-failed';
|
|
700
|
+
} catch (e) { return 'err:' + e.message; }
|
|
701
|
+
})()`],5000),g==="ok")break;if(String(g).startsWith("not-focused"))await c(["click",o,b.input]).catch(()=>{});await new Promise((h)=>setTimeout(h,800))}if(g!=="ok")throw Error(`Perplexity type failed: ${g}`);if(await new Promise((d)=>setTimeout(d,P(400))),await c(["eval",o,`(() => {
|
|
702
|
+
const input = document.querySelector('${b.input}');
|
|
703
|
+
if (!input) return 'no-input';
|
|
704
|
+
input.focus();
|
|
705
|
+
const ev = new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true, cancelable: true });
|
|
706
|
+
input.dispatchEvent(ev);
|
|
707
|
+
return 'ok';
|
|
708
|
+
})()`]),await M(o,{timeout:20000,interval:600,stableRounds:5,minLength:50,selector:`Array.from(document.querySelectorAll('.prose, [class*="prose"]')).pop() || document.querySelector('[data-testid*="answer"], [class*="answer-content"], [class*="response-content"]') || document.body`}),process.env.GREEDY_SEARCH_HEADLESS==="1"){let d=await c(["snap",o]).catch(()=>"");if(/\[dialog\]/i.test(d)&&/Pro|αναβάθμιση|upgrade/i.test(d)&&!/\.prose|\[article\]/i.test(d))throw console.error("[perplexity] Rate Limited — skipping (visible retry won't help)"),i.blockedBy="rate-limit",Error("Rate Limited — Perplexity free search limit reached. Wait a few hours.")}let{answer:p,sources:_}=await we(o,i);if(!p)throw Error("No answer extracted — Perplexity may not have responded");let v=await c(["eval",o,"document.location.href"]).catch(()=>"");i.durationMs=Date.now()-a,R({query:t,url:v,answer:L(p,n),sources:_,_envelope:k(i)})}catch(o){i.durationMs=Date.now()-a,F(o,k(i))}}ve();
|