findweb 0.1.0 → 0.1.1
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/README.md +147 -0
- package/dist/index.js +93 -50
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# findweb
|
|
2
|
+
|
|
3
|
+
Google search CLI powered by system Chrome, with programmatic ad blocking.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install -g findweb
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run directly:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bunx findweb "yc"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Single search
|
|
21
|
+
findweb "Y Combinator"
|
|
22
|
+
|
|
23
|
+
# Batch search
|
|
24
|
+
findweb "yc" "apple" "tesla" --parallel 3
|
|
25
|
+
|
|
26
|
+
# JSON output
|
|
27
|
+
findweb --json "react useEffect"
|
|
28
|
+
|
|
29
|
+
# Custom region and language
|
|
30
|
+
findweb --gl kr --lang ko "startup"
|
|
31
|
+
|
|
32
|
+
# More results
|
|
33
|
+
findweb -n 10 "rust async"
|
|
34
|
+
|
|
35
|
+
# Prepare a signed-in Chrome profile (reduces rate limiting)
|
|
36
|
+
findweb login
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## First Run Behavior
|
|
40
|
+
|
|
41
|
+
`findweb` requires an initialized Google profile before it will run the first search for a given `--userDataDir`.
|
|
42
|
+
|
|
43
|
+
- If the profile is already prepared, search runs immediately.
|
|
44
|
+
- If the profile has not been prepared yet, `findweb` automatically opens the login flow first.
|
|
45
|
+
- After you sign in and close the browser window, `findweb` writes a local prepared-profile marker so future searches can start immediately.
|
|
46
|
+
|
|
47
|
+
In practice, the first search on a fresh profile behaves like this:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
findweb "yc"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
1. detect missing prepared-profile marker
|
|
54
|
+
2. open headed Chrome login flow
|
|
55
|
+
3. wait for you to sign in and close the browser
|
|
56
|
+
4. continue the original search
|
|
57
|
+
|
|
58
|
+
## Options
|
|
59
|
+
|
|
60
|
+
| Option | Default | Description |
|
|
61
|
+
| ------------------ | -------------- | ------------------------------------ |
|
|
62
|
+
| `--gl <country>` | `us` | Google region hint |
|
|
63
|
+
| `-l, --lang` | `en` | Google UI language |
|
|
64
|
+
| `-n, --num` | `3` | Results per query |
|
|
65
|
+
| `--parallel` | `4` | Batch tab concurrency |
|
|
66
|
+
| `--userDataDir` | auto-detected | Chrome profile directory |
|
|
67
|
+
| `--headed` | `false` | Show the Chrome window |
|
|
68
|
+
| `--json` | `false` | Print output as JSON |
|
|
69
|
+
|
|
70
|
+
## How It Works
|
|
71
|
+
|
|
72
|
+
1. Launches system Chrome (`/Applications/Google Chrome.app`) with a free debugging port
|
|
73
|
+
2. Connects via CDP using puppeteer-core
|
|
74
|
+
3. Loads the [Ghostery adblocker](https://github.com/ghostery/adblocker) engine programmatically on each page
|
|
75
|
+
4. Navigates to Google, submits the query through DOM manipulation, and extracts results from the rendered page
|
|
76
|
+
5. Returns results as plain text or JSON, then closes Chrome
|
|
77
|
+
|
|
78
|
+
No Chromium download. No browser extension. No user confirmation.
|
|
79
|
+
|
|
80
|
+
## Batch Mode
|
|
81
|
+
|
|
82
|
+
Pass multiple quoted queries as positional arguments. Each query opens a separate tab in the same browser instance and profile.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
findweb "yc" "apple" "tesla"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Results are returned in input order. Concurrency is controlled by `--parallel` (default: 4).
|
|
89
|
+
|
|
90
|
+
## Login
|
|
91
|
+
|
|
92
|
+
Google rate-limits unauthenticated or fresh-profile searches. `findweb` now enforces an interactive login before the first search on a new profile.
|
|
93
|
+
|
|
94
|
+
You can trigger that ahead of time with:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
findweb login
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This opens a visible Chrome window with the Google sign-in page. After signing in, close the browser. The session is saved to the profile directory, and `findweb` records that the profile is ready for future searches.
|
|
101
|
+
|
|
102
|
+
## Output
|
|
103
|
+
|
|
104
|
+
### Plain text (default)
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
1. Y Combinator
|
|
108
|
+
https://www.ycombinator.com/
|
|
109
|
+
Y Combinator created a new model for funding early stage startups.
|
|
110
|
+
|
|
111
|
+
2. Y Combinator - Wikipedia
|
|
112
|
+
https://en.wikipedia.org/wiki/Y_Combinator
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### JSON (`--json`)
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
[
|
|
119
|
+
{
|
|
120
|
+
"title": "Y Combinator",
|
|
121
|
+
"url": "https://www.ycombinator.com/",
|
|
122
|
+
"snippet": "Y Combinator created a new model for funding early stage startups."
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Requirements
|
|
128
|
+
|
|
129
|
+
- macOS
|
|
130
|
+
- System Chrome (`/Applications/Google Chrome.app`)
|
|
131
|
+
- [Bun](https://bun.sh) >= 1.3.11
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
git clone https://github.com/ysm-dev/findweb.git
|
|
137
|
+
cd findweb
|
|
138
|
+
bun install
|
|
139
|
+
bun run check # tsgo typecheck
|
|
140
|
+
bun run test # unit tests
|
|
141
|
+
bun run dev # run from source
|
|
142
|
+
bun run build # bundle to dist/
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -53157,7 +53157,7 @@ var require_ffi_WASM_RELEASE_SYNC = __commonJS((exports) => {
|
|
|
53157
53157
|
|
|
53158
53158
|
// node_modules/@tootallnate/quickjs-emscripten/dist/generated/emscripten-module.WASM_RELEASE_SYNC.js
|
|
53159
53159
|
var require_emscripten_module_WASM_RELEASE_SYNC = __commonJS((exports, module) => {
|
|
53160
|
-
var __dirname = "/
|
|
53160
|
+
var __dirname = "/home/runner/work/findweb/findweb/node_modules/@tootallnate/quickjs-emscripten/dist/generated", __filename = "/home/runner/work/findweb/findweb/node_modules/@tootallnate/quickjs-emscripten/dist/generated/emscripten-module.WASM_RELEASE_SYNC.js";
|
|
53161
53161
|
var QuickJSRaw = (() => {
|
|
53162
53162
|
var _scriptDir = typeof document !== "undefined" && document.currentScript ? document.currentScript.src : undefined;
|
|
53163
53163
|
if (typeof __filename !== "undefined")
|
|
@@ -66931,9 +66931,6 @@ async function runMain(cmd, opts = {}) {
|
|
|
66931
66931
|
}
|
|
66932
66932
|
}
|
|
66933
66933
|
|
|
66934
|
-
// src/cli/commands/login.ts
|
|
66935
|
-
import fs7 from "fs/promises";
|
|
66936
|
-
|
|
66937
66934
|
// src/search/browser.ts
|
|
66938
66935
|
import { existsSync as existsSync3 } from "fs";
|
|
66939
66936
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -76557,8 +76554,68 @@ async function runLoginSession(options) {
|
|
|
76557
76554
|
});
|
|
76558
76555
|
}
|
|
76559
76556
|
|
|
76560
|
-
// src/cli/
|
|
76557
|
+
// src/cli/profile.ts
|
|
76558
|
+
import fs7 from "fs/promises";
|
|
76561
76559
|
import path12 from "path";
|
|
76560
|
+
var PROFILE_READY_MARKER = ".findweb-profile-ready";
|
|
76561
|
+
async function ensureProfileDir(dirPath) {
|
|
76562
|
+
await fs7.mkdir(dirPath, { recursive: true });
|
|
76563
|
+
}
|
|
76564
|
+
function readyMarkerPath(userDataDir) {
|
|
76565
|
+
return path12.join(userDataDir, PROFILE_READY_MARKER);
|
|
76566
|
+
}
|
|
76567
|
+
async function hasPreparedProfile(userDataDir) {
|
|
76568
|
+
try {
|
|
76569
|
+
await fs7.access(readyMarkerPath(userDataDir));
|
|
76570
|
+
return true;
|
|
76571
|
+
} catch {
|
|
76572
|
+
return false;
|
|
76573
|
+
}
|
|
76574
|
+
}
|
|
76575
|
+
async function markProfilePrepared(userDataDir) {
|
|
76576
|
+
await ensureProfileDir(userDataDir);
|
|
76577
|
+
await fs7.writeFile(readyMarkerPath(userDataDir), `${new Date().toISOString()}
|
|
76578
|
+
`, "utf8");
|
|
76579
|
+
}
|
|
76580
|
+
|
|
76581
|
+
// src/cli/flows/login.ts
|
|
76582
|
+
function printLoginInstructions(userDataDir) {
|
|
76583
|
+
console.log(`Login browser launched with profile: ${userDataDir}`);
|
|
76584
|
+
console.log("Sign in to Google to prepare this profile for future searches.");
|
|
76585
|
+
console.log("Close the browser window when you are done.");
|
|
76586
|
+
}
|
|
76587
|
+
async function runInteractiveLoginFlow(options) {
|
|
76588
|
+
await ensureProfileDir(options.userDataDir);
|
|
76589
|
+
const activeBrowser = await launchSearchBrowser({
|
|
76590
|
+
headed: true,
|
|
76591
|
+
lang: options.lang,
|
|
76592
|
+
userDataDir: options.userDataDir
|
|
76593
|
+
});
|
|
76594
|
+
printLoginInstructions(options.userDataDir);
|
|
76595
|
+
try {
|
|
76596
|
+
await runLoginSession({
|
|
76597
|
+
browser: activeBrowser.browser,
|
|
76598
|
+
gl: options.gl,
|
|
76599
|
+
lang: options.lang
|
|
76600
|
+
});
|
|
76601
|
+
} finally {
|
|
76602
|
+
await closeSearchBrowser(activeBrowser);
|
|
76603
|
+
}
|
|
76604
|
+
await markProfilePrepared(options.userDataDir);
|
|
76605
|
+
}
|
|
76606
|
+
async function ensureInteractiveLogin(options) {
|
|
76607
|
+
await ensureProfileDir(options.userDataDir);
|
|
76608
|
+
if (await hasPreparedProfile(options.userDataDir)) {
|
|
76609
|
+
return false;
|
|
76610
|
+
}
|
|
76611
|
+
console.log("No prepared Google profile was found for this user-data-dir.");
|
|
76612
|
+
console.log("A login step is required before the first search.");
|
|
76613
|
+
await runInteractiveLoginFlow(options);
|
|
76614
|
+
return true;
|
|
76615
|
+
}
|
|
76616
|
+
|
|
76617
|
+
// src/cli/schema.ts
|
|
76618
|
+
import path13 from "path";
|
|
76562
76619
|
|
|
76563
76620
|
// node_modules/zod/v4/classic/external.js
|
|
76564
76621
|
var exports_external = {};
|
|
@@ -77325,10 +77382,10 @@ function mergeDefs(...defs) {
|
|
|
77325
77382
|
function cloneDef(schema) {
|
|
77326
77383
|
return mergeDefs(schema._zod.def);
|
|
77327
77384
|
}
|
|
77328
|
-
function getElementAtPath(obj,
|
|
77329
|
-
if (!
|
|
77385
|
+
function getElementAtPath(obj, path13) {
|
|
77386
|
+
if (!path13)
|
|
77330
77387
|
return obj;
|
|
77331
|
-
return
|
|
77388
|
+
return path13.reduce((acc, key) => acc?.[key], obj);
|
|
77332
77389
|
}
|
|
77333
77390
|
function promiseAllObject(promisesObj) {
|
|
77334
77391
|
const keys = Object.keys(promisesObj);
|
|
@@ -77709,11 +77766,11 @@ function aborted(x, startIndex = 0) {
|
|
|
77709
77766
|
}
|
|
77710
77767
|
return false;
|
|
77711
77768
|
}
|
|
77712
|
-
function prefixIssues(
|
|
77769
|
+
function prefixIssues(path13, issues) {
|
|
77713
77770
|
return issues.map((iss) => {
|
|
77714
77771
|
var _a4;
|
|
77715
77772
|
(_a4 = iss).path ?? (_a4.path = []);
|
|
77716
|
-
iss.path.unshift(
|
|
77773
|
+
iss.path.unshift(path13);
|
|
77717
77774
|
return iss;
|
|
77718
77775
|
});
|
|
77719
77776
|
}
|
|
@@ -77896,7 +77953,7 @@ function formatError(error, mapper = (issue2) => issue2.message) {
|
|
|
77896
77953
|
}
|
|
77897
77954
|
function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
77898
77955
|
const result = { errors: [] };
|
|
77899
|
-
const processError = (error2,
|
|
77956
|
+
const processError = (error2, path13 = []) => {
|
|
77900
77957
|
var _a4, _b2;
|
|
77901
77958
|
for (const issue2 of error2.issues) {
|
|
77902
77959
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -77906,7 +77963,7 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
77906
77963
|
} else if (issue2.code === "invalid_element") {
|
|
77907
77964
|
processError({ issues: issue2.issues }, issue2.path);
|
|
77908
77965
|
} else {
|
|
77909
|
-
const fullpath = [...
|
|
77966
|
+
const fullpath = [...path13, ...issue2.path];
|
|
77910
77967
|
if (fullpath.length === 0) {
|
|
77911
77968
|
result.errors.push(mapper(issue2));
|
|
77912
77969
|
continue;
|
|
@@ -77938,8 +77995,8 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
77938
77995
|
}
|
|
77939
77996
|
function toDotPath(_path) {
|
|
77940
77997
|
const segs = [];
|
|
77941
|
-
const
|
|
77942
|
-
for (const seg of
|
|
77998
|
+
const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
77999
|
+
for (const seg of path13) {
|
|
77943
78000
|
if (typeof seg === "number")
|
|
77944
78001
|
segs.push(`[${seg}]`);
|
|
77945
78002
|
else if (typeof seg === "symbol")
|
|
@@ -89686,13 +89743,13 @@ function resolveRef(ref, ctx) {
|
|
|
89686
89743
|
if (!ref.startsWith("#")) {
|
|
89687
89744
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
89688
89745
|
}
|
|
89689
|
-
const
|
|
89690
|
-
if (
|
|
89746
|
+
const path13 = ref.slice(1).split("/").filter(Boolean);
|
|
89747
|
+
if (path13.length === 0) {
|
|
89691
89748
|
return ctx.rootSchema;
|
|
89692
89749
|
}
|
|
89693
89750
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
89694
|
-
if (
|
|
89695
|
-
const key =
|
|
89751
|
+
if (path13[0] === defsKey) {
|
|
89752
|
+
const key = path13[1];
|
|
89696
89753
|
if (!key || !ctx.defs[key]) {
|
|
89697
89754
|
throw new Error(`Reference not found: ${ref}`);
|
|
89698
89755
|
}
|
|
@@ -90101,7 +90158,7 @@ var sharedSchema = exports_external.object({
|
|
|
90101
90158
|
headed: exports_external.boolean().default(false),
|
|
90102
90159
|
json: exports_external.boolean().default(false),
|
|
90103
90160
|
lang: defaultedString("lang", "en"),
|
|
90104
|
-
userDataDir: defaultedString("userDataDir", defaultUserDataDir()).transform((value) =>
|
|
90161
|
+
userDataDir: defaultedString("userDataDir", defaultUserDataDir()).transform((value) => path13.resolve(value))
|
|
90105
90162
|
});
|
|
90106
90163
|
var searchSchema = sharedSchema.extend({
|
|
90107
90164
|
num: positiveInteger("num").default(3),
|
|
@@ -90145,25 +90202,6 @@ function normalizeLoginOptions(input2) {
|
|
|
90145
90202
|
}
|
|
90146
90203
|
|
|
90147
90204
|
// src/cli/commands/login.ts
|
|
90148
|
-
async function ensureProfileDir(dirPath) {
|
|
90149
|
-
await fs7.mkdir(dirPath, { recursive: true });
|
|
90150
|
-
}
|
|
90151
|
-
async function runLoginFlow(gl, lang, userDataDir) {
|
|
90152
|
-
await ensureProfileDir(userDataDir);
|
|
90153
|
-
const activeBrowser = await launchSearchBrowser({
|
|
90154
|
-
headed: true,
|
|
90155
|
-
lang,
|
|
90156
|
-
userDataDir
|
|
90157
|
-
});
|
|
90158
|
-
console.log(`Login browser launched with profile: ${userDataDir}`);
|
|
90159
|
-
console.log("Sign in to Google if you want to reuse a logged-in search profile.");
|
|
90160
|
-
console.log("Close the browser window when you are done.");
|
|
90161
|
-
try {
|
|
90162
|
-
await runLoginSession({ browser: activeBrowser.browser, gl, lang });
|
|
90163
|
-
} finally {
|
|
90164
|
-
await closeSearchBrowser(activeBrowser);
|
|
90165
|
-
}
|
|
90166
|
-
}
|
|
90167
90205
|
function createLoginArgs() {
|
|
90168
90206
|
return {
|
|
90169
90207
|
gl: {
|
|
@@ -90190,7 +90228,7 @@ async function runLogin(args) {
|
|
|
90190
90228
|
lang: typeof args.lang === "string" ? args.lang : undefined,
|
|
90191
90229
|
userDataDir: typeof args.userDataDir === "string" ? args.userDataDir : undefined
|
|
90192
90230
|
});
|
|
90193
|
-
await
|
|
90231
|
+
await runInteractiveLoginFlow(options);
|
|
90194
90232
|
}
|
|
90195
90233
|
function createLoginCommand(commandName = "findweb login") {
|
|
90196
90234
|
return defineCommand({
|
|
@@ -90207,13 +90245,12 @@ function createLoginCommand(commandName = "findweb login") {
|
|
|
90207
90245
|
}
|
|
90208
90246
|
|
|
90209
90247
|
// src/cli/commands/search.ts
|
|
90210
|
-
import fs9 from "fs/promises";
|
|
90211
90248
|
import process4 from "process";
|
|
90212
90249
|
|
|
90213
90250
|
// src/search/blocker.ts
|
|
90214
90251
|
import fs8 from "fs/promises";
|
|
90215
90252
|
import os9 from "os";
|
|
90216
|
-
import
|
|
90253
|
+
import path14 from "path";
|
|
90217
90254
|
|
|
90218
90255
|
// node_modules/tldts-core/dist/es6/src/domain.js
|
|
90219
90256
|
function shareSameDomainSuffix(hostname3, vhost) {
|
|
@@ -99477,8 +99514,8 @@ class FilterEngine extends EventEmitter5 {
|
|
|
99477
99514
|
if (caching === undefined) {
|
|
99478
99515
|
return init();
|
|
99479
99516
|
}
|
|
99480
|
-
const { path:
|
|
99481
|
-
return read(
|
|
99517
|
+
const { path: path14, read, write } = caching;
|
|
99518
|
+
return read(path14).then((buffer) => this.deserialize(buffer)).catch(() => init().then((engine) => write(path14, engine.serialize()).then(() => engine)));
|
|
99482
99519
|
}
|
|
99483
99520
|
static empty(config3 = {}) {
|
|
99484
99521
|
return new this({ config: config3 });
|
|
@@ -101270,13 +101307,13 @@ class PuppeteerBlocker extends FilterEngine {
|
|
|
101270
101307
|
// src/search/blocker.ts
|
|
101271
101308
|
var blockerPromise;
|
|
101272
101309
|
function defaultCacheDir() {
|
|
101273
|
-
return process.env.GOOGLE_SEARCH_CACHE_DIR ??
|
|
101310
|
+
return process.env.GOOGLE_SEARCH_CACHE_DIR ?? path14.join(os9.homedir(), ".cache", "google-search");
|
|
101274
101311
|
}
|
|
101275
101312
|
async function readCache(filePath) {
|
|
101276
101313
|
return fs8.readFile(filePath);
|
|
101277
101314
|
}
|
|
101278
101315
|
async function writeCache(filePath, buffer) {
|
|
101279
|
-
await fs8.mkdir(
|
|
101316
|
+
await fs8.mkdir(path14.dirname(filePath), { recursive: true });
|
|
101280
101317
|
await fs8.writeFile(filePath, buffer);
|
|
101281
101318
|
}
|
|
101282
101319
|
async function loadBlocker() {
|
|
@@ -101285,7 +101322,7 @@ async function loadBlocker() {
|
|
|
101285
101322
|
const cacheDir = defaultCacheDir();
|
|
101286
101323
|
await fs8.mkdir(cacheDir, { recursive: true });
|
|
101287
101324
|
return PuppeteerBlocker.fromPrebuiltAdsAndTracking(globalThis.fetch.bind(globalThis), {
|
|
101288
|
-
path:
|
|
101325
|
+
path: path14.join(cacheDir, "ghostery-engine.bin"),
|
|
101289
101326
|
read: readCache,
|
|
101290
101327
|
write: writeCache
|
|
101291
101328
|
});
|
|
@@ -101339,9 +101376,6 @@ function exitCodeForOutcomes(items) {
|
|
|
101339
101376
|
}
|
|
101340
101377
|
|
|
101341
101378
|
// src/cli/commands/search.ts
|
|
101342
|
-
async function ensureProfileDir2(dirPath) {
|
|
101343
|
-
await fs9.mkdir(dirPath, { recursive: true });
|
|
101344
|
-
}
|
|
101345
101379
|
function printResults(json2, outcomes) {
|
|
101346
101380
|
if (json2) {
|
|
101347
101381
|
printJsonResults(outcomes);
|
|
@@ -101402,7 +101436,16 @@ async function runSearch(args) {
|
|
|
101402
101436
|
parallel: typeof args.parallel === "string" || typeof args.parallel === "number" ? args.parallel : undefined,
|
|
101403
101437
|
userDataDir: typeof args.userDataDir === "string" ? args.userDataDir : undefined
|
|
101404
101438
|
});
|
|
101405
|
-
await
|
|
101439
|
+
await ensureProfileDir(options.userDataDir);
|
|
101440
|
+
const loginWasRequired = await ensureInteractiveLogin({
|
|
101441
|
+
gl: options.gl,
|
|
101442
|
+
lang: options.lang,
|
|
101443
|
+
userDataDir: options.userDataDir
|
|
101444
|
+
});
|
|
101445
|
+
if (loginWasRequired) {
|
|
101446
|
+
console.log(`Login completed. Continuing with search...
|
|
101447
|
+
`);
|
|
101448
|
+
}
|
|
101406
101449
|
const blocker = await loadBlocker();
|
|
101407
101450
|
const activeBrowser = await launchSearchBrowser({
|
|
101408
101451
|
headed: options.headed,
|