@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 r}from"node:crypto";import{spawn as t}from"node:child_process";import{basename as a}from"node:path";function k(Q=process.env,$=process.execPath){let Z=Q.GREEDY_SEARCH_NODE||Q.NODE_BINARY||Q.NODE;if(Z?.trim())return Z.trim();let K=a($||"").toLowerCase();if(K==="node"||K==="node.exe")return $;return"node"}import{dirname as e,join as QQ}from"node:path";import{fileURLToPath as ZQ}from"node:url";var $Q=e(ZQ(import.meta.url)),KQ=QQ($Q,"..","bin","cdp.mjs"),WQ=new Set(["list","snap","eval","shot","html","nav","net","click","clickxy","type","loadall","evalraw","browse","stop","--tab"]);function JQ(Q){if(!Array.isArray(Q)||Q.length===0)throw Error("cdp: args must be a non-empty array");if(Q[0]==="test")return Q.map(($,Z)=>y($,Z));if(!WQ.has(Q[0]))throw Error(`cdp: unknown subcommand '${Q[0]}'`);return Q.map(($,Z)=>y($,Z))}function y(Q,$){if(typeof Q!=="string")throw Error(`cdp: argv[${$}] must be a string (got ${typeof Q})`);if(Q.includes("\x00"))throw Error(`cdp: argv[${$}] contains a null byte`);return Q}function B(Q,$=30000){return UQ(Q,null,$)}function UQ(Q,$=null,Z=30000){let K=JQ(Q);return new Promise((J,X)=>{let W=t(k(),[KQ,...K],{stdio:[$==null?"ignore":"pipe","pipe","pipe"]});if($!=null)W.stdin.write($),W.stdin.end();let U="",Y="";W.stdout.on("data",(z)=>U+=z),W.stderr.on("data",(z)=>Y+=z);let q=setTimeout(()=>{W.kill(),X(Error(`cdp timeout: ${Q[0]}`))},Z);W.on("close",(z)=>{if(clearTimeout(q),z===0)J(U.trim());else X(Error(Y.trim()||`cdp exit ${z}`))})})}async function E(Q){if(Q)return Q;let Z=(await B(["list"])).split(`
|
|
3
|
+
`)[0]?.slice(0,8);if(!Z)throw Error("No Chrome tabs found. Is Chrome running with --remote-debugging-port=9222?");let K=await B(["evalraw",Z,"Target.createTarget",'{"url":"about:blank"}']),J;try{J=JSON.parse(K)}catch(U){throw Error(`Target.createTarget returned invalid JSON: ${U.message}`)}let{targetId:X}=J;if(!X)throw Error("Target.createTarget did not return a targetId");await B(["list"]);let W=X.slice(0,8);try{await XQ(W)}catch(U){process.stderr.write(`[getOrOpenTab] stealth injection failed: ${U.message}
|
|
4
|
+
`)}return W}async function T(Q,$){let Z=`
|
|
5
5
|
(() => {
|
|
6
|
-
window.${
|
|
6
|
+
window.${$} = 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 c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
14
14
|
: null;
|
|
15
15
|
|
|
16
16
|
_clipboard.writeText = function(text) {
|
|
17
|
-
window.${
|
|
17
|
+
window.${$} = 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 c}from"node:crypto";import{spawn as o}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.${$} = await blob.text();
|
|
32
32
|
break;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -38,7 +38,26 @@ import{randomInt as c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
38
38
|
catch (_) { return undefined; }
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
|
-
`;await
|
|
41
|
+
`;await B(["eval",Q,Z])}async function S(Q,$,Z,K={}){let{timeoutMs:J=2600}=K,X=K.retryClick!=null?K.retryClick:Math.floor(J*0.4),W=String.raw`
|
|
42
|
+
new Promise((resolve) => {
|
|
43
|
+
const _deadline = Date.now() + ${J};
|
|
44
|
+
const _retryAt = Date.now() + ${X};
|
|
45
|
+
let _retried = false;
|
|
46
|
+
function _click() { try { ${$}; } catch(_) {} }
|
|
47
|
+
function _poll() {
|
|
48
|
+
const val = window.${Z};
|
|
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.${Z} || ''); }
|
|
56
|
+
}
|
|
57
|
+
_click();
|
|
58
|
+
_poll();
|
|
59
|
+
})
|
|
60
|
+
`;return await B(["eval",Q,W],J+5000).catch(()=>"")}async function XQ(Q){await B(["evalraw",Q,"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 c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
246
342
|
};
|
|
247
343
|
} catch(_) {}
|
|
248
344
|
})();
|
|
249
|
-
`})])}function
|
|
345
|
+
`})])}function f(Q){if(!Q)return[];let $=[],Z=0;while(Z<Q.length&&$.length<10){let K=Q.indexOf("[",Z);if(K===-1)break;let J=Q.indexOf("](",K);if(J===-1)break;let X=J+2,W=-1;for(let U=X;U<Q.length;U++){let Y=Q[U];if(Y===")"){W=U;break}if(/\s/.test(Y))break}if(W!==-1){let U=Q.slice(K+1,J),Y=Q.slice(X,W);if(/^https?:\/\//i.test(Y)&&U){if(!$.some((q)=>q.url===Y))$.push({title:U,url:Y})}Z=W+1}else Z=K+1}return $}function YQ(Q){if(!Q)return!1;for(let $=0;$<Q.length;$++){let Z=Q.charCodeAt($);if(Z<48||Z>57)return!1}return!0}function h(Q){if(!Q)return[];let $=[],Z=new Map,K=/^\[(\d+)\]:\s*(https?:\/\/[^\s"]+)(?:\s+"([^"]*)")?/gm,J;while((J=K.exec(Q))!==null){let W=J[1],U=J[2],Y=J[3]||"";Z.set(W,{url:U,title:Y})}let X=0;while(X<Q.length){let W=Q.indexOf("[",X);if(W===-1)break;let U=Q.indexOf("]",W+1);if(U===-1)break;if(Q[U+1]!=="["){X=W+1;continue}let Y=Q.indexOf("]",U+2);if(Y===-1)break;let q=Q.slice(W+1,U),z=Q.slice(U+2,Y);if(YQ(z)){let D=Z.get(z);if(D&&!$.some((H)=>H.url===D.url))$.push({title:q.trim()||D.title||"",url:D.url})}X=Y+1}return $}function L(Q){let $=Q*0.2,Z=r(-Math.floor($),Math.floor($)+1);return Math.max(50,Math.round(Q+Z))}async function R(Q,$={}){let{timeout:Z=20000,interval:K=600,stableRounds:J=3,selector:X="document.body",minLength:W=0,isStreamingExpr:U="false"}=$,Y=String.raw`
|
|
250
346
|
new Promise((resolve, reject) => {
|
|
251
|
-
const _deadline = Date.now() + ${
|
|
252
|
-
const _baseInterval = ${
|
|
253
|
-
const _stableRounds = ${
|
|
254
|
-
const _minLength = ${
|
|
347
|
+
const _deadline = Date.now() + ${Z};
|
|
348
|
+
const _baseInterval = ${K};
|
|
349
|
+
const _stableRounds = ${J};
|
|
350
|
+
const _minLength = ${W};
|
|
255
351
|
let _lastLen = -1;
|
|
256
352
|
let _stableCount = 0;
|
|
257
353
|
|
|
@@ -262,9 +358,13 @@ import{randomInt as c}from"node:crypto";import{spawn as o}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 = ${X};
|
|
362
|
+
const cur = el?.textContent?.length ?? 0;
|
|
363
|
+
const streaming = ${U};
|
|
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,18 +376,18 @@ import{randomInt as c}from"node:crypto";import{spawn as o}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 ${Z}ms')); }
|
|
281
381
|
}
|
|
282
382
|
} catch(e) { reject(e); }
|
|
283
383
|
}
|
|
284
384
|
|
|
285
385
|
_poll();
|
|
286
386
|
})
|
|
287
|
-
`,
|
|
387
|
+
`,q=await B(["eval",Q,Y],Z+1e4),z=parseInt(q,10)||0;if(z>=W)return z;throw Error(`Generation did not stabilise within ${Z}ms`)}async function v(Q,$,Z=15000,K=500){let J=String.raw`
|
|
288
388
|
new Promise((resolve) => {
|
|
289
|
-
const _deadline = Date.now() + ${
|
|
290
|
-
const _baseInterval = ${
|
|
389
|
+
const _deadline = Date.now() + ${Z};
|
|
390
|
+
const _baseInterval = ${K};
|
|
291
391
|
|
|
292
392
|
function _jitter(ms) {
|
|
293
393
|
return Math.max(50, ms + (Math.random() * ms * 0.4 - ms * 0.2));
|
|
@@ -295,7 +395,7 @@ import{randomInt as c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
295
395
|
|
|
296
396
|
function _poll() {
|
|
297
397
|
try {
|
|
298
|
-
if (document.querySelector('${
|
|
398
|
+
if (document.querySelector('${$}')) { 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 c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
303
403
|
|
|
304
404
|
_poll();
|
|
305
405
|
})
|
|
306
|
-
`;return await
|
|
307
|
-
`)}function
|
|
308
|
-
`)}process.stderr.write(`Error: ${
|
|
309
|
-
`),process.exit(1)}import{randomInt as
|
|
406
|
+
`;return await B(["eval",Q,J],Z+5000)==="true"}async function x(Q){let $=Q.indexOf("--stdin");if($===-1)return Q;let Z=await new Promise((J)=>{let X="";process.stdin.setEncoding("utf8"),process.stdin.on("data",(W)=>X+=W),process.stdin.on("end",()=>J(X.trim()))}),K=[...Q];return K[$]=Z,K}function g(Q){let $=Q.includes("--short"),Z=Q.filter((Y)=>Y!=="--short"),K=Z.indexOf("--tab"),J=K===-1?null:Z[K+1];if(K!==-1)Z=Z.filter((Y,q)=>q!==K&&q!==K+1);let X=Z.indexOf("--locale"),W=X===-1?null:Z[X+1];if(X!==-1)Z=Z.filter((Y,q)=>q!==X&&q!==X+1);return{query:Z.join(" "),tabPrefix:J,short:$,locale:W}}function b(Q,$){if(!Q.length||Q[0]==="--help")process.stderr.write($),process.exit(1)}function m(Q,$,Z=300){if(!$||Q.length<=Z)return Q;let K=Q.slice(0,Z),J=K.lastIndexOf(" ");return J>0?`${K.slice(0,J)}…`:`${K}…`}function u(Q){process.stdout.write(`${JSON.stringify(Q,null,2)}
|
|
407
|
+
`)}function O(Q,$,Z=null){if(!Q||typeof Q!=="object")return;let K=Z?` (+${Date.now()-Z}ms)`:"";if(Q.lastStage=$,!Array.isArray(Q.stages))Q.stages=[];Q.stages.push({stage:$,at:Date.now()});let J=Q.engine||"extractor";console.error(`[${J}] stage: ${$}${K}`)}function C({engine:Q,mode:$="headless",clipboardEmpty:Z=null,fallbackUsed:K=null,blockedBy:J=null,verificationResult:X=null,inputReady:W=null,durationMs:U=null,lastStage:Y=null,stages:q=null}={}){return{engine:Q,mode:$,clipboardEmpty:Z,fallbackUsed:K,blockedBy:J,verificationResult:X,inputReady:W,durationMs:U,lastStage:Y,stages:q}}function p(Q,$=null){if($){let Z=JSON.stringify({_envelope:$,error:Q.message});process.stdout.write(`${Z}
|
|
408
|
+
`)}process.stderr.write(`Error: ${Q.message}
|
|
409
|
+
`),process.exit(1)}import{randomInt as qQ}from"node:crypto";import{existsSync as zQ,readFileSync as DQ}from"node:fs";import HQ from"node:http";var BQ=`
|
|
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 c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
323
423
|
|
|
324
424
|
return null;
|
|
325
425
|
})()
|
|
326
|
-
`,
|
|
426
|
+
`,GQ=`
|
|
327
427
|
(function() {
|
|
328
428
|
var url = document.location.href;
|
|
329
429
|
|
|
@@ -354,12 +454,24 @@ import{randomInt as c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}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 c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
393
518
|
|
|
394
519
|
return null;
|
|
395
520
|
})()
|
|
396
|
-
`,
|
|
521
|
+
`,VQ=`
|
|
397
522
|
(function() {
|
|
398
523
|
var url = document.location.href;
|
|
399
524
|
var isVerifyPage = url.includes('/sorry/') ||
|
|
@@ -427,25 +552,20 @@ import{randomInt as c}from"node:crypto";import{spawn as o}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 c(Q,$){let Z=await $(["eval",Q,BQ]).catch(()=>null);if(Z&&Z!=="null")await new Promise((K)=>setTimeout(K,1500))}function j(Q,$){return qQ(Q*1000,$*1000)/1000}async function jQ(Q,$){if(!globalThis.WebSocket)return;let Z=process.env.CDP_PROFILE_DIR;if(!Z)return;let K=`${Z.replaceAll("\\","/")}/DevToolsActivePort`;if(!zQ(K))return;let J=DQ(K,"utf8").trim().split(`
|
|
556
|
+
`)[0],X=await new Promise((Y,q)=>{let z=HQ.get(`http://localhost:${J}/json/version`,(D)=>{let H="";D.on("data",(G)=>H+=G),D.on("end",()=>{try{Y(JSON.parse(H))}catch{q(Error("bad JSON"))}})});z.on("error",q),z.setTimeout(1000,()=>{z.destroy(),q(Error("timeout"))})}),W=new globalThis.WebSocket(X.webSocketDebuggerUrl),U=0;await new Promise((Y)=>{W.onopen=async()=>{let q=(H,G)=>new Promise((M)=>{let _=++U,N=(A)=>{if(JSON.parse(A.data).id===_)W.removeEventListener("message",N),M()};W.addEventListener("message",N),W.send(JSON.stringify({id:_,method:H,params:G}))}),z=Q+j(-2,2),D=$+j(-2,2);await q("Input.dispatchMouseEvent",{type:"mouseMoved",x:z,y:D,button:"none"}),await new Promise((H)=>setTimeout(H,j(80,160))),await q("Input.dispatchMouseEvent",{type:"mousePressed",x:z,y:D,button:"left",clickCount:1}),await new Promise((H)=>setTimeout(H,j(30,80))),await q("Input.dispatchMouseEvent",{type:"mouseReleased",x:z+j(-1,1),y:D+j(-1,1),button:"left",clickCount:1}),setTimeout(()=>{W.close(),Y()},200)},W.onerror=()=>Y(),setTimeout(Y,3000)})}async function l(Q,$,Z,K){let J=Number.parseFloat(Z),X=Number.parseFloat(K);if(Number.isNaN(J)||Number.isNaN(X))throw Error(`humanClickXY: invalid coordinates (${Z}, ${K})`);let W={button:"left",clickCount:1,modifiers:0},U=J+j(-3,3),Y=X+j(-3,3);await $(["evalraw",Q,"Input.dispatchMouseEvent",JSON.stringify({...W,type:"mouseMoved",x:U,y:Y})]),await new Promise((G)=>setTimeout(G,j(80,180)));let q=J+j(-2,2),z=X+j(-2,2);await $(["evalraw",Q,"Input.dispatchMouseEvent",JSON.stringify({...W,type:"mousePressed",x:q,y:z})]),await new Promise((G)=>setTimeout(G,j(30,90)));let D=q+j(-1,1),H=z+j(-1,1);return await $(["evalraw",Q,"Input.dispatchMouseEvent",JSON.stringify({...W,type:"mouseReleased",x:D,y:H})]),await jQ(J,X).catch(()=>{}),await new Promise((G)=>setTimeout(G,j(100,300))),`human-clicked at (${J.toFixed(0)}, ${X.toFixed(0)})`}async function NQ(Q,$,Z){let K=await $(["eval",Q,`(function() {
|
|
557
|
+
var el = document.querySelector('${Z.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(!
|
|
437
|
-
`),
|
|
438
|
-
`),
|
|
439
|
-
`)
|
|
440
|
-
`)
|
|
441
|
-
`),"
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if (!btn) return 'no-send';
|
|
445
|
-
btn.click();
|
|
446
|
-
return 'ok';
|
|
447
|
-
})()
|
|
448
|
-
`;if(await Y(["eval",z,K])==="no-send")throw Error("ChatGPT send button not found");await new Promise((W)=>setTimeout(W,j(300)))}var i=String.raw`(() => {
|
|
561
|
+
})()`]).catch(()=>"null");if(!K||K==="null")return null;let J=JSON.parse(K);if(J.w===0||J.h===0||J.x===0&&J.y===0)return null;let{x:X,y:W}=J;return l(Q,$,X,W)}function i(Q,$,Z){if(!Z||Z==="null"||Z==="cleared"||Z==="still-verifying"||Z==="cf-closed-shadow-dom")return Promise.resolve("no-challenge");try{let K=JSON.parse(Z);if(K.t==="sel"&&K.s)return process.stderr.write(`[greedysearch] Human-clicking "${K.txt}" via CDP...
|
|
562
|
+
`),NQ(Q,$,K.s).then((J)=>J!==null?"clicked":"cant-click");if(K.t==="xy"){if(!K.x&&!K.y)return Promise.resolve("cant-click");return process.stderr.write(`[greedysearch] Human-clicking at (${K.x.toFixed(0)}, ${K.y.toFixed(0)})...
|
|
563
|
+
`),l(Q,$,K.x,K.y).then(()=>"clicked")}}catch{}return Promise.resolve("no-challenge")}async function OQ(Q,$){let Z=await $(["eval",Q,GQ]).catch(()=>null);if(Z==="cf-closed-shadow-dom"){let K=await AQ(Q,$).catch(()=>null);if(K)return K;return Z}if(Z&&Z!=="null")return Z;return null}async function AQ(Q,$){if(typeof $!=="function")return null;await $(["evalraw",Q,"DOM.enable","{}"]).catch(()=>{});let Z=await $(["evalraw",Q,"DOM.getDocument",JSON.stringify({depth:-1,pierce:!0})]).catch(()=>null);if(!Z)return null;let K;try{K=JSON.parse(Z)}catch{return null}if(K.error||!K.root)return null;let J=K.root;return await d(J,Q,$)}async function d(Q,$,Z){if(!Q)return null;let K=[];if(Q.shadowRoots&&Q.shadowRoots.length>0)for(let J of Q.shadowRoots)K.push(J);if(Q.children)for(let J of Q.children)K.push(J);for(let J of K){if(J.nodeName==="IFRAME"){let W=J.attributes||[],U=W.indexOf("src"),Y=U>=0?W[U+1]:"";if(Y&&/challenges\.cloudflare\.com|turnstile/i.test(Y)&&J.backendNodeId){let q=await Z(["evalraw",$,"DOM.getBoxModel",JSON.stringify({backendNodeId:J.backendNodeId})]).catch(()=>null);if(!q)continue;let z;try{z=JSON.parse(q)}catch{continue}let D=z?.model?.content||z?.result?.model?.content;if(!D||D.length<8)continue;let H=D[0],G=D[1],M=D[4],_=D[5],N=M-H,A=_-G;if(N<50||A<20)continue;let V=H+N*0.25,I=G+A*0.5;return process.stderr.write(`[greedysearch] Found CF iframe via CDP pierce at (${H.toFixed(0)}, ${G.toFixed(0)}) ${N.toFixed(0)}x${A.toFixed(0)}, clicking checkbox at (${V.toFixed(0)}, ${I.toFixed(0)})
|
|
564
|
+
`),JSON.stringify({t:"xy",x:V,y:I})}}let X=await d(J,$,Z);if(X)return X}return null}async function n(Q,$,Z=30000){let K=await OQ(Q,$);if(!K)return"clear";if(K==="sorry-page"){process.stderr.write(`[greedysearch] Google CAPTCHA detected — please solve it in the browser window (waiting up to ${Math.floor(Z/1000)}s)...
|
|
565
|
+
`);let X=Date.now()+Z;while(Date.now()<X)if(await new Promise((U)=>setTimeout(U,2000)),!(await $(["eval",Q,"document.location.href"]).catch(()=>"")).includes("/sorry/"))return"cleared-by-user";return"needs-human"}let J=await i(Q,$,K);if(J==="clicked"){await new Promise((W)=>setTimeout(W,2000));let X=Date.now()+Z;while(Date.now()<X){let W=await $(["eval",Q,VQ]).catch(()=>null);if(W==="cleared"||!W||W==="null")return process.stderr.write(`[greedysearch] Verification cleared.
|
|
566
|
+
`),"clicked";if(W!=="still-verifying")await i(Q,$,W),await new Promise((U)=>setTimeout(U,2000));else await new Promise((U)=>setTimeout(U,1500))}return process.stderr.write(`[greedysearch] Verification may require manual intervention.
|
|
567
|
+
`),"needs-human"}if(J==="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 w="__chatgptClipboard",P="div.ProseMirror",MQ='button[data-testid="send-button"]',_Q='button[data-testid="copy-turn-action-button"]',F=String.raw`(() => {
|
|
449
569
|
const all = document.querySelectorAll('[data-message-author-role]');
|
|
450
570
|
let lastUserIdx = -1;
|
|
451
571
|
for (let i = 0; i < all.length; i++) {
|
|
@@ -461,43 +581,44 @@ import{randomInt as c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
461
581
|
}
|
|
462
582
|
}
|
|
463
583
|
return bestEl;
|
|
464
|
-
})()
|
|
584
|
+
})()`,FQ=`(() => {
|
|
585
|
+
window.${w} = '';
|
|
586
|
+
const assistant = ${F};
|
|
587
|
+
const assistantCopy = assistant?.querySelector('${_Q}');
|
|
588
|
+
if (assistantCopy) assistantCopy.click();
|
|
589
|
+
})()`;async function IQ(Q,$){await B(["click",Q,P]),await new Promise((X)=>setTimeout(X,L(200)));let Z=await B(["eval",Q,`(() => {
|
|
590
|
+
const editor = document.querySelector('${P}');
|
|
591
|
+
if (!editor) return 'no-editor';
|
|
592
|
+
editor.focus();
|
|
593
|
+
const ok = document.execCommand('insertText', false, ${JSON.stringify($)});
|
|
594
|
+
return ok ? 'ok' : 'exec-failed';
|
|
595
|
+
})()`],5000);if(Z!=="ok")throw Error(`ChatGPT type failed: ${Z}`);await new Promise((X)=>setTimeout(X,L(300)));let K=`
|
|
465
596
|
(() => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
if (lastUserIdx < 0) {
|
|
481
|
-
// No user message at all — page is still on the homepage.
|
|
482
|
-
return JSON.stringify({
|
|
483
|
-
answer: '',
|
|
484
|
-
sources: [],
|
|
485
|
-
skipped: 'no-user-message',
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
let assistant = null;
|
|
489
|
-
for (let i = lastUserIdx + 1; i < all.length; i++) {
|
|
490
|
-
if (all[i].getAttribute('data-message-author-role') === 'assistant') {
|
|
491
|
-
assistant = all[i];
|
|
492
|
-
}
|
|
493
|
-
}
|
|
597
|
+
const btn = document.querySelector('${MQ}');
|
|
598
|
+
if (!btn) return 'no-send';
|
|
599
|
+
if (btn.disabled) return 'send-disabled';
|
|
600
|
+
btn.click();
|
|
601
|
+
return 'ok';
|
|
602
|
+
})()
|
|
603
|
+
`,J=await B(["eval",Q,K]);if(J==="no-send")throw Error("ChatGPT send button not found");if(J==="send-disabled")throw Error("ChatGPT send button disabled — query was not registered");await new Promise((X)=>setTimeout(X,L(300)))}var LQ=String.raw`(() => {
|
|
604
|
+
const el = ${F};
|
|
605
|
+
return !!el?.querySelector('.streaming-animation,[data-is-streaming="true"]') ||
|
|
606
|
+
!!document.querySelector('button[data-testid="stop-button"], button[aria-label="Stop generating"], button[aria-label*="Stop"]');
|
|
607
|
+
})()`;async function o(Q,{required:$=!1}={}){try{return await B(["evalraw",Q,"Page.bringToFront","{}"],5000),!0}catch(Z){if(console.error(`[chatgpt] bringToFront failed: ${Z.message}`),$)throw Z;return!1}}async function s(Q,$=35000){await o(Q,{required:!0});let Z=0,K=!1,J=setInterval(()=>{if(K)return;if(Z>=8){console.error("[chatgpt] stopping foreground keepalive after 8 attempts"),clearInterval(J);return}Z++,K=!0,o(Q).finally(()=>{K=!1})},4000);try{return await R(Q,{timeout:$,interval:700,stableRounds:5,minLength:1,selector:F,isStreamingExpr:LQ})}finally{clearInterval(J)}}async function CQ(Q,$=15000){let Z=Date.now()+$,K=0,J=0;while(Date.now()<Z){let X=await B(["eval",Q,`${F}?.innerText?.length ?? 0`],4000).catch(()=>"0"),W=parseInt(X,10)||0;if(W>=1&&W===K){if(J++,J>=3)return W}else K=W,J=0;await new Promise((U)=>setTimeout(U,1200))}return K}async function PQ(Q){let $=await B(["eval",Q,String.raw`
|
|
608
|
+
(() => {
|
|
609
|
+
const assistant = ${F};
|
|
494
610
|
if (!assistant) {
|
|
611
|
+
const skipped = document.querySelector('[data-message-author-role="user"]')
|
|
612
|
+
? 'no-assistant-response'
|
|
613
|
+
: 'no-user-message';
|
|
495
614
|
return JSON.stringify({
|
|
496
615
|
answer: '',
|
|
497
616
|
sources: [],
|
|
498
|
-
skipped
|
|
617
|
+
skipped,
|
|
499
618
|
});
|
|
500
619
|
}
|
|
620
|
+
const streaming = !!assistant.querySelector('.streaming-animation,[data-is-streaming="true"]') ||
|
|
621
|
+
!!document.querySelector('button[data-testid="stop-button"], button[aria-label="Stop generating"], button[aria-label*="Stop"]');
|
|
501
622
|
const answer = (assistant.innerText || assistant.textContent || '').trim();
|
|
502
623
|
const seen = new Set();
|
|
503
624
|
const sources = [];
|
|
@@ -509,39 +630,7 @@ import{randomInt as c}from"node:crypto";import{spawn as o}from"node:child_proces
|
|
|
509
630
|
sources.push({ title, url });
|
|
510
631
|
if (sources.length >= 10) break;
|
|
511
632
|
}
|
|
512
|
-
return JSON.stringify({ answer, sources });
|
|
633
|
+
return JSON.stringify({ answer, sources, streaming });
|
|
513
634
|
})()
|
|
514
|
-
`]);try{return JSON.parse(
|
|
515
|
-
|
|
516
|
-
let lastUserIdx = -1;
|
|
517
|
-
for (let i = 0; i < all.length; i++) {
|
|
518
|
-
if (all[i].getAttribute('data-message-author-role') === 'user') lastUserIdx = i;
|
|
519
|
-
}
|
|
520
|
-
if (lastUserIdx < 0) return 'no-user';
|
|
521
|
-
let assistantCopy = null;
|
|
522
|
-
for (let i = lastUserIdx + 1; i < all.length; i++) {
|
|
523
|
-
if (all[i].getAttribute('data-message-author-role') === 'assistant') {
|
|
524
|
-
const btn = all[i].querySelector('${u}');
|
|
525
|
-
if (btn) assistantCopy = btn;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
if (assistantCopy) { assistantCopy.click(); return 'clicked'; }
|
|
529
|
-
return 'no-assistant-copy';
|
|
530
|
-
})()`]),await new Promise((J)=>setTimeout(J,600));let K=await Y(["eval",z,`window.${F} || ''`]);if(Z.clipboardEmpty=!K,!K)console.error("[chatgpt] Clipboard empty, retrying in 2s..."),await Y(["eval",z,`(() => {
|
|
531
|
-
const all = document.querySelectorAll('[data-message-author-role]');
|
|
532
|
-
let lastUserIdx = -1;
|
|
533
|
-
for (let i = 0; i < all.length; i++) {
|
|
534
|
-
if (all[i].getAttribute('data-message-author-role') === 'user') lastUserIdx = i;
|
|
535
|
-
}
|
|
536
|
-
if (lastUserIdx < 0) return 'no-user';
|
|
537
|
-
let assistantCopy = null;
|
|
538
|
-
for (let i = lastUserIdx + 1; i < all.length; i++) {
|
|
539
|
-
if (all[i].getAttribute('data-message-author-role') === 'assistant') {
|
|
540
|
-
const btn = all[i].querySelector('${u}');
|
|
541
|
-
if (btn) assistantCopy = btn;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
if (assistantCopy) { assistantCopy.click(); return 'clicked'; }
|
|
545
|
-
return 'no-assistant-copy';
|
|
546
|
-
})()`]),await new Promise((J)=>setTimeout(J,2000)),K=await Y(["eval",z,`window.${F} || ''`]),Z.clipboardEmpty=!K;let Q=null;if(!K)Q=await Bz(z),K=Q.answer,Z.fallbackUsed=K?"dom":null;if(!K)throw Error("Clipboard interceptor returned empty text");let W=k(K),H=E(K),$=new Map;for(let J of[...Q?.sources||[],...H,...W])if(J?.url&&!$.has(J.url))$.set(J.url,J);let X=Array.from($.values()).slice(0,10);return{answer:K.trim(),sources:X}}var Az=`Usage: node extractors/chatgpt.mjs "<query>" [--tab <prefix>]
|
|
547
|
-
`;async function _z(){let z=await S(process.argv.slice(2));h(z,Az);let{query:Z,tabPrefix:K,short:Q}=f(z),W=Date.now(),$={engine:"chatgpt",mode:process.env.GREEDY_SEARCH_VISIBLE==="1"?"visible":"headless",clipboardEmpty:null,fallbackUsed:null,blockedBy:null,verificationResult:null,inputReady:null};try{if(!K)await Y(["list"]);let X=await L(K),J=await Y(["eval",X,"document.location.href"]).catch(()=>""),U=!1;try{U=new URL(J).hostname.toLowerCase()==="chatgpt.com"}catch{}if(!U)V($,"nav",W),await Y(["nav",X,"https://chatgpt.com"],20000),await new Promise((A)=>setTimeout(A,600));V($,"consent",W),await g(X,Y),V($,"verification",W),await b(X,Y,1e4),V($,"input-wait",W);let D=await w(X,p,8000,400);if($.inputReady=D,!D){let A=await Y(["eval",X,"document.body?.innerText || ''"]).catch(()=>"");if(/sign in|log in|sign up|\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7|login/i.test(A))throw Error("ChatGPT requires sign-in — please sign in in the visible browser window");throw Error("ChatGPT input not found — page may be blocked or in unexpected state")}V($,"clipboard-inject",W),await P(X,F),V($,"type-and-submit",W),await Dz(X,Z),V($,"stream-wait",W);let q=0;try{q=await Nz(X,20000)}catch(A){V($,"stream-poll-fallback",W),q=await Gz(X,15000)}if($.assistantTextLen=q,q<1)console.error("[chatgpt] Warning: assistant response may not have completed");V($,"extract",W);let{answer:N,sources:B,skipped:_}=await Vz(X,$);if(!N)throw $.blockedBy="no-response",$.skipped=_||null,Error(_==="no-user-message"?"ChatGPT still on homepage — query was not submitted":_==="no-assistant-response"?"ChatGPT did not return an assistant response after submit":"ChatGPT returned no answer — assistant never responded");V($,"done",W);let O=await Y(["eval",X,"document.location.href"]).catch(()=>"https://chatgpt.com");$.durationMs=Date.now()-W,R({query:Z,url:O,answer:y(N,Q),sources:B,_envelope:C($)})}catch(X){$.durationMs=Date.now()-W,console.error(`[chatgpt] error during stage '${$.lastStage||"unknown"}': ${X.message}`),v(X,C($))}}_z();
|
|
635
|
+
`]);try{return JSON.parse($)}catch(Z){return console.error(`[chatgpt] DOM fallback JSON parse failed (${$.length} chars): ${Z.message}`),{answer:"",sources:[],skipped:"parse-error"}}}async function wQ(Q,$){let Z=await S(Q,FQ,w,{timeoutMs:2600,retryClick:600});$.clipboardEmpty=!Z;let K=null;if(!Z){if(K=await PQ(Q),K.streaming)return{answer:"",sources:[],skipped:"still-streaming"};Z=K.answer,$.fallbackUsed=Z?"dom":null}if(Z){let Y=Z.trim(),q=Y.length>=5&&Y.length<=50&&/\s|[.,!?;:]/.test(Y),z=Y.length>50,D=Y.split(/\s+/).filter(Boolean),H=(Y.match(/\b[a-z0-9-]+\.(?:com|org|net|dev|io)\b/gi)||[]).length,G=H>=4&&H>=Math.max(3,Math.floor(D.length/3));if(!q&&!z||G)return console.error(`[chatgpt] DOM fallback answer suspicious (${Y.length} chars, domainRepeats=${H}: ${JSON.stringify(Y.slice(0,80))}) — returning empty for caller to retry`),$.fallbackUsed=null,{answer:"",sources:[],skipped:G?"citation-stub":"header-stub"}}if(!Z)return{answer:"",sources:[],skipped:"no-answer"};let J=f(Z),X=h(Z),W=new Map;for(let Y of[...K?.sources||[],...X,...J])if(Y?.url&&!W.has(Y.url))W.set(Y.url,Y);let U=Array.from(W.values()).slice(0,10);return{answer:Z.trim(),sources:U}}var kQ=`Usage: node extractors/chatgpt.mjs "<query>" [--tab <prefix>]
|
|
636
|
+
`;async function yQ(){let Q=await x(process.argv.slice(2));b(Q,kQ);let{query:$,tabPrefix:Z,short:K}=g(Q),J=Date.now(),W={engine:"chatgpt",mode:process.env.GREEDY_SEARCH_VISIBLE==="1"?"visible":"headless",clipboardEmpty:null,fallbackUsed:null,blockedBy:null,verificationResult:null,inputReady:null};try{if(!Z)await B(["list"]);let U=await E(Z),Y=await B(["eval",U,"document.location.href"]).catch(()=>""),q=!1;try{q=new URL(Y).hostname.toLowerCase()==="chatgpt.com"}catch{}if(!q)O(W,"nav",J),await B(["nav",U,"https://chatgpt.com"],20000),await new Promise((V)=>setTimeout(V,600));O(W,"consent",J),await c(U,B),O(W,"verification",J);let z=await n(U,B,1e4);if(W.verificationResult=z,z==="needs-human")throw W.blockedBy="cloudflare-closed-shadow-dom",Error("ChatGPT is showing a Cloudflare Turnstile challenge that auto-clicking could not clear — please solve it in the visible browser window");if(z==="clicked")await new Promise((V)=>setTimeout(V,2500));O(W,"input-wait",J);let D=await v(U,P,8000,400);if(W.inputReady=D,!D){let V=await B(["eval",U,"document.body?.innerText || ''"]).catch(()=>"");if(/sign in|log in|sign up|\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7|login/i.test(V))throw Error("ChatGPT requires sign-in — please sign in in the visible browser window");throw Error("ChatGPT input not found — page may be blocked or in unexpected state")}O(W,"clipboard-inject",J),await T(U,w),O(W,"type-and-submit",J),await IQ(U,$),O(W,"stream-wait",J);let H=0;try{H=await s(U,20000)}catch(V){O(W,"stream-poll-fallback",J),H=await CQ(U,15000)}if(W.assistantTextLen=H,H<1)console.error("[chatgpt] Warning: assistant response may not have completed");O(W,"extract",J);let G;for(let V=0;V<3;V++){if(V>0)try{await s(U,1e4)}catch{}if(G=await wQ(U,W),G.answer)break;if(V<2)console.error(`[chatgpt] Extract attempt ${V+1} returned empty, retrying in 2s...`),await new Promise((I)=>setTimeout(I,2000))}let{answer:M,sources:_,skipped:N}=G;if(!M){W.blockedBy="no-response",W.skipped=N||null;let V="ChatGPT returned no answer — assistant never responded";if(N==="no-user-message")V="ChatGPT still on homepage — query was not submitted";else if(N==="no-assistant-response")V="ChatGPT did not return an assistant response after submit";else if(N==="header-stub"||N==="citation-stub"||N==="still-streaming")V="ChatGPT response did not finish rendering after 3 retries — assistant never rendered the body";throw Error(V)}O(W,"done",J);let A=await B(["eval",U,"document.location.href"]).catch(()=>"https://chatgpt.com");W.durationMs=Date.now()-J,u({query:$,url:A,answer:m(M,K),sources:_,_envelope:C(W)})}catch(U){W.durationMs=Date.now()-J,console.error(`[chatgpt] error during stage '${W.lastStage||"unknown"}': ${U.message}`),p(U,C(W))}}yQ();
|